Close Menu
  • Articles
    • Learn Electronics
    • Product Review
    • Tech Articles
  • Electronics Circuits
    • 555 Timer Projects
    • Op-Amp Circuits
    • Power Electronics
  • Microcontrollers
    • Arduino Projects
    • STM32 Projects
    • AMB82-Mini IoT AI Camera
    • BLE Projects
  • IoT Projects
    • ESP8266 Projects
    • ESP32 Projects
    • ESP32 MicroPython
    • ESP32-CAM Projects
    • LoRa/LoRaWAN Projects
  • Raspberry Pi
    • Raspberry Pi Projects
    • Raspberry Pi Pico Projects
    • Raspberry Pi Pico W Projects
  • Electronics Calculator
Facebook X (Twitter) Instagram
  • About Us
  • Disclaimer
  • Privacy Policy
  • Contact Us
  • Advertise With Us
Facebook X (Twitter) Instagram Pinterest YouTube LinkedIn
How To Electronics
  • Articles
    • Learn Electronics
    • Product Review
    • Tech Articles
  • Electronics Circuits
    • 555 Timer Projects
    • Op-Amp Circuits
    • Power Electronics
  • Microcontrollers
    • Arduino Projects
    • STM32 Projects
    • AMB82-Mini IoT AI Camera
    • BLE Projects
  • IoT Projects
    • ESP8266 Projects
    • ESP32 Projects
    • ESP32 MicroPython
    • ESP32-CAM Projects
    • LoRa/LoRaWAN Projects
  • Raspberry Pi
    • Raspberry Pi Projects
    • Raspberry Pi Pico Projects
    • Raspberry Pi Pico W Projects
  • Electronics Calculator
How To Electronics
Home » Ranging & Localization with ESP32 UWB DW3000 Module
ESP32 Projects

Ranging & Localization with ESP32 UWB DW3000 Module

Mamtaz AlamBy Mamtaz AlamUpdated:January 4, 202410 Comments9 Mins Read
Share Facebook Twitter LinkedIn Telegram Reddit WhatsApp
ESP32 UWB DW3000 (Ultra Wideband)
Share
Facebook Twitter LinkedIn Pinterest Email Reddit Telegram WhatsApp

Overview

In this getting started tutorial, we will learn about the Ultra Wideband Technology using ESP32 DW3000 UWB Module. Ultra-wideband (UWB) is a short-range, wireless communication protocol that operates through radio waves, and enables secure reliable ranging and precision sensing, creating a new dimension of spatial context for wireless devices.

The IC DecaWave DW3000 UWB Transceivers chip allows finding the location of objects to a precision of 10cm. The DW3000 Chip has worldwide UWB support through its operation on UWB channel 5 (6.5GHz) and channel 9 (8GHz). The DW3000 is interoperable with the Apple U1 chip, which makes it possible to work with the Apple ecosystem.

In this tutorial, we will go through the board design, specifications, and applications. Then we will learn how to use the ESP32 DW3000 board for High-Accuracy Ranging and Localization.

Before moving ahead, refer to some of our previous articles related to UWB Technology using DW1000 Chip to have a more clear understanding:

  • Getting Started with ESP32 DW1000 UWB Module
  • ESP32 DW1000 UWB Indoor Location Positioning System
  • ESP32 UWB Pro – Ultra Wideband Module with Amplifier
  • ESP32 DW3000 UWB Module Achieving 500m Range



Bill of Materials

We need the following components for this project. You can purchase the components from the given links.

S.N.ComponentsQuantityPurchase Links
1ESP32 DW3000 Board2Makerfabs
20.96" OLED Display1Amazon | AliExpress
3Micro-USB Cable2Amazon | AliExpress
4Breadboard1Amazon | AliExpress
6Jumper Wires10Amazon | AliExpress

What is Ultra Wideband & how does it work?

