Hello everyone,I am currently working on a positioning project that involves 3 anchors and 1 tag. The hardware setup is as follows:
The anchors are based on the ESP32 board, coupled with the DW3000 module, communicating via SPI as per the design provided by the marker fab.
For the code libraries, I am using the code available at the following GitHub link: GitHub - br101/libdeca: UWB Library for Decawave/Qorvo DW3000.
The tag is using the Type2AB module, with the code library taken from the manufacturer’s RAR file: “DW3xxx_XR6.0C_24Feb2022”.
I have posted the code for both the anchor and the tag below.Initially, I ran the Arduino code on the ESP32 board using the example initializer, and the anchor and tag were able to measure distances with accurate results (using the Single Side Two-Way Ranging method). However, after configuring the code on the ESP32 + DW3000 anchor (as shown in the images below), I encountered an issue.When I run the setup, the anchor successfully sends the poll message, which the tag receives (the debug window confirms the tag has received the message and sent a response back). However, the ESP32 + DW3000 anchor still reports an RX TIME OUT error.I have double-checked the configuration, but it seems there may still be something missing or incorrectly configured. Could anyone help me verify if I have overlooked any necessary configurations or settings?To recap, I am using the library from GitHub - br101/libdeca: UWB Library for Decawave/Qorvo DW3000 for the ESP32 + DW3000 anchor.Thank you in advance for your help!
File main esp32+dw3000:
/* SPI Master Half Duplex EEPROM example.
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include “freertos/FreeRTOS.h”
#include “freertos/task.h”
#include “driver/spi_master.h”
#include “driver/gpio.h”
#include “sdkconfig.h”
#include “esp_log.h”
#include “dw3000_hw.h”
#include “dwhw.h”
#include “dwmac.h”
#include “dwphy.h”
#include “dwproto.h”
#include “ranging.h”
#include “dw3000_deca_regs.h”
/
This code demonstrates how to use the SPI master half duplex mode to read/write a AT932C46D EEPROM (8-bit mode).
*/
#define IS_ANCHOR 0
static const char TAG = “main”;
static void twr_done_cb(uint64_t src, uint64_t dst, uint16_t dist,
uint16_t num)
{
ESP_LOGI(TAG,“TWR Done %04X: %d cm”, (uint16_t)dst, dist);
}
void test_twr(void)
{
// 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);
uint16_t PANID = 0xDECA; // Example PANID
#if IS_ANCHOR == 1
uint16_t MAC16 = 0x5678; // Example MAC16
#else
// uint16_t MAC16 = 0x5677; // Example MAC16
uint16_t MAC16 = (uint16_t)‘E’<<8 | (uint16_t)‘V’; // Example destination address
#endif
dwmac_init(PANID, MAC16, dwprot_rx_handler, NULL, NULL);
dwmac_set_frame_filter();
twr_init(400 /TWR_PROCESSING_DELAY/, false);
twr_set_observer(twr_done_cb);
// two way ranging to 0x0001
#if IS_ANCHOR == 1
dwmac_set_rx_reenable(true);
dwt_forcetrxoff();
dwt_rxenable(DWT_START_RX_IMMEDIATE);
#else
uint16_t dst = (uint16_t)‘A’<<8 | (uint16_t)‘W’; // Example destination address
twr_start_ss(dst);
// twr_start_ss(0x5678);
#endif
}
void app_main(void)
{
test_twr();
uint16_t dst = (uint16_t)'A'<<8 | (uint16_t)'W'; // Example destination address
while (1) {
// Add your main loop handling code here.
vTaskDelay(1000/portTICK_PERIOD_MS);
#if IS_ANCHOR == 0
twr_start_ss(dst);
// twr_start_ss(0x5678);
#endif
}
}
File main TAG Type2AB:
#include <boards.h>
#include <deca_spi.h>
#include <examples_defines.h>
#include <port.h>
#include <sdk_config.h>
#include <stdio.h>
#include <stdlib.h>
#include “deca_probe_interface.h”
#include <config_options.h>
#include <deca_device_api.h>
#include <deca_spi.h>
#include <example_selection.h>
#include <port.h>
#include <shared_defines.h>
#include <shared_functions.h>
#include “nrfx_twi_ex.h”
#include “li2sdw12_reg.h”
#include “nrf_drv_twi.h”
/* Example application name */
#define APP_NAME “SS TWR RESP v1.0”
/* Default communication configuration. We use default non-STS DW mode. /
static dwt_config_t config = {
5, / Channel number. /
DWT_PLEN_128, / Preamble length. Used in TX only. /
DWT_PAC8, / Preamble acquisition chunk size. Used in RX only. /
9, / TX preamble code. Used in TX only. /
9, / RX preamble code. Used in RX only. /
1, / 0 to use standard 8 symbol SFD, 1 to use non-standard 8 symbol, 2 for non-standard 16 symbol SFD and 3 for 4z 8 symbol SDF type /
DWT_BR_6M8, / Data rate. /
DWT_PHRMODE_STD, / PHY header mode. /
DWT_PHRRATE_STD, / PHY header rate. /
(129 + 8 - 8), / SFD timeout (preamble length + 1 + SFD length - PAC size). Used in RX only. /
DWT_STS_MODE_OFF, / STS disabled /
DWT_STS_LEN_64, / STS length see allowed values in Enum dwt_sts_lengths_e /
DWT_PDOA_M0 / PDOA mode off */
};
/* Default antenna delay values for 64 MHz PRF. See NOTE 2 below. */
#define TX_ANT_DLY 16385
#define RX_ANT_DLY 16385
/* Frames used in the ranging process. See NOTE 3 below. /
static uint8_t rx_poll_msg[] = { 0x41, 0x88, 0, 0xCA, 0xDE, ‘W’, ‘A’, ‘V’, ‘E’, 0xE0, 0, 0 };
static uint8_t tx_resp_msg[] = { 0x41, 0x88, 0, 0xCA, 0xDE, ‘V’, ‘E’, ‘W’, ‘A’, 0xE1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0 };
/ Length of the common part of the message (up to and including the function code, see NOTE 3 below). /
#define ALL_MSG_COMMON_LEN 10
/ Index to access some of the fields in the frames involved in the process. */
#define ALL_MSG_SN_IDX 2
#define RESP_MSG_POLL_RX_TS_IDX 10
#define RESP_MSG_RESP_TX_TS_IDX 14
#define RESP_MSG_TS_LEN 4
#define LIS2DW12_ADDR 0x19
#define MAX_TX_BUFF_LEN 8
#define BOOT_TIME 20 //20 ms
#define SENSOR_INDEX 18
/* Frame sequence number, incremented after each transmission. */
static uint8_t frame_seq_nb = 0;
/* Buffer to store received messages.
- Its size is adjusted to longest frame that this example code is supposed to handle. */
#define RX_BUF_LEN 12 // Must be less than FRAME_LEN_MAX_EX
static uint8_t rx_buffer[RX_BUF_LEN];
/* Hold copy of status register state here for reference so that it can be examined at a debug breakpoint. */
static uint32_t status_reg = 0;
/* Delay between frames, in UWB microseconds. See NOTE 1 below. */
#define POLL_RX_TO_RESP_TX_DLY_UUS 650
/* Timestamps of frames transmission/reception. */
static uint64_t poll_rx_ts;
static uint64_t resp_tx_ts;
/* Values for the PG_DELAY and TX_POWER registers reflect the bandwidth and power of the spectrum at the current
- temperature. These values can be calibrated prior to taking reference measurements. See NOTE 5 below. */
extern dwt_txconfig_t txconfig_options;
void test_run_info(unsigned char *data)
{
printf(“%s\n”, data);
}
int i = 1;
const nrfx_twi_t* m_p_acc_twi = NULL;
static stmdev_ctx_t dev_ctx;
static uint8_t whoamI, rst;
static lis2dw12_reg_t int_route;
static int16_t data_raw_acceleration[3];
static float acceleration_mg[3];
float sensor_data[100];
static uint8_t tx_buffer_sensor[4];
float max_sensor_value;
static const nrfx_twi_t m_twi = NRFX_TWI_INSTANCE_EX(0);
static int32_t platform_read(void *ptr, uint8_t addr, uint8_t buf, uint16_t len)
{
int32_t err_code = 0;
uint8_t p_buffer = buf;
for (int i = 0; i < len; i++)
{
nrfx_twi_tx(m_p_acc_twi, LIS2DW12_ADDR, &addr, 1, true);
APP_ERROR_CHECK(err_code);
err_code = nrfx_twi_rx(m_p_acc_twi, LIS2DW12_ADDR, p_buffer, 1);
if(err_code == NRFX_ERROR_BUSY ){
printf("NRFX_ERROR_BUSY");
}
else if(err_code == NRFX_ERROR_INTERNAL ){
printf("NRFX_ERROR_INTERNAL ");
}
else if(err_code == NRFX_ERROR_INVALID_STATE ){
printf("NRFX_ERROR_INVALID_STATE");
}
else if(err_code == NRFX_ERROR_DRV_TWI_ERR_ANACK ){
printf("NRFX_ERROR_DRV_TWI_ERR_ANACK");
}
else if(err_code == NRFX_ERROR_DRV_TWI_ERR_DNACK ){
printf("NRFX_ERROR_DRV_TWI_ERR_DNACK");
}
APP_ERROR_CHECK(err_code);
p_buffer++;
addr++;
}
return err_code;
}
static int32_t platform_write(void *ptr, uint8_t addr, const uint8_t *buf, uint16_t len)
{
uint8_t txBuf[MAX_TX_BUFF_LEN];
APP_ERROR_CHECK(len > MAX_TX_BUFF_LEN);
txBuf[0] = addr;
for (int i = 0; i < len; i++)
{
txBuf[i+1] = buf[i];
}
int32_t err_code = nrfx_twi_tx(m_p_acc_twi, LIS2DW12_ADDR, txBuf, len+1, true);
APP_ERROR_CHECK(err_code);
return err_code;
}
static void platform_delay(uint32_t millisec)
{
nrf_delay_ms(millisec);
}
bool LIS2DW_init(nrfx_twi_t const * p_instance)
{
m_p_acc_twi = p_instance;
/* Initialize mems driver interface */
dev_ctx.write_reg = platform_write;
dev_ctx.read_reg = platform_read;
dev_ctx.mdelay = platform_delay;
dev_ctx.handle = &m_p_acc_twi;
/* Wait sensor boot time */
platform_delay(BOOT_TIME);
/* Check device ID */
lis2dw12_device_id_get(&dev_ctx, &whoamI);
printf("%x\n", whoamI);
if (whoamI != LIS2DW12_ID)
{
printf("Broken \n");
while (1);
return false;
}
/* Restore default configuration */
lis2dw12_reset_set(&dev_ctx, PROPERTY_ENABLE);
do
{
lis2dw12_reset_get(&dev_ctx, &rst);
}
while (rst);
return true;
}
static lis2dw12_reg_t int_route;
float findMax( float arr, int size) {
float max = arr[0];
for (int i = 1; i < size; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
int main(void)
{
ret_code_t err_code;
int position = 0;
const nrfx_twi_config_t m_config = {
.scl = NRF_GPIO_PIN_MAP(0,19),
.sda = NRF_GPIO_PIN_MAP(0,22),
.frequency = NRF_DRV_TWI_FREQ_100K,
.interrupt_priority = NRFX_TWI_DEFAULT_CONFIG_IRQ_PRIORITY,
};
err_code = nrfx_twi_init(&m_twi, &m_config, NULL, NULL);
if(err_code == NRFX_SUCCESS ){
printf("NRFX_SUCCESS ");
}
else if(err_code == NRFX_ERROR_INVALID_STATE ){
printf(" NRFX_ERROR_INVALID_STATE ");
}
else if(err_code == NRFX_ERROR_BUSY ){
printf("NRFX_ERROR_BUSY ");
}
APP_ERROR_CHECK(err_code);
nrfx_twi_enable(&m_twi);
if (LIS2DW_init(&m_twi) == 1){
printf(“Successfully init for 3 axis sensor”);
};
lis2dw12_i2c_interface_set(&dev_ctx,LIS2DW12_I2C_ENABLE);
lis2dw12_full_scale_set(&dev_ctx, LIS2DW12_2g);
do{
lis2dw12_full_scale_get(&dev_ctx, LIS2DW12_2g );
}while(rst);
lis2dw12_filter_path_set(&dev_ctx, LIS2DW12_LPF_ON_OUT);
lis2dw12_filter_bandwidth_set(&dev_ctx, LIS2DW12_ODR_DIV_4);
/* Configure power mode /
lis2dw12_power_mode_set(&dev_ctx, LIS2DW12_CONT_LOW_PWR_LOW_NOISE_12bit);
/ Set Output Data Rate */
lis2dw12_data_rate_set(&dev_ctx, LIS2DW12_XL_ODR_25Hz);
/* Initialize all configured peripherals */
bsp_board_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS);
/* Initialise nRF52840-DK GPIOs */
gpio_init();
/* Initialise the SPI for nRF52840-DK */
nrf52840_dk_spi_init();
/* Configuring interrupt*/
dw_irq_init();
/* Small pause before startup */
nrf_delay_ms(2);
test_run_info((unsigned char *)"DEV ID OK");
/* Display application name on LCD. */
test_run_info((unsigned char *)APP_NAME);
/* Configure SPI rate, DW3000 supports up to 36 MHz */
port_set_dw_ic_spi_fastrate();
/* Reset and initialize DW chip. */
reset_DWIC(); /* Target specific drive of RSTn line into DW3000 low for a period. */
Sleep(2); // Time needed for DW3000 to start up (transition from INIT_RC to IDLE_RC, or could wait for SPIRDY event)
/* Probe for the correct device driver. */
dwt_probe((struct dwt_probe_s *)&dw3000_probe_interf);
while (!dwt_checkidlerc()) /* Need to make sure DW IC is in IDLE_RC before proceeding */ { };
if (dwt_initialise(DWT_DW_INIT) == DWT_ERROR)
{
test_run_info((unsigned char *)"INIT FAILED ");
while (1) { };
}
/* Enabling LEDs here for debug so that for each TX the D1 LED will flash on DW3000 red eval-shield boards. */
dwt_setleds(DWT_LEDS_ENABLE | DWT_LEDS_INIT_BLINK);
/* Configure DW IC. See NOTE 13 below. */
/* if the dwt_configure returns DWT_ERROR either the PLL or RX calibration has failed the host should reset the device */
if (dwt_configure(&config))
{
test_run_info((unsigned char *)"CONFIG FAILED ");
while (1) { };
}
/* Configure the TX spectrum parameters (power, PG delay and PG count) */
dwt_configuretxrf(&txconfig_options);
/* Apply default antenna delay value. See NOTE 2 below. */
dwt_setrxantennadelay(RX_ANT_DLY);
dwt_settxantennadelay(TX_ANT_DLY);
/* Next can enable TX/RX states output on GPIOs 5 and 6 to help debug, and also TX/RX LEDs
* Note, in real low power applications the LEDs should not be used. */
dwt_setlnapamode(DWT_LNA_ENABLE | DWT_PA_ENABLE);
/* Loop forever initiating ranging exchanges. */
float sensor_data_num;
while (1)
{
memset(sensor_data, 0x00, 100*sizeof(sensor_data));
do{
memset(tx_buffer_sensor, 0x00,4);
uint8_t reg = 0;
/* Read output only if new value is available */
lis2dw12_flag_data_ready_get(&dev_ctx, ®);
if (reg) {
/* Read acceleration data */
memset(data_raw_acceleration, 0x00, 3 * sizeof(int16_t));
lis2dw12_acceleration_raw_get(&dev_ctx, data_raw_acceleration);
acceleration_mg[0] = lis2dw12_from_fs2_to_mg(
data_raw_acceleration[0]);
acceleration_mg[1] = lis2dw12_from_fs2_to_mg(
data_raw_acceleration[1]);
acceleration_mg[2] = lis2dw12_from_fs2_to_mg(
data_raw_acceleration[2]);
sensor_data_num = sqrt(acceleration_mg[0] * 0.000061 + acceleration_mg[1]* 0.000061 + acceleration_mg[2]* 0.000061) - 0.23;
if(sensor_data_num > 0){
sprintf((char *)tx_buffer_sensor,
"%4.2f\n",
sensor_data_num);
printf("%s\n", tx_buffer_sensor); // x,xx
sensor_data[position] = sensor_data_num;
position++;
Sleep(100);
}
}
}while(sensor_data_num >= 0.03 || sensor_data_num <0);
max_sensor_value = findMax(sensor_data,--position);
sprintf(tx_buffer_sensor,"%f", max_sensor_value);
printf("%s\n", tx_buffer_sensor);
/* Activate reception immediately. */
dwt_rxenable(DWT_START_RX_IMMEDIATE);
/* Poll for reception of a frame or error/timeout. See NOTE 6 below. */
waitforsysstatus(&status_reg, NULL, (DWT_INT_RXFCG_BIT_MASK | SYS_STATUS_ALL_RX_ERR), 0);
if (status_reg & DWT_INT_RXFCG_BIT_MASK)
{
uint16_t frame_len;
/* Clear good RX frame event in the DW IC status register. */
dwt_writesysstatuslo(DWT_INT_RXFCG_BIT_MASK);
/* A frame has been received, read it into the local buffer. */
frame_len = dwt_getframelength();
if (frame_len <= sizeof(rx_buffer))
{
dwt_readrxdata(rx_buffer, frame_len, 0);
/* Check that the frame is a poll sent by "SS TWR initiator" example.
* As the sequence number field of the frame is not relevant, it is cleared to simplify the validation of the frame. */
rx_buffer[ALL_MSG_SN_IDX] = 0;
if (memcmp(rx_buffer, rx_poll_msg, ALL_MSG_COMMON_LEN) == 0)
{
uint32_t resp_tx_time;
int ret;
/* Retrieve poll reception timestamp. */
poll_rx_ts = get_rx_timestamp_u64();
/* Compute response message transmission time. See NOTE 7 below. */
resp_tx_time = (poll_rx_ts + (POLL_RX_TO_RESP_TX_DLY_UUS * UUS_TO_DWT_TIME)) >> 8;
dwt_setdelayedtrxtime(resp_tx_time);
/* Response TX timestamp is the transmission time we programmed plus the antenna delay. */
resp_tx_ts = (((uint64_t)(resp_tx_time & 0xFFFFFFFEUL)) << 8) + TX_ANT_DLY;
/* Write all timestamps in the final message. See NOTE 8 below. */
resp_msg_set_ts(&tx_resp_msg[RESP_MSG_POLL_RX_TS_IDX], poll_rx_ts);
resp_msg_set_ts(&tx_resp_msg[RESP_MSG_RESP_TX_TS_IDX], resp_tx_ts);
for(int i = 0 ; i < 4; i++){
tx_resp_msg[SENSOR_INDEX + i] = tx_buffer_sensor[i];
}
/* Write and send the response message. See NOTE 9 below. */
tx_resp_msg[ALL_MSG_SN_IDX] = frame_seq_nb;
dwt_writetxdata(sizeof(tx_resp_msg), tx_resp_msg, 0); /* Zero offset in TX buffer. */
dwt_writetxfctrl(sizeof(tx_resp_msg), 0, 1); /* Zero offset in TX buffer, ranging. */
ret = dwt_starttx(DWT_START_TX_DELAYED);
/* If dwt_starttx() returns an error, abandon this ranging exchange and proceed to the next one. See NOTE 10 below. */
if (ret == DWT_SUCCESS)
{
/* Poll DW IC until TX frame sent event set. See NOTE 6 below. */
waitforsysstatus(NULL, NULL, DWT_INT_TXFRS_BIT_MASK, 0);
/* Clear TXFRS event. */
dwt_writesysstatuslo(DWT_INT_TXFRS_BIT_MASK);
/* Increment frame sequence number after transmission of the poll message (modulo 256). */
frame_seq_nb++;
Sleep(1000);
}
}
}
}
else
{
/* Clear RX error events in the DW IC status register. */
dwt_writesysstatuslo(SYS_STATUS_ALL_RX_ERR);
}
position = 0;
}
}
Config dwt in esp32 lib:
Error in Anchor: