From 9a668b759ef693e3bf262065dfa24c1bb5f85558 Mon Sep 17 00:00:00 2001
From: Alexey Zholtikov <git@zh.com.ru>
Date: Thu, 16 May 2024 16:02:27 +0300
Subject: [PATCH] 5

---
 CMakeLists.txt    |   2 +-
 include/zh_rf24.h |  69 +++-------
 main.c            |  72 ++++++++++
 zh_rf24.c         | 336 ++++++++++++++++++++++++++++++++++++++++++----
 4 files changed, 401 insertions(+), 78 deletions(-)
 create mode 100644 main.c

diff --git a/CMakeLists.txt b/CMakeLists.txt
index e01365a..71e28a1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1 +1 @@
-idf_component_register(SRCS "zh_rf24.c" INCLUDE_DIRS "include" REQUIRES driver esp_event)
\ No newline at end of file
+idf_component_register(SRCS "main.c" "zh_rf24.c" INCLUDE_DIRS "include" REQUIRES driver esp_event)
\ No newline at end of file
diff --git a/include/zh_rf24.h b/include/zh_rf24.h
index 247bff5..8befd55 100644
--- a/include/zh_rf24.h
+++ b/include/zh_rf24.h
@@ -26,28 +26,24 @@
  * @brief Default values for zh_rf24_init_config_t structure for initial initialization of RF24 interface.
  *
  */