UWB is a short-range, wireless communication protocol similar to Bluetooth or Wi-Fi. It also uses radio waves for communication & operates at a very high frequency. As its name denotes, it also uses a wide spectrum of several GHz. One way to think of it is as a radar that can continuously scan an entire room and precisely lock onto an object like a laser beam to discover its location and communicate data.

Ultra Wideband Technology

The primary purpose of Ultra Wideband is location discovery and device ranging. While both Wi-Fi and Bluetooth have been modified to allow greater accuracy in locating other devices and connecting to them, UWB is natively more precise & uses less power.

A UWB transmitter works by sending billions of pulses across the wide spectrum frequency. A corresponding receiver receives the signal, which translates the pulses into data by listening for a familiar pulse sequence sent by the transmitter. Pulses are sent about one every two nanoseconds, which helps UWB achieve its real-time accuracy.

UWB Working

The concepts “anchor” and “tag” are important to understand distance and location measurement with UWB. An anchor is generally a fixed UWB device with a known location. A tag generally refers to a mobile UWB device. An anchor and tag exchange information to establish the distance between them. The exact location of a tag can be determined by communicating with multiple anchors. Some devices can act as either an anchor or tag.




DW3000 UWB Transceivers Chip

The DW3000 chip is a microcontroller chip produced by the company Decawave. It is used in the development of ultra-wideband (UWB) wireless technology for location-based services and precision indoor positioning. The chip is designed for use in applications such as asset tracking, industrial automation, and robotics. It utilizes time-of-flight (ToF) measurement to determine the distance between devices, which allows for precise positioning and location tracking.

DW3000

The module has worldwide UWB support through its operation on UWB channel 5 (6.5GHz) and channel 9 (8GHz). The DW3000 is Optimized for low-power battery-operated operation and has much lower Power consumption, almost 1/3 of the DW1000.


Features of DW3000

  • Interoperable with Apple U1 chip
  • Supports UWB channels 5 (6.5GHz) and 9 (8GHz)
  • Fully aligned with FiRa™ PHY, MAC, and certification development
  • Integrated UWB IC, UWB chip antenna, power management, and crystal simplifies design integration
  • Optimized for Low Power battery operation
  • Allows for cost-effective implementation of UWB solutions
  • Supply voltage: 2.5 to 3.6V (VDD3V3), 1.62 to 3.6V (VDD1)

Applications of DW3000

  • High-precision real-time location systems
  • Location-aware wireless sensor networks
  • Asset tracking
  • Factory/warehouse automation and security
  • Healthcare staff and patient location
  • Retail security, navigation and customer analytics
  • Connected home

ESP32 DW3000 UWB(Ultra Wideband) Board

The ESP32 UWB Ultra Wideband module consists of Decawave DW3000 and ESP32 WiFi Module. This board is manufactured by Makerfabs. The board acts like a continuously scanning radar, that precisely locks onto another device (called Anchor) and communicates with it, thus calculating its own location. The best part about the board is it is interoperable with the Apple U1 chip, which makes it possible to work with the Apple system.

ESP32 DW3000 UWB front

The top side of the board has ESP32 WROOM/Wrover Module embedded with DW3000 Module with other passive electronic components. There are two push buttons, one for flash and the other for rest. The board has a micro-USB port for uploading the firmware and for Serial Communication.

ESP32 DW3000 UWB

On the backside of the board, there is CP2102 Chip for UART communication. The name of the input/output ports is also assigned on the board. You can use male or female headers & solder on both sides of the board.



DW3000 Library Installation

To use the DW3000 with ESP32 on Arduino IDE, we need a DW3000 library. The DW3000 library offers the functionality to use Decawave’s DW3000 chips/modules with any microcontroller.

DW3000 Library

The DW3000 library was developed by NConcepts but you can download it from the following GitHub repository maintained by Makerfabs.

Download: DW3000 Library

The library supports the transmission of messages, and timestamp handling (for ranging and location sensing applications), and implements the different operation modes the DW3000 has to offer. The library design is intended to offer an easy-to-use interface to the otherwise complex and configuration-intense handling of the DW3000.


Anchor & Tag Hardware Setup

