commit bb0d3f53353654e4e91dd7c54103aa16a1b6a695 Author: Olivier Date: Mon Dec 8 11:59:47 2025 +0100 Initial commit diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100755 index 0000000..d6528d5 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.16.0) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +idf_build_set_property(MINIMAL_BUILD ON) +project(BLE_Button_Matrix) diff --git a/include/ble_stream_deck.h b/include/ble_stream_deck.h new file mode 100755 index 0000000..8878dbd --- /dev/null +++ b/include/ble_stream_deck.h @@ -0,0 +1,7 @@ +#define TAG "NimBLE HID Keyboard" + +int init_nvs(void); +int init_gatt_server(void); +void host_task(void *param); +void on_sync(void); +void on_reset(int reason); \ No newline at end of file diff --git a/include/gap.h b/include/gap.h new file mode 100755 index 0000000..73b73a3 --- /dev/null +++ b/include/gap.h @@ -0,0 +1,3 @@ +struct gap_advertise_fields { + +} gap_advertise_fields; \ No newline at end of file diff --git a/include/gatt_server.h b/include/gatt_server.h new file mode 100755 index 0000000..95feac9 --- /dev/null +++ b/include/gatt_server.h @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + +#include "hid_keyboard.h" +#include "utils.h" + +#define GATT_DEVICE_INFO_UUID 0x180A +#define GATT_MANUFACTURER_NAME_UUID 0x2A29 +#define GAP_DEVICE_NAME "Stream Deck Lookalike" +#define GAP_ADV_APPAREANCE 0x03C1 +#define ADV_DEVICE_NAME "Stream Deck" +#define MANUFACTURER_NAME "Sobralia's lab" + +int init_gatt_server(void); +void gatt_advertise(void); +int gatt_device_info_access_callback(uint16_t connection, uint16_t attribute, struct ble_gatt_access_ctxt *context, void *arg); +int gatt_battery_level_access_callback(uint16_t connection, uint16_t attribute, struct ble_gatt_access_ctxt *context, void *arg); diff --git a/include/hid_descriptor.h b/include/hid_descriptor.h new file mode 100755 index 0000000..bdcd9f6 --- /dev/null +++ b/include/hid_descriptor.h @@ -0,0 +1,41 @@ +#ifndef HID_DESCRIPTOR_H +#define HID_DESCRIPTOR_H + +/* Main Items Datatype flag */ +#define HID_DATA 0x00 +#define HID_CONSTANT 0x01 +#define HID_ARRAY 0x00 +#define HID_VARIABLE 0x02 +#define HID_ABSOLUTE 0x00 +#define HID_RELATIVE 0x04 +#define HID_NO_WRAP 0x00 +#define HID_WRAP 0x08 +#define HID_LINEAR 0x00 +#define HID_NON_LINEAR 0x10 +#define HID_PREFERRED_STATE 0x00 +#define HID_NO_PREFERRED_STATE 0x20 +#define HID_NO_NULL_POSITION 0x00 +#define HID_NULL_STATE 0x40 +#define HID_BIT_FIELD 0x00 +#define HID_BUFFERED_BYTE 0x100 + +#define HID_USAGE_PAGE 0x05 +#define HID_USAGE 0x09 +#define HID_COLLECTION_APPLICATION 0xA1 +#define HID_USAGE_MINIMUM 0x19 +#define HID_USAGE_MAXIMUM 0x29 +#define HID_LOGICAL_MINIMUM 0x15 +#define HID_LOGICAL_MAXIMUM 0x25 +#define HID_REPORT_SIZE 0x75 +#define HID_REPORT_COUNT 0x95 +#define HID_INPUT 0x81 +#define HID_OUTPUT 0x91 +#define HID_FEATURE 0xB1 +#define HID_END_COLLECTION 0xC0 + +#define HID_USAGE_PAGE_GENERIC_DESKTOP 0x01 +#define HID_USAGE_KEYBOARD 0x06 +#define HID_USAGE_KEYPAD 0x07 +#define HID_USAGE_PAGE_LED 0x08 + +#endif \ No newline at end of file diff --git a/include/hid_keyboard.h b/include/hid_keyboard.h new file mode 100755 index 0000000..983fd21 --- /dev/null +++ b/include/hid_keyboard.h @@ -0,0 +1,96 @@ +#ifndef HID_KEYBOARD_H +#define HID_KEYBOARD_H + +#include +#include +#include +#include +#include "hid_descriptor.h" +#include "hid_keyboard_callbacks.h" +#include "utils.h" + +/* UUID for services, characteristics and descriptors */ +#define HID_SERVICE_UUID 0x1812 +#define HID_REPORT_REFERENCE_UUID 0x2908 +#define HID_DESCR_EXTERNAL_REPORT_REFERENCE 0x2907 +#define HID_BOOT_KEYBOARD_INPUT_REPORT_UUID 0x2A22 +#define HID_BOOT_KEYBOARD_OUTPUT_REPORT_UUID 0x2A32 +#define HID_INFORMATION_UUID 0x2A4A +#define HID_REPORT_MAP_UUID 0x2A4B +#define HID_CONTROL_POINT_UUID 0x2A4C +#define HID_REPORT_UUID 0x2A4D +#define HID_PROTOCOL_MODE_UUID 0x2A4E + +#define HID_BOOT_PROTOCOL 0x00 +#define HID_REPORT_PROTOCOL 0x01 + +/* handles index for characteristics */ +#define PROTOCOL_MODE_HANDLE_INDEX 0 +#define REPORT_MAP_HANDLE_INDEX 1 +#define REPORT_HANDLE_INDEX 2 +#define BOOT_KEYBOARD_INPUT_HANDLE_INDEX 3 +#define BOOT_KEYBOARD_OUTPUT_REPORT 4 +#define HID_INFORMATION_HANDLE_INDEX 5 +#define HID_CONTROL_POINT_HANDLE_INDEX 6 +#define HANDLE_INDEX_LENGTH 7 + +#define REPORT_DESCRIPTOR_INPUT 0x01 +#define REPORT_DESCRIPTOR_OUTPUT 0x02 +#define REPORT_DESCRIPTOR_FEATURE 0x03 + +int init_keyboard_service(void); +void keyboard_subscribe_event(struct ble_gap_event *event, void *arg); +void set_connection_handle(uint16_t connection_handle); +void send_key(TimerHandle_t ev); + + +typedef struct s_boot_input_report +{ + uint8_t modifiers_keycode_mask; + uint8_t reserved_byte; + uint8_t key_list[6]; +} t_boot_input_report; + +typedef struct s_boot_output_report +{ + uint8_t num_lock : 1; + uint8_t caps_lock : 1; + uint8_t scroll_lock : 1; + uint8_t compose : 1; + uint8_t kana : 1; + uint8_t constant : 3; +} t_boot_output_report; + +typedef struct s_input_report +{ + uint8_t modifiers_keycode_mask; + uint8_t reserved_byte; + uint8_t key_list[6]; +} t_input_report; + +typedef struct s_report_descriptor +{ + uint8_t id; + uint8_t type; +} t_report_descriptor; + +typedef struct s_hid_info +{ + uint16_t bcdHID; + uint8_t bCountryCode; + uint8_t flags; +} t_hid_info; + +typedef struct s_keyboard +{ + t_boot_input_report boot_input_report; + t_boot_output_report boot_output_report; + uint8_t *report_map; + t_report_descriptor report_descriptor; + t_input_report input_report; + t_hid_info hid_info; + uint8_t *control_point; +} t_keyboard; + +extern t_keyboard keyboard; +#endif \ No newline at end of file diff --git a/include/hid_keyboard_callbacks.h b/include/hid_keyboard_callbacks.h new file mode 100755 index 0000000..4a6172b --- /dev/null +++ b/include/hid_keyboard_callbacks.h @@ -0,0 +1,15 @@ +#ifndef HID_KEYBOARD_CALLBACKS_H +#define HID_KEYBOARD_CALLBACKS_H + +#include "hid_keyboard.h" + +int hid_keyboard_protocol_mode_callback(uint16_t connection, uint16_t attribute, struct ble_gatt_access_ctxt *context, void *arg); +int hid_keyboard_report_map_callback(uint16_t connection, uint16_t attribute, struct ble_gatt_access_ctxt *context, void *arg); +int hid_keyboard_report_map_descr_callback(uint16_t connection, uint16_t attribute, struct ble_gatt_access_ctxt *context, void *arg); +int hid_keyboard_input_report_callback(uint16_t connection, uint16_t attribute, struct ble_gatt_access_ctxt *context, void *arg); +int hid_keyboard_boot_input_report_callback(uint16_t connection, uint16_t attribute, struct ble_gatt_access_ctxt *context, void *arg); +int hid_keyboard_boot_output_report_callback(uint16_t connection, uint16_t attribute, struct ble_gatt_access_ctxt *context, void *arg); +int hid_keyboard_information_callback(uint16_t connection, uint16_t attribute, struct ble_gatt_access_ctxt *context, void *arg); +int hid_keyboard_control_point_callback(uint16_t connection, uint16_t attribute, struct ble_gatt_access_ctxt *context, void *arg); + +#endif \ No newline at end of file diff --git a/include/utils.h b/include/utils.h new file mode 100755 index 0000000..6e0d10b --- /dev/null +++ b/include/utils.h @@ -0,0 +1,11 @@ +#ifndef UTILS_H +#define UTILS_H + +#include +#include + +void print_uint8(void *pointer); +void printf_uint16_t_array(uint16_t *array,uint16_t length); +void printf_keyboard_report_map(const uint8_t *report_map); + +#endif \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100755 index 0000000..8f895f0 --- /dev/null +++ b/readme.md @@ -0,0 +1,17 @@ +# Guide BLE with NimBLE on esp32 with esp-idf in C + +## BLE principles + +### General +### GAP +### GATT + +## References documents +### ESP-IDF +[Official documentation]() +[Github]() +### NimBLE +[Official documentation]() +[Github]() +### Bluetooth +### USB diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100755 index 0000000..ab3ad38 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,6 @@ +# This file was automatically generated for projects +# without default 'CMakeLists.txt' file. + +FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*) + +idf_component_register(SRCS ${app_sources}) diff --git a/src/gatt_server.c b/src/gatt_server.c new file mode 100755 index 0000000..3ebdcee --- /dev/null +++ b/src/gatt_server.c @@ -0,0 +1,191 @@ +#include "gatt_server.h" + +/* DEV test */ +static TimerHandle_t send_key_timer; +uint16_t conn_handle; + +const struct ble_gatt_svc_def gatt_service_list[] = +{ + { + /* Device Information */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = BLE_UUID16_DECLARE(GATT_DEVICE_INFO_UUID), + .characteristics = (struct ble_gatt_chr_def[]) + { + { + /* Manufacturer */ + .uuid = BLE_UUID16_DECLARE(GATT_MANUFACTURER_NAME_UUID), + .access_cb = gatt_device_info_access_callback, + .flags = BLE_GATT_CHR_F_READ, + }, + { + 0, /* No more characteristics */ + }, + } + }, + { + /* Battery service */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = BLE_UUID16_DECLARE(0x180F), + .characteristics = (struct ble_gatt_chr_def[]) + { + { + /* Battery level */ + .uuid = BLE_UUID16_DECLARE(0x2A19), + .access_cb = gatt_battery_level_access_callback, + .flags = BLE_GATT_CHR_F_READ, + }, + { + 0, + }, + } + }, + { + 0, + }, +}; + +int init_gatt_server(void) +{ + int result = 0; + + ble_svc_gap_init(); + ble_svc_gatt_init(); + + result = ble_gatts_count_cfg(gatt_service_list); + if (result == 0) + { + result = ble_gatts_add_svcs(gatt_service_list); + } + + /* Press a key every second - test purpose */ + send_key_timer = xTimerCreate("send_key_timer", pdMS_TO_TICKS(1000), pdTRUE, (void *)0, send_key); + xTimerStart(send_key_timer, 0); + return result; +} + +static int blehr_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_LINK_ESTAB: + /* A new connection was established or a connection attempt failed */ + MODLOG_DFLT(INFO, "connection %s; status=%d\n", + event->connect.status == 0 ? "established" : "failed", + event->connect.status); + + if (event->connect.status != 0) { + /* Connection failed; resume advertising */ + gatt_advertise(); + } + conn_handle = event->connect.conn_handle; + break; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "disconnect; reason=%d\n", event->disconnect.reason); + + /* Connection terminated; resume advertising */ + gatt_advertise(); + break; + + case BLE_GAP_EVENT_ADV_COMPLETE: + MODLOG_DFLT(INFO, "adv complete\n"); + gatt_advertise(); + break; + + case BLE_GAP_EVENT_SUBSCRIBE: + keyboard_subscribe_event(event, arg); + break; + + case BLE_GAP_EVENT_MTU: + MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d mtu=%d\n", + event->mtu.conn_handle, + event->mtu.value); + break; + + } + + return 0; +} + +void gatt_advertise(void) +{ + struct ble_gap_adv_params adv_params; + struct ble_hs_adv_fields fields; + int rc; + + /* + * Set the advertisement data included in our advertisements: + * o Flags (indicates advertisement type and other general info) + * o Advertising tx power + * o Device name + */ + memset(&fields, 0, sizeof(fields)); + + /* + * Advertise two flags: + * o Discoverability in forthcoming advertisement (general) + * o BLE-only (BR/EDR unsupported) + */ + fields.flags = BLE_HS_ADV_F_DISC_GEN | + BLE_HS_ADV_F_BREDR_UNSUP; + + /* + * Indicate that the TX power level field should be included; have the + * stack fill this value automatically. This is done by assigning the + * special value BLE_HS_ADV_TX_PWR_LVL_AUTO. + */ + fields.tx_pwr_lvl_is_present = 1; + fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; + + fields.name = (uint8_t *)ADV_DEVICE_NAME; + fields.name_len = strlen(ADV_DEVICE_NAME); + fields.name_is_complete = 1; + + fields.appearance = GAP_ADV_APPAREANCE; + fields.appearance_is_present = 1; + + rc = ble_gap_adv_set_fields(&fields); + //ble_gap_adv_set_data((uint8_t *)&fields, sizeof(fields)); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error setting advertisement data; rc=%d\n", rc); + return; + } + + /* Begin advertising */ + memset(&adv_params, 0, sizeof(adv_params)); + adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; + adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; + rc = ble_gap_adv_start(BLE_OWN_ADDR_PUBLIC, NULL, BLE_HS_FOREVER, + &adv_params, blehr_gap_event, NULL); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc); + return; + } +} + +int gatt_device_info_access_callback(uint16_t connection, uint16_t attribute, struct ble_gatt_access_ctxt *context, void *arg) +{ + uint16_t uuid = ble_uuid_u16(context->chr->uuid); + int result; + + result = 0; + + if (uuid == GATT_MANUFACTURER_NAME_UUID) + { + result = os_mbuf_append(context->om, MANUFACTURER_NAME, strlen(MANUFACTURER_NAME)); + } + return result; +} + + +int gatt_battery_level_access_callback(uint16_t connection, uint16_t attribute, struct ble_gatt_access_ctxt *context, void *arg) +{ + int result; + uint8_t battery_level = 90; + + result = 0; + + result = os_mbuf_append(context->om, (const void *)&battery_level, sizeof battery_level); + + return result; +} \ No newline at end of file diff --git a/src/hid_keyboard_callbacks.c b/src/hid_keyboard_callbacks.c new file mode 100755 index 0000000..2e965c8 --- /dev/null +++ b/src/hid_keyboard_callbacks.c @@ -0,0 +1,148 @@ +#include "hid_keyboard_callbacks.h" + +int hid_keyboard_protocol_mode_callback(uint16_t connection, uint16_t attribute, struct ble_gatt_access_ctxt *context, void *arg) +{ + int result = 0; + uint8_t protocol = HID_REPORT_PROTOCOL; + + if (context->op == BLE_GATT_ACCESS_OP_READ_CHR) + { + MODLOG_DFLT(INFO, "hid_keyboard_protocol_mode_callback READ CHR"); + result = os_mbuf_append(context->om, &protocol, sizeof protocol); + } + + if (context->op == BLE_GATT_ACCESS_OP_WRITE_CHR) + { + MODLOG_DFLT(INFO, "hid_keyboard_protocol_mode_callback WRITE CHR"); + // TODO + } + + return result; +} + +int hid_keyboard_report_map_callback(uint16_t connection, uint16_t attribute, struct ble_gatt_access_ctxt *context, void *arg) +{ + int result = 0; + + MODLOG_DFLT(INFO, "hid_keyboard_report_map_callback"); + if (context->op == BLE_GATT_ACCESS_OP_READ_CHR) + { + MODLOG_DFLT(INFO, "hid_keyboard_report_map_callback READ CHR"); + result = os_mbuf_append(context->om, &keyboard.report_map, sizeof keyboard.report_map); + } + + if (context->op == BLE_GATT_ACCESS_OP_READ_DSC) + { + MODLOG_DFLT(INFO, "hid_keyboard_report_map_callback READ DSC"); + //result = os_mbuf_append(context->om, &keyboard_hid_report_description, sizeof keyboard_hid_report_description); + } + + return result; +} + +int hid_keyboard_report_map_descr_callback(uint16_t connection, uint16_t attribute, struct ble_gatt_access_ctxt *context, void *arg) +{ + int result = 0; + uint16_t external_reference; + + external_reference = 0x2A19; /* Battery level characteristc UUID */ + MODLOG_DFLT(INFO, "hid_keyboard_report_map_descr_callback"); + result = os_mbuf_append(context->om, &external_reference, sizeof external_reference); + + return result; +} + +int hid_keyboard_input_report_callback(uint16_t connection, uint16_t attribute, struct ble_gatt_access_ctxt *context, void *arg) +{ + int result; + + result = 0; + keyboard.report_descriptor.id = 0; + keyboard.report_descriptor.type = REPORT_DESCRIPTOR_INPUT; + + MODLOG_DFLT(INFO, "hid_keyboard_keyboard_input_report_callback"); + + /* Report */ + if (context->op == BLE_GATT_ACCESS_OP_READ_CHR) + { + MODLOG_DFLT(INFO, "hid_keyboard_keyboard_input_report_callback READ CHR"); + result = os_mbuf_append(context->om, &keyboard.input_report, sizeof keyboard.input_report); + } + if (context->op == BLE_GATT_ACCESS_OP_WRITE_CHR) + { + MODLOG_DFLT(INFO, "hid_keyboard_keyboard_input_report_callback WRITE CHR"); + } + + /* Descriptor */ + if (context->op == BLE_GATT_ACCESS_OP_READ_DSC) + { + MODLOG_DFLT(INFO, "hid_keyboard_keyboard_input_report_callback READ DESC"); + result = os_mbuf_append(context->om, &keyboard.report_descriptor, sizeof keyboard.report_descriptor); + } + if (context->op == BLE_GATT_ACCESS_OP_WRITE_DSC) + { + MODLOG_DFLT(INFO, "hid_keyboard_keyboard_input_report_callback WRITE DESC"); + } + + return result; +} + +int hid_keyboard_boot_input_report_callback(uint16_t connection, uint16_t attribute, struct ble_gatt_access_ctxt *context, void *arg) +{ + int result = 0; + + MODLOG_DFLT(INFO, "hid_keyboard_boot_keyboard_input_report_callback"); + if (context->op == BLE_GATT_ACCESS_OP_READ_CHR) + { + MODLOG_DFLT(INFO, "hid_keyboard_boot_keyboard_input_report_callback READ CHR"); + result = os_mbuf_append(context->om, &keyboard.boot_input_report, sizeof keyboard.boot_input_report); + } + + return result; +} + +int hid_keyboard_boot_output_report_callback(uint16_t connection, uint16_t attribute, struct ble_gatt_access_ctxt *context, void *arg) +{ + int result = 0; + + MODLOG_DFLT(INFO, "hid_keyboard_boot_keyboard_output_report_callback"); + if (context->op == BLE_GATT_ACCESS_OP_READ_CHR) + { + MODLOG_DFLT(INFO, "hid_keyboard_boot_keyboard_output_report_callback READ CHR"); + } + if (context->op == BLE_GATT_ACCESS_OP_WRITE_CHR) + { + MODLOG_DFLT(INFO, "hid_keyboard_boot_keyboard_output_report_callback WRITE CHR"); + } + + return result; +} + +int hid_keyboard_information_callback(uint16_t connection, uint16_t attribute, struct ble_gatt_access_ctxt *context, void *arg) +{ + int result = 0; + + MODLOG_DFLT(INFO, "hid_keyboard_information_callback"); + if (context->op == BLE_GATT_ACCESS_OP_READ_CHR) + { + MODLOG_DFLT(INFO, "hid_keyboard_information_callback READ CHR"); + os_mbuf_append(context->om, &keyboard.hid_info, sizeof keyboard.hid_info); + } + + return result; +} + +int hid_keyboard_control_point_callback(uint16_t connection, uint16_t attribute, struct ble_gatt_access_ctxt *context, void *arg) +{ + int result = 0; + + MODLOG_DFLT(INFO, "hid_keyboard_control_point_callback"); + if (context->op == BLE_GATT_ACCESS_OP_WRITE_CHR) + { + MODLOG_DFLT(INFO, "hid_keyboard_control_point_callback WRITE CHR"); + keyboard.control_point = (uint8_t *)context->om; + context->om = NULL; + } + + return result; +} \ No newline at end of file diff --git a/src/keyboard.c b/src/keyboard.c new file mode 100755 index 0000000..c198efe --- /dev/null +++ b/src/keyboard.c @@ -0,0 +1,185 @@ +#include "hid_keyboard.h" + +static uint16_t characteristics_handle[HANDLE_INDEX_LENGTH]; +t_keyboard keyboard; + +/* Dev */ +static uint16_t connection; + +static const uint8_t report_map[] = +{ + HID_USAGE_PAGE, HID_USAGE_PAGE_GENERIC_DESKTOP, + HID_USAGE, HID_USAGE_KEYBOARD, + HID_COLLECTION_APPLICATION, 1, + HID_USAGE_PAGE, HID_USAGE_KEYPAD, + HID_USAGE_MINIMUM, 224, + HID_USAGE_MAXIMUM, 231, + HID_LOGICAL_MINIMUM,0, + HID_LOGICAL_MAXIMUM,1, + HID_REPORT_SIZE, 1, + HID_REPORT_COUNT, 8, + HID_INPUT, HID_DATA | HID_VARIABLE | HID_ABSOLUTE, /* Modifier */ + + HID_REPORT_SIZE, 8, + HID_REPORT_COUNT, 1, + HID_INPUT, HID_CONSTANT, /* Reserved byte */ + + HID_REPORT_SIZE, 1, + HID_REPORT_COUNT, 5, + HID_USAGE_PAGE, HID_USAGE_PAGE_LED, + HID_USAGE_MINIMUM, 1, + HID_USAGE_MAXIMUM, 5, + HID_OUTPUT, HID_DATA | HID_VARIABLE | HID_ABSOLUTE, /* LED */ + HID_REPORT_SIZE, 3, + HID_REPORT_COUNT, 1, + HID_OUTPUT, HID_CONSTANT, /* LED report padding */ + + HID_REPORT_SIZE, 8, + HID_REPORT_COUNT, 6, + HID_LOGICAL_MINIMUM, 0, + HID_LOGICAL_MAXIMUM, 101, + HID_USAGE_PAGE, HID_USAGE_KEYPAD, + HID_USAGE_MINIMUM, 0, + HID_USAGE_MAXIMUM, 101, + HID_INPUT, HID_DATA | HID_ARRAY, /* Array of 6 keys */ + + HID_END_COLLECTION +}; + +static const struct ble_gatt_svc_def keyboard_services[10] = +{ + { + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = BLE_UUID16_DECLARE(HID_SERVICE_UUID), + .characteristics = (struct ble_gatt_chr_def[]) + { + { + /* Protocol mode */ + .uuid = BLE_UUID16_DECLARE(HID_PROTOCOL_MODE_UUID), + .access_cb = hid_keyboard_protocol_mode_callback, + .val_handle = &characteristics_handle[PROTOCOL_MODE_HANDLE_INDEX], + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE_NO_RSP, + }, + { + /* Report map */ + .uuid = BLE_UUID16_DECLARE(HID_REPORT_MAP_UUID), + .access_cb = hid_keyboard_report_map_callback, + .val_handle = &characteristics_handle[REPORT_MAP_HANDLE_INDEX], + .flags = BLE_GATT_CHR_F_READ, + .descriptors = (struct ble_gatt_dsc_def[]) + { + { + .uuid = BLE_UUID16_DECLARE(HID_DESCR_EXTERNAL_REPORT_REFERENCE), + .access_cb = hid_keyboard_report_map_descr_callback, + .att_flags = BLE_ATT_F_READ, + }, + { + 0, + } + } + }, + { + /* Report */ + .uuid = BLE_UUID16_DECLARE(HID_REPORT_UUID), + .access_cb = hid_keyboard_input_report_callback, + .val_handle = &characteristics_handle[REPORT_HANDLE_INDEX], + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_NOTIFY, + .descriptors = (struct ble_gatt_dsc_def[]) + { + { + /* Report reference */ + .uuid = BLE_UUID16_DECLARE(HID_REPORT_REFERENCE_UUID), + .access_cb = hid_keyboard_input_report_callback, + .att_flags = BLE_ATT_F_READ, + }, + { + 0, + } + } + }, + { + /* Boot Keyboard Input Report */ + .uuid = BLE_UUID16_DECLARE(HID_BOOT_KEYBOARD_INPUT_REPORT_UUID), + .access_cb = hid_keyboard_boot_input_report_callback, + .val_handle = &characteristics_handle[BOOT_KEYBOARD_INPUT_HANDLE_INDEX], + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY, + }, + { + /* Boot Keyboard Output Report */ + .uuid = BLE_UUID16_DECLARE(HID_BOOT_KEYBOARD_OUTPUT_REPORT_UUID), + .access_cb = hid_keyboard_boot_output_report_callback, + .val_handle = &characteristics_handle[BOOT_KEYBOARD_OUTPUT_REPORT], + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_NO_RSP, + }, + { + /* HID Information */ + .uuid = BLE_UUID16_DECLARE(HID_INFORMATION_UUID), + .access_cb = hid_keyboard_information_callback, + .val_handle = &characteristics_handle[HID_INFORMATION_HANDLE_INDEX], + .flags = BLE_GATT_CHR_F_READ, + }, + { + /* HID Control Point */ + .uuid = BLE_UUID16_DECLARE(HID_CONTROL_POINT_UUID), + .access_cb = hid_keyboard_control_point_callback, + .val_handle = &characteristics_handle[HID_CONTROL_POINT_HANDLE_INDEX], + .flags = BLE_GATT_CHR_F_WRITE_NO_RSP, + }, + { + 0, + }, + } + }, + { + 0, + }, +}; + +int init_keyboard_service(void) +{ + int result; + + memset(&characteristics_handle, 0, sizeof characteristics_handle); + memset(&keyboard, 0, sizeof keyboard); + + keyboard.report_map = (uint8_t *)&report_map; + + /* + ** 0x0111 USB HID specs 1.11 + ** 0x08 FR + ** 0x00 ??? + */ + keyboard.hid_info.bcdHID = 0x0111; + keyboard.hid_info.bCountryCode = 0x08; + keyboard.hid_info.flags = 0x00; + + result = ble_gatts_count_cfg(keyboard_services); + if (result == 0) + { + result = ble_gatts_add_svcs(keyboard_services); + } + + return result; +} + +void keyboard_subscribe_event(struct ble_gap_event *event, void *arg) +{ + /*MODLOG_DFLT(INFO, "subscribe event; cur_notify=%d cur_indicate=%d \n value handle; " + "val_handle=%d\n", + event->subscribe.cur_notify, event->subscribe.cur_indicate, event->subscribe.attr_handle);*/ + connection = event->connect.conn_handle; + printf_uint16_t_array(characteristics_handle, sizeof HANDLE_INDEX_LENGTH); +} + +void set_connection_handle(uint16_t connection_handle) +{ + connection = connection_handle; +} + +void send_key(TimerHandle_t ev) +{ + keyboard.input_report.key_list[0] = keyboard.input_report.key_list[0] == 0xE3 ? 0 : 0xE3; + struct os_mbuf *om = ble_hs_mbuf_from_flat(&keyboard.input_report, sizeof keyboard.input_report); + ble_gatts_notify_custom(connection, characteristics_handle[REPORT_HANDLE_INDEX], om); + //ble_gatts_notify(connection, characteristics_handle[REPORT_HANDLE_INDEX]); +} diff --git a/src/main .c b/src/main .c new file mode 100755 index 0000000..5cf88e0 --- /dev/null +++ b/src/main .c @@ -0,0 +1,102 @@ +#include +#include +#include + +/* BLE */ +#include +#include + +/* Pas bien clair encore */ +#include // Host Event +#include // Device macros/functions + +#include "utils.h" +#include "hid_keyboard.h" +#include "ble_stream_deck.h" +#include "gatt_server.h" + +void ble_store_config_init(void); +static uint8_t blehr_addr_type; + +void app_main(void) +{ + int result; + + setbuf(stdout, NULL); + result = init_nvs(); + ESP_ERROR_CHECK(result); + + result = nimble_port_init(); + ESP_ERROR_CHECK(result); + + /* NimBLE host config */ + ble_hs_cfg.sync_cb = on_sync; + ble_hs_cfg.reset_cb = on_reset; + ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_NO_IO; + + result = ble_att_set_preferred_mtu(23); + ESP_ERROR_CHECK(result); + + result = init_gatt_server(); + ESP_ERROR_CHECK(result); + + result = init_keyboard_service(); + ESP_ERROR_CHECK(result); + + result = ble_svc_gap_device_name_set(GAP_DEVICE_NAME); + ESP_ERROR_CHECK(result); + + result = ble_svc_gap_device_appearance_set(GAP_ADV_APPAREANCE); + ESP_ERROR_CHECK(result); + + ble_store_config_init(); + + nimble_port_freertos_init(host_task); + + //ble_gatts_show_local(); +} + +int init_nvs() +{ + int result; + + /* Init NVS */ + result = nvs_flash_init(); + if (result == ESP_ERR_NVS_NO_FREE_PAGES || result == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + result = nvs_flash_init(); + } + + return result; +} + +void host_task(void *param) +{ + ESP_LOGI(TAG, "BLE Host Task Started"); + nimble_port_run(); + + nimble_port_freertos_deinit(); +} + +void on_sync(void) +{ + int rc; + + rc = ble_hs_id_infer_auto(0, &blehr_addr_type); + assert(rc == 0); + + uint8_t addr_val[7] = {0}; + rc = ble_hs_id_copy_addr(blehr_addr_type, addr_val, NULL); + + ESP_LOGI(TAG, "Device Address: "); + print_uint8(addr_val); + ESP_LOGI(TAG, "\n"); + + /* Begin advertising */ + gatt_advertise(); +} + +void on_reset(int reason) +{ + MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason); +} \ No newline at end of file diff --git a/src/utils.c b/src/utils.c new file mode 100755 index 0000000..45fd763 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,37 @@ +#include "utils.h" + +void print_uint8(void *pointer) +{ + const uint8_t *u8p; + + u8p = pointer; + MODLOG_DFLT(INFO, "%02x:%02x:%02x:%02x:%02x:%02x", + u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]); +} + +void printf_uint16_t_array(uint16_t *array, uint16_t length) +{ + int i; + + i = 0; + while (i < length) + { + printf("array[%i] = %i\n", i, array[i]); + i++; + } + +} + +void printf_keyboard_report_map(const uint8_t *report_map) +{ + int i; + + i = 0; + + while (i < sizeof report_map - 1) + { + printf("0x%X, 0x%X\n", report_map[i], report_map[i + 1]); + i += 2; + } + printf("0x%X\n", report_map[sizeof(report_map) - 1]); +} \ No newline at end of file