-#define ZH_RF24_INIT_CONFIG_DEFAULT()                    \
-    {                                                    \
-        .ce_pin = 1,                                     \
-        .csn_pin = 2,                                    \
-        .sck_pin = 3,                                    \
-        .mosi_pin = 4,                                   \
-        .miso_pin = 5,                                   \
-        .irq_pin = 6,                                    \
-        .work_mode = ZH_RF24_RECEIVER,                   \
-        .channel = 100,                                  \
-        .pa_level = ZH_RF24_PA_MAX,                      \
-        .data_rate = ZH_RF24_250KBPS,                    \
-        .tx_address = {0xE7, 0xE7, 0xE7, 0xE7, 0xE7},    \
-        .rx_address_preamble = {0xC2, 0xC2, 0xC2, 0xC2}, \
-        .rx_pipe_1_address = 0xC2,                       \
-        .rx_pipe_2_address = 0xC3,                       \
-        .rx_pipe_3_address = 0xC4,                       \
-        .rx_pipe_4_address = 0xC5,                       \
-        .rx_pipe_5_address = 0xC6,                       \
-        .task_priority = 3,                              \
-        .stack_size = 2048,                              \
-        .queue_size = 16                                 \
+#define ZH_RF24_INIT_CONFIG_DEFAULT()                        \
+    {                                                        \
+        .ce_pin = 1,                                         \
+        .csn_pin = 2,                                        \
+        .sck_pin = 3,                                        \
+        .mosi_pin = 4,                                       \
+        .miso_pin = 5,                                       \
+        .irq_pin = 6,                                        \
+        .channel = 100,                                      \
+        .tx_address = {0xE7, 0xE7, 0xE7, 0xE7, 0xE7},        \
+        .rx_pipe_1_address = {0xC2, 0xC2, 0xC2, 0xC2, 0xC2}, \
+        .rx_pipe_2_address = 0xC3,                           \
+        .rx_pipe_3_address = 0xC4,                           \
+        .rx_pipe_4_address = 0xC5,                           \
+        .rx_pipe_5_address = 0xC6,                           \
+        .task_priority = 3,                                  \
+        .stack_size = 2048,                                  \
+        .queue_size = 16                                     \
     }
 
 #ifdef __cplusplus
@@ -55,27 +51,6 @@ extern "C"
 {
 #endif
 
-    typedef enum
-    {
-        ZH_RF24_PA_MIN,
-        ZH_RF24_PA_LOW,
-        ZH_RF24_PA_HIGH,
-        ZH_RF24_PA_MAX,
-    } __attribute__((packed)) zh_rf24_pa_level_t;
-
-    typedef enum
-    {
-        ZH_RF24_1MBPS,
-        ZH_RF24_2MBPS,
-        ZH_RF24_250KBPS
-    } __attribute__((packed)) zh_rf24_data_rate_t;
-
-    typedef enum
-    {
-        ZH_RF24_TRANSMITTER,
-        ZH_RF24_RECEIVER
-    } __attribute__((packed)) zh_rf24_work_mode_t;
-
     /**
      * @brief Structure for initial initialization of RF24 interface.
      *
@@ -93,13 +68,9 @@ extern "C"
         uint8_t mosi_pin;
         uint8_t miso_pin;
         uint8_t irq_pin;
-        zh_rf24_work_mode_t work_mode;
         uint8_t channel;
-        zh_rf24_pa_level_t pa_level;
-        zh_rf24_data_rate_t data_rate;
         uint8_t tx_address[5];
-        uint8_t rx_address_preamble[4];
-        uint8_t rx_pipe_1_address;
+        uint8_t rx_pipe_1_address[5];
         uint8_t rx_pipe_2_address;
         uint8_t rx_pipe_3_address;
         uint8_t rx_pipe_4_address;
@@ -198,7 +169,7 @@ extern "C"
      *              - ESP_ERR_INVALID_STATE if queue for outgoing data is almost full
      *              - ESP_FAIL if ESP-NOW is not initialized
      */
-    esp_err_t zh_rf24_send(const uint8_t *data, const uint8_t data_len, const bool confirm);
+    esp_err_t zh_rf24_send(const uint8_t *data, const uint8_t data_len);
 
 #ifdef __cplusplus
 }
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..56258de
--- /dev/null
+++ b/main.c
@@ -0,0 +1,72 @@
+#include "nvs_flash.h"
+#include "esp_netif.h"
+#include "zh_rf24.h"
+#ifdef CONFIG_IDF_TARGET_ESP8266
+#include "esp_system.h"
+#else
+#include "esp_random.h"
+#endif
+
+void zh_rf24_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
+
+typedef struct
+{
+    char char_value[10];
+    int int_value;
+    float float_value;
+    bool bool_value;
+} example_message_t;
+
+void app_main(void)
+{
+    // esp_log_level_set("zh_rf24", ESP_LOG_NONE);
+    nvs_flash_init();
+    esp_netif_init();
+    esp_event_loop_create_default();
+    zh_rf24_init_config_t zh_rf24_init_config = ZH_RF24_INIT_CONFIG_DEFAULT();
+    zh_rf24_init(&zh_rf24_init_config);
+#ifdef CONFIG_IDF_TARGET_ESP8266
+    esp_event_handler_register(ZH_RF24, ESP_EVENT_ANY_ID, &zh_rf24_event_handler, NULL);
+#else
+    esp_event_handler_instance_register(ZH_RF24, ESP_EVENT_ANY_ID, &zh_rf24_event_handler, NULL, NULL);
+#endif
+    example_message_t send_message = {0};
+    strcpy(send_message.char_value, "CHAR");
+    send_message.float_value = 1.234;
+    send_message.bool_value = false;
+    for (;;)
+    {
+        send_message.int_value = esp_random();
+        // zh_rf24_send((uint8_t *)&send_message, sizeof(send_message), true);
+        vTaskDelay(5000 / portTICK_PERIOD_MS);
+    }
+}
+
+void zh_rf24_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
+{
+    switch (event_id)
+    {
+    case ZH_RF24_ON_RECV_EVENT:;
+        zh_rf24_event_on_recv_t *recv_data = event_data;
+        printf("Message via pipe %d is received. Data lenght %d bytes.\n", recv_data->pipe, recv_data->data_len);
+        example_message_t *recv_message = (example_message_t *)recv_data->data;
+        printf("Char %s\n", recv_message->char_value);
+        printf("Int %d\n", recv_message->int_value);
+        printf("Float %f\n", recv_message->float_value);
+        printf("Bool %d\n", recv_message->bool_value);
+        free(recv_data->data); // Do not delete to avoid memory leaks!
+        break;
+    case ZH_RF24_ON_SEND_EVENT:;
+        zh_rf24_event_on_send_t *send_data = event_data;
+        if (send_data->status == ZH_RF24_SEND_SUCCESS)
+        {
+            printf("Message sent success.\n");
+        }
+        else
+        {
+            printf("Message sent fail.\n");
+        }
+    default:
+        break;
+    }
+}
\ No newline at end of file
diff --git a/zh_rf24.c b/zh_rf24.c
index 933ce27..2637ea7 100644
--- a/zh_rf24.c
+++ b/zh_rf24.c
@@ -12,10 +12,178 @@
 #define ZH_SPI_HOST SPI2_HOST
 /// \endcond
 
+/* Команды */
+
+#define R_REGISTER 0x00         // + n Прочитать регистр n
+#define W_REGISTER 0x20         // + n Записать регистр n
+#define R_RX_PAYLOAD 0x61       // Принять данные данные из верхнего слота очереди приёмника.
+#define W_TX_PAYLOAD 0xA0       // Записать в очередь передатчика данные для отправки
+#define FLUSH_TX 0xE1           // Сбросить очередь передатчика
+#define FLUSH_RX 0xE2           // Сбросить очередь приёмника
+#define REUSE_TX_PL 0xE3        // Использовать повторно последний переданный пакет
+#define R_RX_PL_WID 0x60        // Прочитать размер данных принятого пакета в начале очереди приёмника.
+#define W_ACK_PAYLOAD 0xA8      // + p Записать данные для отправки с пакетом подтверждения по каналу p.
+#define W_TX_PAYLOAD_NOACK 0xB0 // Записать в очередь передатчика данные, для отправки без подтверждения
+#define NOP 0xFF                // Нет операции. Может быть использовано для чтения регистра статуса
+
+/* Регистры */
+
+#define CONFIG 0x00      // Регистр настроек
+#define EN_AA 0x01       // Выбор автоподтверждения
+#define EN_RXADDR 0x02   // Выбор каналов приёмника
+#define SETUP_AW 0x03    // Настройка размера адреса
+#define SETUP_RETR 0x04  // Настройка повторной отправки
+#define RF_CH 0x05       // Номер радиоканала, на котором осуществляется работа. От 0 до 125.
+#define RF_SETUP 0x06    // Настройка радиоканала
+#define STATUS 0x07      // Регистр статуса.
+#define OBSERVE_TX 0x08  // Количество повторов передачи и потерянных пакетов
+#define RPD 0x09         // Мощность принимаемого сигнала. Если младший бит = 1, то уровень более -64dBm
+#define RX_ADDR_P0 0x0A  // 3-5 байт (начиная с младшего байта). Адрес канала 0 приёмника.
+#define RX_ADDR_P1 0x0B  // 3-5 байт (начиная с младшего байта). Адрес канала 1 приёмника.
+#define RX_ADDR_P2 0x0C  // Младший байт адреса канала 2 приёмника. Старшие байты из RX_ADDR_P1
+#define RX_ADDR_P3 0x0D  // Младший байт адреса канала 3 приёмника. Старшие байты из RX_ADDR_P1
+#define RX_ADDR_P4 0x0E  // Младший байт адреса канала 4 приёмника. Старшие байты из RX_ADDR_P1
+#define RX_ADDR_P5 0x0F  // Младший байт адреса канала 5 приёмника. Старшие байты из RX_ADDR_P1
+#define TX_ADDR 0x10     // 3-5 байт (начиная с младшего байта). Адрес удалённого устройства для передачи
+#define RX_PW_P0 0x11    // Размер данных при приёме по каналу 0: от 1 до 32. 0 - канал не используется.
+#define RX_PW_P1 0x12    // Размер данных при приёме по каналу 1: от 1 до 32. 0 - канал не используется.
+#define RX_PW_P2 0x13    // Размер данных при приёме по каналу 2: от 1 до 32. 0 - канал не используется.
+#define RX_PW_P3 0x14    // Размер данных при приёме по каналу 3: от 1 до 32. 0 - канал не используется.
+#define RX_PW_P4 0x15    // Размер данных при приёме по каналу 4: от 1 до 32. 0 - канал не используется.
+#define RX_PW_P5 0x16    // Размер данных при приёме по каналу 5: от 1 до 32. 0 - канал не используется.
+#define FIFO_STATUS 0x17 // Состояние очередей FIFO приёмника и передатчика
+#define DYNPD 0x1C       // Выбор каналов приёмника для которых используется произвольная длина пакетов.
+#define FEATURE 0x1D     // Регистр опций
+
+/* Биты регистров */
+
+// CONFIG
+#define MASK_RX_DR 6  // Запрещает прерывание по RX_DR (получение пакета)
+#define MASK_TX_DS 5  // Запрещает прерывание по TX_DS (завершение отправки пакета)
+#define MASK_MAX_RT 4 // Запрещает прерывание по MAX_RT (превышение числа повторных попыток отправки)
+#define EN_CRC 3      // Включает CRC
+#define CRCO 2        // Размер поля CRC: 0 - 1 байт; 1 - 2 байта
+#define PWR_UP 1      // Включение питания
+#define PRIM_RX 0     // Выбор режима: 0 - PTX (передатчик) 1 - PRX (приёмник)
+
+// EN_AA
+#define ENAA_P5 5 // Включает автоподтверждение данных, полученных по каналу 5
+#define ENAA_P4 4 // Включает автоподтверждение данных, полученных по каналу 4
+#define ENAA_P3 3 // Включает автоподтверждение данных, полученных по каналу 3
+#define ENAA_P2 2 // Включает автоподтверждение данных, полученных по каналу 2
+#define ENAA_P1 1 // Включает автоподтверждение данных, полученных по каналу 1
+#define ENAA_P0 0 // Включает автоподтверждение данных, полученных по каналу 0
+
+// EN_RXADDR
+#define ERX_P5 5 // Включает канал 5 приёмника
+#define ERX_P4 4 // Включает канал 4 приёмника
+#define ERX_P3 3 // Включает канал 3 приёмника
+#define ERX_P2 2 // Включает канал 2 приёмника
+#define ERX_P1 1 // Включает канал 1 приёмника
+#define ERX_P0 0 // Включает канал 0 приёмника
+
+// SETUP_AW
+#define AW 0 // Два бита, Выбирает ширину поля адреса: 1 - 3 байта; 2 - 4 байта; 3 - 5 байт.
+
+#define SETUP_AW_3BYTES_ADDRESS (1 << AW)
+#define SETUP_AW_4BYTES_ADDRESS (2 << AW)
+#define SETUP_AW_5BYTES_ADDRESS (3 << AW)
+
+// SETUP_RETR
+#define ARD 4 // 4 бита. Задаёт значение задержки перед повторной отправкой пакета: 250 x (n + 1) мкс
+#define ARC 0 // 4 битай. Количество повторных попыток отправки, 0 - повторная отправка отключена.
+
+#define SETUP_RETR_DELAY_250MKS (0 << ARD)
+#define SETUP_RETR_DELAY_500MKS (1 << ARD)
+#define SETUP_RETR_DELAY_750MKS (2 << ARD)
+#define SETUP_RETR_DELAY_1000MKS (3 << ARD)
+#define SETUP_RETR_DELAY_1250MKS (4 << ARD)
+#define SETUP_RETR_DELAY_1500MKS (5 << ARD)
+#define SETUP_RETR_DELAY_1750MKS (6 << ARD)
+#define SETUP_RETR_DELAY_2000MKS (7 << ARD)
+#define SETUP_RETR_DELAY_2250MKS (8 << ARD)
+#define SETUP_RETR_DELAY_2500MKS (9 << ARD)
+#define SETUP_RETR_DELAY_2750MKS (10 << ARD)
+#define SETUP_RETR_DELAY_3000MKS (11 << ARD)
+#define SETUP_RETR_DELAY_3250MKS (12 << ARD)
+#define SETUP_RETR_DELAY_3500MKS (13 << ARD)
+#define SETUP_RETR_DELAY_3750MKS (14 << ARD)
+#define SETUP_RETR_DELAY_4000MKS (15 << ARD)
+
+#define SETUP_RETR_NO_RETRANSMIT (0 << ARC)
+#define SETUP_RETR_UP_TO_1_RETRANSMIT (1 << ARC)
+#define SETUP_RETR_UP_TO_2_RETRANSMIT (2 << ARC)
+#define SETUP_RETR_UP_TO_3_RETRANSMIT (3 << ARC)
+#define SETUP_RETR_UP_TO_4_RETRANSMIT (4 << ARC)
+#define SETUP_RETR_UP_TO_5_RETRANSMIT (5 << ARC)
+#define SETUP_RETR_UP_TO_6_RETRANSMIT (6 << ARC)
+#define SETUP_RETR_UP_TO_7_RETRANSMIT (7 << ARC)
+#define SETUP_RETR_UP_TO_8_RETRANSMIT (8 << ARC)
+#define SETUP_RETR_UP_TO_9_RETRANSMIT (9 << ARC)
+#define SETUP_RETR_UP_TO_10_RETRANSMIT (10 << ARC)
+#define SETUP_RETR_UP_TO_11_RETRANSMIT (11 << ARC)
+#define SETUP_RETR_UP_TO_12_RETRANSMIT (12 << ARC)
+#define SETUP_RETR_UP_TO_13_RETRANSMIT (13 << ARC)
+#define SETUP_RETR_UP_TO_14_RETRANSMIT (14 << ARC)
+#define SETUP_RETR_UP_TO_15_RETRANSMIT (15 << ARC)
+
+// RF_SETUP
+#define CONT_WAVE 7  // (Только для nRF24L01+) Непрерывная передача несущей (для тестов)
+#define RF_DR_LOW 5  // (Только для nRF24L01+) Включает скорость 250кбит/с. RF_DR_HIGH должен быть 0
+#define PLL_LOCK 4   // Для тестов
+#define RF_DR_HIGH 3 // Выбор скорости обмена (при значении бита RF_DR_LOW = 0): 0 - 1Мбит/с; 1 - 2Мбит/с
+#define RF_PWR 1     // 2бита. Выбирает мощность передатчика: 0 - -18dBm; 1 - -16dBm; 2 - -6dBm; 3 - 0dBm
+
+#define RF_SETUP_MINUS18DBM (0 << RF_PWR)
+#define RF_SETUP_MINUS12DBM (1 << RF_PWR)
+#define RF_SETUP_MINUS6DBM (2 << RF_PWR)
+#define RF_SETUP_0DBM (3 << RF_PWR)
+
+#define RF_SETUP_1MBPS (0 << RF_DR_HIGH)
+#define RF_SETUP_2MBPS (1 << RF_DR_HIGH)
+#define RF_SETUP_250KBPS (1 << RF_DR_LOW) // этот режим не должен использоваться с контролем доставки
+
+// STATUS
+#define RX_DR 6          // Флаг получения новых данных в FIFO приёмника. Для сброса флага нужно записать 1
+#define TX_DS 5          // Флаг завершения передачи. Для сброса флага нужно записать 1
+#define MAX_RT 4         // Флаг превышения установленного числа повторов. Без сброса (записать 1) обмен невозможен
+#define RX_P_NO 1        // 3 бита. Номер канала, данные для которого доступны в FIFO приёмника. 7 -  FIFO пусто.
+#define TX_FULL_STATUS 0 // Признак заполнения FIFO передатчика: 1 - заполнено; 0 - есть доступные слоты
+                         // (переименовано из TX_FULL во избежание путаницы с одноимённым битом из регистра FIFO_STATUS)
+
+// OBSERVE_TX
+#define PLOS_CNT 4 // 4 бита. Общее количество пакетов без подтверждения. Сбрасывается записью RF_CH
+#define ARC_CNT 0  // 4 бита. Количество предпринятых повторов при последней отправке.
+
+// FIFO_STATUS
+#define TX_REUSE 6     // Признак готовности последнего пакета для повторной отправки.
+#define TX_FULL_FIFO 5 // Флаг переполнения FIFO очереди передатчика.
+                       // (переименовано из TX_FULL во избежание путаницы с одноимённым битом из регистра STATUS)
+#define TX_EMPTY 4     // Флаг освобождения FIFO очереди передатчика.
+#define RX_FULL 1      // Флаг переполнения FIFO очереди приёмника.
+#define RX_EMPTY 0     // Флаг освобождения FIFO очереди приёмника.
+
+// DYNDP
+#define DPL_P5 5 // Включает приём пакетов произвольной длины по каналу 5
+#define DPL_P4 4 // Включает приём пакетов произвольной длины по каналу 4
+#define DPL_P3 3 // Включает приём пакетов произвольной длины по каналу 3
+#define DPL_P2 2 // Включает приём пакетов произвольной длины по каналу 2
+#define DPL_P1 1 // Включает приём пакетов произвольной длины по каналу 1
+#define DPL_P0 0 // Включает приём пакетов произвольной длины по каналу 0
+
+// FEATURE
+#define EN_DPL 2     // Включает поддержку приёма и передачи пакетов произвольной длины
+#define EN_ACK_PAY 1 // Разрешает передачу данных с пакетами подтверждения приёма
+#define EN_DYN_ACK 0 // Разрешает использование W_TX_PAYLOAD_NOACK
+
 static void s_zh_rf24_processing(void *pvParameter);
 static void s_zh_rf24_irq_isr_handler(void *arg);
 static void s_zh_rf24_irq_processing(void *pvParameter);
-static void s_zh_rf24_read_register(const uint8_t *address, uint8_t *value, const uint8_t length);
+static void s_zh_rf24_read_register(const uint8_t *reg, uint8_t *value, const uint8_t length);
+static void s_zh_rf24_write_register(const uint8_t *reg, uint8_t *value, const uint8_t length);
+static void s_zh_rf24_read_data(uint8_t *data, const uint8_t length);
+static void s_zh_rf24_write_data(uint8_t *data, const uint8_t length);
+static uint8_t s_zh_rf24_send_command(uint8_t *command);
 
 static const char *TAG = "zh_rf24";
 
@@ -37,7 +205,6 @@ typedef enum
 typedef struct
 {
     uint8_t pipe;
-    bool confirm;
     uint8_t *data;
     uint8_t data_len;
 } __attribute__((packed)) zh_rf24_queue_data_t;
@@ -99,8 +266,8 @@ esp_err_t zh_rf24_init(zh_rf24_init_config_t *config)
         .sclk_io_num = s_zh_rf24_init_config.sck_pin,
         .mosi_io_num = s_zh_rf24_init_config.mosi_pin,
         .miso_io_num = s_zh_rf24_init_config.miso_pin,
-        .quadwp_io_num = -1,
-        .quadhd_io_num = -1};
+        .quadwp_io_num = -1,  // ??
+        .quadhd_io_num = -1}; // ??
     if (spi_bus_initialize(ZH_SPI_HOST, &spi_bus_config, SPI_DMA_CH_AUTO) != ESP_OK)
     {
         ESP_LOGE(TAG, "RF24 initialization fail. SPI initialization error.");
@@ -109,28 +276,63 @@ esp_err_t zh_rf24_init(zh_rf24_init_config_t *config)
     spi_device_interface_config_t spi_device_interface_config = {
         .clock_speed_hz = 4000000,
         .spics_io_num = -1,
-        .queue_size = 7,
-        .mode = 0,
+        .queue_size = 7, // ??
+        .mode = 0,       // ??
         .flags = SPI_DEVICE_NO_DUMMY};
     if (spi_bus_add_device(ZH_SPI_HOST, &spi_device_interface_config, &s_zh_rf24_spi_handle) != ESP_OK)
     {
         ESP_LOGE(TAG, "RF24 initialization fail. SPI initialization error.");
         return ESP_FAIL;
     }
-    s_zh_rf24_irq_semaphore = xSemaphoreCreateBinary();
-    if (gpio_install_isr_service(0) != ESP_OK || gpio_isr_handler_add(s_zh_rf24_init_config.irq_pin, s_zh_rf24_irq_isr_handler, NULL) != ESP_OK)
+    for (uint8_t i = 0; i < 100; ++i)
     {
-        ESP_LOGE(TAG, "RF24 initialization fail. Internal error.");
-        return ESP_FAIL;
+        s_zh_rf24_write_register(CONFIG, (1 << EN_CRC) | (1 << CRCO) | (1 << PRIM_RX), 1);
+        uint8_t value = 0;
+        s_zh_rf24_read_register(CONFIG, &value, 1);
+        if (value == ((1 << EN_CRC) | (1 << CRCO) | (1 << PRIM_RX)))
+        {
+            s_zh_rf24_write_register(EN_AA, 0x3F, 1);                                          // Enable auto acknowledgment for all pipes.
+            s_zh_rf24_write_register(EN_RXADDR, 0x3F, 1);                                      // Enable all pipes.
+            s_zh_rf24_write_register(SETUP_AW, 0x03, 1);                                       // Setup address widths for 5 bytes.                                                                                         // выбор длины адреса 5 байт
+            s_zh_rf24_write_register(SETUP_RETR, 0x1F, 1);                                     // Setup automatic retransmission a maximum of 15 times with delay of 500 µs between attempts.
+            s_zh_rf24_write_register(RF_CH, &s_zh_rf24_init_config.channel, 1);                // Setup RF channel.
+            s_zh_rf24_write_register(RF_SETUP, 0x06, 1);                                       // Setup data rate 1 Mbps and 0 dBm RF output power in TX mode.
+            s_zh_rf24_write_register(RX_ADDR_P0, &s_zh_rf24_init_config.tx_address, 5);        // Setup receive address for pipe 0.
+            s_zh_rf24_write_register(RX_ADDR_P1, &s_zh_rf24_init_config.rx_pipe_1_address, 5); // Setup receive address for pipe 1.
+            s_zh_rf24_write_register(RX_ADDR_P2, &s_zh_rf24_init_config.rx_pipe_2_address, 1); // Setup receive address for pipe 2.
+            s_zh_rf24_write_register(RX_ADDR_P3, &s_zh_rf24_init_config.rx_pipe_3_address, 1); // Setup receive address for pipe 3.
+            s_zh_rf24_write_register(RX_ADDR_P4, &s_zh_rf24_init_config.rx_pipe_4_address, 1); // Setup receive address for pipe 4.
+            s_zh_rf24_write_register(RX_ADDR_P5, &s_zh_rf24_init_config.rx_pipe_5_address, 1); // Setup receive address for pipe 5.
+            s_zh_rf24_write_register(TX_ADDR, &s_zh_rf24_init_config.tx_address, 5);           // Setup transmitter address.
+            s_zh_rf24_write_register(RX_PW_P0, 32, 1);                                         // Setup the maximum data length for pipe 0.
+            s_zh_rf24_write_register(RX_PW_P1, 32, 1);                                         // Setup the maximum data length for pipe 1.
+            s_zh_rf24_write_register(RX_PW_P2, 32, 1);                                         // Setup the maximum data length for pipe 2.
+            s_zh_rf24_write_register(RX_PW_P3, 32, 1);                                         // Setup the maximum data length for pipe 3.
+            s_zh_rf24_write_register(RX_PW_P4, 32, 1);                                         // Setup the maximum data length for pipe 4.
+            s_zh_rf24_write_register(RX_PW_P5, 32, 1);                                         // Setup the maximum data length for pipe 5.
+            s_zh_rf24_write_register(FEATURE, 0x04, 1);                                        // Enable dynamic payload length.
+            s_zh_rf24_write_register(DYNPD, 0x3F, 1);                                          // Setup dynamic payload length for all pipes.
+            s_zh_rf24_write_register(CONFIG, (1 << EN_CRC) | (1 << CRCO) | (1 << PWR_UP) | (1 << PRIM_RX), 1);
+            s_zh_rf24_irq_semaphore = xSemaphoreCreateBinary();
+            if (gpio_install_isr_service(0) != ESP_OK || gpio_isr_handler_add(s_zh_rf24_init_config.irq_pin, s_zh_rf24_irq_isr_handler, NULL) != ESP_OK)
+            {
+                ESP_LOGE(TAG, "RF24 initialization fail. Internal error.");
+                return ESP_FAIL;
+            }
+            if (xTaskCreatePinnedToCore(&s_zh_rf24_irq_processing, "NULL", s_zh_rf24_init_config.stack_size, NULL, s_zh_rf24_init_config.task_priority, NULL, tskNO_AFFINITY) != pdPASS)
+            {
+                ESP_LOGE(TAG, "RF24 initialization fail. Internal error.");
+                return ESP_FAIL;
+            }
+            s_zh_rf24_is_initialized = true;
+            gpio_set_level(s_zh_rf24_init_config.ce_pin, 1);
+            ESP_LOGI(TAG, "RF24 initialization success.");
+            return ESP_OK;
+        }
+        esp_rom_delay_us(1);
     }
-    if (xTaskCreatePinnedToCore(&s_zh_rf24_irq_processing, "NULL", s_zh_rf24_init_config.stack_size, NULL, s_zh_rf24_init_config.task_priority, NULL, tskNO_AFFINITY) != pdPASS)
-    {
-        ESP_LOGE(TAG, "RF24 initialization fail. Internal error.");
-        return ESP_FAIL;
-    }
-    s_zh_rf24_is_initialized = true;
-    ESP_LOGI(TAG, "RF24 initialization success.");
-    return ESP_OK;
+    ESP_LOGE(TAG, "RF24 initialization fail. Module not connected or not responding.");
+    return ESP_FAIL;
 }
 
 esp_err_t zh_rf24_deinit(void)