Let’s learn how we can use the ESP32 DW3000 UWB (Ultra Wideband) Board with Arduino IDE and measure the distance between the boards. For this project, you will need pair of boards. One of the boards will act like an Anchor and the other like a Tag.

For the Anchor Part, we need nothing except a Micro-USB Cable. But for the tag part, we need a 0.96″ I2C OLED Display and a Powerbank. The I2C OLED will display the distance of the Tag from the Anchor. The Power bank or a battery will provide power to the movable Tag.

On the tag part, connect the 0.96″ I2C OLED display I2C Pins (SDA & SCL) to ESP32 GP21 & GP22 Pin. Connect the VCC & GND Pin of OLED Display to ESP32 3.3V & GND pin.


Source Code/Program for Range Testing

The code that we will use to test the ESP32 DW3000 Board is taken from the library example. Using this example, we can do the range testing which practically lies between a 10-20 meter distance.

Before moving to the testing part, we need to understand the Antenna Delay Calibration. In ultra-wideband (UWB) systems, antenna delay calibration is the process of measuring and compensating for the inherent time delay that occurs between the transmission and reception of a signal due to the physical properties of the antenna. This delay can vary depending on factors such as the frequency of the signal, the size and shape of the antenna, and the distance between the transmitter and receiver. Antenna delay calibration is important for ensuring accurate time-of-flight (ToF) measurements in UWB systems and for improving the overall performance of the system. This can be done by measuring the impulse response of the channel and then compensating for the delay.



For our code part, we have set the default antenna delay values for 64 MHz PRF as 16385. You can modify it according to your UWB Module Behavior & ranging accuracy in the following lines of code.

1
2
3
/* Default antenna delay values for 64 MHz PRF. See NOTE 2 below. */
#define TX_ANT_DLY 16385
#define RX_ANT_DLY 16385

To measure the distance between the boards, we need to upload an “Anchor” code to one ESP32 Board and the “Tag” code to other ESP32 Board.


Anchor Code

Copy the following code and upload it to the first UWB(Ultra Wideband) Board which is the Anchor.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#include "dw3000.h"
 
#define APP_NAME "SS TWR RESP v1.0"
 
// connection pins
const uint8_t PIN_RST = 27; // reset pin
const uint8_t PIN_IRQ = 34; // irq pin
const uint8_t PIN_SS = 4; // spi select pin
 
/* 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};
/* 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
/* 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. */
#ifdef RPI_BUILD
#define POLL_RX_TO_RESP_TX_DLY_UUS 550
#endif //RPI_BUILD
#ifdef STM32F429xx
#define POLL_RX_TO_RESP_TX_DLY_UUS 450
#endif //STM32F429xx
#ifdef NRF52840_XXAA
#define POLL_RX_TO_RESP_TX_DLY_UUS 650
#endif //NRF52840_XXAA
 
#define POLL_RX_TO_RESP_TX_DLY_UUS 450
 
/* 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 setup() {
  UART_init();
  test_run_info((unsigned char *)APP_NAME);
 
  /* Configure SPI rate, DW3000 supports up to 38 MHz */
  /* Reset DW IC */
  spiBegin(PIN_IRQ, PIN_RST);
  spiSelect(PIN_SS);
 
  delay(2); // Time needed for DW3000 to start up (transition from INIT_RC to IDLE_RC, or could wait for SPIRDY event)
 
  while (!dwt_checkidlerc()) // Need to make sure DW IC is in IDLE_RC before proceeding
  {
    UART_puts("IDLE FAILED\r\n");
    while (1) ;
  }
 
  if (dwt_initialise(DWT_DW_INIT) == DWT_ERROR)
  {
    UART_puts("INIT FAILED\r\n");
    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 6 below. */
  if (dwt_configure(&config)) // if the dwt_configure returns DWT_ERROR either the PLL or RX calibration has failed the host should reset the device
  {
    UART_puts("CONFIG FAILED\r\n");
    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);
}
 
