Tracking 1 Arduino Stella as a Tag with 2 Portenta c33/UWB Shield

Hi everyone,

i already wrote this topic into the german channel but should try it again in the Portenta category.

With the example sketch of the UWB Shield i am able to track 2 UWB Stella. For each Stella (Tag) i just have to start a new session with an individual preamble.

What i cant do is tracking one Tag with Multiple c33 + UWB Shield. The goal is to set up a RTLS with 8 Anchors (all c33+UWB Shiel) and 30 Tags (all Stella).

I am playing around with 2 Anchors and 1 Tag right now.

btw: my coding experiance is 2 out of 10 so please dont judge me :slight_smile:

Anchor 1:

#include <PortentaUWBShield.h>

void rangingHandler(UWBRangingData &rangingData) {
  
  if(rangingData.measureType()==(uint8_t)uwb::MeasurementType::TWO_WAY)
  {
    RangingMeasures twr=rangingData.twoWayRangingMeasure();
    for(int j=0;j<rangingData.available();j++)
    {
      if(twr[j].status==0 && twr[j].distance!=0xFFFF)
      {
        Serial.print("Entfernung zur Session ");
        Serial.print(rangingData.sessionHandle(), HEX);
        Serial.print(" - ");
        Serial.print(twr[j].distance);
        Serial.println(" cm");  
      }
    }
  }
}

void setup()
{
  Serial.begin(115200);

#if defined(ARDUINO_PORTENTA_C33)
  pinMode(LEDR, OUTPUT);
  digitalWrite(LEDR, LOW);
#endif

  UWB.registerRangingCallback(rangingHandler);
  
  UWB.begin(); 
  Serial.println("Starting UWB ...");
  
  while(UWB.state()!=0)
    delay(10);

// ============ SESSION 1 START ============
  Serial.println("Starte Session 1 ...");
  
  uint8_t anchor1Addr[] = {0x21, 0x01}; // Verbindung von Anchor 1 zu Tag 1.
  uint8_t tag1Addr[] = {0x10, 0x01};    // Verbindung zu Tag 1.
  UWBMacAddress anchor1Mac(UWBMacAddress::Size::SHORT, anchor1Addr);
  UWBMacAddress tag1Mac(UWBMacAddress::Size::SHORT, tag1Addr);
  
  //Session 100001 für Tag 1 - bis *30 abändern
  UWBMultiSessionAnchor session1(0x100101, anchor1Mac, tag1Mac, 11);
  
  UWBSessionManager.addSession(session1);
  session1.init();
  session1.start();
// ============ SESSION 1 ENDE ============

}

void loop()
{
#if defined(ARDUINO_PORTENTA_C33)
  digitalWrite(LEDR, !digitalRead(LEDR));
#endif
  delay(1000);
}

Anchor 2

#include <PortentaUWBShield.h>

void rangingHandler(UWBRangingData &rangingData) {
  
  if(rangingData.measureType()==(uint8_t)uwb::MeasurementType::TWO_WAY)
  {
    RangingMeasures twr=rangingData.twoWayRangingMeasure();
    for(int j=0;j<rangingData.available();j++)
    {
      if(twr[j].status==0 && twr[j].distance!=0xFFFF)
      {
        Serial.print("Entfernung zur Session ");
        Serial.print(rangingData.sessionHandle(), HEX);
        Serial.print(" - ");
        Serial.print(twr[j].distance);
        Serial.println(" cm");  
      }
    }
  }
}

void setup()
{
  Serial.begin(115200);

#if defined(ARDUINO_PORTENTA_C33)
  pinMode(LEDR, OUTPUT);
  digitalWrite(LEDR, LOW);
#endif

  UWB.registerRangingCallback(rangingHandler);
  
  UWB.begin(); 
  Serial.println("Starting UWB ...");
  
  while(UWB.state()!=0)
    delay(10);

// ============ SESSION 1 START ============
  Serial.println("Starte Session 1 ...");
  
  uint8_t anchor1Addr[] = {0x22, 0x01}; // Verbindung von Anchor 2 zu Tag 1.
  uint8_t tag1Addr[] = {0x10, 0x01};    // Verbindung zu Tag 1.
  UWBMacAddress anchor1Mac(UWBMacAddress::Size::SHORT, anchor1Addr);
  UWBMacAddress tag1Mac(UWBMacAddress::Size::SHORT, tag1Addr);
  
  //Session 100001 für Tag 1 - bis *30 abändern
  UWBMultiSessionAnchor session1(0x100102, anchor1Mac, tag1Mac, 11);

  UWBSessionManager.addSession(session1);
  session1.init();
  session1.start();
// ============ SESSION 1 ENDE ============

}

void loop()
{
#if defined(ARDUINO_PORTENTA_C33)
  digitalWrite(LEDR, !digitalRead(LEDR));
#endif
  delay(1000);
}

Tag

#include "StellaUWB.h"

void rangingHandler(UWBRangingData &rangingData) {

  if(rangingData.measureType()==(uint8_t)uwb::MeasurementType::TWO_WAY)
  {
    RangingMeasures twr=rangingData.twoWayRangingMeasure();
    for(int j=0;j<rangingData.available();j++)
    {
      if(twr[j].status==0 && twr[j].distance!=0xFFFF)
      {
        Serial.print("Distance: ");
        Serial.println(twr[j].distance);  
      }
    }
  }
}

