Hi,
I've got two ESB transceivers talking to each other - a PTX on nRF52832 (E73 module) and a PRX on nRF52810 (Holyiot module, the "tag"). Both chips can obviously TX and RX, they just have different roles here.
Originally I had a full challenge-response setup with HMAC running on top of this, and it kept breaking down after a while - I assumed it was an auth bug. To rule that out I stripped it down to a bare echo test with no crypto at all, and the exact same symptom showed up, so it's clearly not an authentication issue, something more fundamental is going on.
SDK is nRF5 SDK v17.1.0 (legacy, not NCS), using nrf_esb.
Setup: the tag sleeps most of the time, wakes up via app_timer/RTC every 5 seconds, opens an RX window for 25ms. HFCLK and LFCLK are requested once at boot and just stay on the whole time - never toggled between cycles, did that on purpose to rule it out as a variable. The PTX side sends the same 8-byte test packet every 5ms for up to 25 seconds per session. When the tag catches a packet it echoes the same bytes back via ACK payload, PTX sees the match and stops.
The weird part: it's not about how many times the tag wakes up, it can wake up as many times as it wants with no issue. It's specifically after 8-10 successful echoes (packet in, echo out, confirmed) that it just stops sending the echo back. RX still works fine at that point - RX_RECEIVED still fires, I can see the payload content in the log just fine. It's the TX side that stops. Hard to tell if it's not building the response or building it but not actually sending it - from the outside it just looks stuck at that point.
Only a full power cycle of the tag (nRF52810 side, PTX is untouched and keeps running fine) brings it back. Software reset / reinit doesn't do anything.
Tried so far, no luck:
- Draining the RX FIFO in a loop on every RX_RECEIVED instead of a single read
- Explicit nrf_esb_flush_rx() both after handling the event and before every start_rx()
Checked APP_TIMER_CONFIG_IRQ_PRIORITY too, it's at 6, well within the allowed range.
Attaching main.c. Any idea what could be blocking the TX side specifically after a fixed-ish number of successful round trips, while RX keeps working?
main.c
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "nordic_common.h"
#include "app_error.h"
#include "app_timer.h"
#include "nrf_drv_clock.h"
#include "nrf_pwr_mgmt.h"
#include "nrf_esb.h"
#include "nrf_esb_error_codes.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
#define WAKE_INTERVAL_MS 5000
#define RX_WINDOW_MS 25
#define TEST_PACKET_LEN 8
static nrf_esb_payload_t m_esb_rx_payload;
static nrf_esb_payload_t m_esb_tx_payload;
APP_TIMER_DEF(m_wake_timer);
APP_TIMER_DEF(m_window_timer);
static volatile bool s_rx_window_open = false;
static uint32_t s_wake_count = 0;
static uint32_t s_hit_count = 0;
static volatile bool s_response_ready = false;
void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name)
{
app_error_handler(0xDEADBEEF, line_num, p_file_name);
}
/* ==== ESB PRX */
static void nrf_esb_event_handler(nrf_esb_evt_t const * p_event)
{
switch (p_event->evt_id)
{
case NRF_ESB_EVENT_RX_RECEIVED:
{
while (nrf_esb_read_rx_payload(&m_esb_rx_payload) == NRF_SUCCESS)
{
if (m_esb_rx_payload.length != TEST_PACKET_LEN)
{
continue; /* n_match_read_cont */
}
if (s_response_ready)
{
continue;
}
s_response_ready = true;
s_hit_count++;
NRF_LOG_INFO("Packet captured (hit %u/%u), echoing back...", s_hit_count, s_wake_count);
NRF_LOG_HEXDUMP_INFO(m_esb_rx_payload.data, TEST_PACKET_LEN);
/* =simplifyed */
memset(&m_esb_tx_payload, 0, sizeof(m_esb_tx_payload));
m_esb_tx_payload.length = TEST_PACKET_LEN;
memcpy(m_esb_tx_payload.data, m_esb_rx_payload.data, TEST_PACKET_LEN);
(void) nrf_esb_flush_tx();
(void) nrf_esb_write_payload(&m_esb_tx_payload);
NRF_LOG_INFO("Echo queued.");
}
/* ==== addd 08_06 clean RX FIFO */
(void) nrf_esb_flush_rx();
} break;
default:
break;
}
}
static void esb_init(void)
{
uint32_t err_code;
/* ardr must to match (main_e73_esb.c) */
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.bitrate = NRF_ESB_BITRATE_2MBPS;
nrf_esb_config.event_handler = nrf_esb_event_handler;
nrf_esb_config.mode = NRF_ESB_MODE_PRX;
nrf_esb_config.selective_auto_ack = false;
err_code = nrf_esb_init(&nrf_esb_config);
APP_ERROR_CHECK(err_code);
err_code = nrf_esb_set_base_address_0(base_addr_0);
APP_ERROR_CHECK(err_code);
err_code = nrf_esb_set_base_address_1(base_addr_1);
APP_ERROR_CHECK(err_code);
err_code = nrf_esb_set_prefixes(addr_prefix, 8);
APP_ERROR_CHECK(err_code);
}
/* ==== timer rx wind_close thru RX_WINDOW_MS /
static void window_timer_handler(void * p_context)
{
s_rx_window_open = false;
(void) nrf_esb_stop_rx();
(void) nrf_esb_flush_tx(); /* if not sendd from this */
NRF_LOG_INFO("RX window closed, back to sleep.");
}
static volatile bool s_wake_pending = false;
static void wake_timer_handler(void * p_context)
{
s_wake_pending = true;
}
static void clock_init(void)
{
ret_code_t err_code = nrf_drv_clock_init();
APP_ERROR_CHECK(err_code);
nrf_drv_clock_lfclk_request(NULL);
while (!nrf_drv_clock_lfclk_is_running())
{
}
nrf_drv_clock_hfclk_request(NULL);
while (!nrf_drv_clock_hfclk_is_running())
{
}
}
static void timers_init(void)
{
ret_code_t err_code = app_timer_init();
APP_ERROR_CHECK(err_code);
err_code = app_timer_create(&m_wake_timer, APP_TIMER_MODE_REPEATED, wake_timer_handler);
APP_ERROR_CHECK(err_code);
err_code = app_timer_create(&m_window_timer, APP_TIMER_MODE_SINGLE_SHOT, window_timer_handler);
APP_ERROR_CHECK(err_code);
}
static void log_init(void)
{
ret_code_t err_code = NRF_LOG_INIT(NULL);
APP_ERROR_CHECK(err_code);
NRF_LOG_DEFAULT_BACKENDS_INIT();
}
static void power_management_init(void)
{
ret_code_t err_code = nrf_pwr_mgmt_init();
APP_ERROR_CHECK(err_code);
}
static void idle_state_handle(void)
{
if (s_wake_pending)
{
s_wake_pending = false;
ret_code_t err_code;
s_wake_count++;
NRF_LOG_INFO("Wake #%u, opening RX window...", s_wake_count);
s_response_ready = false;
(void) nrf_esb_flush_tx();
(void) nrf_esb_flush_rx();
s_rx_window_open = true;
err_code = nrf_esb_start_rx();
APP_ERROR_CHECK(err_code);
app_timer_start(m_window_timer, APP_TIMER_TICKS(RX_WINDOW_MS), NULL);
}
if (NRF_LOG_PROCESS() == false)
{
nrf_pwr_mgmt_run();
}
}
int main(void)
{
log_init();
clock_init();
timers_init();
power_management_init();
esb_init();
NRF_LOG_INFO("Metka (ESB PRX) ready. Waking every %d ms.", WAKE_INTERVAL_MS);
app_timer_start(m_wake_timer, APP_TIMER_TICKS(WAKE_INTERVAL_MS), NULL);
for (;;)
{
idle_state_handle();
}
}