void loop() {
  /* Activate reception immediately. */
  dwt_rxenable(DWT_START_RX_IMMEDIATE);
 
  /* Poll for reception of a frame or error/timeout. See NOTE 6 below. */
  while (!((status_reg = dwt_read32bitreg(SYS_STATUS_ID)) & (SYS_STATUS_RXFCG_BIT_MASK | SYS_STATUS_ALL_RX_ERR)))
  { };
 
  if (status_reg & SYS_STATUS_RXFCG_BIT_MASK)
  {
    uint32_t frame_len;
 
    /* Clear good RX frame event in the DW IC status register. */
    dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_RXFCG_BIT_MASK);
 
    /* A frame has been received, read it into the local buffer. */
    frame_len = dwt_read32bitreg(RX_FINFO_ID) & RXFLEN_MASK;
    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);
 
        /* 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. */
          while (!(dwt_read32bitreg(SYS_STATUS_ID) & SYS_STATUS_TXFRS_BIT_MASK))
          { };
 
          /* Clear TXFRS event. */
          dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_TXFRS_BIT_MASK);
 
          /* Increment frame sequence number after transmission of the poll message (modulo 256). */
          frame_seq_nb++;
        }
      }
    }
  }
  else
  {
    /* Clear RX error events in the DW IC status register. */
    dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_ALL_RX_ERR);
  }
}




Tag Code

Copy the following code and upload it to the second UWB(Ultra Wideband) Board called Tag.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
#include "dw3000.h"
#include <Wire.h>
#include <Adafruit_SSD1306.h>
 
#define APP_NAME "SS TWR INIT v1.0"
 
#define SCREEN_WIDTH 128    // OLED display width, in pixels
#define SCREEN_HEIGHT 64    // OLED display height, in pixels
#define OLED_RESET     4    // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C // 0x3D for 128x64, 0x3C for 128x32
 
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
 
 
// connection pins
const uint8_t PIN_RST = 27; // reset pin
const uint8_t PIN_IRQ = 34; // irq pin
const uint8_t PIN_SS = 4; // spi select pin
 
/* 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 */
};
 
/* Inter-ranging delay period, in milliseconds. */
#define RNG_DELAY_MS 1000
 
/* 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 tx_poll_msg[] = {0x41, 0x88, 0, 0xCA, 0xDE, 'W', 'A', 'V', 'E', 0xE0, 0, 0};
static uint8_t rx_resp_msg[] = {0x41, 0x88, 0, 0xCA, 0xDE, 'V', 'E', 'W', 'A', 0xE1, 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
/* Indexes to access some of the fields in the frames defined above. */
#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
/* Frame sequence number, incremented after each transmission. */
static uint8_t frame_seq_nb = 0;
 
/* Buffer to store received response message.
   Its size is adjusted to longest frame that this example code is supposed to handle. */
#define RX_BUF_LEN 20
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. */
#ifdef RPI_BUILD
#define POLL_TX_TO_RESP_RX_DLY_UUS 240
#endif //RPI_BUILD
#ifdef STM32F429xx
#define POLL_TX_TO_RESP_RX_DLY_UUS 240
#endif //STM32F429xx
#ifdef NRF52840_XXAA
#define POLL_TX_TO_RESP_RX_DLY_UUS 240
#endif //NRF52840_XXAA
/* Receive response timeout. See NOTE 5 below. */
#ifdef RPI_BUILD
#define RESP_RX_TIMEOUT_UUS 270
#endif //RPI_BUILD
#ifdef STM32F429xx
#define RESP_RX_TIMEOUT_UUS 210
#endif //STM32F429xx
#ifdef NRF52840_XXAA
#define RESP_RX_TIMEOUT_UUS 400
#endif //NRF52840_XXAA
 
#define POLL_TX_TO_RESP_RX_DLY_UUS 240
#define RESP_RX_TIMEOUT_UUS 400
 
 
/* Hold copies of computed time of flight and distance here for reference so that it can be examined at a debug breakpoint. */
static double tof;
static double distance;
 