@@ -138,9 +340,49 @@ esp_err_t zh_rf24_deinit(void)
     return 0;
 }
 
-esp_err_t zh_rf24_send(const uint8_t *data, const uint8_t data_len, const bool confirm)
+esp_err_t zh_rf24_send(const uint8_t *data, const uint8_t data_len)
 {
-    return 0;
+    ESP_LOGI(TAG, "Adding outgoing RF24 data to queue begin.");
+    if (s_zh_rf24_is_initialized == false)
+    {
+        ESP_LOGE(TAG, "Adding outgoing RF24 data to queue fail. RF24 not initialized.");
+        return ESP_FAIL;
+    }
+    if (data == NULL || data_len == 0 || data_len > ZH_RF24_MAX_MESSAGE_SIZE)
+    {
+        ESP_LOGE(TAG, "Adding outgoing RF24 data to queue fail. Invalid argument.");
+        return ESP_ERR_INVALID_ARG;
+    }
+    if (uxQueueSpacesAvailable(s_zh_rf24_queue_handle) < s_zh_rf24_init_config.queue_size / 10)
+    {
+        ESP_LOGW(TAG, "Adding outgoing RF24 data to queue fail. Queue is almost full.");
+        return ESP_ERR_INVALID_STATE;
+    }
+    zh_rf24_queue_t rf24_queue = {0};
+    rf24_queue.id = ZH_RF24_SEND;
+    zh_rf24_queue_data_t *send_data = &rf24_queue.data;
+    if (data_len / sizeof(void *) == 0)
+    {
+        send_data->data = heap_caps_malloc(data_len, MALLOC_CAP_32BIT);
+    }
+    else
+    {
+        send_data->data = heap_caps_malloc(data_len, MALLOC_CAP_8BIT);
+    }
+    if (send_data->data == NULL)
+    {
+        ESP_LOGE(TAG, "Adding outgoing ESP-NOW data to queue fail. Memory allocation fail or no free memory in the heap.");
+        return ESP_ERR_NO_MEM;
+    }
+    memset(send_data->data, 0, data_len);
+    memcpy(send_data->data, data, data_len);
+    send_data->data_len = data_len;
+    ESP_LOGI(TAG, "Adding outgoing RF24 data to queue success.");
+    if (xQueueSend(s_zh_rf24_queue_handle, &rf24_queue, portTICK_PERIOD_MS) != pdTRUE)
+    {
+        ESP_LOGE(TAG, "RF24 message processing task internal error.");
+    }
+    return ESP_OK;
 }
 
 static void s_zh_rf24_processing(void *pvParameter)
