Challenger RP2040 UWB (DWM3000)

The Challenger RP2040 SubGHz board is an Arduino/Circuitpython compatible Adafruit Feather format micro controller board based on the Raspberry Pico (RP2040) chip.

We wanted to start playing around with ultra wide band (UWB) radios and positioning so we created this new Challenger board based on our proven RP2040 technology combined with a DWM3000 UWB module. This module is compatible with Apple’s U1 chip and supports channels 5 (6.5GHz) and 9 (8GHz).


Qorvo’s DWM3000 module is based on the DW3110 ultra-wideband (UWB) transceiver IC. It is IEEE 802.15.4a and IEEE 802.15.4z BPRF mode compliant and allows location of objects to a precision of 10 cm. The module has worldwide UWB support through its operation on UWB channel 5 (6.5 GHz) and channel 9 (8 GHz). The module is also interoperable with the Apple U1 chip and is designed to be compliant to the FiRa™ PHY and MAC specifications enabling interoperability with other FiRa™ compliant devices.

Here’s a simple table with a few of the highlights of the module listed:

Challenger RP2040 UWB Pinout

LiPo battery / charger

The board is equipped with a standard 2.0mm JST connector for connecting a rechargeable LiPo battery. There is also an internal battery charger circuit that charges your battery as long as a USB cable is inserted or the VUSB connection is connected to 5V.

USB Type C

In the recent years we have noticed that we are seeing more and more USB Type C cable laying around the lab due to the fact that all new phones and accessories use them. As of yet we haven’t seen any shortage of micro USB cables but we are not getting any new ones any more and old ones do break occasionally. So we decided to go for a USB Type C connector for this board. A bonus of this is that they are quite bit more durable and you don’t have to fiddle with the cable before plugging it in.

For more detailed information about this development board check out the datasheet.

Weight 0.009 kg
Dimensions 5.07 × 2.28 × 0.72 cm

Using the Arduino environment

We’ve teamed up with Earle F. Philhower over at his Github page to provide Arduino support for our RP2040 based boards. You can follow the instructions on Earle’s github page or you can check out our instructions here on how to install the package.

Library support for the UWB transceiver.

We have created a an arduino support library which can be found here.


The Challenger RP2040 UWB board is compatible Adafruits CircuitPython, however to date there does not exist a support library in Python for the DWM3000 module. Maybe you will be the one that writes it =)


We have created a fork from an existing support library and adapted it to our board. Here’s a simple example on how to get started sending data packets.

#include "dw3000.h"
#define APP_NAME "SIMPLE TX v1.1"
/* 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_LEN_64, /* STS length, see allowed values in Enum dwt_sts_lengths_e */
DWT_PDOA_M0 /* PDOA mode off */

/* The frame sent in this example is an 802.15.4e standard blink. It is a 12-byte frame composed of the following fields:
* - byte 0: frame type (0xC5 for a blink).
* - byte 1: sequence number, incremented for each new frame.
* - byte 2 -> 9: device ID, see NOTE 1 below.
static uint8_t tx_msg[] = {0xC5, 0, 'D', 'E', 'C', 'A', 'W', 'A', 'V', 'E'};
/* Index to access to sequence number of the blink frame in the tx_msg array. */
#define FRAME_LENGTH (sizeof(tx_msg) + FCS_LEN) // The real length that is going to be transmitted

/* Inter-frame delay period, in milliseconds. */
#define TX_DELAY_MS 500

extern dwt_txconfig_t txconfig_options;

void setup()
while (!Serial)


/* Start SPI and get stuff going*/

delay(200); // 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
Serial.println("IDLE FAILED01");
while (1);


while (!dwt_checkidlerc()) // Need to make sure DW IC is in IDLE_RC before proceeding
Serial.println("IDLE FAILED02");
while (1);

// Serial.println("IDLE OK");
if (dwt_initialise(DWT_DW_INIT) == DWT_ERROR)
Serial.println("INIT FAILED");
while (1);
// Serial.println("INIT OK");

// Configure DW IC. See NOTE 5 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
Serial.println("CONFIG FAILED");
while (1);
// Serial.println("CONFIG OK");
/* Configure the TX spectrum parameters (power PG delay and PG Count) */

void loop()
/* Write frame data to DW IC and prepare transmission. See NOTE 3 below.*/
dwt_writetxdata(FRAME_LENGTH - FCS_LEN, tx_msg, 0); /* Zero offset in TX buffer. */

/* In this example since the length of the transmitted frame does not change,
* nor the other parameters of the dwt_writetxfctrl function, the
* dwt_writetxfctrl call could be outside the main while(1) loop.
dwt_writetxfctrl(FRAME_LENGTH, 0, 0); /* Zero offset in TX buffer, no ranging. */

/* Start transmission. */
delay(10); // Sleep(TX_DELAY_MS);

/* Poll DW IC until TX frame sent event set. See NOTE 4 below.
* STATUS register is 4 bytes long but, as the event we are looking at is in the first byte of the register, we can use this simplest API
* function to access it.*/

while (!(dwt_read32bitreg(SYS_STATUS_ID) & SYS_STATUS_TXFRS_BIT_MASK))
/* Reads and validate device ID returns DWT_ERROR if it does not match expected else DWT_SUCCESS */
// if (dwt_check_dev_id() == DWT_SUCCESS)
// UART_puts((char *)"DEV ID OK");
} else {
// UART_puts((char *)"DEV ID FAILED");
// delay(500);
// delay(1000);

/* Clear TX frame sent event. */

Serial.println("TX Frame Sent");

/* Execute a delay between transmissions. */

/* Increment the blink frame sequence number (modulo 256). */

Documentation for the Challenger RP2040 UWB board.

You can always get our products from your local reseller if you like. Here is a list of current resellers.

  • The Pi Hut Raspberry Pi Superstore
  • The largest maker shop in Switzerland


There are no reviews yet.

Only logged in customers who have purchased this product may leave a review.