This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

ESB on nRF52 SDK11: not sending ACK payloads

I'm trying to set up an ESB PRX on an nRF52 that has to communicate with an nRF24L01 chip acting as PTX. Because I want to react to the received data as quickly as possible, I tend to call nrf_esb_flush_tx() before writing a new payload into the TX FIFO via nrf_esb_write_payload().

I noticed that transmission of any ACK payload beyond the first one seem to fail. In nrf_esb.c, I stumbled on the following piece of code in the on_radio_disabled_rx() handler:

// Pipe stays in ACK with payload until TX fifo is empty
// Do not report TX success on first ack payload or retransmit
if (p_pipe_info->m_ack_payload != 0 && !retransmit_payload)
{
    if(++m_tx_fifo.exit_point >= NRF_ESB_TX_FIFO_SIZE)
    {
        m_tx_fifo.exit_point = 0;
    }

    m_tx_fifo.count--;
    // ACK payloads also require TX_DS
    // (page 40 of the 'nRF24LE1_Product_Specification_rev1_6.pdf').
    m_interrupt_flags |= NRF_ESB_INT_TX_SUCCESS_MSK;
}

This code seems to be at least part of the cause for my problem. I don't really understand what is supposed to happen here. The m_ack_payload flag is set when there is at least one entry in the m_tx_fifo (immediately after the above snippet). The flag seems to only be reset if either on_radio_disabled_rx() is called while the TX FIFO is empty or if the ESB driver is initialized or disabled. This leads to the above code being executed when receiving the second ESB packet. Because in the meantime, the FIFO has been flushed, incrementing the FIFO exit point will make the current TX payload be an invalid payload.

According to the referenced page 40 in nRF24LE1_Product_Specification_rev1_6.pdf, the TX_DS event is only relevant for a PTX after receiving an ACK payload. Since on_radio_disabled_rx() is called on the PRX side, I don't see, why a TX success event has to be generated here.

In my opinion, a cleaner solution would be to always increment the FIFO exit point after the FIFOs contents have been copied to the current TX payload buffer in on_radio_disabled_rx().

I'm sure there is a good reason for the above code to exist, so I'd be happy if someone could clarify why this is needed and what would go wrong if I removed it and instead called tx_fifo_remove_last() after copying the current FIFO exit point data to the actual TX buffer.

  • Have you had any progress on this issue? It seems like a problem in the case where you flush the TX FIFO and use ACK payload, which is something I don't think we have tested.

  • I modified the ESB driver to not use a FIFO in my use case. Instead, I preload one payload per pipe which keeps being sent as an ack payload (even on retransmissions) as long as it is not replaced or explicitly cleared.

  • Thanks for reporting this issue. I have forwarded it internally in Nordic, so that hopefully it can be fixed in a future SDK release.

  • Just experienced this myself today. Looks like the ESB library has a bug.

    Here's how I solved the issue: I changed the following code (starting line 773 nrf_esb.c in SDK 14.1.0):

            case NRF_ESB_PROTOCOL_ESB_DPL:
                {
                    if (m_tx_fifo.count > 0 &&
                        (m_tx_fifo.p_payload[m_tx_fifo.exit_point]->pipe == NRF_RADIO->RXMATCH)
                       )
                    {
                        // Pipe stays in ACK with payload until TX FIFO is empty
                        // Do not report TX success on first ack payload or retransmit
                        if (p_pipe_info->ack_payload == true && !retransmit_payload)
                        {
                            if (++m_tx_fifo.exit_point >= NRF_ESB_TX_FIFO_SIZE)
                            {
                                m_tx_fifo.exit_point = 0;
                            }
    
                            m_tx_fifo.count--;
    
                            // ACK payloads also require TX_DS
                            // (page 40 of the 'nRF24LE1_Product_Specification_rev1_6.pdf').
                            m_interrupt_flags |= NRF_ESB_INT_TX_SUCCESS_MSK;
                        }
    
                        p_pipe_info->ack_payload = true;
    
                        mp_current_payload = m_tx_fifo.p_payload[m_tx_fifo.exit_point];
    
                        update_rf_payload_format(mp_current_payload->length);
                        m_tx_payload_buffer[0] = mp_current_payload->length;
                        memcpy(&m_tx_payload_buffer[2],
                               mp_current_payload->data,
                               mp_current_payload->length);
                    }
                    else
                    {
                        p_pipe_info->ack_payload = false;
                        update_rf_payload_format(0);
                        m_tx_payload_buffer[0] = 0;
                    }
    
                    m_tx_payload_buffer[1] = m_rx_payload_buffer[1];
                }
                break;
    

    to the following:

            case NRF_ESB_PROTOCOL_ESB_DPL:
                {
                	// Pipe stays in ACK with payload until TX FIFO is empty
    				// Do not report TX success on first ack payload or retransmit
    				if (p_pipe_info->ack_payload == true && !retransmit_payload)
    				{
    					if (++m_tx_fifo.exit_point >= NRF_ESB_TX_FIFO_SIZE)
    					{
    						m_tx_fifo.exit_point = 0;
    					}
    
    					m_tx_fifo.count--;
    
    					// ACK payloads also require TX_DS
    					// (page 40 of the 'nRF24LE1_Product_Specification_rev1_6.pdf').
    					m_interrupt_flags |= NRF_ESB_INT_TX_SUCCESS_MSK;
    				}
    
                    if (m_tx_fifo.count > 0 &&
                        (m_tx_fifo.p_payload[m_tx_fifo.exit_point]->pipe == NRF_RADIO->RXMATCH)
                       )
                    {
                        p_pipe_info->ack_payload = true;
    
                        mp_current_payload = m_tx_fifo.p_payload[m_tx_fifo.exit_point];
    
                        update_rf_payload_format(mp_current_payload->length);
                        m_tx_payload_buffer[0] = mp_current_payload->length;
                        memcpy(&m_tx_payload_buffer[2],
                               mp_current_payload->data,
                               mp_current_payload->length);
                    }
                    else
                    {
                        p_pipe_info->ack_payload = false;
                        update_rf_payload_format(0);
                        m_tx_payload_buffer[0] = 0;
                    }
    
                    m_tx_payload_buffer[1] = m_rx_payload_buffer[1];
                }
                break;
    

    This moves the completion code outside of the check for new packets to send. The SDK's code would send uninitialized data from the FIFO, which usually worked, but sometimes failed disastrously.

    It's a pretty serious bug that in practice makes PRX ACK payloads unusable. Hopefully someone from Nordic will see this post and incorporate the fix into the next SDK release.

  • You are absolutely right that this is a bug in the ESB library. Unfortunately, it has not been fixed in the SDK yet, but I will bump this issue internally.

    Your modifications seem valid, and can hopefully be useful for others until the SDK incorporates a fix.

Related