vad7 2 лет назад
Родитель
Сommit
42d8e2c494
4 измененных файлов с 176 добавлено и 317 удалено
  1. 15 15
      Distr/nrf24batch/CO2_mini.txt
  2. 60 235
      lib/nrf24/nrf24.c
  3. 5 0
      lib/nrf24/nrf24.h
  4. 96 67
      nrf24batch.c

+ 15 - 15
Distr/nrf24batch/CO2_mini.txt

@@ -2,15 +2,17 @@ Info: CO2 sensor mini
 Rate: 1
 Ch: 121
 CRC: 2
+DPL: 0
 Address: C8C8CF
-Delay_ms: 10
+Resend: 3
+Delay_ms: 30
 
 Payload struct: 2,1,1
 EEPROM=0; RAM=1; PROGMEM=2; ID=3; RESET=4
 
 R default: ,EEPROM,0xC1
 W default: n,,0x81
-Write start: ,,0x8F
+Write start: 0,0,0x8F
 
 R: ID*=,ID
 R: OSCCAL=0x51,RAM
@@ -24,10 +26,7 @@ R: Ch=2
 W: Ch=,2
 
 R: nRF RETR=3
-W: nRF RETR 1/750uS=0x21,3
-
-R: AnswerDelay=9
-W: AnswerDelay=,9
+W: nRF RETR=,3
 
 R: Send period=4
 W: Send period=,4
@@ -38,19 +37,20 @@ W: CO2 threshold=,5,0x82
 R: CO2 correct*2=7,,0xC2
 W: CO2 correct=,7,0x82
 
-R: FanLSB[10]=i:10
-W: FanLSB=,i:19
+R: FanLSB[10]=i:9
+W: FanLSB=,i:9
 
 W: Reset=,RESET,0xC1
 
 RBatch: Settings: ID;OSCCAL;RxAddr;Ch;nRF RETR;CO2 threshold;CO2 correct;FanLSB
 RBatch: Settings2: OSCCAL;OSCCAL_EMEM;RxAddr;Ch;nRF RETR;CO2 threshold;CO2 correct;FanLSB
 
-WBatch: Default Ch-121: RxAddr=0xCF;Ch=121;CO2 threshold=1000;CO2 correct=0;Send period=30;FanLSB={0xC1,0xC2,0xC3,0};Reset
-WBatch: Default Ch-10: RxAddr=0xCF;Ch=10;CO2 threshold=1000;CO2 correct=0;Send period=31;FanLSB={0xC1,0};Reset
+WBatch: Default Ch-121: RxAddr=0xCF;Ch=121;CO2 threshold=1000;CO2 correct=0;Send period=30;nRF RETR=0x17;FanLSB={0xC1,0};Reset
+WBatch: Default Ch-10: RxAddr=0xCF;Ch=10;CO2 threshold=1000;CO2 correct=0;Send period=31;FanLSB={0xC1,0xC2,0};Reset
 WBatch: CO2: CO2 threshold=1000;CO2 correct=1
-WBatch: Fan: FanLSB={0xC2,0xC3,0};Reset
-WBatch: DELAY=0: AnswerDelay=0
-WBatch: DELAY=100: AnswerDelay=100
-WBatch: DELAY=200: AnswerDelay=200
-WBatch: DELAY=255: AnswerDelay=255
+WBatch: Fan2: FanLSB={0xC2,0xC3,0};Reset
+WBatch: RETR 0x17: nRF RETR=0x17
+WBatch: RETR 0x2F: nRF RETR=0x2F
+WBatch: RETR 0x0F: nRF RETR=0x0F
+WBatch: RETR 0x01: nRF RETR=0x01
+WBatch: Reset: Reset

+ 60 - 235
lib/nrf24/nrf24.c

@@ -1,4 +1,4 @@
-// Modified by vad7, 25.11.2022
+// Modified by vad7, 24.02.2023
 //
 #include "nrf24.h"
 #include <furi.h>