/* 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 2 below. */
extern dwt_txconfig_t txconfig_options;
 
void setup()
{
  UART_init();
  test_run_info((unsigned char *)APP_NAME);
 
  Wire.begin();
 
  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS))
  {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;); // Don't proceed, loop forever
  }
  display.clearDisplay();
 
  /* Configure SPI rate, DW3000 supports up to 38 MHz */
  /* Reset DW IC */
  spiBegin(PIN_IRQ, PIN_RST);
  spiSelect(PIN_SS);
 
  delay(2); // Time needed for DW3000 to start up (transition from INIT_RC to IDLE_RC, or could wait for SPIRDY event)
 
  while (!dwt_checkidlerc()) // Need to make sure DW IC is in IDLE_RC before proceeding
  {
    UART_puts("IDLE FAILED\r\n");
    while (1) ;
  }
 
  if (dwt_initialise(DWT_DW_INIT) == DWT_ERROR)
  {
    UART_puts("INIT FAILED\r\n");
    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 6 below. */
  if (dwt_configure(&config)) // if the dwt_configure returns DWT_ERROR either the PLL or RX calibration has failed the host should reset the device
  {
    UART_puts("CONFIG FAILED\r\n");
    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);
 
  /* Set expected response's delay and timeout. See NOTE 1 and 5 below.
     As this example only handles one incoming frame with always the same delay and timeout, those values can be set here once for all. */
  dwt_setrxaftertxdelay(POLL_TX_TO_RESP_RX_DLY_UUS);
  dwt_setrxtimeout(RESP_RX_TIMEOUT_UUS);
 
  /* 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);
}
 
void loop()
{
  /* Write frame data to DW IC and prepare transmission. See NOTE 7 below. */
  tx_poll_msg[ALL_MSG_SN_IDX] = frame_seq_nb;
  dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_TXFRS_BIT_MASK);
  dwt_writetxdata(sizeof(tx_poll_msg), tx_poll_msg, 0); /* Zero offset in TX buffer. */
  dwt_writetxfctrl(sizeof(tx_poll_msg), 0, 1); /* Zero offset in TX buffer, ranging. */
 
  /* Start transmission, indicating that a response is expected so that reception is enabled automatically after the frame is sent and the delay
     set by dwt_setrxaftertxdelay() has elapsed. */
  dwt_starttx(DWT_START_TX_IMMEDIATE | DWT_RESPONSE_EXPECTED);
 
  /* We assume that the transmission is achieved correctly, poll for reception of a frame or error/timeout. See NOTE 8 below. */
  while (!((status_reg = dwt_read32bitreg(SYS_STATUS_ID)) & (SYS_STATUS_RXFCG_BIT_MASK | SYS_STATUS_ALL_RX_TO | SYS_STATUS_ALL_RX_ERR)))
  { };
 
  /* Increment frame sequence number after transmission of the poll message (modulo 256). */
  frame_seq_nb++;
 
  if (status_reg & SYS_STATUS_RXFCG_BIT_MASK)
  {
    uint32_t frame_len;
 
    /* Clear good RX frame event in the DW IC status register. */
    dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_RXFCG_BIT_MASK);
 
    /* A frame has been received, read it into the local buffer. */
    frame_len = dwt_read32bitreg(RX_FINFO_ID) & RXFLEN_MASK;
    if (frame_len <= sizeof(rx_buffer))
    {
      dwt_readrxdata(rx_buffer, frame_len, 0);
 
      /* Check that the frame is the expected response from the companion "SS TWR responder" 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_resp_msg, ALL_MSG_COMMON_LEN) == 0)
      {
        uint32_t poll_tx_ts, resp_rx_ts, poll_rx_ts, resp_tx_ts;
        int32_t rtd_init, rtd_resp;
        float clockOffsetRatio ;
 
        /* Retrieve poll transmission and response reception timestamps. See NOTE 9 below. */
        poll_tx_ts = dwt_readtxtimestamplo32();
        resp_rx_ts = dwt_readrxtimestamplo32();
 
        /* Read carrier integrator value and calculate clock offset ratio. See NOTE 11 below. */
        clockOffsetRatio = ((float)dwt_readclockoffset()) / (uint32_t)(1 << 26);
 
        /* Get timestamps embedded in response message. */
        resp_msg_get_ts(&rx_buffer[RESP_MSG_POLL_RX_TS_IDX], &poll_rx_ts);
        resp_msg_get_ts(&rx_buffer[RESP_MSG_RESP_TX_TS_IDX], &resp_tx_ts);
 
        /* Compute time of flight and distance, using clock offset ratio to correct for differing local and remote clock rates */
        rtd_init = resp_rx_ts - poll_tx_ts;
        rtd_resp = resp_tx_ts - poll_rx_ts;
 
        tof = ((rtd_init - rtd_resp * (1 - clockOffsetRatio)) / 2.0) * DWT_TIME_UNITS;
        distance = tof * SPEED_OF_LIGHT;
 
        /* Display computed distance on LCD. */
        snprintf(dist_str, sizeof(dist_str), "DIST: %3.2f m", distance);
        test_run_info((unsigned char *)dist_str);
        
        display.setCursor(15, 0); //oled display
        display.setTextSize(2);
        display.setTextColor(WHITE);
        display.print("Distance");
        display.setCursor(30, 30); //oled display
        display.setTextSize(2);
        display.print(distance);
        display.print("m");
        display.display();
        display.clearDisplay();
 
      }
    }
  }
  else
  {
    /* Clear RX error/timeout events in the DW IC status register. */
    dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_ALL_RX_TO | SYS_STATUS_ALL_RX_ERR);
  }
 
  /* Execute a delay between ranging exchanges. */
  Sleep(RNG_DELAY_MS);
}


