[DWM3001CDK]What should I do to start ranging between the two boards?

I have two DWM3001CDK boards,and I use Embedded Studio compile and download.
One is initiator, and the other is responder.The D20 led of two board is always flashing.
In official package(Qorvo_Apple_Nearby_Interaction_1.0.0),I don’t find any example.
What should I do to start ranging between the two boards?just show distance on RTT Viewer is ok.
I would be so appreciate if someone give some help.

About DWM1001 and DWM1001-DEV,I use DS-TWR example (5a & 5b) in offical package can range well.
DWM3001CDK should program pull msg and response msg myself?

Hi @Doney!

The sample code you got is to be used with an iPhone equipped with a U1 UWB chip. The iPhone running the Qorvo iOS app will be able to track your DWM3001CD Kits. Despite your modification, the boards are still waiting for an iPhone command to start TWR.

It is possible to modify the source code to track each other, by removing the BLE and NIQ part and starting FiRa with default configuration on both boards, one as responder and the other as initiator, on startup. Check the functions in ble_niq.c and fira_niq.c.


@carlos.silva Thank you so much.
I modified a little code,and the task started.
About ***_niq.c,I could’t see detail,because they were encapsulated as ***.a file.

Here is another question, RTT Viewer log:{“TWR”: {“R”:6613,“a16”:“0x0001”,“S”:“ERR”,“D cm”:0}}
I found in [reportTask]function,results->measurements[i].status is not 0.
"results"data is assigned by fira_helper_parse_ranging_report function,but detailed processing is invisible.
How can I get the distance? >

1 Like

This is related to Nearby Interaction, you don’t need it to FiRa TWR. You can remove any reference to this (or the library).

From _reportTask, did you check results? results is a ranging_results structure, in this, you will find a ranging_measurements array called measurements, one of its parameters is int32_t distance_mm

Hi Doney,

Do you have successfully obtained the distance?
How do you modify the code to obtain the TWR results between two DWM3001CDK boards? Could you please help us to do that? We’d really appreciate it.


Thanks for the suggestions. I’m attempting to perform TWR with two DWM3001CDK units and I’m trying to unravel your suggestions.

I’ve edited main.c to comment out the ble_init, niq_init and niq_set_ranging_role. I’m now left with the main_uwb function.

It seems to enter the main_uwb function but get hung up waiting for the Signal to return. I suspect this is some flow control with the other tasks. I’ve tried bypassing this but haven’t had luck with sending or receiving frame results data.

Any suggestions or pointers on how to proceed will be much appreciated!

  • kmatch

distance_mm is empty,i think it is because of the wrong statement.
i want to know why does [results->measurements[i].status is not 0] happened, but i have no way do know that.

signal is set here [ResumeUwbTasks]

no, 3001CDK only communicate with iPhone replied by official customer service.
if you only want the TWR data,DWM1001 or DWM3000 would be ok i think.

Hi Kmatch,

We also attempt to perform TWR with two DWM3001CDK kits, and do the same setups and get the same results.

Are there any guys successfully do that.

Any suggestions will be much appreciated!



If we comment out the ble_init , niq_init and niq_set_ranging_role , how can we know the currently style of the board, Tag or Anchor. I think there must some place that we can set the model of the board.


what is this function, is it for the model of the board, tag or anchor.

I change the set and I just get the message, no distance. The schedule seem no work.

00> {“TWR”: {“R”:1,“a16”:“0x0001”,“S”:“ERR”,“D cm”:0}}

Hi @carlos.silva

We modified the code a little.

Based on the LED D13. It seems that he Responder can receive the message of the Initiator, but the Initiator can not receive the feedback message from the Initiator.
So we get this message {“TWR”: {“R”:34,“a16”:“0x0001”,“S”:“ERR”,“D cm”:0}}, the ranging is error.

Now, we do not know how to do to the next step. Could you give us some suggestions.


Hi @zyliu , looks like a good progress!

If you received only one message, maybe something is breaking the code after start ranging. The initiator should keep sending “poll” packets even after a ranging error, and you would get many of these messages per second, even if they are “ERR”.

It is ok to get a bad packet like this at the very start, but after that, you should receive only good packets.
If you received many of these “Err” messages, you probably have a different configuration between devices. Please check the fira_param structure to confirm that the parameters are the same for both devices.


So, I’m definitely getting some response by the Responder when the Initiator is powered up, however I’m not getting any ranging data.

I suspect that something is wrong with my configurations, as mentioned by @carlos.silva, but I’m unsure where to start to resolve this. One thing I was suspecting is that perhaps the two units are using the same address, but it’s unclear how to verify or set these.