@@ -32,33 +32,34 @@ void nrf24_spi_trx(
 }
 
 uint8_t nrf24_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data) {
-    uint8_t tx[2] = {W_REGISTER | (REGISTER_MASK & reg), data};
-    uint8_t rx[2] = {0};
-    nrf24_spi_trx(handle, tx, rx, 2);
+    uint8_t buf[] = {W_REGISTER | (REGISTER_MASK & reg), data};
+    nrf24_spi_trx(handle, buf, buf, 2);
     //FURI_LOG_D("NRF_WR", " #%02X=%02X", reg, data);
-    return rx[0];
+    return buf[0];
 }
 
 uint8_t nrf24_write_buf_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size) {
-    uint8_t tx[size + 1];
-    uint8_t rx[size + 1];
-    memset(rx, 0, size + 1);
-    tx[0] = W_REGISTER | (REGISTER_MASK & reg);
-    memcpy(&tx[1], data, size);
-    nrf24_spi_trx(handle, tx, rx, size + 1);
+    uint8_t buf[size + 1];
+    buf[0] = W_REGISTER | (REGISTER_MASK & reg);
+    memcpy(&buf[1], data, size);
+    nrf24_spi_trx(handle, buf, buf, size + 1);
     //FURI_LOG_D("NRF_WR", " #%02X(%02X)=0x%02X%02X%02X%02X%02X", reg, size, data[0], data[1], data[2], data[3], data[4] );
-    return rx[0];
+    return buf[0];
 }
 
 uint8_t nrf24_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size) {
-    uint8_t tx[size + 1];
-    uint8_t rx[size + 1];
-    memset(rx, 0, size + 1);
-    tx[0] = R_REGISTER | (REGISTER_MASK & reg);
-    memset(&tx[1], 0, size);
-    nrf24_spi_trx(handle, tx, rx, size + 1);
-    memcpy(data, &rx[1], size);
-    return rx[0];
+    uint8_t buf[size + 1];
+    memset(buf, 0, size + 1);
+    buf[0] = R_REGISTER | (REGISTER_MASK & reg);
+    nrf24_spi_trx(handle, buf, buf, size + 1);
+    memcpy(data, &buf[1], size);
+    return buf[0];
+}
+
+uint8_t nrf24_read_register(FuriHalSpiBusHandle* handle, uint8_t reg) {
+    uint8_t buf[] = { R_REGISTER | (REGISTER_MASK & reg), 0xFF };
+    nrf24_spi_trx(handle, buf, buf, 2);
+    return buf[1];
 }
 
 uint8_t nrf24_flush_rx(FuriHalSpiBusHandle* handle) {
@@ -90,10 +91,9 @@ uint8_t nrf24_set_maclen(FuriHalSpiBusHandle* handle, uint8_t maclen) {
 }
 
 uint8_t nrf24_status(FuriHalSpiBusHandle* handle) {
-    uint8_t status;
-    uint8_t tx[] = {R_REGISTER | (REGISTER_MASK & REG_STATUS)};
-    nrf24_spi_trx(handle, tx, &status, 1);
-    return status;
+    uint8_t tx = RF24_NOP;
+    nrf24_spi_trx(handle, &tx, &tx, 1);
+    return tx;
 }
 
 uint32_t nrf24_get_rate(FuriHalSpiBusHandle* handle) {
@@ -191,32 +191,33 @@ uint8_t nrf24_set_packetlen(FuriHalSpiBusHandle* handle, uint8_t len) {
 // packet_size: 0 - dyn payload (read from PL_WID), 1 - read from pipe size, >1 - override
 uint8_t nrf24_rxpacket(FuriHalSpiBusHandle* handle, uint8_t* packet, uint8_t* ret_packetsize, uint8_t packet_size) {
     uint8_t status = 0;
-    uint8_t tx_cmd[33] = {0}; // 32 max payload size + 1 for command
-    uint8_t tmp_packet[33] = {0};
+    uint8_t buf[33]; // 32 max payload size + 1 for command
 
     status = nrf24_status(handle);
     if(!(status & RX_DR)) {
-        tx_cmd[0] = R_REGISTER | (REGISTER_MASK & REG_FIFO_STATUS);
-        nrf24_spi_trx(handle, tx_cmd, tmp_packet, 2);
-        if((tmp_packet[1] & 1) == 0) status |= RX_DR; // packet in FIFO buffer
+        if((nrf24_read_register(handle, REG_FIFO_STATUS) & 1) == 0) {
+            FURI_LOG_D("NRF", "FIFO PKT");
+            status |= RX_DR; // packet in FIFO buffer
+        }
     }
     if(status & RX_DR) {
         if(packet_size == 1)
             packet_size = nrf24_get_packetlen(handle, (status >> 1) & 7);
         else if(packet_size == 0){
-            tx_cmd[0] = R_RX_PL_WID; tx_cmd[1] = 0;
-            nrf24_spi_trx(handle, tx_cmd, tmp_packet, 2);
-            packet_size = tmp_packet[1];
+            buf[0] = R_RX_PL_WID; buf[1] = 0xFF;
+            nrf24_spi_trx(handle, buf, buf, 2);
+            packet_size = buf[1];
         }
         if(packet_size > 32 || packet_size == 0) packet_size = 32;
-        tx_cmd[0] = R_RX_PAYLOAD; tx_cmd[1] = 0;
-        nrf24_spi_trx(handle, tx_cmd, tmp_packet, packet_size + 1);
-        memcpy(packet, &tmp_packet[1], packet_size);
+        memset(buf, 0, packet_size + 1);
+        buf[0] = R_RX_PAYLOAD;
+        nrf24_spi_trx(handle, buf, buf, packet_size + 1);
+        memcpy(packet, &buf[1], packet_size);
         nrf24_write_reg(handle, REG_STATUS, RX_DR); // clear RX_DR
     }
-    if(status & (TX_DS | MAX_RT)) { // MAX_RT, TX_DS
-        nrf24_write_reg(handle, REG_STATUS, (TX_DS | MAX_RT)); // clear RX_DR, MAX_RT.
-    }
+    // if(status & (TX_DS | MAX_RT)) { // MAX_RT, TX_DS
+    //     nrf24_write_reg(handle, REG_STATUS, (TX_DS | MAX_RT)); // clear RX_DR, MAX_RT.
+    // }
 
     *ret_packetsize = packet_size;
     return status;
@@ -225,28 +226,21 @@ uint8_t nrf24_rxpacket(FuriHalSpiBusHandle* handle, uint8_t* packet, uint8_t* re
 // Return 0 when error
 uint8_t nrf24_txpacket(FuriHalSpiBusHandle* handle, uint8_t* payload, uint8_t size, bool ack) {
     uint8_t status = 0;
-    uint8_t tx[size + 1];
-    uint8_t rx[size + 1];
-    memset(tx, 0, size + 1);
-    memset(rx, 0, size + 1);
-
-    if(!ack)
-        tx[0] = W_TX_PAYLOAD_NOACK;
-    else
-        tx[0] = W_TX_PAYLOAD;
-
-    memcpy(&tx[1], payload, size);
-    nrf24_spi_trx(handle, tx, rx, size + 1);
+    uint8_t buf[size + 1];
+    buf[0] = ack ? W_TX_PAYLOAD : W_TX_PAYLOAD_NOACK;
+    memcpy(&buf[1], payload, size);
     nrf24_set_tx_mode(handle);
-
+    nrf24_spi_trx(handle, buf, buf, size + 1);
     uint32_t start_time = furi_get_tick();
     do {
-        furi_delay_ms(1);
+        furi_delay_us(100);
         status = nrf24_status(handle);
-    } while(!(status & (TX_DS | MAX_RT)) && furi_get_tick() - start_time < 500UL);
-
-    if(status & MAX_RT) nrf24_flush_tx(handle);
-
+    } while(!(status & (TX_DS | MAX_RT)) && furi_get_tick() - start_time < 100UL);
+    if(status & MAX_RT) {
+        if(furi_log_get_level() == FuriLogLevelDebug) FURI_LOG_D("NRF", "MAX RT: %X (%X)", nrf24_read_register(handle, REG_OBSERVE_TX), status);
+        nrf24_flush_tx(handle);
+    }
+    furi_hal_gpio_write(nrf24_CE_PIN, false);
     //nrf24_set_idle(handle);
     if(status & (TX_DS | MAX_RT)) nrf24_write_reg(handle, REG_STATUS, TX_DS | MAX_RT);
     return status & TX_DS;
@@ -268,109 +262,29 @@ uint8_t nrf24_set_idle(FuriHalSpiBusHandle* handle) {
     nrf24_read_reg(handle, REG_CONFIG, &cfg, 1);
     cfg &= 0xfc; // clear bottom two bits to power down the radio
     status = nrf24_write_reg(handle, REG_CONFIG, cfg);
-    //nr204_write_reg(handle, REG_EN_RXADDR, 0x0);
     furi_hal_gpio_write(nrf24_CE_PIN, false);
     return status;
 }
 
 uint8_t nrf24_set_rx_mode(FuriHalSpiBusHandle* handle) {
-    uint8_t status = 0;
     uint8_t cfg = 0;
-    //status = nrf24_write_reg(handle, REG_CONFIG, 0x0F); // enable 2-byte CRC, PWR_UP, and PRIM_RX
-    nrf24_read_reg(handle, REG_CONFIG, &cfg, 1);
+    cfg = nrf24_read_register(handle, REG_CONFIG);
     cfg |= 0x03; // PWR_UP, and PRIM_RX
-    status = nrf24_write_reg(handle, REG_CONFIG, cfg);
-    //nr204_write_reg(REG_EN_RXADDR, 0x03) // Set RX Pipe 0 and 1
+    cfg = nrf24_write_reg(handle, REG_CONFIG, cfg);
     furi_hal_gpio_write(nrf24_CE_PIN, true);
-    //furi_delay_ms(2);
-    return status;
+    return cfg;
 }
 
 uint8_t nrf24_set_tx_mode(FuriHalSpiBusHandle* handle) {
-    uint8_t status = 0;
-    uint8_t cfg = 0;
+    uint8_t reg;
     furi_hal_gpio_write(nrf24_CE_PIN, false);
-    nrf24_write_reg(handle, REG_STATUS, TX_DS | MAX_RT);
-    //status = nrf24_write_reg(handle, REG_CONFIG, 0x0E); // enable 2-byte CRC, PWR_UP
-    nrf24_read_reg(handle, REG_CONFIG, &cfg, 1);
-    cfg &= 0xFE; // disable PRIM_RX
-    cfg |= 0x02; // PWR_UP
-    status = nrf24_write_reg(handle, REG_CONFIG, cfg);
+    //nrf24_write_reg(handle, REG_STATUS, TX_DS | MAX_RT);
+    reg = nrf24_read_register(handle, REG_CONFIG);
+    reg &= ~0x01; // disable PRIM_RX
+    reg |= 0x02; // PWR_UP
+    reg = nrf24_write_reg(handle, REG_CONFIG, reg);
     furi_hal_gpio_write(nrf24_CE_PIN, true);
-    //furi_delay_ms(2);
-    return status;
-}
-
-void nrf24_configure(
-    FuriHalSpiBusHandle* handle,
-    uint8_t rate,
-    uint8_t* srcmac,
-    uint8_t* dstmac,
-    uint8_t maclen,
-    uint8_t channel,
-    bool noack,
-    bool disable_aa) {
-    assert(channel <= 125);
-    assert(rate == 1 || rate == 2);
-    if(rate == 2)
-        rate = 8; // 2Mbps
-    else
-        rate = 0; // 1Mbps
-
-    nrf24_write_reg(handle, REG_CONFIG, 0x00); // Stop nRF
-    nrf24_set_idle(handle);
-    nrf24_write_reg(handle, REG_STATUS, 0x70); // clear interrupts
-    if(disable_aa)
-        nrf24_write_reg(handle, REG_EN_AA, 0x00); // Disable Shockburst
-    else
-        nrf24_write_reg(handle, REG_EN_AA, 0x1F); // Enable Shockburst
-
-    nrf24_write_reg(handle, REG_DYNPD, 0x3F); // enable dynamic payload length on all pipes
-    if(noack)
-        nrf24_write_reg(handle, REG_FEATURE, 0x05); // disable payload-with-ack, enable noack
-    else {
-        nrf24_write_reg(handle, REG_CONFIG, 0x0C); // 2 byte CRC
-        nrf24_write_reg(handle, REG_FEATURE, 0x07); // enable dyn payload and ack
-        nrf24_write_reg(
-            handle, REG_SETUP_RETR, 0x1f); // 15 retries for AA, 500us auto retransmit delay
-    }
-
-    nrf24_set_idle(handle);
-    nrf24_flush_rx(handle);
-    nrf24_flush_tx(handle);
-
-    if(maclen) nrf24_set_maclen(handle, maclen);
-    if(srcmac) nrf24_set_src_mac(handle, srcmac, maclen);
-    if(dstmac) nrf24_set_dst_mac(handle, dstmac, maclen);
-
-    nrf24_write_reg(handle, REG_RF_CH, channel);
-    nrf24_write_reg(handle, REG_RF_SETUP, rate);
-    furi_delay_ms(200);
-}
-
-void nrf24_init_promisc_mode(FuriHalSpiBusHandle* handle, uint8_t channel, uint8_t rate) {
-    //uint8_t preamble[] = {0x55, 0x00}; // little endian
-    uint8_t preamble[] = {0xAA, 0x00}; // little endian
-    //uint8_t preamble[] = {0x00, 0x55}; // little endian
-    //uint8_t preamble[] = {0x00, 0xAA}; // little endian
-    nrf24_write_reg(handle, REG_CONFIG, 0x00); // Stop nRF
-    nrf24_write_reg(handle, REG_STATUS, 0x70); // clear interrupts
-    nrf24_write_reg(handle, REG_DYNPD, 0x0); // disable shockburst
-    nrf24_write_reg(handle, REG_EN_AA, 0x00); // Disable Shockburst
-    nrf24_write_reg(handle, REG_FEATURE, 0x05); // disable payload-with-ack, enable noack
-    nrf24_set_maclen(handle, 2); // shortest address
-    nrf24_set_src_mac(handle, preamble, 2); // set src mac to preamble bits to catch everything
-    nrf24_set_packetlen(handle, 32); // set max packet length
-    nrf24_set_idle(handle);
-    nrf24_flush_rx(handle);
-    nrf24_flush_tx(handle);
-    nrf24_write_reg(handle, REG_RF_CH, channel);
-    nrf24_write_reg(handle, REG_RF_SETUP, rate);
-
-    // prime for RX, no checksum
-    nrf24_write_reg(handle, REG_CONFIG, 0x03); // PWR_UP and PRIM_RX, disable AA and CRC
-    furi_hal_gpio_write(nrf24_CE_PIN, true);
-    furi_delay_ms(100);
+    return reg;
 }
 
 void hexlify(uint8_t* in, uint8_t size, char* out) {
@@ -439,95 +353,6 @@ void int16_to_bytes(uint16_t val, uint8_t* out, bool bigendian) {
     }
 }
 
-// handle iffyness with preamble processing sometimes being a bit (literally) off
-void alt_address_old(uint8_t* packet, uint8_t* altaddr) {
-    uint8_t macmess_hi_b[4];
-    uint8_t macmess_lo_b[2];
-    uint32_t macmess_hi;
-    uint16_t macmess_lo;
-    uint8_t preserved;
-
-    // get first 6 bytes into 32-bit and 16-bit variables
-    memcpy(macmess_hi_b, packet, 4);
-    memcpy(macmess_lo_b, packet + 4, 2);
-
-    macmess_hi = bytes_to_int32(macmess_hi_b, true);
-
-    //preserve least 7 bits from hi that will be shifted down to lo
-    preserved = macmess_hi & 0x7f;
-    macmess_hi >>= 7;
-
-    macmess_lo = bytes_to_int16(macmess_lo_b, true);
-    macmess_lo >>= 7;
-    macmess_lo = (preserved << 9) | macmess_lo;
-    int32_to_bytes(macmess_hi, macmess_hi_b, true);
-    int16_to_bytes(macmess_lo, macmess_lo_b, true);
-    memcpy(altaddr, &macmess_hi_b[1], 3);
-    memcpy(altaddr + 3, macmess_lo_b, 2);
-}
-
-bool validate_address(uint8_t* addr) {
-    uint8_t bad[][3] = {{0x55, 0x55}, {0xAA, 0xAA}, {0x00, 0x00}, {0xFF, 0xFF}};
-    for(int i = 0; i < 4; i++)
-        for(int j = 0; j < 2; j++)
-            if(!memcmp(addr + j * 2, bad[i], 2)) return false;
-
-    return true;
-}
-
-bool nrf24_sniff_address(FuriHalSpiBusHandle* handle, uint8_t maclen, uint8_t* address) {
-    bool found = false;
-    uint8_t packet[32] = {0};
-    uint8_t packetsize;
-    //char printit[65];
-    uint8_t status = 0;
-    status = nrf24_rxpacket(handle, packet, &packetsize, true);
-    if(status & 0x40) {
-        if(validate_address(packet)) {
-            for(int i = 0; i < maclen; i++) address[i] = packet[maclen - 1 - i];
-
-            /*
-            alt_address(packet, packet);
-
-            for(i = 0; i < maclen; i++)
-                address[i + 5] = packet[maclen - 1 - i];
-            */
-
-            //memcpy(address, packet, maclen);
-            //hexlify(packet, packetsize, printit);
-            found = true;
-        }
-    }
-
-    return found;
-}
-
-uint8_t nrf24_find_channel(
-    FuriHalSpiBusHandle* handle,
-    uint8_t* srcmac,
-    uint8_t* dstmac,
-    uint8_t maclen,
-    uint8_t rate,
-    uint8_t min_channel,
-    uint8_t max_channel,
-    bool autoinit) {
-    uint8_t ping_packet[] = {0x0f, 0x0f, 0x0f, 0x0f}; // this can be anything, we just need an ack
-    uint8_t ch = max_channel + 1; // means fail
-    nrf24_configure(handle, rate, srcmac, dstmac, maclen, 2, false, false);
-    for(ch = min_channel; ch <= max_channel + 1; ch++) {
-        nrf24_write_reg(handle, REG_RF_CH, ch);
-        if(nrf24_txpacket(handle, ping_packet, 4, true)) break;
-    }
-
-    if(autoinit) {
-        FURI_LOG_D("nrf24", "initializing radio for channel %d", ch);
-        nrf24_configure(handle, rate, srcmac, dstmac, maclen, ch, false, false);
-        return ch;
-    }
-
-    return ch;
-}
-
 uint8_t nrf24_set_mac(uint8_t mac_addr, uint8_t *mac, uint8_t mlen)
 {
     uint8_t addr[5];

+ 5 - 0
lib/nrf24/nrf24.h

@@ -39,6 +39,7 @@ extern "C" {
 #define REG_RF_CH 0x05
 #define REG_TX_ADDR 0x10
 #define REG_FIFO_STATUS 0x17
+#define REG_OBSERVE_TX 0x08
 
 #define RX_PW_P0 0x11
 #define RX_PW_P1 0x12
@@ -49,6 +50,7 @@ extern "C" {
 #define RX_DR    0x40
 #define TX_DS    0x20
 #define MAX_RT   0x10
+#define NRF24_EN_DYN_ACK 0x01
 
 #define nrf24_TIMEOUT 500
 #define nrf24_CE_PIN &gpio_ext_pb2
@@ -87,6 +89,9 @@ uint8_t nrf24_write_buf_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* d
  */
 uint8_t nrf24_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size);
 
+// Read single register (1 byte)
+uint8_t nrf24_read_register(FuriHalSpiBusHandle* handle, uint8_t reg);
+
 /** Power up the radio for operation
  * 
  * @param      handle  - pointer to FuriHalSpiHandle

+ 96 - 67
nrf24batch.c

@@ -20,14 +20,8 @@
 #define SCAN_APP_PATH_FOLDER "/ext/nrf24batch"
 #define LOG_FILENAME 		"log"
 #define LOG_FILEEXT	 		".txt"
-#define MAX_LOG_RECORDS		200
-#define MAX_FOUND_RECORDS	70
-#define LOG_REC_SIZE		34		// max packet size
-#define VIEW_LOG_MAX_X		22
-#define VIEW_LOG_WIDTH_B	10		// bytes
-#define NRF_REPEAT_ATTEMPTS	3
-#define NRF_READ_TIMEOUT	700UL	// ms
-#define WORK_PERIOD			10		// ms, frequency of individual cmds
+#define NRF_READ_TIMEOUT	300UL	// ms
+#define WORK_PERIOD			2		// ms, frequency of individual cmds
 
 const char SettingsFld_Info[] = "Info:";
 const char SettingsFld_Ch[] = "Ch:";
@@ -35,6 +29,7 @@ const char SettingsFld_Rate[] = "Rate:";
 const char SettingsFld_DPL[] = "DPL:";
 const char SettingsFld_CRC[] = "CRC:";
 const char SettingsFld_Address[] = "Address:";
+const char SettingsFld_Resend[] = "Resend:";
 const char SettingsFld_Delay[] = "Delay_ms:";
 const char SettingsFld_WriteStart[] = "Write start:";
 const char SettingsFld_Payload[] = "Payload struct:";
@@ -86,7 +81,8 @@ uint8_t NRF_AA_OFF;	// Disable Auto Acknowledgement
 bool NRF_ERROR = 0;
 bool NRF_INITED = false;
 uint8_t NRF_last_packet_send_st = 0;
-uint8_t NRF_repeat = 0;
+uint8_t NRF_resend = 1; // number of transaction attempts 
+uint8_t NRF_repeat = 0; // count number of repeated requests (until < NRF_resend)
 uint32_t NRF_time;
 
 uint8_t addr[5];			// nRF24 address, MSB first
@@ -98,6 +94,7 @@ uint8_t payload_fields = 0;
 uint8_t payload_size = 0;		// bytes
 FuriString *ReadDefault = NULL;
 FuriString *WriteDefault = NULL;
+FuriString *WriteStart = NULL;
 uint32_t delay_between_pkt = 5;// ms
 FuriString *Constants = NULL;		// text of STR=x
 FuriString **Read_cmd = NULL;		// Names of read cmd
@@ -191,6 +188,10 @@ void free_store(void)
 		furi_string_free(WriteDefault);
 		WriteDefault = NULL;
 	}
+	if(WriteStart) {
+		furi_string_free(WriteStart);
+		WriteDefault = NULL;
+	}
 	if(Read_cmd_Total) {
 		for(uint16_t i = 0; i < Read_cmd_Total; i++) furi_string_free(Read_cmd[i]);
 		Read_cmd_Total = 0;
@@ -248,60 +249,66 @@ static bool select_settings_file() {
 
 static void prepare_nrf24(void)
 {
-	if(NRF_INITED) return;
-	nrf24_write_reg(nrf24_HANDLE, REG_STATUS, 0x70); // clear interrupts
-	nrf24_write_reg(nrf24_HANDLE, REG_RF_CH, NRF_channel);
-	nrf24_write_reg(nrf24_HANDLE, REG_RF_SETUP, (NRF_rate == 0 ? 0b00100000 : NRF_rate == 1 ? 0 : 0b00001000) | 0b111); // +TX high power
-	nrf24_write_reg(nrf24_HANDLE, REG_CONFIG, 0x70 | ((NRF_CRC == 1 ? 0b1000 : NRF_CRC == 2 ? 0b1100 : 0))); // Mask all interrupts
-	nrf24_write_reg(nrf24_HANDLE, REG_SETUP_RETR, 0b00010111); // Automatic Retransmission, 0.5ms, 7 times
-	nrf24_write_reg(nrf24_HANDLE, REG_EN_AA, 0x01); // Auto acknowledgement
-	nrf24_write_reg(nrf24_HANDLE, REG_FEATURE, NRF_DPL ? 4+1 : 1); // Enables the W_TX_PAYLOAD_NOACK command, Disable Payload with ACK, set Dynamic Payload
-	nrf24_write_reg(nrf24_HANDLE, REG_DYNPD, NRF_DPL ? 0x3F : 0); // Enable dynamic payload reg
-	nrf24_write_reg(nrf24_HANDLE, RX_PW_P0, payload_size);
-	nrf24_set_maclen(nrf24_HANDLE, addr_len);
-	nrf24_set_mac(REG_RX_ADDR_P0, addr, addr_len);
-	uint8_t tmp[5] = { 0 };
-	nrf24_read_reg(nrf24_HANDLE, REG_RX_ADDR_P0, tmp, addr_len);
-	for(uint8_t i = 0; i < addr_len / 2; i++) {
-		uint8_t tb = tmp[i];
-		tmp[i] = tmp[addr_len - i - 1];
-		tmp[addr_len - i - 1] = tb;
+	if(!NRF_INITED) {
+		nrf24_write_reg(nrf24_HANDLE, REG_RF_CH, NRF_channel);
+		nrf24_write_reg(nrf24_HANDLE, REG_RF_SETUP, (NRF_rate == 0 ? 0b00100000 : NRF_rate == 1 ? 0 : 0b00001000) | 0b111); // +TX high power
+		nrf24_write_reg(nrf24_HANDLE, REG_CONFIG, 0x70 | ((NRF_CRC == 1 ? 0b1000 : NRF_CRC == 2 ? 0b1100 : 0))); // Mask all interrupts
+		nrf24_write_reg(nrf24_HANDLE, REG_SETUP_RETR, ((NRF_rate == 0 ? 0b0010 : 0b0001)<<4) | 0b0111); // Automatic Retransmission, ARD, ARC
+		nrf24_write_reg(nrf24_HANDLE, REG_EN_AA, 0x01); // Auto acknowledgement
+		nrf24_write_reg(nrf24_HANDLE, REG_FEATURE, NRF24_EN_DYN_ACK | (NRF_DPL ? 4 : 0)); // Enables the W_TX_PAYLOAD_NOACK command, Disable Payload with ACK, set Dynamic Payload
+		nrf24_write_reg(nrf24_HANDLE, REG_DYNPD, NRF_DPL ? 0x3F : 0); // Enable dynamic payload reg
+		nrf24_write_reg(nrf24_HANDLE, RX_PW_P0, payload_size);
+		nrf24_set_maclen(nrf24_HANDLE, addr_len);
+		nrf24_set_mac(REG_RX_ADDR_P0, addr, addr_len);
+		uint8_t tmp[5] = { 0 };
+		nrf24_read_reg(nrf24_HANDLE, REG_RX_ADDR_P0, tmp, addr_len);
+		for(uint8_t i = 0; i < addr_len / 2; i++) {
+			uint8_t tb = tmp[i];
+			tmp[i] = tmp[addr_len - i - 1];
+			tmp[addr_len - i - 1] = tb;
+		}
+		NRF_ERROR = memcmp(addr, tmp, addr_len) != 0;
+		nrf24_set_mac(REG_TX_ADDR, addr, addr_len);
+		nrf24_write_reg(nrf24_HANDLE, REG_EN_RXADDR, 1);
+		//nrf24_set_idle(nrf24_HANDLE);
+		NRF_INITED = true;
 	}
-	NRF_ERROR = memcmp(addr, tmp, addr_len) != 0;
-	nrf24_set_mac(REG_TX_ADDR, addr, addr_len);
-	nrf24_write_reg(nrf24_HANDLE, REG_EN_RXADDR, 1);
-	nrf24_flush_rx(nrf24_HANDLE);
 	nrf24_flush_tx(nrf24_HANDLE);
-	//nrf24_set_idle(nrf24_HANDLE);
-	NRF_INITED = true;
+	nrf24_flush_rx(nrf24_HANDLE);
+	nrf24_write_reg(nrf24_HANDLE, REG_STATUS, MAX_RT | RX_DR | TX_DS);
+	furi_hal_gpio_write(nrf24_CE_PIN, false);
 }
 
 // true - ok
 uint8_t nrf24_send_packet()
 {
 	if(furi_log_get_level() == FuriLogLevelDebug) {
-		char buf[64]; buf[0] = 0; add_to_str_hex_bytes(buf, payload, payload_size); FURI_LOG_D(TAG, "SEND: %s", buf);
-	}
-	// nrf24_write_reg(nrf24_HANDLE, REG_STATUS, RX_DR | TX_DS | MAX_RT);
-	// nrf24_flush_rx(nrf24_HANDLE);
-	if(nrf24_status(nrf24_HANDLE) & MAX_RT) {
-		nrf24_flush_tx(nrf24_HANDLE);
-		nrf24_write_reg(nrf24_HANDLE, REG_STATUS, MAX_RT);
+		char buf[65]; buf[0] = 0; add_to_str_hex_bytes(buf, payload, payload_size); FURI_LOG_D(TAG, "SEND: %s", buf);
 	}
-	NRF_last_packet_send_st = nrf24_txpacket(nrf24_HANDLE, payload, payload_size, true);
-	NRF_time = furi_get_tick();
-	FURI_LOG_D(TAG, "Send packet = %d", NRF_last_packet_send_st);
+	//nrf24_flush_tx(nrf24_HANDLE);
+	//nrf24_write_reg(nrf24_HANDLE, REG_STATUS, RX_DR | TX_DS | MAX_RT);
+	NRF_last_packet_send_st = nrf24_txpacket(nrf24_HANDLE, payload, payload_size, true); // ACK
 	if(NRF_last_packet_send_st) { 
 		if((rw_type == rwt_read_cmd || rw_type == rwt_read_batch) && send_status == sst_sending) { // Read
 			nrf24_set_rx_mode(nrf24_HANDLE);
 			send_status = sst_receiving; // receiving
-			FURI_LOG_D(TAG, "Receiving...");
 		}
-
 	} else notification_message(APP->notification, &sequence_blink_red_100);
+	NRF_time = furi_get_tick();
+	FURI_LOG_D(TAG, "Send packet: %d%s", rw_type, send_status, NRF_last_packet_send_st, send_status == sst_receiving ? ", Receiving" : "");
 	return NRF_last_packet_send_st;
 }
 
+uint8_t nrf24_resend_packet()
+{
+	if(Log_Total) {
+		FuriString *str = Log[Log_Total - 1];
+		char *p = strstr(furi_string_get_cstr(str), ": ");
+		if(p) furi_string_left(str, p - furi_string_get_cstr(str) + 2);
+	}
+	return nrf24_send_packet();
+}
+
 // true - new packet
 bool nrf24_read_newpacket() {
 	bool found = false;
@@ -310,7 +317,7 @@ bool nrf24_read_newpacket() {
 	if(st & RX_DR) {
 		NRF_time = furi_get_tick();
 		if(furi_log_get_level() == FuriLogLevelDebug) {
-			char buf[64]; buf[0] = 0; add_to_str_hex_bytes(buf, payload_receive, packetsize); FURI_LOG_D(TAG, "READ: %s", buf);
+			char buf[65]; buf[0] = 0; add_to_str_hex_bytes(buf, payload_receive, packetsize); FURI_LOG_D(TAG, "READ(%X): %s", st, buf);
 		}
 		if(Log_Total) {
 			FuriString *str = Log[Log_Total - 1];
@@ -343,9 +350,9 @@ bool nrf24_read_newpacket() {
 				if(--cmd_array_cnt) {
 					furi_string_cat_str(str, ",");
 					payload[cmd_array_idx]++;
-					furi_delay_ms(delay_between_pkt);
+					NRF_repeat = 0;
 					send_status = sst_sending;
-					if(!nrf24_send_packet()) send_status = sst_error;
+					nrf24_send_packet();
 				} else send_status = sst_ok;
 			} else {
 				if(size == 0) { // string, until '\0'
@@ -460,10 +467,10 @@ bool Run_Read_cmd(FuriString *cmd)
 			if(cmd_array_cnt > 1) cmd_array = true; // array
 		}
 	}
-	NRF_repeat = 0;
 	prepare_nrf24();
 	if(NRF_ERROR) return false;
 	what_doing = 2;
+	NRF_repeat = 0;
 	send_status = sst_sending; // Read - sending
 	nrf24_send_packet();
 	return true;
@@ -509,12 +516,27 @@ bool Run_WriteBatch_cmd(FuriString *cmd)
 {
 	char *p;
 	send_status = sst_none;
+	prepare_nrf24();
+	if(NRF_ERROR) return false;
 	if(cmd) {
 		p = strchr((char*)furi_string_get_cstr(cmd), ':');
 		if(p == NULL) return false;
 		p += 2;
 		WriteBatch_cmd_curr = NULL;
 		free_Log();
+		if(WriteStart) {
+			if(!fill_payload((char*)furi_string_get_cstr(WriteStart), NULL, VAR_EMPTY)) return false;
+			send_status = sst_sending;
+			uint8_t i = 0;
+			do {
+				if(nrf24_send_packet()) break;
+				furi_delay_ms(delay_between_pkt);
+			} while(i++ < NRF_resend);
+			if(i >= NRF_resend && i) { // not ok
+				send_status = sst_error;
+				return false;
+			}
+		}
 	} else {
 		if(WriteBatch_cmd_curr) p = WriteBatch_cmd_curr; else return false;
 	}
@@ -566,9 +588,6 @@ bool Run_WriteBatch_cmd(FuriString *cmd)
 		if(strncmp(p, w, len) != 0) continue;
 		delim_col++;
 		str_rtrim(delim_col);
-		NRF_repeat = 0;
-		prepare_nrf24();
-		if(NRF_ERROR) return false;
 		cmd_array_cnt = 255;
 		do {
 			memset(payload, 0, sizeof(payload));
@@ -577,13 +596,14 @@ bool Run_WriteBatch_cmd(FuriString *cmd)
 			if(cmd_array && cmd_array_idx != 255) {
  				if(cmd_array_cnt != 255) payload[cmd_array_idx] = cmd_array_cnt;
 			} else cmd_array = false;
-			send_status = sst_sending; // Read - sending
+			send_status = sst_sending;
+			NRF_repeat = 0;
 			uint8_t i = 0;
-			for(; i < NRF_repeat; i++) {
+			do {
 				if(nrf24_send_packet()) break;
 				furi_delay_ms(delay_between_pkt);
-			}
-			if(i < NRF_repeat) {
+			} while(i++ < NRF_resend);
+			if(i < NRF_resend || i == 0) { // ok
 				if(cmd_array) { // array
 					for(; arr != NULL;) {
 						if(*arr == ',') break;
@@ -644,6 +664,8 @@ static uint8_t load_settings_file() {
 				NRF_CRC = atoi(p + sizeof(SettingsFld_CRC));
 			} else if(strncmp(p, SettingsFld_DPL, sizeof(SettingsFld_DPL)-1) == 0) {
 				NRF_DPL = atoi(p + sizeof(SettingsFld_DPL));
+			} else if(strncmp(p, SettingsFld_Resend, sizeof(SettingsFld_Resend)-1) == 0) {
+				NRF_resend = atoi(p + sizeof(SettingsFld_Resend));
 			} else if(strncmp(p, SettingsFld_Delay, sizeof(SettingsFld_Delay)-1) == 0) {
 				delay_between_pkt = atoi(p + sizeof(SettingsFld_Delay));
 			} else if(strncmp(p, SettingsFld_Payload, sizeof(SettingsFld_Payload)-1) == 0) {
@@ -666,6 +688,8 @@ static uint8_t load_settings_file() {
 				FURI_LOG_D(TAG, "Payload fields %d: %d,%d,%d", payload_fields, payload_struct[0], payload_struct[1], payload_struct[2]);
 			} else if(strncmp(p, SettingsFld_ReadDefault, sizeof(SettingsFld_ReadDefault)-1) == 0) {
 				ReadDefault = furi_string_alloc_set_str(p + sizeof(SettingsFld_ReadDefault));
+			} else if(strncmp(p, SettingsFld_WriteStart, sizeof(SettingsFld_WriteStart)-1) == 0) {
+				WriteStart = furi_string_alloc_set_str(p + sizeof(SettingsFld_WriteStart));
 			} else if(strncmp(p, SettingsFld_WriteDefault, sizeof(SettingsFld_WriteDefault)-1) == 0) {
 				WriteDefault = furi_string_alloc_set_str(p + sizeof(SettingsFld_WriteDefault));
 			} else if(strncmp(p, SettingsFld_Read, sizeof(SettingsFld_Read)-1) == 0) {
@@ -857,25 +881,24 @@ void work_timer_callback(void* ctx)
 			}
 		} else if(send_status == sst_sending) { // sending
 			if(!NRF_last_packet_send_st) { // No ACK on last attempt
-				if(furi_get_tick() - NRF_time >= delay_between_pkt) {
-					if(++NRF_repeat < NRF_REPEAT_ATTEMPTS) nrf24_send_packet(); else send_status = sst_error; // error
+				if(furi_get_tick() - NRF_time > delay_between_pkt) {
+					if(NRF_repeat++ < NRF_resend) nrf24_resend_packet(); else send_status = sst_error; // error NO_ACK
 				}
 			}
 		} else if(send_status == sst_receiving) { // receiving
-			for(uint8_t i = 0; i < 10; i++) {
+			for(uint8_t i = 0; i < 3; i++) {
 				bool new = nrf24_read_newpacket();
 				if(new) {
-					if(send_status != sst_receiving) {
-						NRF_repeat = 0;
-						break;
-					}
-				} else if(furi_get_tick() - NRF_time >= NRF_READ_TIMEOUT) {
-					if(++NRF_repeat < NRF_REPEAT_ATTEMPTS) nrf24_send_packet();
-					else {
+					if(send_status != sst_receiving) break;
+				} else if(furi_get_tick() - NRF_time > NRF_READ_TIMEOUT) {
+					if(NRF_repeat++ < NRF_resend) {
+						send_status = sst_sending;
+						nrf24_resend_packet();
+					} else {
 						FURI_LOG_D(TAG, "TIMEOUT: %lu", furi_get_tick() - NRF_time);
 						send_status = sst_timeout;
-						break;
 					}
+					break;
 				}
 			}
 		} else if(send_status == sst_ok) {
@@ -921,6 +944,12 @@ int32_t nrf24batch_app(void* p) {
 	for(bool processing = true; processing;) {
 		FuriStatus event_status = furi_message_queue_get(APP->event_queue, &event, 200);
 		PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex);
+		
+		static FuriLogLevel FuriLogLevel = FuriLogLevelDefault;
+		if(furi_log_get_level() != FuriLogLevel) {
+		 	FuriLogLevel = furi_log_get_level();
+		 	if(FuriLogLevel == FuriLogLevelDebug) furi_hal_uart_set_br(FuriHalUartIdUSART1, 460800);
+		}
 
 		if(event_status == FuriStatusOk) {
 			// press events