@@ -171,17 +413,55 @@ static void s_zh_rf24_irq_processing(void *pvParameter)
     vTaskDelete(NULL);
 }
 
-static void s_zh_rf24_read_register(const uint8_t *address, uint8_t *value, const uint8_t length)
+static void s_zh_rf24_read_register(const uint8_t *reg, uint8_t *value, const uint8_t length)
 {
+    // uint8_t status = 0;
     gpio_set_level(s_zh_rf24_init_config.csn_pin, 0);
     spi_transaction_t spi_transaction = {0};
-    // spi_transaction.length = DataLength * 8;
-    // spi_transaction.tx_buffer = Dataout;
-    // spi_transaction.rx_buffer = Datain;
+    spi_transaction.length = 8;
+    spi_transaction.tx_buffer = ((*reg & 0x1F) | R_REGISTER);
+    spi_transaction.rx_buffer = NULL;
+    spi_device_transmit(s_zh_rf24_spi_handle, &spi_transaction);
+    spi_transaction.length = length * 8;
+    spi_transaction.tx_buffer = NULL;
+    spi_transaction.rx_buffer = value;
     spi_device_transmit(s_zh_rf24_spi_handle, &spi_transaction);
-
-    // spi_transfer(dev, R_REGISTER | (REGISTER_MASK & reg));
-    // spi_read_byte(dev, value, value, len);
-
     gpio_set_level(s_zh_rf24_init_config.csn_pin, 1);
+}
+
+static void s_zh_rf24_write_register(const uint8_t *reg, uint8_t *value, const uint8_t length)
+{
+    // uint8_t status = 0;
+    gpio_set_level(s_zh_rf24_init_config.csn_pin, 0);
+    spi_transaction_t spi_transaction = {0};
+    spi_transaction.length = 8;
+    spi_transaction.tx_buffer = ((*reg & 0x1F) | W_REGISTER);
+    spi_transaction.rx_buffer = NULL;
+    spi_device_transmit(s_zh_rf24_spi_handle, &spi_transaction);
+    spi_transaction.length = length * 8;
+    spi_transaction.tx_buffer = value;
+    spi_transaction.rx_buffer = NULL;
+    spi_device_transmit(s_zh_rf24_spi_handle, &spi_transaction);
+    gpio_set_level(s_zh_rf24_init_config.csn_pin, 1);
+}
+
+static void s_zh_rf24_read_data(uint8_t *data, const uint8_t length)
+{
+}
+
+static void s_zh_rf24_write_data(uint8_t *data, const uint8_t length)
+{
+}
+
+static uint8_t s_zh_rf24_send_command(uint8_t *command)
+{
+    uint8_t status = 0;
+    gpio_set_level(s_zh_rf24_init_config.csn_pin, 0);
+    spi_transaction_t spi_transaction = {0};
+    spi_transaction.length = 8;
+    spi_transaction.tx_buffer = command;
+    spi_transaction.rx_buffer = status;
+    spi_device_transmit(s_zh_rf24_spi_handle, &spi_transaction);
+    gpio_set_level(s_zh_rf24_init_config.csn_pin, 1);
+    return status;
 }
\ No newline at end of file