Ranging Test with ESP32 UWB DW3000 Module

Once you upload the anchor and Tag Code, the devices are ready for test. To test the range and its accuracy, you can mark the distance on the floor or on a table.

Ranging Localization with ESP32 UWB DW3000 Module Test

I marked 1 meter distance and between the I marked positions as 30cm, 50cm, 75cm, 100cm. Now the device is ready for testing.

At 30cm, it shows a distance of 28cm.

At 50cm, it shows a distance of 48cm.

On increasing the distance to 75cm, it shows a distance of 77cm.

Finally for a 100cm distance it shows a distance of 107cm.


But it still requires a distance test of 10 meters or more. So, I went outdoors to test the distances. Currently, it’s the snowy season in Canada. Despite I need to do the testing. I have my two friends with me who will help me test this product even in such cold snowy weather. One of the friends has Tag on his hand and he is powering the ESP32 UWB Module using the power bank. The other friend has Anchor which is powered by a laptop.

ESP32 DW3000 Range Testing

When the tag is nearby the detected distance was almost 2 meters. While moving away, the detected distance was almost 11.77 meters.

On moving further to a longer distance, the OLED shows a distance of 23.75 meters. Then it stopped receiving signals. So as far as our test the maximum distance detected was almost 24 meters which is more than the maximum measuring ability of 20 meters.

Overall, we were satisfied with the working and accuracy of the DW3000 UWB Module. Hence, we can say you can use this module for High Accuracy Localization and testing. The ESP32 DW3000 UWB Board also interpolates with the Apple chip, which also requires testing.


Video Tutorial & Guide

High Accuracy Ranging & Localization with ESP32 DW3000 UWB (Ultra Wideband) Module
Watch this video on YouTube.

Share. Facebook Twitter Pinterest LinkedIn Tumblr Email Reddit Telegram WhatsApp
Previous ArticleArduino Water Flow Sensor to Measure Flow Rate & Volume
Next Article Smart Home Controller using DWIN HMI Display & ESP32

Related Posts

ESP32 Fingerprint Attendance System with Live Web Dashboard

ESP32 Fingerprint Attendance System with Live Web Dashboard

Updated:June 16, 2026
IoT Based PM & Air Quality Monitoring System using ESP32

