wip:
This commit is contained in:
1 parent
0daf4de19f
commit
036b856a93
2 files changed
+148
-39
No files matched your search
+68
-21
@@ -10,8 +10,11 @@
|
||||
*
|
||||
* 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
|
||||
* - ZH_NRF_GATEWAY_EVENT_SENSOR_PAIRED — pairing completed (gateway
|
||||
* assigned a new id and the ACK was delivered)
|
||||
* - ZH_NRF_GATEWAY_EVENT_SENSOR_REGISTERED — data packet arrived from a
|
||||
* {id, type} that was missing from the binding table; it was added
|
||||
* on the fly (covers gateway replacement / NVS wipe without re-pairing)
|
||||
*
|
||||
* Binding table semantics:
|
||||
* - each binding is a pair {id, type};
|
||||
@@ -43,32 +46,29 @@ ESP_EVENT_DECLARE_BASE(ZH_NRF_GATEWAY_EVENTS);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
/**
|
||||
* Sensor telemetry received.
|
||||
* Event data: zh_nrf24_sensor_transmitted_data_t
|
||||
*/
|
||||
/** Sensor telemetry (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
|
||||
*/
|
||||
/** Pairing completed (data: zh_nrf_gateway_binding_t) */
|
||||
ZH_NRF_GATEWAY_EVENT_SENSOR_PAIRED,
|
||||
|
||||
/** Unknown {id, type} seen in a data packet, auto-added to bindings
|
||||
* (data: zh_nrf_gateway_binding_t) */
|
||||
ZH_NRF_GATEWAY_EVENT_SENSOR_REGISTERED,
|
||||
} zh_nrf_gateway_event_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
spi_host_device_t spi_host;
|
||||
int pin_sck;
|
||||
int pin_miso;
|
||||
int pin_mosi;
|
||||
int pin_csn;
|
||||
int pin_ce;
|
||||
int pin_irq;
|
||||
uint8_t channel;
|
||||
uint32_t address;
|
||||
uint32_t spi_clock_hz;
|
||||
spi_host_device_t spi_host; /*!< SPI host (SPI2_HOST / SPI3_HOST) */
|
||||
int pin_sck; /*!< SCK GPIO */
|
||||
int pin_miso; /*!< MISO GPIO */
|
||||
int pin_mosi; /*!< MOSI GPIO */
|
||||
int pin_csn; /*!< CSN GPIO (chip select, active low) */
|
||||
int pin_ce; /*!< CE GPIO (chip enable, active high) */
|
||||
int pin_irq; /*!< IRQ GPIO (open-drain, active low) */
|
||||
uint8_t channel; /*!< RF channel (0..125) */
|
||||
uint32_t address; /*!< 24-bit pipe address, transmitted as LE bytes */
|
||||
uint32_t spi_clock_hz; /*!< SPI clock, 0 → default 4 MHz */
|
||||
} zh_nrf_gateway_config_t;
|
||||
|
||||
/** A single {id, type} binding. */
|
||||
@@ -78,15 +78,62 @@ typedef struct
|
||||
uint8_t type; /**< zh_nrf24_sensor_type_t */
|
||||
} zh_nrf_gateway_binding_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize the gateway.
|
||||
*
|
||||
* Configures SPI, the IRQ GPIO with falling-edge interrupt, the nRF24L01+
|
||||
* transceiver (DPL enabled, RX mode) and prepares NVS and the default
|
||||
* event loop if they are not already initialized.
|
||||
*
|
||||
* @param[in] config Gateway configuration (must not be NULL)
|
||||
*
|
||||
* @return ESP_OK on success, or an ESP-IDF error code
|
||||
*/
|
||||
esp_err_t zh_nrf_gateway_init(const zh_nrf_gateway_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Start the receiver task.
|
||||
*
|
||||
* The task blocks on the IRQ semaphore (or polls the FIFO as a fallback
|
||||
* once per second) and dispatches received packets.
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
*/
|
||||
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).
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief Auto-register {id, type} if not present.
|
||||
*
|
||||
* Validates both fields, then adds the pair to the table and persists
|
||||
* to NVS if it is not already there. The id is taken from the packet
|
||||
* as-is (unlike pairing, which assigns a new id).
|
||||
*
|
||||
* @return 1 binding was newly added
|
||||
* @return 0 binding already existed
|
||||
* @return negative ESP error (invalid arg, NVS failure, etc.)
|
||||
*/
|
||||
int zh_nrf_gateway_binding_auto_register(uint8_t id, uint8_t type);
|
||||
|
||||
/**
|
||||
* @brief Delete a {id, type} binding.
|
||||
*
|
||||
|
||||
+80
-18
@@ -202,7 +202,7 @@ static esp_err_t nrf_irq_init(void)
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* NVS BINDING TABLE
|
||||
* NVS BINDING TABLE (internal)
|
||||
* ============================================================ */
|
||||
static esp_err_t bindings_load(void)
|
||||
{
|
||||
@@ -251,22 +251,30 @@ static esp_err_t bindings_save(void)
|
||||
return err;
|
||||
}
|
||||
|
||||
static uint8_t bindings_alloc_id(uint8_t type)
|
||||
/* Caller must hold bindings_mutex. */
|
||||
static bool bindings_exists_locked(uint8_t id, uint8_t type)
|
||||
{
|
||||
for (uint16_t id = BINDING_ID_MIN; id <= BINDING_ID_MAX; id++) {
|
||||
bool used = false;
|
||||
for (uint16_t i = 0; i < s_ctx.bindings.count; i++) {
|
||||
if (s_ctx.bindings.items[i].id == id &&
|
||||
s_ctx.bindings.items[i].type == type) {
|
||||
used = true;
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!used) return (uint8_t)id;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Caller must hold bindings_mutex. */
|
||||
static uint8_t bindings_alloc_id(uint8_t type)
|
||||
{
|
||||
for (uint16_t id = BINDING_ID_MIN; id <= BINDING_ID_MAX; id++) {
|
||||
if (!bindings_exists_locked((uint8_t)id, type)) {
|
||||
return (uint8_t)id;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Caller must hold bindings_mutex. */
|
||||
static esp_err_t bindings_add(uint8_t id, uint8_t type)
|
||||
{
|
||||
if (s_ctx.bindings.count >= ZH_NRF_GATEWAY_MAX_BINDINGS) {
|
||||
@@ -278,6 +286,7 @@ static esp_err_t bindings_add(uint8_t id, uint8_t type)
|
||||
return bindings_save();
|
||||
}
|
||||
|
||||
/* Caller must hold bindings_mutex. */
|
||||
static esp_err_t bindings_delete(uint8_t id, uint8_t type)
|
||||
{
|
||||
for (uint16_t i = 0; i < s_ctx.bindings.count; i++) {
|
||||
@@ -319,18 +328,38 @@ bool zh_nrf_gateway_binding_exists(uint8_t id, uint8_t type)
|
||||
{
|
||||
if (!s_ctx.initialized || !s_ctx.bindings_mutex) return false;
|
||||
xSemaphoreTake(s_ctx.bindings_mutex, portMAX_DELAY);
|
||||
bool found = false;
|
||||
for (uint16_t i = 0; i < s_ctx.bindings.count; i++) {
|
||||
if (s_ctx.bindings.items[i].id == id &&
|
||||
s_ctx.bindings.items[i].type == type) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool found = bindings_exists_locked(id, type);
|
||||
xSemaphoreGive(s_ctx.bindings_mutex);
|
||||
return found;
|
||||
}
|
||||
|
||||
int zh_nrf_gateway_binding_auto_register(uint8_t id, uint8_t type)
|
||||
{
|
||||
if (!s_ctx.initialized || !s_ctx.bindings_mutex) return -ESP_ERR_INVALID_STATE;
|
||||
if (id < BINDING_ID_MIN || id > BINDING_ID_MAX) return -ESP_ERR_INVALID_ARG;
|
||||
if (zh_get_nrf24_sensor_payload_size(type) == 0) return -ESP_ERR_INVALID_ARG;
|
||||
|
||||
xSemaphoreTake(s_ctx.bindings_mutex, portMAX_DELAY);
|
||||
|
||||
int result = 0;
|
||||
if (!bindings_exists_locked(id, type)) {
|
||||
esp_err_t err = bindings_add(id, type);
|
||||
if (err == ESP_OK) {
|
||||
result = 1;
|
||||
} else {
|
||||
if (s_ctx.bindings.count > 0 &&
|
||||
s_ctx.bindings.items[s_ctx.bindings.count - 1].id == id &&
|
||||
s_ctx.bindings.items[s_ctx.bindings.count - 1].type == type) {
|
||||
s_ctx.bindings.count--;
|
||||
}
|
||||
result = -(int)err;
|
||||
}
|
||||
}
|
||||
|
||||
xSemaphoreGive(s_ctx.bindings_mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
esp_err_t zh_nrf_gateway_binding_delete(uint8_t id, uint8_t type)
|
||||
{
|
||||
if (!s_ctx.initialized) return ESP_ERR_INVALID_STATE;
|
||||
@@ -526,7 +555,6 @@ static void handle_pairing_request(const uint8_t *data)
|
||||
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 = {
|
||||
@@ -564,6 +592,37 @@ static void handle_sensor_data(const uint8_t *buf, uint8_t len)
|
||||
len, expected, d.type);
|
||||
}
|
||||
|
||||
/* ----- Auto-register unknown {id, type} ----- */
|
||||
if (d.id >= BINDING_ID_MIN && d.id <= BINDING_ID_MAX) {
|
||||
int r = zh_nrf_gateway_binding_auto_register((uint8_t)d.id,
|
||||
(uint8_t)d.type);
|
||||
if (r == 1) {
|
||||
ESP_LOGI(TAG, "Auto-registered sensor: id=%d type=%d (%s)",
|
||||
d.id, d.type,
|
||||
zh_get_nrf24_sensor_type_name((zh_nrf24_sensor_type_t)d.type));
|
||||
|
||||
zh_nrf_gateway_binding_t b = {
|
||||
.id = (uint8_t)d.id,
|
||||
.type = (uint8_t)d.type,
|
||||
};
|
||||
esp_err_t perr = esp_event_post(ZH_NRF_GATEWAY_EVENTS,
|
||||
ZH_NRF_GATEWAY_EVENT_SENSOR_REGISTERED,
|
||||
&b, sizeof(b),
|
||||
pdMS_TO_TICKS(100));
|
||||
if (perr != ESP_OK) {
|
||||
ESP_LOGW(TAG, "esp_event_post(registered) failed: %s",
|
||||
esp_err_to_name(perr));
|
||||
}
|
||||
} else if (r < 0) {
|
||||
ESP_LOGW(TAG, "Auto-register failed for id=%d type=%d: %s",
|
||||
d.id, d.type, esp_err_to_name((esp_err_t)(-r)));
|
||||
}
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Sensor id %d out of range [%d..%d], skipping auto-register",
|
||||
d.id, BINDING_ID_MIN, BINDING_ID_MAX);
|
||||
}
|
||||
|
||||
/* ----- Parse type-specific fields ----- */
|
||||
switch (d.type) {
|
||||
case ZH_NRF24_SENSOR_TOUCH_SWITCH:
|
||||
break;
|
||||
@@ -598,11 +657,12 @@ static void handle_sensor_data(const uint8_t *buf, uint8_t len)
|
||||
break;
|
||||
}
|
||||
|
||||
/* ----- Post SENSOR_DATA ----- */
|
||||
esp_err_t err = esp_event_post(ZH_NRF_GATEWAY_EVENTS,
|
||||
ZH_NRF_GATEWAY_EVENT_SENSOR_DATA,
|
||||
&d, sizeof(d), pdMS_TO_TICKS(100));
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "esp_event_post failed: %s", esp_err_to_name(err));
|
||||
ESP_LOGW(TAG, "esp_event_post(data) failed: %s", esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -712,7 +772,7 @@ esp_err_t zh_nrf_gateway_init(const zh_nrf_gateway_config_t *config)
|
||||
nrf_write_register(NRF_REG_CONFIG, 0x00);
|
||||
nrf_write_register(NRF_REG_EN_AA, 0x01);
|
||||
nrf_write_register(NRF_REG_EN_RXADDR, 0x01);
|
||||
nrf_write_register(NRF_REG_SETUP_AW, 0x01);
|
||||
nrf_write_register(NRF_REG_SETUP_AW, 0x01); /* 3-byte address */
|
||||
nrf_write_register(NRF_REG_SETUP_RETR, 0xFF);
|
||||
nrf_write_register(NRF_REG_RF_CH, s_ctx.cfg.channel);
|
||||
nrf_write_register(NRF_REG_RF_SETUP, 0x26); /* 250 kbps, 0 dBm */
|
||||
@@ -728,10 +788,12 @@ esp_err_t zh_nrf_gateway_init(const zh_nrf_gateway_config_t *config)
|
||||
nrf_write_register(NRF_REG_STATUS,
|
||||
NRF_STATUS_RX_DR | NRF_STATUS_TX_DS | NRF_STATUS_MAX_RT);
|
||||
|
||||
/* Enable Dynamic Payload Length */
|
||||
nrf_activate();
|
||||
nrf_write_register(NRF_REG_FEATURE, NRF_FEATURE_EN_DPL);
|
||||
nrf_write_register(NRF_REG_DYNPD, NRF_DYNPD_DPL_P0);
|
||||
|
||||
/* Enter RX */
|
||||
nrf_write_register(NRF_REG_CONFIG, NRF_CONFIG_RX_UNMASKED);
|
||||
vTaskDelay(pdMS_TO_TICKS(5));
|
||||
nrf_ce_high();
|
||||
|
||||
Reference in new issue
Block a user