diff --git a/README.md b/README.md index 4879adc..aa3f377 100755 --- a/README.md +++ b/README.md @@ -1,8 +1,3 @@ -# esp_component_template - -esp_component_template - -```c #include #include @@ -12,7 +7,7 @@ esp_component_template #include "esp_event.h" #include "zh_nrf24_gateway.h" -// #include "zh_nrf24_config.h" +#include "zh_nrf24_config.h" static const char *TAG = "MAIN"; @@ -32,17 +27,57 @@ static const zh_nrf_gateway_config_t s_gw_config = { .spi_clock_hz = 0, }; -static void dump_bindings(const char *reason) +static void handle_paired_event(const zh_nrf_gateway_binding_t *b) { - zh_nrf_gateway_binding_t tmp[ZH_NRF_GATEWAY_MAX_BINDINGS]; - size_t got = zh_nrf_gateway_bindings_get(tmp, ZH_NRF_GATEWAY_MAX_BINDINGS); + ESP_LOGI(TAG, ">>> SENSOR PAIRED: id=%u type=%u (%s)", + b->id, b->type, + zh_get_nrf24_sensor_type_name((zh_nrf24_sensor_type_t)b->type)); - ESP_LOGI(TAG, "=== Bindings (%s): %u total ===", reason, (unsigned)got); - for (size_t i = 0; i < got; i++) { - ESP_LOGI(TAG, " [%u] id=%u type=%u (%s)", - (unsigned)i, - tmp[i].id, tmp[i].type, - zh_get_nrf24_sensor_type_name((zh_nrf24_sensor_type_t)tmp[i].type)); + /* Здесь можно добавить: + * - публикацию в MQTT: "sensor/paired" {id, type}; + * - сохранение метаданных во внешнюю БД; + * - запись в журнал / уведомление; + * - поиск пользователя, которому принадлежит новое устройство, + * и т.п. + */ +} + +static void handle_sensor_data_event(const zh_nrf24_sensor_transmitted_data_t *d) +{ + const char *name = zh_get_nrf24_sensor_type_name((zh_nrf24_sensor_type_t)d->type); + + ESP_LOGI(TAG, "<<< sensor id=%d type=%s battery=%.2fV", + d->id, name, d->battery / 100.0); + + switch (d->type) + { + case ZH_NRF24_SENSOR_TOUCH_SWITCH: + ESP_LOGI(TAG, " button event"); + break; + case ZH_NRF24_SENSOR_WATER_LEAKAGE: + ESP_LOGI(TAG, " status=%s", + d->value_1 == ZH_NRF24_SENSOR_STATUS_ALARM ? "ALARM" : "DRY"); + break; + case ZH_NRF24_SENSOR_OPEN_CLOSE: + ESP_LOGI(TAG, " state=%s", + d->value_1 == ZH_NRF24_SENSOR_STATUS_OPEN ? "OPEN" : "CLOSE"); + break; + case ZH_NRF24_SENSOR_PLANT_HUMIDITY: + ESP_LOGI(TAG, " humidity=%d%%", d->value_1); + break; + case ZH_NRF24_SENSOR_BMP280: + ESP_LOGI(TAG, " T=%d C, P=%d", d->value_1, d->value_2); + break; + case ZH_NRF24_SENSOR_BME280: + ESP_LOGI(TAG, " H=%d%% T=%d C P=%d mmHg", + d->value_1, d->value_2, d->value_3); + break; + case ZH_NRF24_SENSOR_BME680: + ESP_LOGI(TAG, " H=%d%% T=%d C P=%d Q=%d", + d->value_1, d->value_2, d->value_3, d->value_4); + break; + default: + break; } } @@ -55,65 +90,19 @@ static void gateway_event_handler(void *arg, switch (id) { - case ZH_NRF_GATEWAY_EVENT_SENSOR_DATA: { - const zh_nrf24_sensor_transmitted_data_t *d = event_data; - const char *name = zh_get_nrf24_sensor_type_name((zh_nrf24_sensor_type_t)d->type); - - ESP_LOGI(TAG, "<<< sensor id=%d type=%s battery=%.2fV", - d->id, name, d->battery / 100.0); - - switch (d->type) - { - case ZH_NRF24_SENSOR_TOUCH_SWITCH: - ESP_LOGI(TAG, " button event"); - break; - case ZH_NRF24_SENSOR_WATER_LEAKAGE: - ESP_LOGI(TAG, " status=%s", - d->value_1 == ZH_NRF24_SENSOR_STATUS_ALARM ? "ALARM" : "DRY"); - break; - case ZH_NRF24_SENSOR_OPEN_CLOSE: - ESP_LOGI(TAG, " state=%s", - d->value_1 == ZH_NRF24_SENSOR_STATUS_OPEN ? "OPEN" : "CLOSE"); - break; - case ZH_NRF24_SENSOR_PLANT_HUMIDITY: - ESP_LOGI(TAG, " humidity=%d%%", d->value_1); - break; - case ZH_NRF24_SENSOR_BMP280: - ESP_LOGI(TAG, " T=%d C, P=%d", d->value_1, d->value_2); - break; - case ZH_NRF24_SENSOR_BME280: - ESP_LOGI(TAG, " H=%d%% T=%d C P=%d mmHg", - d->value_1, d->value_2, d->value_3); - break; - case ZH_NRF24_SENSOR_BME680: - ESP_LOGI(TAG, " H=%d%% T=%d C P=%d Q=%d", - d->value_1, d->value_2, d->value_3, d->value_4); - break; - default: - break; - } + case ZH_NRF_GATEWAY_EVENT_SENSOR_DATA: + handle_sensor_data_event((const zh_nrf24_sensor_transmitted_data_t *)event_data); break; - } + + case ZH_NRF_GATEWAY_EVENT_SENSOR_PAIRED: + handle_paired_event((const zh_nrf_gateway_binding_t *)event_data); + break; + default: break; } } -/* Пример: удаление одной привязки (из MQTT, кнопки, HTTP и т.п.) */ -static void demo_manual_delete(void) -{ - uint8_t id = 5; - uint8_t type = ZH_NRF24_SENSOR_BME280; - - if (zh_nrf_gateway_binding_exists(id, type)) { - ESP_LOGI(TAG, "Binding {id=%u, type=%u} exists, deleting", id, type); - esp_err_t err = zh_nrf_gateway_binding_delete(id, type); - ESP_LOGI(TAG, "delete → %s", esp_err_to_name(err)); - } else { - ESP_LOGW(TAG, "Binding {id=%u, type=%u} not found, nothing to do", id, type); - } -} - void app_main(void) { ESP_LOGI(TAG, "Starting nRF24L01+ gateway"); @@ -126,24 +115,8 @@ void app_main(void) NULL, NULL)); ESP_ERROR_CHECK(zh_nrf_gateway_init(&s_gw_config)); - dump_bindings("after init"); - ESP_ERROR_CHECK(zh_nrf_gateway_start()); ESP_LOGI(TAG, "Gateway running: channel=%d, address=0x%06X", GW_CHANNEL, GW_ADDRESS); - - /* Здесь можно, например, ждать команд по MQTT/HTTP и вызывать: - * - * zh_nrf_gateway_binding_delete(id, type) — точное удаление - * zh_nrf_gateway_bindings_delete_by_id(id) — всё с этим id - * zh_nrf_gateway_bindings_delete_by_type(type) — всё этого типа - * zh_nrf_gateway_binding_clear_all() — полный сброс - * zh_nrf_gateway_binding_exists(id, type) — проверка - * zh_nrf_gateway_bindings_get(...) / _count() — чтение - * - * После удаления id сразу доступен для следующего pairing-запроса - * того же типа. - */ -} -``` \ No newline at end of file +} \ No newline at end of file diff --git a/include/zh_nrf24_gateway.h b/include/zh_nrf24_gateway.h index 5634c4c..277e2e0 100644 --- a/include/zh_nrf24_gateway.h +++ b/include/zh_nrf24_gateway.h @@ -6,7 +6,12 @@ * Receives RF sensor telemetry and posts it as events on the default * ESP event loop. Pairing is fully internal: the gateway allocates * per-type device IDs, persists the binding table in NVS and sends - * ACK packets. No pairing event reaches the event loop. + * ACK packets. + * + * Events emitted: + * - ZH_NRF_GATEWAY_EVENT_SENSOR_DATA — sensor telemetry + * - ZH_NRF_GATEWAY_EVENT_SENSOR_PAIRED — a new {id, type} binding was + * created and the ACK was successfully delivered to the sensor * * Binding table semantics: * - each binding is a pair {id, type}; @@ -38,8 +43,18 @@ ESP_EVENT_DECLARE_BASE(ZH_NRF_GATEWAY_EVENTS); typedef enum { - /** Sensor telemetry received (data: zh_nrf24_sensor_transmitted_data_t) */ + /** + * Sensor telemetry received. + * Event data: zh_nrf24_sensor_transmitted_data_t + */ ZH_NRF_GATEWAY_EVENT_SENSOR_DATA, + + /** + * A new {id, type} binding was created (pairing succeeded and the + * ACK was delivered to the sensor). + * Event data: zh_nrf_gateway_binding_t + */ + ZH_NRF_GATEWAY_EVENT_SENSOR_PAIRED, } zh_nrf_gateway_event_t; typedef struct @@ -68,61 +83,27 @@ esp_err_t zh_nrf_gateway_start(void); /* ===================== BINDING TABLE API ===================== */ -/** - * @brief Copy current bindings into @p buf. - * - * Thread-safe. Returns number of entries copied (≤ max_count and ≤ - * current table size). Use with zh_nrf_gateway_bindings_count() to - * decide buffer size. - */ size_t zh_nrf_gateway_bindings_get(zh_nrf_gateway_binding_t *buf, size_t max_count); - -/** @brief Current number of bindings in the table. */ size_t zh_nrf_gateway_bindings_count(void); - -/** - * @brief Check whether a {id, type} binding exists. - * - * @return true if the binding is present in the table - */ -bool zh_nrf_gateway_binding_exists(uint8_t id, uint8_t type); +bool zh_nrf_gateway_binding_exists(uint8_t id, uint8_t type); /** * @brief Delete a {id, type} binding. * - * Composite key: both @p id AND @p type must match an existing entry. - * If only one of them matches, nothing is deleted and ESP_ERR_NOT_FOUND - * is returned. The id becomes immediately available for the next - * pairing request of the same type. The change is persisted to NVS. + * Composite key: both @p id AND @p type must match. If only one matches, + * nothing is deleted and ESP_ERR_NOT_FOUND is returned. * - * @return ESP_OK on success - * @return ESP_ERR_NOT_FOUND no such {id, type} pair in the table - * @return ESP_ERR_INVALID_ARG id out of 1..254, or unknown type - * @return ESP_ERR_INVALID_STATE gateway not initialized - * @return other NVS error + * @return ESP_OK / ESP_ERR_NOT_FOUND / ESP_ERR_INVALID_ARG / ESP_ERR_INVALID_STATE / NVS error */ esp_err_t zh_nrf_gateway_binding_delete(uint8_t id, uint8_t type); -/** - * @brief Delete every binding whose id equals @p id (any type). - * - * Use when the same physical device changed its type and the old - * binding must be dropped regardless of the type field. - * - * @return Number of bindings deleted (≥ 0), or negative ESP error code - */ +/** Delete every binding whose id equals @p id (any type). Returns count, or -err. */ int zh_nrf_gateway_bindings_delete_by_id(uint8_t id); -/** - * @brief Delete every binding whose type equals @p type (any id). - * - * Use for mass-forget of one sensor category. - * - * @return Number of bindings deleted (≥ 0), or negative ESP error code - */ +/** Delete every binding whose type equals @p type (any id). Returns count, or -err. */ int zh_nrf_gateway_bindings_delete_by_type(uint8_t type); -/** @brief Remove all bindings (persisted). */ +/** Remove all bindings (persisted). */ esp_err_t zh_nrf_gateway_binding_clear_all(void); #ifdef __cplusplus diff --git a/zh_nrf24_gateway.c b/zh_nrf24_gateway.c index 0ed3bdd..5d9286b 100644 --- a/zh_nrf24_gateway.c +++ b/zh_nrf24_gateway.c @@ -68,7 +68,6 @@ static const char *TAG = "NRF_GW"; #define NVS_NAMESPACE "zh_gw" #define NVS_KEY_BINDINGS "bindings" -/* Valid id range for allocation */ #define BINDING_ID_MIN 1 #define BINDING_ID_MAX 254 @@ -205,14 +204,13 @@ static esp_err_t nrf_irq_init(void) /* ============================================================ * NVS BINDING TABLE * ============================================================ */ - static esp_err_t bindings_load(void) { nvs_handle_t h; esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READONLY, &h); if (err != ESP_OK) { memset(&s_ctx.bindings, 0, sizeof(s_ctx.bindings)); - return ESP_OK; /* empty table on first run */ + return ESP_OK; } size_t size = sizeof(s_ctx.bindings); @@ -253,7 +251,6 @@ static esp_err_t bindings_save(void) return err; } -/* Find first free id in [BINDING_ID_MIN, BINDING_ID_MAX] for @p type. */ static uint8_t bindings_alloc_id(uint8_t type) { for (uint16_t id = BINDING_ID_MIN; id <= BINDING_ID_MAX; id++) { @@ -299,7 +296,6 @@ static esp_err_t bindings_delete(uint8_t id, uint8_t type) /* ============================================================ * BINDING TABLE PUBLIC API * ============================================================ */ - size_t zh_nrf_gateway_bindings_get(zh_nrf_gateway_binding_t *buf, size_t max_count) { if (!buf || max_count == 0 || !s_ctx.bindings_mutex) return 0; @@ -338,8 +334,6 @@ bool zh_nrf_gateway_binding_exists(uint8_t id, uint8_t type) esp_err_t zh_nrf_gateway_binding_delete(uint8_t id, uint8_t type) { if (!s_ctx.initialized) return ESP_ERR_INVALID_STATE; - - /* Argument validation */ if (id == 0 || id == 0xFF) return ESP_ERR_INVALID_ARG; if (zh_get_nrf24_sensor_payload_size(type) == 0) return ESP_ERR_INVALID_ARG; @@ -382,11 +376,8 @@ int zh_nrf_gateway_bindings_delete_by_id(uint8_t id) xSemaphoreGive(s_ctx.bindings_mutex); if (err != ESP_OK) return -(int)err; - if (deleted > 0) { - ESP_LOGI(TAG, "Deleted %d binding(s) with id=%u", deleted, id); - } else { - ESP_LOGW(TAG, "No bindings with id=%u", id); - } + if (deleted > 0) ESP_LOGI(TAG, "Deleted %d binding(s) with id=%u", deleted, id); + else ESP_LOGW(TAG, "No bindings with id=%u", id); return deleted; } @@ -491,7 +482,6 @@ static bool send_pairing_ack(uint8_t req_type, uint16_t token, uint8_t new_id) /* ============================================================ * ОБРАБОТКА ПАКЕТОВ * ============================================================ */ - static void handle_pairing_request(const uint8_t *data) { uint8_t req_type = data[0]; @@ -533,8 +523,22 @@ static void handle_pairing_request(const uint8_t *data) xSemaphoreTake(s_ctx.bindings_mutex, portMAX_DELAY); bindings_delete(new_id, req_type); xSemaphoreGive(s_ctx.bindings_mutex); - } else { - ESP_LOGI(TAG, "Pairing complete: id=%u type=%u", new_id, req_type); + return; + } + + /* --- ACK delivered → binding is final. Notify the application. --- */ + ESP_LOGI(TAG, "Pairing complete: id=%u type=%u", new_id, req_type); + + zh_nrf_gateway_binding_t paired = { + .id = new_id, + .type = req_type, + }; + esp_err_t post_err = esp_event_post(ZH_NRF_GATEWAY_EVENTS, + ZH_NRF_GATEWAY_EVENT_SENSOR_PAIRED, + &paired, sizeof(paired), + pdMS_TO_TICKS(100)); + if (post_err != ESP_OK) { + ESP_LOGW(TAG, "esp_event_post(paired) failed: %s", esp_err_to_name(post_err)); } }