void setup() {
  Serial.begin(115200);
  
  uint8_t devAddr[]={0x10,0x01};
  uint8_t destination1[]={0x21,0x01}; // Verbindung zu Anchor 1 von Tag 1.
  uint8_t destination2[]={0x22,0x01}; // Verbindung zu Anchor 2 von Tag 1.

  UWBMacAddress srcAddr(UWBMacAddress::Size::SHORT,devAddr);
  UWBMacAddress dstAddr1(UWBMacAddress::Size::SHORT,destination1);
  UWBMacAddress dstAddr2(UWBMacAddress::Size::SHORT,destination2);

  UWB.registerRangingCallback(rangingHandler);
  
  UWB.begin(); 
  Serial.println("Starting UWB ...");

  while(UWB.state()!=0)
    delay(10);

  Serial.println("Starting session ...");
  UWBMultiSessionTag myTag1(0x100101, srcAddr, dstAddr1, 11);
  UWBMultiSessionTag myTag2(0x100102, srcAddr, dstAddr2, 11);

  UWBSessionManager.addSession(myTag1);
  UWBSessionManager.addSession(myTag2);

  myTag1.init();
  myTag2.init();

  myTag1.start();
  myTag2.start();
}

void loop() {
  delay(1000);
}

Anchor 1 > Tag 1 is working fine
Anchor 2 > Tag 1 is not

In the Tag code i defined 2 destinations: (0x21, 0x01) and (0x22, 0x01).
So the Tag "knows" both Anchors.

In each Anchor i defined the Tag with (0x10, 0x01)
So both Anchors knows the Tag

Aso both Anchors are defined as anchor mac (0x21, 0x01) and (0x22, 0x01), which is matching to the tag.

What i am doing whrong here?

Hi @angelarts, I’m also struggling with setting up several Portenta C33 as anchors to track one Stella. Did you make any progress?

I tried uploading the “UWB_OneToMany” example from the StellaUWB library to Stella and the “UWB_RangingControlee” from the PortentaUWBShield library to the Portentas (each with its own 2-bytes MAC address), but it didn’t work for me. In Serial Monitor, I would get only this message:

GOT RANGING DATA - Type: 1

but no actual ranging. So I came up with another solution, but it is rather poor. It allows me to collect five-six pieces of ranging data at one anchor, then this anchor resets completely, and during this time the ranging data is collected at the next anchor. This means that real-time trilateration is not possible, but rather a sequential querying of anchors. Here is my code:

Portenta (Anchors):

#include <PortentaUWBShield.h>

// === ANCHOR 1: 0x111111 / {0x22, 0x22} ===
// === ANCHOR 2: 0x222222 / {0x33, 0x33} ===

const uint32_t SESSION_ID = 0x111111;
uint8_t myAddr[]          = {0x22, 0x22};  
uint8_t tagAddr[]         = {0x11, 0x11}; //Stella's MAC address

UWBMacAddress srcAddr(UWBMacAddress::Size::SHORT, myAddr);
UWBMacAddress dstAddr(UWBMacAddress::Size::SHORT, tagAddr);
UWBRangingControlee* myControlee = nullptr;

// Watchdog
volatile uint32_t lastMeasurementTime = 0;
const uint32_t WATCHDOG_TIMEOUT = 5000;

void rangingHandler(UWBRangingData &rangingData) {
  if (rangingData.measureType() == (uint8_t)uwb::MeasurementType::TWO_WAY) {
    RangingMeasures twr = rangingData.twoWayRangingMeasure();
    for (int j = 0; j < rangingData.available(); j++) {
      if (twr[j].status == 0 && twr[j].distance != 0xFFFF) {
        lastMeasurementTime = millis();
        Serial.print("Dist: ");
        Serial.println(twr[j].distance);
      }
    }
  }
}

void setup() {
  Serial.begin(115200);
  delay(2000); 

  #if defined(ARDUINO_PORTENTA_C33)
    pinMode(LEDR, OUTPUT);
    digitalWrite(LEDR, LOW);
  #endif

  Serial.println("=== Anchor Start ===");

  UWB.registerRangingCallback(rangingHandler);
  UWB.begin();
  
  // Wait till ready
  uint32_t startWait = millis();
  while (UWB.state() != 0) {
    delay(10);
    if (millis() - startWait > 10000) {
      Serial.println("UWB.begin() Timeout - Hardware Reset!");
      delay(100);
      NVIC_SystemReset();  // complete reset
    }
  }

  myControlee = new UWBRangingControlee(SESSION_ID, srcAddr, dstAddr);
  UWBSessionManager.addSession(*myControlee);
  myControlee->init();
  myControlee->start();

  lastMeasurementTime = millis();

  Serial.print("Anchor ready. ID: 0x");
  Serial.println(SESSION_ID, HEX);
}

