Initial commit

This commit is contained in:
Olivier 2025-12-08 11:59:47 +01:00
commit bb0d3f5335
15 changed files with 883 additions and 0 deletions

4
CMakeLists.txt Executable file
View File

@ -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)

7
include/ble_stream_deck.h Executable file
View File

@ -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);

3
include/gap.h Executable file
View File

@ -0,0 +1,3 @@
struct gap_advertise_fields {
} gap_advertise_fields;

20
include/gatt_server.h Executable file
View File

@ -0,0 +1,20 @@
#include <esp_log.h>
#include <nimble/nimble_port.h>
#include <services/gap/ble_svc_gap.h>
#include <services/gatt/ble_svc_gatt.h>
#include <host/ble_hs.h>
#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);

41
include/hid_descriptor.h Executable file
View File

@ -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

96
include/hid_keyboard.h Executable file
View File

@ -0,0 +1,96 @@
#ifndef HID_KEYBOARD_H
#define HID_KEYBOARD_H
#include <nimble/nimble_port.h>
#include <services/gap/ble_svc_gap.h>
#include <services/gatt/ble_svc_gatt.h>
#include <host/ble_hs.h>
#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

View File

@ -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

11
include/utils.h Executable file
View File

@ -0,0 +1,11 @@
#ifndef UTILS_H
#define UTILS_H
#include <freertos/FreeRTOSConfig.h>
#include <host/ble_hs.h>
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

17
readme.md Executable file
View File

@ -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

6
src/CMakeLists.txt Executable file
View File

@ -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})

191
src/gatt_server.c Executable file
View File

@ -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;
}

148
src/hid_keyboard_callbacks.c Executable file
View File

@ -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;
}

185
src/keyboard.c Executable file
View File

@ -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]);
}

102
src/main .c Executable file
View File

@ -0,0 +1,102 @@
#include <esp_log.h>
#include <nvs_flash.h>
#include <freertos/FreeRTOSConfig.h>
/* BLE */
#include <nimble/nimble_port.h>
#include <nimble/nimble_port_freertos.h>
/* Pas bien clair encore */
#include <host/ble_hs.h> // Host Event
#include <services/gap/ble_svc_gap.h> // 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);
}

37
src/utils.c Executable file
View File

@ -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]);
}