Skip to content

Repeated TWR stops after the DWM's go out of range and only recovers after the responder restarts #6

@Ricardo-Innoventix

Description

@Ricardo-Innoventix

Good day
Thank you for all the work you have completed on this driver, it is very useful.

I trying to run repeated ranging on two ESP32-S3s with two DWM3000s. I am using your latest driver version (17/04/2026) and a slightly modified version of your new ESP-IDF sample code. All I added was a new task that attempts to TWR every second, getting a live feed of the distance between the two devices.

The system works normally until the devices go out of range once. After that, the initiator keeps retrying and timing out, but the responder no longer seems to receive or process new poll messages. Even if I return the devices back to within range, the initiator keeps timing out. The problem only clears if I restart the responder. This suggests the responder RX/event handling path is getting stuck after loss of range.

The modified main is as follows:

#include <stdio.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "dw3000_hw.h"
#include "dwhw.h"
#include "dwmac.h"
#include "dwphy.h"
#include "dwproto.h"
#include "ranging.h"

#include "log.h"

// Set this on one side to receive and reply to TWR messages
// Otherwise we will initate TWR
#define TWR_RECEIVER 0

#define PANID 0xdeca
#if TWR_RECEIVER
#define MAC16 0x0001
#else
#define MAC16 0x0002
#endif

static const char* LOG_TAG = "TWR_TEST";

#if !TWR_RECEIVER
static void twr_done_cb(uint64_t src, uint64_t dst, uint16_t dist, uint16_t num)
{
	LOG_INF("TWR Done %04X: %d cm", (uint16_t)dst, dist);
}

static void twr_task(void* arg)
{
	(void)arg;

	while (1) {
		twr_start(0x0001);
		vTaskDelay(pdMS_TO_TICKS(1000));
	}
}
#endif

void app_main(void)
{
	LOG_INF("libdeca TWR test");

	// decadriver init
	dw3000_hw_init();
	dw3000_hw_reset();
	dw3000_hw_init_interrupt();

	// libdeca init
	dwhw_init();
	dwphy_config();
	dwphy_set_antenna_delay(DWPHY_ANTENNA_DELAY);
	dwmac_init(PANID, MAC16, dwprot_rx_handler, NULL, NULL);
	dwmac_set_frame_filter();

	// TWR
	twr_init(2000, true);

#if TWR_RECEIVER

	// Just enable receive mode, dwprot_rx_handler will handle TWR
	LOG_INF("Receiver start");
	dwmac_set_rx_reenable(true);
	dwt_forcetrxoff();
	dwt_rxenable(DWT_START_RX_IMMEDIATE);

#else

	// Initiate two way ranging to 0x0001 once per second
	LOG_INF("TWR task to 0x0001");
	twr_set_observer(twr_done_cb);
	xTaskCreate(twr_task, "twr_task", 2048, NULL, 5, NULL);

#endif
}

Here you can see the output of the initiator as I walk away:

I (8356) TWR: #9 [0000000000000002] -> [0000000000000001]: 13 cm REP
I (8356) TWR_TEST: TWR Done 0001: 13 cm
I (9356) TWR: #10 [0000000000000002] -> [0000000000000001]: 26 cm REP
I (9356) TWR_TEST: TWR Done 0001: 26 cm
I (10356) TWR: #11 [0000000000000002] -> [0000000000000001]: 41 cm REP
I (10356) TWR_TEST: TWR Done 0001: 41 cm
I (11356) TWR: #12 [0000000000000002] -> [0000000000000001]: 43 cm REP
I (11356) TWR_TEST: TWR Done 0001: 43 cm
I (12356) TWR: #13 [0000000000000002] -> [0000000000000001]: 45 cm REP
I (12356) TWR_TEST: TWR Done 0001: 45 cm
I (13356) TWR: #14 [0000000000000002] -> [0000000000000001]: 55 cm REP
I (13356) TWR_TEST: TWR Done 0001: 55 cm
I (14356) TWR: #15 [0000000000000002] -> [0000000000000001]: 66 cm REP
I (14356) TWR_TEST: TWR Done 0001: 66 cm
I (15356) TWR: #16 [0000000000000002] -> [0000000000000001]: 86 cm REP
I (15356) TWR_TEST: TWR Done 0001: 86 cm
I (16356) TWR: #17 [0000000000000002] -> [0000000000000001]: 153 cm REP
I (16356) TWR_TEST: TWR Done 0001: 153 cm
I (17356) TWR: #18 [0000000000000002] -> [0000000000000001]: 264 cm REP
I (17356) TWR_TEST: TWR Done 0001: 264 cm
I (18356) TWR: #19 [0000000000000002] -> [0000000000000001]: 406 cm REP
I (18356) TWR_TEST: TWR Done 0001: 406 cm
I (19356) TWR: #20 [0000000000000002] -> [0000000000000001]: 640 cm REP
I (19356) TWR_TEST: TWR Done 0001: 640 cm
I (20356) TWR: #21 [0000000000000002] -> [0000000000000001]: 635 cm REP
I (20356) TWR_TEST: TWR Done 0001: 635 cm
I (21356) TWR: #22 [0000000000000002] -> [0000000000000001]: 688 cm REP
I (21356) TWR_TEST: TWR Done 0001: 688 cm
I (23356) TWR: #24 [0000000000000002] -> [0000000000000001]: 938 cm REP
I (23356) TWR_TEST: TWR Done 0001: 938 cm
I (24356) TWR: #25 [0000000000000002] -> [0000000000000001]: 1014 cm REP
I (24356) TWR_TEST: TWR Done 0001: 1014 cm
E (27356) TWR: RX timeout from [0000000000000001]
I (27366) TWR: retry 1 to [0000000000000001] after 10 ms
E (27366) TWR: RX timeout from [0000000000000001]
I (27366) TWR: retry 2 to [0000000000000001] after 6 ms
E (27376) TWR: RX timeout from [0000000000000001]
E (27376) TWR: retry limit exceeded [0000000000000001]
I (27376) TWR_TEST: TWR Done 0001: 65535 cm
E (28356) TWR: RX timeout from [0000000000000001]
I (28356) TWR: retry 1 to [0000000000000001] after 9 ms
E (28356) TWR: RX timeout from [0000000000000001]
I (28366) TWR: retry 2 to [0000000000000001] after 12 ms
E (28366) TWR: RX timeout from [0000000000000001]
E (28366) TWR: retry limit exceeded [0000000000000001]
I (28366) TWR_TEST: TWR Done 0001: 65535 cm

After which it never recovers. There are no logs or errors on the responder side. I would really appreciate it if you could recreate the problem and suggest a possible solution.

Kind regards
Ricardo

Metadata

Metadata

Assignees

No one assigned

    Labels

    wontfixThis will not be worked on

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions