This commit is contained in:
aZholtikov committed 2026-10-31 09:25:15 +03:00
1 parent 30bbbc9303
commit 0daf4de19f
3 files changed
+72 -114

No files matched your search

+31 -58
View File
@@ -1,8 +1,3 @@
# esp_component_template
esp_component_template
```c
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@@ -12,7 +7,7 @@ esp_component_template
#include "esp_event.h" #include "esp_event.h"
#include "zh_nrf24_gateway.h" #include "zh_nrf24_gateway.h"
// #include "zh_nrf24_config.h" #include "zh_nrf24_config.h"
static const char *TAG = "MAIN"; static const char *TAG = "MAIN";
@@ -32,31 +27,23 @@ static const zh_nrf_gateway_config_t s_gw_config = {
.spi_clock_hz = 0, .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]; ESP_LOGI(TAG, ">>> SENSOR PAIRED: id=%u type=%u (%s)",
size_t got = zh_nrf_gateway_bindings_get(tmp, ZH_NRF_GATEWAY_MAX_BINDINGS); 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++) { * - публикацию в MQTT: "sensor/paired" {id, type};
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)); * и т.п.
} */
} }
static void gateway_event_handler(void *arg, static void handle_sensor_data_event(const zh_nrf24_sensor_transmitted_data_t *d)
esp_event_base_t base,
int32_t id,
void *event_data)
{ {
if (base != ZH_NRF_GATEWAY_EVENTS) return;
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); 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", ESP_LOGI(TAG, "<<< sensor id=%d type=%s battery=%.2fV",
@@ -92,28 +79,30 @@ static void gateway_event_handler(void *arg,
default: default:
break; break;
} }
break;
} }
static void gateway_event_handler(void *arg,
esp_event_base_t base,
int32_t id,
void *event_data)
{
if (base != ZH_NRF_GATEWAY_EVENTS) return;
switch (id)
{
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: default:
break; 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) void app_main(void)
{ {
ESP_LOGI(TAG, "Starting nRF24L01+ gateway"); ESP_LOGI(TAG, "Starting nRF24L01+ gateway");
@@ -126,24 +115,8 @@ void app_main(void)
NULL, NULL)); NULL, NULL));
ESP_ERROR_CHECK(zh_nrf_gateway_init(&s_gw_config)); ESP_ERROR_CHECK(zh_nrf_gateway_init(&s_gw_config));
dump_bindings("after init");
ESP_ERROR_CHECK(zh_nrf_gateway_start()); ESP_ERROR_CHECK(zh_nrf_gateway_start());
ESP_LOGI(TAG, "Gateway running: channel=%d, address=0x%06X", ESP_LOGI(TAG, "Gateway running: channel=%d, address=0x%06X",
GW_CHANNEL, GW_ADDRESS); 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-запроса
* того же типа.
*/
} }
```
+23 -42
View File
@@ -6,7 +6,12 @@
* Receives RF sensor telemetry and posts it as events on the default * Receives RF sensor telemetry and posts it as events on the default
* ESP event loop. Pairing is fully internal: the gateway allocates * ESP event loop. Pairing is fully internal: the gateway allocates
* per-type device IDs, persists the binding table in NVS and sends * 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: * Binding table semantics:
* - each binding is a pair {id, type}; * - each binding is a pair {id, type};
@@ -38,8 +43,18 @@ ESP_EVENT_DECLARE_BASE(ZH_NRF_GATEWAY_EVENTS);
typedef enum 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, 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; } zh_nrf_gateway_event_t;
typedef struct typedef struct
@@ -68,61 +83,27 @@ esp_err_t zh_nrf_gateway_start(void);
/* ===================== BINDING TABLE API ===================== */ /* ===================== 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); 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); 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. * @brief Delete a {id, type} binding.
* *
* Composite key: both @p id AND @p type must match an existing entry. * Composite key: both @p id AND @p type must match. If only one matches,
* If only one of them matches, nothing is deleted and ESP_ERR_NOT_FOUND * nothing is deleted and ESP_ERR_NOT_FOUND is returned.
* is returned. The id becomes immediately available for the next
* pairing request of the same type. The change is persisted to NVS.
* *
* @return ESP_OK on success * @return ESP_OK / ESP_ERR_NOT_FOUND / ESP_ERR_INVALID_ARG / ESP_ERR_INVALID_STATE / NVS error
* @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
*/ */
esp_err_t zh_nrf_gateway_binding_delete(uint8_t id, uint8_t type); esp_err_t zh_nrf_gateway_binding_delete(uint8_t id, uint8_t type);
/** /** Delete every binding whose id equals @p id (any type). Returns count, or -err. */
* @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
*/
int zh_nrf_gateway_bindings_delete_by_id(uint8_t id); int zh_nrf_gateway_bindings_delete_by_id(uint8_t id);
/** /** Delete every binding whose type equals @p type (any id). Returns count, or -err. */
* @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
*/
int zh_nrf_gateway_bindings_delete_by_type(uint8_t type); 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); esp_err_t zh_nrf_gateway_binding_clear_all(void);
#ifdef __cplusplus #ifdef __cplusplus
+18 -14
View File
@@ -68,7 +68,6 @@ static const char *TAG = "NRF_GW";
#define NVS_NAMESPACE "zh_gw" #define NVS_NAMESPACE "zh_gw"
#define NVS_KEY_BINDINGS "bindings" #define NVS_KEY_BINDINGS "bindings"
/* Valid id range for allocation */
#define BINDING_ID_MIN 1 #define BINDING_ID_MIN 1
#define BINDING_ID_MAX 254 #define BINDING_ID_MAX 254
@@ -205,14 +204,13 @@ static esp_err_t nrf_irq_init(void)
/* ============================================================ /* ============================================================
* NVS BINDING TABLE * NVS BINDING TABLE
* ============================================================ */ * ============================================================ */
static esp_err_t bindings_load(void) static esp_err_t bindings_load(void)
{ {
nvs_handle_t h; nvs_handle_t h;
esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READONLY, &h); esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READONLY, &h);
if (err != ESP_OK) { if (err != ESP_OK) {
memset(&s_ctx.bindings, 0, sizeof(s_ctx.bindings)); 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); size_t size = sizeof(s_ctx.bindings);
@@ -253,7 +251,6 @@ static esp_err_t bindings_save(void)
return err; 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) static uint8_t bindings_alloc_id(uint8_t type)
{ {
for (uint16_t id = BINDING_ID_MIN; id <= BINDING_ID_MAX; id++) { 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 * BINDING TABLE PUBLIC API
* ============================================================ */ * ============================================================ */
size_t zh_nrf_gateway_bindings_get(zh_nrf_gateway_binding_t *buf, size_t max_count) 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; 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) esp_err_t zh_nrf_gateway_binding_delete(uint8_t id, uint8_t type)
{ {
if (!s_ctx.initialized) return ESP_ERR_INVALID_STATE; if (!s_ctx.initialized) return ESP_ERR_INVALID_STATE;
/* Argument validation */
if (id == 0 || id == 0xFF) return ESP_ERR_INVALID_ARG; if (id == 0 || id == 0xFF) return ESP_ERR_INVALID_ARG;
if (zh_get_nrf24_sensor_payload_size(type) == 0) 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); xSemaphoreGive(s_ctx.bindings_mutex);
if (err != ESP_OK) return -(int)err; if (err != ESP_OK) return -(int)err;
if (deleted > 0) { if (deleted > 0) ESP_LOGI(TAG, "Deleted %d binding(s) with id=%u", deleted, id);
ESP_LOGI(TAG, "Deleted %d binding(s) with id=%u", deleted, id); else ESP_LOGW(TAG, "No bindings with id=%u", id);
} else {
ESP_LOGW(TAG, "No bindings with id=%u", id);
}
return deleted; 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) static void handle_pairing_request(const uint8_t *data)
{ {
uint8_t req_type = data[0]; 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); xSemaphoreTake(s_ctx.bindings_mutex, portMAX_DELAY);
bindings_delete(new_id, req_type); bindings_delete(new_id, req_type);
xSemaphoreGive(s_ctx.bindings_mutex); xSemaphoreGive(s_ctx.bindings_mutex);
} else { return;
}
/* --- ACK delivered → binding is final. Notify the application. --- */
ESP_LOGI(TAG, "Pairing complete: id=%u type=%u", new_id, req_type); 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));
} }
} }