/** * @file zh_nrf24_gateway.h * * nRF24L01+ gateway for ESP-IDF. * * 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. * * Events emitted: * - ZH_NRF_GATEWAY_EVENT_SENSOR_DATA — sensor telemetry * - 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}; * - the same id may be reused across different types; * - on pairing, the gateway allocates the smallest free id in 1..254 * for the requested type; * - deleting a binding makes its id available to the next pairing * request of the same type. */ #pragma once #include #include #include #include #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" #include "driver/gpio.h" #include "esp_log.h" #include "esp_rom_sys.h" #include "nvs_flash.h" #include "nvs.h" #include "esp_err.h" #include "esp_event.h" #include "driver/spi_master.h" #include "zh_nrf24_config.h" #ifdef __cplusplus extern "C" { #endif extern TaskHandle_t zh_nrf24_gateway; typedef struct { 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_nrf24_gateway_init_config_t; typedef enum { ZH_NRF_GATEWAY_EVENT_SENSOR_DATA, ZH_NRF_GATEWAY_EVENT_SENSOR_PAIRED, ZH_NRF_GATEWAY_EVENT_SENSOR_REGISTERED, } zh_nrf24_gateway_event_t; ESP_EVENT_DECLARE_BASE(ZH_NRF24_GATEWAY_EVENTS); /** A single {id, type} binding. */ typedef struct { uint8_t id; /**< 1..254, 0xFF = unassigned */ 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_nrf24_gateway_init_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. * * 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 / 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); /** 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); /** 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); /** Remove all bindings (persisted). */ esp_err_t zh_nrf_gateway_binding_clear_all(void); #ifdef __cplusplus } #endif