From de103142a4e66381fce14cbe26baefebd62da65a Mon Sep 17 00:00:00 2001 From: Peter Siegmund Date: Fri, 30 May 2025 22:43:12 +0200 Subject: [PATCH] starting with notify Signed-off-by: Peter Siegmund --- .../remote_control/capability_service.c | 84 +++++++++++++++++++ .../include/capability_service.h | 1 + components/remote_control/remote_control.c | 30 +++++++ 3 files changed, 115 insertions(+) diff --git a/components/remote_control/capability_service.c b/components/remote_control/capability_service.c index 7af101d..9824eb1 100644 --- a/components/remote_control/capability_service.c +++ b/components/remote_control/capability_service.c @@ -2,6 +2,10 @@ #include "esp_log.h" #include "storage.h" #include +#include // For malloc, free +#include "host/ble_hs.h" // For ble_hs_mbuf_from_flat, ble_att_mtu +#include "host/ble_uuid.h" // For BLE_ATT_MTU_DFLT (often included via ble_hs.h) +#include "nimble/nimble_port.h" // For os_mbuf related functions static const char *TAG_CS = "capability_service"; @@ -70,3 +74,83 @@ int capa_char_1979_user_desc(uint16_t con_handle, uint16_t attr_handle, struct b os_mbuf_append(ctxt->om, desc, strlen(desc)); return 0; } + +void capa_notify_data(uint16_t conn_handle, uint16_t char_val_handle) +{ + esp_err_t storage_status = storage_init(); + if (storage_status != ESP_OK) + { + ESP_LOGE(TAG_CS, "Notify: Failed to initialize storage: %s", esp_err_to_name(storage_status)); + return; + } + + const char *filename = "/storage/capability.json"; + + uint16_t mtu = ble_att_mtu(conn_handle); + if (mtu == 0) { // Should not happen for an active connection, fallback + ESP_LOGW(TAG_CS, "Notify: ble_att_mtu returned 0, using default MTU %d.", BLE_ATT_MTU_DFLT); + mtu = BLE_ATT_MTU_DFLT; + } + + // Max payload for notification is MTU - 3 (1 byte opcode for Notification, 2 bytes attribute handle) + size_t notify_chunk_size = (mtu > 3) ? (mtu - 3) : (BLE_ATT_MTU_DFLT - 3); // Ensure mtu > 3 + + // Further cap by CAPA_READ_CHUNK_SIZE if it's smaller and meant as an upper limit for any single read op + if (notify_chunk_size > CAPA_READ_CHUNK_SIZE) { + notify_chunk_size = CAPA_READ_CHUNK_SIZE; + } + + if (notify_chunk_size == 0) { // Safety check + ESP_LOGE(TAG_CS, "Notify: Calculated notify_chunk_size is 0. Aborting."); + storage_uninit(); + return; + } + + char *read_buffer = malloc(notify_chunk_size); + if (!read_buffer) + { + ESP_LOGE(TAG_CS, "Notify: Failed to allocate read_buffer (%zu bytes)", notify_chunk_size); + storage_uninit(); + return; + } + + ESP_LOGI(TAG_CS, "Notify: Reading capabilities from %s to notify conn %u, attr %u (chunk size %zu, MTU %u)", + filename, conn_handle, char_val_handle, notify_chunk_size, mtu); + + FILE *fp = fopen(filename, "r"); + if (!fp) + { + ESP_LOGE(TAG_CS, "Notify: Failed to open %s for reading.", filename); + free(read_buffer); + storage_uninit(); + return; + } + + ssize_t bytes_read; + while ((bytes_read = fread(read_buffer, 1, notify_chunk_size, fp)) > 0) + { + ESP_LOGD(TAG_CS, "Notify: Read %zd bytes from storage for notification", bytes_read); + + struct os_mbuf *om = ble_hs_mbuf_from_flat(read_buffer, bytes_read); + if (!om) { + ESP_LOGE(TAG_CS, "Notify: Failed to allocate mbuf for notification. Stopping."); + break; // Stop sending if mbuf allocation fails + } + + int rc = ble_gatts_notify_custom(conn_handle, char_val_handle, om); + if (rc != 0) { + ESP_LOGE(TAG_CS, "Notify: Error sending notification (rc=%d). Stopping.", rc); + // ble_gatts_notify_custom frees 'om' on success or if it takes ownership even on some errors. + // If it returns an error where 'om' is not freed, we might need os_mbuf_free_chain(om); + // For now, assume NimBLE handles 'om' correctly in error cases like BLE_HS_ENOMEM. + break; // Stop if notification fails + } + ESP_LOGD(TAG_CS, "Notify: Sent %zd bytes successfully.", bytes_read); + } + + if (ferror(fp)) { ESP_LOGE(TAG_CS, "Notify: File read error from %s.", filename); } + fclose(fp); + free(read_buffer); + storage_uninit(); + ESP_LOGI(TAG_CS, "Notify: Finished sending capability data for conn %u.", conn_handle); +} diff --git a/components/remote_control/include/capability_service.h b/components/remote_control/include/capability_service.h index cab8e63..8841fcc 100644 --- a/components/remote_control/include/capability_service.h +++ b/components/remote_control/include/capability_service.h @@ -5,6 +5,7 @@ /// Service Characteristics Callback int capa_read(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg); +void capa_notify_data(uint16_t conn_handle, uint16_t char_val_handle); /// Service Characteristics User Description int capa_char_1979_user_desc(uint16_t con_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg); diff --git a/components/remote_control/remote_control.c b/components/remote_control/remote_control.c index c6919fd..f4027fb 100644 --- a/components/remote_control/remote_control.c +++ b/components/remote_control/remote_control.c @@ -28,6 +28,9 @@ static const ble_uuid16_t led_service_uuid = BLE_UUID16_INIT(0x1007); uint8_t ble_addr_type; +// Handle for the capability characteristic value +static uint16_t g_capa_char_val_handle; + static void ble_app_advertise(void); static struct ble_gatt_dsc_def char_0xA000_descs[] = {{ @@ -69,6 +72,7 @@ static const struct ble_gatt_svc_def gatt_svcs[] = { .uuid = BLE_UUID16_DECLARE(0x1979), .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY, .access_cb = capa_read, + .val_handle = &g_capa_char_val_handle, .descriptors = char_0x1979_desc, }, {0}}, @@ -118,6 +122,32 @@ static int ble_gap_event(struct ble_gap_event *event, void *arg) ble_app_advertise(); break; + case BLE_GAP_EVENT_SUBSCRIBE: + ESP_LOGI(TAG, + "BLE GAP EVENT SUBSCRIBE conn_handle=%d attr_handle=%d reason=%d " + "prev_notify=%d cur_notify=%d prev_indicate=%d cur_indicate=%d", + event->subscribe.conn_handle, event->subscribe.attr_handle, event->subscribe.reason, + event->subscribe.prev_notify, event->subscribe.cur_notify, event->subscribe.prev_indicate, + event->subscribe.cur_indicate); + + // Check if subscription is for the capability characteristic's CCCD. + // The CCCD handle is typically the characteristic value handle + 1. + // g_capa_char_val_handle stores the handle of the characteristic value itself. + if (event->subscribe.attr_handle == g_capa_char_val_handle + 1) + { + if (event->subscribe.cur_notify) + { + ESP_LOGI(TAG, "Client subscribed to capability notifications. Sending data..."); + // Call the function to send capability data via notifications + capa_notify_data(event->subscribe.conn_handle, g_capa_char_val_handle); + } + else + { + ESP_LOGI(TAG, "Client unsubscribed from capability notifications."); + } + } + break; + default: break; }