Problem with ESB retransmit_delay and retransmit_count Settings

Hi,
I am using the nRF52840 DK with SDK 17.0.2.

I am working on a project using the ESB protocol. In my design, an ACK packet is transmitted to the receiver every 5 seconds, controlled by an APP_TIMER.

However, I am encountering an issue related to retransmit_delay and retransmit_count. Even though I have configured these parameters, the transmitter does not perform any retransmissions or report TX_FAILED when the receiver does not acknowledge the packet. Instead, it simply sends the packet every 5 seconds until a fatal error occurs.

#define NRF_ESB_DEFAULT_CONFIG {.protocol               = NRF_ESB_PROTOCOL_ESB_DPL,         \
                                .mode                   = NRF_ESB_MODE_PTX,                 \
                                .event_handler          = 0,                                \
                                .bitrate                = NRF_ESB_BITRATE_2MBPS,            \
                                .crc                    = NRF_ESB_CRC_16BIT,                \
                                .tx_output_power        = NRF_ESB_TX_POWER_8DBM,            \
                                .retransmit_delay       = 100,                              \
                                .retransmit_count       = 3,                                 \
                                .tx_mode                = NRF_ESB_TXMODE_AUTO,              \
                                .radio_irq_priority     = 1,                                \
                                .event_irq_priority     = 2,                                \
                                .payload_length         = 32,                               \
                                .selective_auto_ack     = false                             \
}

uint32_t esb_init(void)
{
    uint32_t ret;
    uint8_t base_addr_0[4] = {0xE7, 0xE7, 0xE7, 0xE7};
    uint8_t base_addr_1[4] = {0xC2, 0xC2, 0xC2, 0xC2};
    uint8_t addr_prefix[8] = {0xE7, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8};

    nrf_esb_config_t nrf_esb_config = NRF_ESB_DEFAULT_CONFIG;
    nrf_esb_config.protocol = NRF_ESB_PROTOCOL_ESB_DPL;
    nrf_esb_config.retransmit_count = 3;
    nrf_esb_config.retransmit_delay = 100;
    nrf_esb_config.bitrate = NRF_ESB_BITRATE_2MBPS;
    nrf_esb_config.event_handler = nrf_esb_event_handler;
    nrf_esb_config.mode = NRF_ESB_MODE_PTX;
    nrf_esb_config.selective_auto_ack = true;

    ret = nrf_esb_init(&nrf_esb_config);

    VERIFY_SUCCESS(ret);

    ret = nrf_esb_set_base_address_0(base_addr_0);
    VERIFY_SUCCESS(ret);

    ret = nrf_esb_set_base_address_1(base_addr_1);
    VERIFY_SUCCESS(ret);

    ret = nrf_esb_set_prefixes(addr_prefix, NRF_ESB_PIPE_COUNT);
    VERIFY_SUCCESS(ret);

    return ret;
}

This test was performed inside my project. When I test using the original ESB example, the TX correctly reports TX_FAILED when no ACK is received, but it still does not perform any retransmissions.

I traced the code into on_radio_disabled_tx_wait_for_ack, and I found that it does not continue triggering NRF_ESB_SYS_TIMER->CC[1] according to my configured retransmit_count.

For better readability, I am providing the relevant section of the on_radio_disabled_tx_wait_for_ack function below:

static void on_radio_disabled_tx_wait_for_ack()
{
    NRF_LOG_ERROR("on_radio_disabled_tx_wait_for_ack");
    // This marks the completion of a TX_RX sequence (TX with ACK)

    // Make sure the timer will not deactivate the radio if a packet is received
    NRF_PPI->CHENCLR = (1 << NRF_ESB_PPI_TIMER_START) |
                       (1 << NRF_ESB_PPI_RX_TIMEOUT)  |
                       (1 << NRF_ESB_PPI_TIMER_STOP);

    // If the radio has received a packet and the CRC status is OK
    if (NRF_RADIO->EVENTS_END && NRF_RADIO->CRCSTATUS != 0)
    {

        NRF_LOG_ERROR("radio has received a packet and the CRC status is OK");

        NRF_ESB_SYS_TIMER->TASKS_SHUTDOWN = 1;
        NRF_PPI->CHENCLR = (1 << NRF_ESB_PPI_TX_START);
        m_interrupt_flags |= NRF_ESB_INT_TX_SUCCESS_MSK;
        m_last_tx_attempts = m_config_local.retransmit_count - m_retransmits_remaining + 1;

        (void) nrf_esb_skip_tx();

        if (m_config_local.protocol != NRF_ESB_PROTOCOL_ESB && m_rx_payload_buffer[0] > 0)
        {
            if (rx_fifo_push_rfbuf((uint8_t)NRF_RADIO->TXADDRESS, m_rx_payload_buffer[1] >> 1))
            {
                m_interrupt_flags |= NRF_ESB_INT_RX_DATA_RECEIVED_MSK;
            }
        }

        if ((m_tx_fifo.count == 0) || (m_config_local.tx_mode == NRF_ESB_TXMODE_MANUAL))
        {
            m_nrf_esb_mainstate = NRF_ESB_STATE_IDLE;
            NVIC_SetPendingIRQ(ESB_EVT_IRQ);
        }
        else
        {
            NVIC_SetPendingIRQ(ESB_EVT_IRQ);
            start_tx_transaction(); 
        }
    }
    else
    {
        if (m_retransmits_remaining-- == 0)
        {
            NRF_ESB_SYS_TIMER->TASKS_SHUTDOWN = 1;
            NRF_PPI->CHENCLR = (1 << NRF_ESB_PPI_TX_START);
            // All retransmits are expended, and the TX operation is suspended
            m_last_tx_attempts = m_config_local.retransmit_count + 1;

            m_interrupt_flags |= NRF_ESB_INT_TX_FAILED_MSK;

            m_nrf_esb_mainstate = NRF_ESB_STATE_IDLE;
            NVIC_SetPendingIRQ(ESB_EVT_IRQ);
        }
        else
        {
            // There are still more retransmits left, TX mode should be
            // entered again as soon as the system timer reaches CC[1].
            NRF_RADIO->SHORTS = m_radio_shorts_common | RADIO_SHORTS_DISABLED_RXEN_Msk;
            update_rf_payload_format(mp_current_payload->length);
            NRF_RADIO->PACKETPTR = (uint32_t)m_tx_payload_buffer;
            on_radio_disabled = on_radio_disabled_tx;
            m_nrf_esb_mainstate = NRF_ESB_STATE_PTX_TX_ACK;
            NRF_ESB_SYS_TIMER->TASKS_START = 1;
            NRF_PPI->CHENSET = (1 << NRF_ESB_PPI_TX_START);
            if (NRF_ESB_SYS_TIMER->EVENTS_COMPARE[1])
            {
                NRF_RADIO->TASKS_TXEN = 1;
            }
        }
    }
}