void loop() {
  // Watchdog: Hardware reset if no ranging data
  if (millis() - lastMeasurementTime > WATCHDOG_TIMEOUT) {
    Serial.println("!!! Watchdog: Hardware Reset !!!");
    delay(100); 
    NVIC_SystemReset(); 
  }
  
  delay(100);
}

Stella (Tag):

#include "StellaUWB.h"

uint8_t tagAddr[]     = {0x11, 0x11};
uint8_t anchor1Addr[] = {0x22, 0x22};
uint8_t anchor2Addr[] = {0x33, 0x33};

UWBMacAddress srcAddr(UWBMacAddress::Size::SHORT, tagAddr);
UWBMacAddress dstAddr1(UWBMacAddress::Size::SHORT, anchor1Addr);
UWBMacAddress dstAddr2(UWBMacAddress::Size::SHORT, anchor2Addr);

const uint32_t session1ID = 0x111111;
const uint32_t session2ID = 0x222222;

UWBTracker* tracker1 = nullptr;
UWBTracker* tracker2 = nullptr;

volatile uint16_t distAnchor1 = 0;
volatile uint16_t distAnchor2 = 0;

void rangingHandler(UWBRangingData &rangingData) {
  uint32_t id = rangingData.sessionHandle();
  
  if (rangingData.measureType() == (uint8_t)uwb::MeasurementType::TWO_WAY) {
    RangingMeasures twr = rangingData.twoWayRangingMeasure();
    for (int j = 0; j < rangingData.available(); j++) {
      if (twr[j].status == 0 && twr[j].distance != 0xFFFF) {
        if (id == session1ID) {
          distAnchor1 = twr[j].distance;
          Serial.print("A1: ");
          Serial.println(distAnchor1);
        } else if (id == session2ID) {
          distAnchor2 = twr[j].distance;
          Serial.print("A2: ");
          Serial.println(distAnchor2);
        }
      }
    }
  }
}

void setup() {
  Serial.begin(115200);
  delay(5000);
  Serial.println("=== Stella Start ===");

  UWB.registerRangingCallback(rangingHandler);
  UWB.begin();
  while (UWB.state() != 0) delay(10);

  tracker1 = new UWBTracker(session1ID, srcAddr, dstAddr1);
  UWBSessionManager.addSession(*tracker1);
  tracker1->init();

  tracker2 = new UWBTracker(session2ID, srcAddr, dstAddr2);
  UWBSessionManager.addSession(*tracker2);
  tracker2->init();

  Serial.println("Setup ready");
}

void loop() {
  // === Anchor 1 measurement ===
  Serial.println("\n-> Anchor 1");
  tracker1->start();
  delay(2000);  // 2 s measurement
  tracker1->stop();
  delay(1000);  // 1 s pause

  // === Anchor 2 measurement ===
  Serial.println("\n-> Anchor 2");
  tracker2->start();
  delay(2000);  
  tracker2->stop();
  delay(1000); 

  Serial.println("\n=== Distances===");
  Serial.print("A1: ");
  Serial.print(distAnchor1);
  Serial.print(" cm | A2: ");
  Serial.print(distAnchor2);
  Serial.println(" cm");
}

Without resetting, I would get something like this at one anchor:

Anchor ready. ID: 0x222222
Distance: 81
Distance: 83
Distance: 82
Distance: 83
No handler for notification type: 9
APP :ERROR:uwb_bus_io_irq_wait:
phOsalUwb_ConsumeSemaphore_WithTimeout failed

and I have no idea what these errors mean or how to fix them. So in theory, it is possible to obtain data at each anchor, but it fails after 3-5 measurements if I don't reset.

Hi Hesea,

yes indeed. I have a solution to run multiple tags and multiple anchors but doing step by step in this project. I tested 2 anchors and 2 Tags and with the next step i tryed to do a wifi connection and send everything via UDP to my network. Arduino replyed to me, that they still have bugs with UDP that after a few sends the board stops sending. So untill this is solved, i put the project on hold. I think i will also focus on TdoA once a demo sketch is available.

Hello everyone,

I'd like to participate in this discussion/development project, as my project involves a RTLS (in triangulation with 2 anchors (c33 + UWB Shield) and 2 tags (stella) or in trilateration with 3 anchors (c33 + UWB Shield) and 2 tags (stella)).

I'm new to Arduino...

What timeframe does Arduino expect to resolve bugs? ("Arduino replyed to me, that they still have bugs with UDP that after a few sends the board stops sending")

Bye for now
Olivier Bessire

I received the same Answer but without any timeframe. Arduino also informed me, that soon a demo sketch with TdoA will be available so i have this topic on hold for now until it is up.

Have you had any success by now?

If this question is for me, the answer is no. Last contact with Arduino i got the reply, that the TdoA is included in the backlog but the developer team is working on higher-priority tasks. So there is no timeframe for any near updates from Arduino / Truesense.

I have returned / send back all my c33 and Stella as i fortunately brought them via Amazon.

Now i am keeping this project on hold untill Arduino fixes all technical issues and bring a usable demo of TdoA. Not sure if the Stella has already reached the End of Life but i dont have any financial (negative) impact.

My guess is, that this project will continue with implementing it to the Arduino Zephyr Core.