fix(nimble): Added change to read initial BAS level post connection

This commit is contained in:
Mayank Tyagi
2026-06-04 15:58:14 +05:30
committed by Rahul Tank
parent 937cc637c6
commit c169bea2bd
2 changed files with 208 additions and 39 deletions
+203 -39
View File
@@ -9,6 +9,7 @@
#include "esp_hidh_private.h" #include "esp_hidh_private.h"
#include "esp_err.h" #include "esp_err.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_check.h"
#include "esp_hid_common.h" #include "esp_hid_common.h"
@@ -17,6 +18,7 @@
#include "freertos/semphr.h" #include "freertos/semphr.h"
#include <assert.h> #include <assert.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include "nimble/nimble_opt.h" #include "nimble/nimble_opt.h"
@@ -38,6 +40,7 @@
static const char *TAG = "NIMBLE_HIDH"; static const char *TAG = "NIMBLE_HIDH";
static SemaphoreHandle_t s_ble_hidh_cb_semaphore = NULL; static SemaphoreHandle_t s_ble_hidh_cb_semaphore = NULL;
static SemaphoreHandle_t s_ble_hidh_op_mutex = NULL;
/* variables used for attribute discovery */ /* variables used for attribute discovery */
static int services_discovered; static int services_discovered;
@@ -55,12 +58,32 @@ static inline void SEND_CB(void)
xSemaphoreGive(s_ble_hidh_cb_semaphore); xSemaphoreGive(s_ble_hidh_cb_semaphore);
} }
static inline void LOCK_OPS(void)
{
if (s_ble_hidh_op_mutex) {
xSemaphoreTakeRecursive(s_ble_hidh_op_mutex, portMAX_DELAY);
}
}
static inline void UNLOCK_OPS(void)
{
if (s_ble_hidh_op_mutex) {
xSemaphoreGiveRecursive(s_ble_hidh_op_mutex);
}
}
static esp_event_loop_handle_t event_loop_handle; static esp_event_loop_handle_t event_loop_handle;
static uint8_t *s_read_data_val = NULL;
static uint16_t s_read_data_len = 0;
static int s_read_status = 0;
static esp_event_handler_t s_event_callback; static esp_event_handler_t s_event_callback;
struct read_ctx {
uint8_t **out_val;
uint16_t *out_len;
int status;
SemaphoreHandle_t sem;
uint8_t *data_val;
uint16_t data_len;
};
/** /**
* Utility function to log an array of bytes. * Utility function to log an array of bytes.
*/ */
@@ -97,66 +120,153 @@ nimble_on_read(uint16_t conn_handle,
struct ble_gatt_attr *attr, struct ble_gatt_attr *attr,
void *arg) void *arg)
{ {
struct read_ctx *ctx = (struct read_ctx *)arg;
int old_offset; int old_offset;
uint16_t chunk_len;
uint8_t *tmp;
MODLOG_DFLT(INFO, "Read complete; status=%d conn_handle=%d", error->status, MODLOG_DFLT(INFO, "Read complete; status=%d conn_handle=%d", error->status,
conn_handle); conn_handle);
s_read_status = error->status; ctx->status = error->status;
switch(s_read_status) { switch(ctx->status) {
case 0: case 0:
if (attr == NULL || attr->om == NULL) {
ctx->status = BLE_HS_EINVAL;
xSemaphoreGive(ctx->sem);
return BLE_HS_EINVAL;
}
MODLOG_DFLT(DEBUG, " attr_handle=%d value=", attr->handle); MODLOG_DFLT(DEBUG, " attr_handle=%d value=", attr->handle);
old_offset = s_read_data_len; old_offset = ctx->data_len;
s_read_data_len += OS_MBUF_PKTLEN(attr->om); chunk_len = OS_MBUF_PKTLEN(attr->om);
s_read_data_val = realloc(s_read_data_val, s_read_data_len + 1); // 1 extra byte to store null char tmp = realloc(ctx->data_val, old_offset + chunk_len + 1); // 1 extra byte to store null char
ble_hs_mbuf_to_flat(attr->om, s_read_data_val + old_offset, OS_MBUF_PKTLEN(attr->om), NULL); if (tmp == NULL) {
ctx->status = BLE_HS_ENOMEM;
free(ctx->data_val);
ctx->data_val = NULL;
ctx->data_len = 0;
xSemaphoreGive(ctx->sem);
return BLE_HS_ENOMEM;
}
ctx->data_val = tmp;
ctx->data_len = old_offset + chunk_len;
ble_hs_mbuf_to_flat(attr->om, ctx->data_val + old_offset, chunk_len, NULL);
print_mbuf(attr->om); print_mbuf(attr->om);
return 0; return 0;
case BLE_HS_EDONE: case BLE_HS_EDONE:
s_read_data_val[s_read_data_len] = 0; // to insure strings are ended with \0 */ if (ctx->data_val == NULL) {
s_read_status = 0; ctx->data_val = malloc(1);
SEND_CB(); if (ctx->data_val == NULL) {
ctx->status = BLE_HS_ENOMEM;
xSemaphoreGive(ctx->sem);
return BLE_HS_ENOMEM;
}
}
ctx->data_val[ctx->data_len] = 0; // to insure strings are ended with \0 */
ctx->status = 0;
xSemaphoreGive(ctx->sem);
return 0; return 0;
default: {
int rc = ctx->status;
xSemaphoreGive(ctx->sem);
return rc;
}
} }
return 0;
} }
static int read_char(uint16_t conn_handle, uint16_t handle, uint8_t **out, uint16_t *out_len) static int read_char(uint16_t conn_handle, uint16_t handle, uint8_t **out, uint16_t *out_len)
{ {
s_read_data_val = NULL; struct read_ctx ctx;
s_read_data_len = 0;
int rc; int rc;
if (out) {
*out = NULL;
}
if (out_len) {
*out_len = 0;
}
ctx.sem = xSemaphoreCreateBinary();
if (ctx.sem == NULL) {
return BLE_HS_ENOMEM;
}
ctx.out_val = out;
ctx.out_len = out_len;
ctx.status = 0;
ctx.data_val = NULL;
ctx.data_len = 0;
/* read long because the server may not support the large enough mtu */ /* read long because the server may not support the large enough mtu */
rc = ble_gattc_read_long(conn_handle, handle, 0, nimble_on_read, NULL); LOCK_OPS();
rc = ble_gattc_read_long(conn_handle, handle, 0, nimble_on_read, &ctx);
if (rc != 0) { if (rc != 0) {
ESP_LOGE(TAG, "read_char failed"); ESP_LOGE(TAG, "read_char failed");
UNLOCK_OPS();
vSemaphoreDelete(ctx.sem);
return rc; return rc;
} }
WAIT_CB(); xSemaphoreTake(ctx.sem, portMAX_DELAY);
if (s_read_status == 0) { if (ctx.status == 0) {
*out = s_read_data_val; if (out) {
*out_len = s_read_data_len; *out = ctx.data_val;
} else {
free(ctx.data_val);
}
if (out_len) {
*out_len = ctx.data_len;
}
} else {
free(ctx.data_val);
} }
return s_read_status; vSemaphoreDelete(ctx.sem);
UNLOCK_OPS();
return ctx.status;
} }
static int read_descr(uint16_t conn_handle, uint16_t handle, uint8_t **out, uint16_t *out_len) static int read_descr(uint16_t conn_handle, uint16_t handle, uint8_t **out, uint16_t *out_len)
{ {
struct read_ctx ctx;
int rc; int rc;
s_read_data_val = NULL; if (out) {
s_read_data_len = 0; *out = NULL;
}
if (out_len) {
*out_len = 0;
}
rc = ble_gattc_read_long(conn_handle, handle, 0, nimble_on_read, NULL); ctx.sem = xSemaphoreCreateBinary();
if (ctx.sem == NULL) {
return BLE_HS_ENOMEM;
}
ctx.out_val = out;
ctx.out_len = out_len;
ctx.status = 0;
ctx.data_val = NULL;
ctx.data_len = 0;
LOCK_OPS();
rc = ble_gattc_read_long(conn_handle, handle, 0, nimble_on_read, &ctx);
if (rc != 0) { if (rc != 0) {
ESP_LOGE(TAG, "read_descr failed"); ESP_LOGE(TAG, "read_descr failed");
UNLOCK_OPS();
vSemaphoreDelete(ctx.sem);
return rc; return rc;
} }
WAIT_CB(); xSemaphoreTake(ctx.sem, portMAX_DELAY);
if (s_read_status == 0) { if (ctx.status == 0) {
*out = s_read_data_val; if (out) {
*out_len = s_read_data_len; *out = ctx.data_val;
} else {
free(ctx.data_val);
}
if (out_len) {
*out_len = ctx.data_len;
}
} else {
free(ctx.data_val);
} }
return s_read_status; vSemaphoreDelete(ctx.sem);
UNLOCK_OPS();
return ctx.status;
} }
static int static int
@@ -463,14 +573,20 @@ static void read_device_services(esp_hidh_dev_t *dev)
else { else {
chr_end_handle = service_result[s].end_handle; chr_end_handle = service_result[s].end_handle;
} }
rc = ble_gattc_disc_all_dscs(dev->ble.conn_id, char_result[c].val_handle, // Skip descriptor discovery if range is invalid (start >= end)
chr_end_handle, desc_disced, descr_result); if (char_result[c].val_handle >= chr_end_handle) {
WAIT_CB(); dcount = 0;
if(status != 0) { rc = ESP_OK;
ESP_LOGE(TAG, "failed to find discriptors for characteristic : %d",c); } else {
assert(status == 0); rc = ble_gattc_disc_all_dscs(dev->ble.conn_id, char_result[c].val_handle,
chr_end_handle, desc_disced, descr_result);
WAIT_CB();
if(status != 0) {
ESP_LOGE(TAG, "failed to find discriptors for characteristic : %d",c);
assert(status == 0);
}
dcount = dscs_discovered;
} }
dcount = dscs_discovered;
if (rc == ESP_OK) { if (rc == ESP_OK) {
for (uint16_t d = 0; d < dcount; d++) { for (uint16_t d = 0; d < dcount; d++) {
duuid = ble_uuid_u16(&descr_result[d].uuid.u); duuid = ble_uuid_u16(&descr_result[d].uuid.u);
@@ -572,12 +688,16 @@ static void register_for_notify(uint16_t conn_handle, uint16_t handle)
int rc; int rc;
value[0] = 1; value[0] = 1;
value[1] = 0; value[1] = 0;
LOCK_OPS();
rc = ble_gattc_write_flat(conn_handle, handle, value, sizeof value, on_subscribe,(void *)&conn_handle); rc = ble_gattc_write_flat(conn_handle, handle, value, sizeof value, on_subscribe,(void *)&conn_handle);
if (rc != 0) { if (rc != 0) {
ESP_LOGE(TAG, "Error: Failed to subscribe to characteristic; " ESP_LOGE(TAG, "Error: Failed to subscribe to characteristic; "
"rc=%d\n", rc); "rc=%d\n", rc);
UNLOCK_OPS();
return;
} }
WAIT_CB(); WAIT_CB();
UNLOCK_OPS();
} }
static int static int
@@ -600,13 +720,22 @@ on_write(uint16_t conn_handle,
} }
static void write_char_descr(uint16_t conn_id, uint16_t handle, uint16_t value_len, uint8_t *value) static void write_char_descr(uint16_t conn_id, uint16_t handle, uint16_t value_len, uint8_t *value)
{ {
ble_gattc_write_flat(conn_id, handle, value, value_len, on_write, &conn_id); int rc;
LOCK_OPS();
rc = ble_gattc_write_flat(conn_id, handle, value, value_len, on_write, &conn_id);
if (rc != 0) {
ESP_LOGE(TAG, "Error: Failed to write descriptor; rc=%d\n", rc);
UNLOCK_OPS();
return;
}
WAIT_CB(); WAIT_CB();
UNLOCK_OPS();
} }
static void attach_report_listeners(esp_hidh_dev_t *dev) static void attach_report_listeners(esp_hidh_dev_t *dev)
{ {
if (dev == NULL) { if (dev == NULL) {
ESP_LOGE(TAG, "dev is NULL, returning");
return; return;
} }
uint16_t ccc_data = 1; uint16_t ccc_data = 1;
@@ -614,10 +743,23 @@ static void attach_report_listeners(esp_hidh_dev_t *dev)
//subscribe to battery notifications //subscribe to battery notifications
if (dev->ble.battery_handle) { if (dev->ble.battery_handle) {
register_for_notify(dev->ble.conn_id, dev->ble.battery_handle); uint8_t *rdata = NULL;
uint16_t rlen = 0;
if (event_loop_handle &&
read_char(dev->ble.conn_id, dev->ble.battery_handle, &rdata, &rlen) == 0 &&
rlen >= 1 && rdata != NULL) {
esp_hidh_event_data_t p = {0};
p.battery.dev = dev;
p.battery.level = rdata[0];
esp_event_post_to(event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_BATTERY_EVENT,
&p, sizeof(esp_hidh_event_data_t), portMAX_DELAY);
}
free(rdata);
if (dev->ble.battery_ccc_handle) { if (dev->ble.battery_ccc_handle) {
//Write CCC descr to enable notifications //Write CCC descr to enable notifications
write_char_descr(dev->ble.conn_id, dev->ble.battery_ccc_handle, 2, (uint8_t *)&ccc_data); write_char_descr(dev->ble.conn_id, dev->ble.battery_ccc_handle, sizeof(ccc_data), (uint8_t *)&ccc_data);
} }
} }
@@ -839,8 +981,10 @@ static esp_err_t esp_ble_hidh_dev_report_read(esp_hidh_dev_t *dev, size_t map_in
} }
*value_len = len; *value_len = len;
memcpy(value, v, len); memcpy(value, v, len);
free(v);
return ESP_OK; return ESP_OK;
} }
free(v);
ESP_LOGE(TAG, "%s report %d read failed: 0x%x", esp_hid_report_type_str(report_type), report_id, s); ESP_LOGE(TAG, "%s report %d read failed: 0x%x", esp_hid_report_type_str(report_type), report_id, s);
return ESP_FAIL; return ESP_FAIL;
} }
@@ -933,12 +1077,27 @@ esp_err_t esp_ble_hidh_init(const esp_hidh_config_t *config)
if (ret != ESP_OK) { if (ret != ESP_OK) {
if (event_loop_handle) { if (event_loop_handle) {
esp_event_loop_delete(event_loop_handle); esp_event_loop_delete(event_loop_handle);
event_loop_handle = NULL;
} }
if (s_ble_hidh_cb_semaphore) { if (s_ble_hidh_cb_semaphore) {
vSemaphoreDelete(s_ble_hidh_cb_semaphore); vSemaphoreDelete(s_ble_hidh_cb_semaphore);
s_ble_hidh_cb_semaphore = NULL; s_ble_hidh_cb_semaphore = NULL;
} }
s_event_callback = NULL;
return ret;
}
s_ble_hidh_op_mutex = xSemaphoreCreateRecursiveMutex();
if (s_ble_hidh_op_mutex == NULL) {
if (event_loop_handle) {
esp_event_loop_delete(event_loop_handle);
event_loop_handle = NULL;
}
vSemaphoreDelete(s_ble_hidh_cb_semaphore);
s_ble_hidh_cb_semaphore = NULL;
s_event_callback = NULL;
return ESP_ERR_NO_MEM;
} }
ble_hs_cfg.reset_cb = nimble_host_reset; ble_hs_cfg.reset_cb = nimble_host_reset;
@@ -955,6 +1114,11 @@ esp_err_t esp_ble_hidh_deinit(void)
if (event_loop_handle) { if (event_loop_handle) {
esp_event_loop_delete(event_loop_handle); esp_event_loop_delete(event_loop_handle);
event_loop_handle = NULL;
}
if (s_ble_hidh_op_mutex) {
vSemaphoreDelete(s_ble_hidh_op_mutex);
s_ble_hidh_op_mutex = NULL;
} }
vSemaphoreDelete(s_ble_hidh_cb_semaphore); vSemaphoreDelete(s_ble_hidh_cb_semaphore);
s_ble_hidh_cb_semaphore = NULL; s_ble_hidh_cb_semaphore = NULL;
@@ -38,6 +38,7 @@
#include "esp_hid_gap.h" #include "esp_hid_gap.h"
static const char *TAG = "HID_DEV_DEMO"; static const char *TAG = "HID_DEV_DEMO";
#define HID_BATTERY_LEVEL 60
typedef struct typedef struct
{ {
@@ -615,6 +616,10 @@ static void ble_hidd_event_callback(void *handler_args, esp_event_base_t base, i
} }
case ESP_HIDD_CONNECT_EVENT: { case ESP_HIDD_CONNECT_EVENT: {
ESP_LOGI(TAG, "CONNECT"); ESP_LOGI(TAG, "CONNECT");
esp_err_t err = esp_hidd_dev_battery_set(s_ble_hid_param.hid_dev, HID_BATTERY_LEVEL);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to set battery level: %d", err);
}
break; break;
} }
case ESP_HIDD_PROTOCOL_MODE_EVENT: { case ESP_HIDD_PROTOCOL_MODE_EVENT: {