If I enable the RX, the code does enter the final section of on_radio_disabled_tx_wait_for_ack, where the comment says “There are still more retransmits left, TX mode should be entered again as soon as the system timer reaches CC[1].”. In this part, I logged the value of m_last_tx_attempts and confirmed that it is 1. However, NRF_RADIO->TASKS_TXEN = 1; is never triggered, even though retransmissions should still be pending.

What is even more confusing is that when I enable the RX, the receiver does send back a valid ACK, but the transmitter does not enter the code path under if (m_retransmits_remaining-- == 0) nor the following section of code that I have pasted below.

if (NRF_RADIO->EVENTS_END && NRF_RADIO->CRCSTATUS != 0)
    {

        NRF_LOG_ERROR("radio has received a packet and the CRC status is OK");

        NRF_ESB_SYS_TIMER->TASKS_SHUTDOWN = 1;
        NRF_PPI->CHENCLR = (1 << NRF_ESB_PPI_TX_START);
        m_interrupt_flags |= NRF_ESB_INT_TX_SUCCESS_MSK;
        m_last_tx_attempts = m_config_local.retransmit_count - m_retransmits_remaining + 1;

        (void) nrf_esb_skip_tx();

        if (m_config_local.protocol != NRF_ESB_PROTOCOL_ESB && m_rx_payload_buffer[0] > 0)
        {
            if (rx_fifo_push_rfbuf((uint8_t)NRF_RADIO->TXADDRESS, m_rx_payload_buffer[1] >> 1))
            {
                m_interrupt_flags |= NRF_ESB_INT_RX_DATA_RECEIVED_MSK;
            }
        }

        if ((m_tx_fifo.count == 0) || (m_config_local.tx_mode == NRF_ESB_TXMODE_MANUAL))
        {
            m_nrf_esb_mainstate = NRF_ESB_STATE_IDLE;
            NVIC_SetPendingIRQ(ESB_EVT_IRQ);
        }
        else
        {
            NVIC_SetPendingIRQ(ESB_EVT_IRQ);
            start_tx_transaction(); 
        }
    }

which should be executed when a packet with a valid CRC is received.

This unexpected behavior makes me unsure why the TX neither follows the retransmission path nor the CRC-OK ACK reception path.

I would like to ask whether any modifications are required in the ESB source code in order to make the retransmission mechanism work properly?

If you could help identify what I might be doing wrong, I would greatly appreciate it.


Thank you!

  • Hi,

    This is an forum that officially uses only English. I relied on google translate but would not want to do that next time. Next time I wont be doing that.

    The behavior you are seeing is what the library does when an ACK isn’t requested for the packet.

    In DPL mode with selective_auto_ack = true, ESB only waits for ACK/retries when the payload has noack = false. If noack is 1 or uninitialized, it takes the no-ack path and never enters the retransmit/ACK wait state, and it reports TX_SUCCESS every time.

    you have to make sure every payload that should be acknowledged sets noack = false (and that the struct is zero-initialized). for example

    nrf_esb_payload_t tx = NRF_ESB_CREATE_PAYLOAD(0, /* data */);
    tx.noack = false;
    tx.pipe = 0;
    nrf_esb_write_payload(&tx);
    

    or set selective_auto_ack = false in the config to force ACKs for all packets. Your retransmit_delay = 100 is below the internal minimum for 2 Mbps; ESB clamps it to about 110 µs. It won’t block retries, but you might set it to for example greater than 110 explicitly.

    You can test that with the receiver off, send a payload with noack = false (or selective_auto_ack = false) and watch the event handler, you should see ~4 attempts (1 original + 3 retries) and then TX_FAILED when no ACK arrives.

  • I sincerely apologize, Mr. Susheel Nuguru. There was a misunderstanding. I am certain that I originally wrote my post in English, and I am aware that all communication on the forum must be in English, but somehow my computer automatically translated it into Chinese due to some settings, and it was submitted that way. Even I could not understand the translated version. I will make sure to double-check carefully next time. I am very sorry for the confusion caused.

    Thank you very much for your help. I will proceed with the testing as you suggested.

Related