IoT Based PM & Air Quality Monitoring System using ESP32

Updated:June 14, 2026
DIY ESP32 MLX90640 IR Thermal Camera with Live Web Display

DIY ESP32 MLX90640 IR Thermal Camera with Live Web Display

Updated:May 10, 20262K
IoT Activity Tracker with ESP32 & Accelerometer Gyroscope

IoT Activity Tracker with ESP32 & Accelerometer/Gyroscope

Updated:May 2, 2026

ESP32 IoT Vehicle Motion Analyzer with MPU6050 & LIS3MDL

Updated:April 27, 20261K
High-Accuracy Pitch, Roll, Yaw with ESP32 & BNO08x IMU

High-Accuracy Pitch, Roll, Yaw with ESP32 & BNO08x IMU

Updated:April 27, 20262K
View 10 Comments

10 Comments

  1. Markus on February 27, 2023 7:17 AM

    Is it possible to scan and measure the distance to a Iphone or Apples Airtag? If yes, can you please provide a code for that?

    Is it also compatible to Qorvos Nearby Interaction” iOS App?

    Reply
  2. Darren W on April 9, 2023 4:12 PM

    I am having trouble adding the dw3000 zip file from maker fabs website. When I add zip into library I get this error. “Error: 13 INTERNAL: Library install failed: moving extracted archive to destination dir: library not valid” Can anyone help?

    Reply
  3. ponslet on May 8, 2023 3:08 AM

    Hi,
    From inside the folder “Makerfabs-ESP32-UWB-DW3000-main”, you have to copy only the subfolder “Dw3000” to YOUR subfolder ../Arduino/libraries
    So you will have a new folder : ../Arduino/libraries/Dw3000
    regards,
    Jean

    Reply
  4. Markus on May 11, 2023 7:37 AM

    Hi,
    I’m using two of your modules with the range example and very often the distance information will not be updated for seconds. Has anybody the same behavior? How can I fix this?

    Second question: Is it possible to change the communication to 10Hz?
    Right now I’ve only changed the parameter: RNG_DELAY_MS to 100.
    Do I need to change further parameters such as POLL_RX_TO_RESP_TX_DLY_UUS for example?

    Thanks!

    Reply
  5. Michael on May 11, 2023 5:42 PM

    I am looking for a sample tdoa code but I couldn’t find it. How can I find a sample code for TDoA?

    Reply
  6. Jaywalkerblank on June 8, 2023 7:52 AM

    Is it possible to mix DW1000 and DW3000 devices in the same application? Say two DW3000 as anchors and two DW1000 devices as tags? Thanks

    Reply
  7. Guy Provost on June 9, 2023 1:51 PM

    Just go to github (https://github.com/Makerfabs/Makerfabs-ESP32-UWB-DW3000), extract the project somewhere (download it as a zip file), unzip it somewhere, copy the subfolder DW3000 of the extracted zip to your local Arduino “libraries” folder. Dont go the usual Arduino way of “add zip to lib”. At the end, you should have a “DW3000” folder in your arduino libraries folder.

    Reply
  8. Marco on August 9, 2023 8:30 AM

    Hi, I have successfully run the Range demo available in DW3000 lib subfolders, but when I use your code for Anchor and Tag, it doesnt work…
    I did cut and paste of your source, but seems the boards do not receive uwb frames…
    Are you shure the code published on this page work as expected ?

    Reply
  9. Marcus on April 22, 2024 1:15 PM

    Did anyone use this module for multiple tags and anchors ?

    Reply
  10. Jeandre Terblanche on September 26, 2024 2:38 AM

    Hi, I am currently busy with an UWB project that requires long range. I see the article mentioning the 500 meters uses different libraries than the first article (DW3000.h).

    Is there a way to increase the range using the dw3000.h library?

    Reply

CommentsCancel reply

Latest Posts
ESP32 Fingerprint Attendance System with Live Web Dashboard

ESP32 Fingerprint Attendance System with Live Web Dashboard

June 16, 2026
IoT Based PM & Air Quality Monitoring System using ESP32

IoT Based PM & Air Quality Monitoring System using ESP32

June 14, 2026
DIY ESP32 MLX90640 IR Thermal Camera with Live Web Display

DIY ESP32 MLX90640 IR Thermal Camera with Live Web Display

May 10, 2026
IoT Activity Tracker with ESP32 & Accelerometer Gyroscope

IoT Activity Tracker with ESP32 & Accelerometer/Gyroscope

May 2, 2026
A Guide to Sourcing Obsolete ICs for Vintage Projects

Beyond AliExpress: A Guide to Sourcing Obsolete ICs for Vintage Projects

April 21, 2026

ESP32 IoT Vehicle Motion Analyzer with MPU6050 & LIS3MDL

April 27, 2026
Building a Smart Sensor Node with a BLE Microcontroller

Building a Smart Sensor Node with a BLE Microcontroller

February 26, 2026
High-Accuracy Pitch, Roll, Yaw with ESP32 & BNO08x IMU

High-Accuracy Pitch, Roll, Yaw with ESP32 & BNO08x IMU

April 27, 2026
Top Posts & Pages
  • ESP32 Fingerprint Attendance System with Live Web Dashboard
    ESP32 Fingerprint Attendance System with Live Web Dashboard
  • IoT Based PM & Air Quality Monitoring System using ESP32
    IoT Based PM & Air Quality Monitoring System using ESP32
  • Buck Converter: Basics, Working, Design & Application
    Buck Converter: Basics, Working, Design & Application
  • 12V DC to 220V AC Inverter Circuit & PCB
    12V DC to 220V AC Inverter Circuit & PCB
  • ESP32 CAN Bus Tutorial | Interfacing MCP2515 CAN Module with ESP32
    ESP32 CAN Bus Tutorial | Interfacing MCP2515 CAN Module with ESP32
  • IoT AC Energy Meter with PZEM-004T & ESP32 WebServer
    IoT AC Energy Meter with PZEM-004T & ESP32 WebServer
  • Designing of MPPT Solar Charge Controller using Arduino
    Designing of MPPT Solar Charge Controller using Arduino
  • How to use Modbus RTU with ESP32 to read Sensor Data
    How to use Modbus RTU with ESP32 to read Sensor Data
Categories
  • Arduino Projects (197)
  • Articles (60)
    • Learn Electronics (19)
    • Product Review (15)
    • Tech Articles (28)
  • Electronics Circuits (46)
    • 555 Timer Projects (21)
    • Op-Amp Circuits (7)
    • Power Electronics (13)
  • IoT Projects (205)
    • ESP32 MicroPython (7)
    • ESP32 Projects (82)
    • ESP32-CAM Projects (15)
    • ESP8266 Projects (76)
    • LoRa/LoRaWAN Projects (22)
  • Microcontrollers (38)
    • AMB82-Mini IoT AI Camera (4)
    • BLE Projects (18)
    • STM32 Projects (19)
  • Raspberry Pi (93)
    • Raspberry Pi Pico Projects (57)
    • Raspberry Pi Pico W Projects (12)
    • Raspberry Pi Projects (24)
Follow Us
  • Facebook
  • Twitter
  • Pinterest
  • Instagram
  • YouTube
About Us

“‘How to Electronics’ is a vibrant community for electronics enthusiasts and professionals. We deliver latest insights in areas such as Embedded Systems, Power Electronics, AI, IoT, and Robotics. Our goal is to stimulate innovation and provide practical solutions for students, organizations, and industries. Join us to transform learning into a joyful journey of discovery and innovation.

Copyright © How To Electronics. All rights reserved.
  • About Us
  • Disclaimer
  • Privacy Policy
  • Contact Us
  • Advertise With Us

Type above and press Enter to search. Press Esc to cancel.

Ad Blocker Enabled!
Ad Blocker Enabled!
Looks like you're using an ad blocker. Please allow ads on our site. We rely on advertising to help fund our site.