/* * event_reporter.c -- Drain BT events from a FreeRTOS queue, emit NDJSON. */ #include "event_reporter.h" #include "protocol.h" #include "uart_handler.h" #include #include #include "freertos/FreeRTOS.h" #include "freertos/queue.h" #include "freertos/task.h" #include "cJSON.h" #include "esp_log.h" #define TAG "event" #define EVENT_QUEUE_DEPTH 32 #define REPORTER_STACK 4096 #define REPORTER_PRIORITY 5 #define EVENT_NAME_MAX 32 /* ------------------------------------------------------------------ */ /* Queue payload */ /* ------------------------------------------------------------------ */ typedef struct { char event_name[EVENT_NAME_MAX]; cJSON *data; /* ownership transfers to uart_send_event */ } event_msg_t; static QueueHandle_t s_event_queue; /* ------------------------------------------------------------------ */ /* Reporter task */ /* ------------------------------------------------------------------ */ static void reporter_task(void *arg) { (void)arg; event_msg_t msg; for (;;) { if (xQueueReceive(s_event_queue, &msg, portMAX_DELAY) == pdTRUE) { /* * uart_send_event() takes ownership of msg.data via * cJSON_AddItemToObject -- the root (and therefore data) * is freed inside uart_send_event. No delete here. */ uart_send_event(msg.event_name, msg.data); } } } /* ------------------------------------------------------------------ */ /* Public API */ /* ------------------------------------------------------------------ */ void event_reporter_init(void) { s_event_queue = xQueueCreate(EVENT_QUEUE_DEPTH, sizeof(event_msg_t)); if (!s_event_queue) { ESP_LOGE(TAG, "failed to create event queue"); return; } BaseType_t rc = xTaskCreate(reporter_task, "evt_rpt", REPORTER_STACK, NULL, REPORTER_PRIORITY, NULL); if (rc != pdPASS) { ESP_LOGE(TAG, "failed to create reporter task"); } } void event_report(const char *event_name, cJSON *data) { if (!s_event_queue) { ESP_LOGW(TAG, "reporter not initialised, dropping %s", event_name); cJSON_Delete(data); return; } event_msg_t msg; memset(&msg, 0, sizeof(msg)); strncpy(msg.event_name, event_name, EVENT_NAME_MAX - 1); msg.data = data; if (xQueueSend(s_event_queue, &msg, 0) != pdTRUE) { ESP_LOGW(TAG, "event queue full, dropping %s", event_name); cJSON_Delete(data); } } /* ------------------------------------------------------------------ */ /* Convenience helpers */ /* ------------------------------------------------------------------ */ void event_report_pair_request(const char *address, const char *type, int passkey) { cJSON *d = cJSON_CreateObject(); cJSON_AddStringToObject(d, "address", address); cJSON_AddStringToObject(d, "type", type); cJSON_AddNumberToObject(d, "passkey", passkey); event_report(EVT_PAIR_REQUEST, d); } void event_report_pair_complete(const char *address, bool success) { cJSON *d = cJSON_CreateObject(); cJSON_AddStringToObject(d, "address", address); cJSON_AddBoolToObject(d, "success", success); event_report(EVT_PAIR_COMPLETE, d); } void event_report_connect(const char *address, const char *transport) { cJSON *d = cJSON_CreateObject(); cJSON_AddStringToObject(d, "address", address); cJSON_AddStringToObject(d, "transport", transport); event_report(EVT_CONNECT, d); } void event_report_disconnect(const char *address, const char *transport) { cJSON *d = cJSON_CreateObject(); cJSON_AddStringToObject(d, "address", address); cJSON_AddStringToObject(d, "transport", transport); event_report(EVT_DISCONNECT, d); } void event_report_gatt_read(uint16_t char_handle, const char *address) { cJSON *d = cJSON_CreateObject(); cJSON_AddNumberToObject(d, "handle", char_handle); cJSON_AddStringToObject(d, "address", address); event_report(EVT_GATT_READ, d); } void event_report_gatt_write(uint16_t char_handle, const char *address, const uint8_t *value, uint16_t len) { cJSON *d = cJSON_CreateObject(); cJSON_AddNumberToObject(d, "handle", char_handle); cJSON_AddStringToObject(d, "address", address); /* Encode raw bytes as a hex string (2 chars per byte + NUL). */ char *hex = malloc((size_t)len * 2 + 1); if (hex) { for (uint16_t i = 0; i < len; i++) { sprintf(hex + i * 2, "%02x", value[i]); } hex[len * 2] = '\0'; cJSON_AddStringToObject(d, "value", hex); free(hex); } else { cJSON_AddStringToObject(d, "value", ""); ESP_LOGW(TAG, "hex alloc failed for %u bytes", len); } cJSON_AddNumberToObject(d, "length", len); event_report(EVT_GATT_WRITE, d); } void event_report_gatt_subscribe(uint16_t char_handle, bool subscribed) { cJSON *d = cJSON_CreateObject(); cJSON_AddNumberToObject(d, "handle", char_handle); cJSON_AddBoolToObject(d, "subscribed", subscribed); event_report(EVT_GATT_SUBSCRIBE, d); }