Many thanks, since your suggestions above have been helping me make progress, but there’s still a bit further to get it to work property.

Any guidance on how to debug through this is very welcome. See details below in the next reply.

Sorry, made an error in my previous replies, so correcting here. I’ve made progress, but still not ranging properly. Here is what I’ve uncovered.

I’m using two DWM3001CDK to work toward Two-Way Ranging TWR. My starting point is the code for the tag to be used with the provided iPhone app.

I deleted the ble and niq initializations in main.c. I finally realized that the main_uwb.c function called _defaultTask is not properly setting up the controllER versus controllEE. With the current code, it was always setting the unit as “controlEE”. So, to make it work, I added a variable ranging_role that I could set to 0/1 for Responder/Initiator (same as the value set in ACCESSORY_RANGING_ROLE). See snippet of code below. You will also need to create a:
static uint32_t ranging_role = 1; /**< Responder 0, Initiator 1 */

[Note: I struggled for a bit since I first tried using AppSetFiRaRole and AppGetFiRaRole, but I can’t seem to make sense about what they are doing. I try setting with a value and then I read back a different value (that’s strange). These two functions are also used in construct_fira_param_from_config so they may require additional scrutiny in that function too.]

Current behavior is still not providing measurements, but at least there is some back and forth communications.

  • If I plug in Responder alone, there is no report printed, it just waits. D13 flashes Red

  • If I plug in Initiator alone, there is a string of ERR ranging reports printed, with increasing Block Index. D13 flashes Yellow and Red:

report_cb ->
report_cb <-
{"TWR": {"R":54,"a16":"0x0001","S":"ERR","D cm":0}}
report_cb ->
report_cb <-
{"TWR": {"R":55,"a16":"0x0001","S":"ERR","D cm":0}}
report_cb ->
report_cb <-
{"TWR": {"R":56,"a16":"0x0001","S":"ERR","D cm":0}}
report_cb ->
report_cb <-
{"TWR": {"R":57,"a16":"0x0001","S":"ERR","D cm":0}}
report_cb ->
  • If I plug in the Responder, and subsequently plug in the Initiator and watch the response on the responder, I start start to see a string of ERR ranging reports, all with a Block Index = 1. Responder D13 flashes Red, Initiator D13 flashes Yellow and Red.
report_cb ->
report_cb <-
{"TWR": {"R":1,"a16":"0x0001","S":"ERR","D cm":0}}
report_cb ->
report_cb <-
{"TWR": {"R":1,"a16":"0x0001","S":"ERR","D cm":0}}
report_cb ->
report_cb <-
{"TWR": {"R":1,"a16":"0x0001","S":"ERR","D cm":0}}
report_cb ->
report_cb <-
{"TWR": {"R":1,"a16":"0x0001","S":"ERR","D cm":0}}
report_cb ->
report_cb <-
{"TWR": {"R":1,"a16":"0x0001","S":"ERR","D cm":0}}

 * This task will start an Accessory as a UWB Initiator or UWB Responder
static void _defaultTask(const void *arg)
    char str[256];
    osEvent evt;
    fira_param_t *fira_param = get_fira_config();
    param_block_t* app_config = AppConfigGet();

    char result[256];
    snprintf(result, sizeof(result), "_defaultTask ->\n");            
    port_tx_msg(result, strlen(result));


        evt = osSignalWait(Signal, 10000);
        osMutexWait(defaultTask.MutexId, 0);


        if(evt.status == osEventTimeout)




        snprintf(result, sizeof(result), "ResumeUWBTasks: evt.value_signals %d\n", evt.value.signals);            
        port_tx_msg(result, strlen(result));

        snprintf(result, sizeof(result), "_defaultTask - AppGetFiraRole: %d\n", AppGetFiRaRole());            
        port_tx_msg(result, strlen(result));

        if (ranging_role == 1) {
          construct_fira_param_from_config(fira_param, (void*)&AppConfigGet()->s.fira_config, 1);
          fira_helper_controller((void const *)fira_param);

        } else {
          construct_fira_param_from_config(fira_param, (void*)&AppConfigGet()->s.fira_config, 0);
          fira_helper_controlee((void const *)fira_param);

    snprintf(result, sizeof(result), "_defaultTask <-\n");            
    port_tx_msg(result, strlen(result));

I finally got double-sided two way ranging working. In addition to above changes, I needed to configure the short address and destination addresses so that the units would recognize each other.

I initially just hacked the <default_config.h> to verify it, but then eventually updated the construct_fira_param_from_config function in <main_uwb.c> to adjust the addresses based on the ACCESSORY_RANGING_ROLE.