环境配置:参考 Arduino IDE 上手教程完成 IDE 安装,并安装 Stamp C5 对应的板管理和驱动库。
使用到的驱动库:
使用到的硬件产品:




Stamp C5 与 Stamp UWB F 的引脚连接如下:
| Stamp UWB F 引脚 | Stamp C5 GPIO |
|---|---|
| GP7 | G23 |
| IRQ | G0 |
| WAKEUP | G24 |
| RST | G25 |
| MISO | G26 |
| MOSI | G27 |
| CS | G11 |
| SCK | G12 |
#include <Arduino.h>
#include <M5Stamp_UWB.h>
static constexpr uint32_t RANGE_INTERVAL_MS = 200;
static constexpr uint32_t LOG_INTERVAL = 10;
M5Stamp_UWB uwb;
M5Stamp_UWBDSRangeConfig rangeConfig;
bool uwbReady = false;
uint32_t lastRangeMs = 0;
uint32_t rangeCount = 0;
uint32_t okCount = 0;
static bool initUwb()
{
// Stamp-C5 connections to the onboard QM33120 UWB transceiver.
M5Stamp_UWBConfig config;
config.pin_gp7 = 23;
config.pin_irq = 0;
config.pin_wakeup = 24;
config.pin_rst = 25;
config.pin_miso = 26;
config.pin_mosi = 27;
config.pin_cs = 11;
config.pin_sck = 12;
M5Stamp_UWBPHYConfig phy;
phy.channel = M5Stamp_UWBChannel::Channel5;
// Both devices must use the same network addresses and DS-TWR timing.
rangeConfig.panId = 0xDECA;
rangeConfig.initiatorAddress = 0x0001;
rangeConfig.responderAddress = 0x0002;
rangeConfig.responseRxAfterTxDelayUus = 1500;
rangeConfig.responseTxDelayUus = 3000;
rangeConfig.finalTxDelayUus = 1800;
rangeConfig.finalRxAfterResponseTxDelayUus = 500;
rangeConfig.resultRxAfterFinalTxDelayUus = 500;
rangeConfig.rxTimeoutUus = 3000;
rangeConfig.hostTimeoutMs = 100;
rangeConfig.resultRepeatCount = 3;
rangeConfig.resultRepeatGapMs = 3;
if (!uwb.begin(config, phy)) {
Serial.printf("UWB_BEGIN,result=FAIL,error=%s\n", uwb.lastErrorName());
Serial.printf("UWB_RAW_ID,dev_id=0x%08lX\n", static_cast<unsigned long>(uwb.readRawDeviceId()));
return false;
}
const uint32_t devId = uwb.deviceId();
Serial.printf("UWB_ID,dev_id=0x%08lX,chip=%s\n", static_cast<unsigned long>(devId), uwb.chipName());
if (devId != M5STAMP_UWB_QM33120_DEVICE_ID) {
Serial.printf("UWB_ID,result=FAIL,expected=0xDECA0314\n");
return false;
}
Serial.printf("UWB_CONFIG,result=OK,ch=%u,plen=%u,rate=6M8,tx_power=0x%08lX\n", static_cast<unsigned>(phy.channel),
static_cast<unsigned>(phy.preambleLength), static_cast<unsigned long>(phy.txPower));
return true;
}
static void runRanging()
{
// Start one DS-TWR exchange at the configured interval.
if (millis() - lastRangeMs < RANGE_INTERVAL_MS) return;
lastRangeMs = millis();
const M5Stamp_UWBDSRangeResult result = uwb.requestDSRange(rangeConfig);
rangeCount++;
okCount += result.success;
// Print accumulated statistics every LOG_INTERVAL attempts.
if (rangeCount % LOG_INTERVAL) return;
const uint32_t failCount = rangeCount - okCount;
if (result.success) {
Serial.printf(
"DS_RANGE_STAT,count=%lu,ok=%lu,fail=%lu,last=OK,seq=%u,distance_mm=%ld,distance_m=%.3f,elapsed_ms=%lu\n",
static_cast<unsigned long>(rangeCount), static_cast<unsigned long>(okCount),
static_cast<unsigned long>(failCount), result.sequence, static_cast<long>(result.distanceMm),
result.distanceM, static_cast<unsigned long>(result.elapsedMs));
} else {
Serial.printf("DS_RANGE_STAT,count=%lu,ok=%lu,fail=%lu,last=FAIL,seq=%u,error=%s\n",
static_cast<unsigned long>(rangeCount), static_cast<unsigned long>(okCount),
static_cast<unsigned long>(failCount), result.sequence, uwb.lastErrorName());
}
}
void setup()
{
Serial.begin(115200);
delay(1000);
Serial.printf("M5Stamp UWB DS-TWR TAG\n");
Serial.printf("ROLE,mode=TAG\n");
Serial.printf("TWR_MODE,mode=DS-TWR\n");
uwbReady = initUwb();
Serial.printf("TEST_START,result=%s\n", uwbReady ? "OK" : "FAIL");
}
void loop()
{
if (uwbReady) runRanging();
else delay(1000);
}串口输出示例:
M5Stamp UWB DS-TWR TAG
ROLE,mode=TAG
TWR_MODE,mode=DS-TWR
UWB_ID,dev_id=0xDECA0314,chip=QM33120/DW3720
UWB_CONFIG,result=OK,ch=5,plen=128,rate=6M8,tx_power=0xFEFEFEFE
TEST_START,result=OK
DS_RANGE_STAT,count=10,ok=10,fail=0,last=OK,seq=10,distance_mm=164,distance_m=0.164,elapsed_ms=7
DS_RANGE_STAT,count=20,ok=20,fail=0,last=OK,seq=20,distance_mm=166,distance_m=0.166,elapsed_ms=7
DS_RANGE_STAT,count=30,ok=30,fail=0,last=OK,seq=30,distance_mm=171,distance_m=0.171,elapsed_ms=7
DS_RANGE_STAT,count=40,ok=40,fail=0,last=OK,seq=40,distance_mm=168,distance_m=0.168,elapsed_ms=7
DS_RANGE_STAT,count=50,ok=50,fail=0,last=OK,seq=50,distance_mm=153,distance_m=0.153,elapsed_ms=7
DS_RANGE_STAT,count=60,ok=60,fail=0,last=OK,seq=60,distance_mm=196,distance_m=0.196,elapsed_ms=7
DS_RANGE_STAT,count=70,ok=70,fail=0,last=OK,seq=70,distance_mm=172,distance_m=0.172,elapsed_ms=7
DS_RANGE_STAT,count=80,ok=80,fail=0,last=OK,seq=80,distance_mm=182,distance_m=0.182,elapsed_ms=7
DS_RANGE_STAT,count=90,ok=90,fail=0,last=OK,seq=90,distance_mm=160,distance_m=0.160,elapsed_ms=7
DS_RANGE_STAT,count=100,ok=100,fail=0,last=OK,seq=100,distance_mm=157,distance_m=0.157,elapsed_ms=7
DS_RANGE_STAT,count=110,ok=110,fail=0,last=OK,seq=110,distance_mm=210,distance_m=0.210,elapsed_ms=7
DS_RANGE_STAT,count=120,ok=120,fail=0,last=OK,seq=120,distance_mm=164,distance_m=0.164,elapsed_ms=7
DS_RANGE_STAT,count=130,ok=130,fail=0,last=OK,seq=130,distance_mm=174,distance_m=0.174,elapsed_ms=7 #include <Arduino.h>
#include <M5Stamp_UWB.h>
static constexpr uint32_t LOG_INTERVAL = 20;
M5Stamp_UWB uwb;
M5Stamp_UWBDSRangeConfig rangeConfig;
bool uwbReady = false;
uint32_t responseCount = 0;
uint32_t failCount = 0;
static bool initUwb()
{
// Stamp-C5 connections to the onboard QM33120 UWB transceiver.
M5Stamp_UWBConfig config;
config.pin_gp7 = 23;
config.pin_irq = 0;
config.pin_wakeup = 24;
config.pin_rst = 25;
config.pin_miso = 26;
config.pin_mosi = 27;
config.pin_cs = 11;
config.pin_sck = 12;
M5Stamp_UWBPHYConfig phy;
phy.channel = M5Stamp_UWBChannel::Channel5;
// Both devices must use the same network addresses and DS-TWR timing.
rangeConfig.panId = 0xDECA;
rangeConfig.initiatorAddress = 0x0001;
rangeConfig.responderAddress = 0x0002;
rangeConfig.responseRxAfterTxDelayUus = 1500;
rangeConfig.responseTxDelayUus = 3000;
rangeConfig.finalTxDelayUus = 1800;
rangeConfig.finalRxAfterResponseTxDelayUus = 500;
rangeConfig.resultRxAfterFinalTxDelayUus = 500;
rangeConfig.rxTimeoutUus = 3000;
rangeConfig.hostTimeoutMs = 100;
rangeConfig.resultRepeatCount = 3;
rangeConfig.resultRepeatGapMs = 3;
if (!uwb.begin(config, phy)) {
Serial.printf("UWB_BEGIN,result=FAIL,error=%s\n", uwb.lastErrorName());
Serial.printf("UWB_RAW_ID,dev_id=0x%08lX\n", static_cast<unsigned long>(uwb.readRawDeviceId()));
return false;
}
const uint32_t devId = uwb.deviceId();
Serial.printf("UWB_ID,dev_id=0x%08lX,chip=%s\n", static_cast<unsigned long>(devId), uwb.chipName());
if (devId != M5STAMP_UWB_QM33120_DEVICE_ID) {
Serial.printf("UWB_ID,result=FAIL,expected=0xDECA0314\n");
return false;
}
Serial.printf("UWB_CONFIG,result=OK,ch=%u,plen=%u,rate=6M8,tx_power=0x%08lX\n", static_cast<unsigned>(phy.channel),
static_cast<unsigned>(phy.preambleLength), static_cast<unsigned long>(phy.txPower));
return true;
}
static void runResponder()
{
// Wait for a Poll/Final exchange and return the DS-TWR distance result.
const M5Stamp_UWBDSResponderResult result = uwb.respondDSRange(rangeConfig);
if (!result.success) {
// Idle receive timeouts are expected and are not counted as failures.
if (result.error == M5Stamp_UWBError::RxTimeout) return;
if (++failCount % LOG_INTERVAL == 0) {
Serial.printf("DS_RESP_STAT,count=%lu,fail=%lu,last=FAIL,error=%s\n",
static_cast<unsigned long>(responseCount), static_cast<unsigned long>(failCount),
uwb.lastErrorName());
}
return;
}
// Print accumulated statistics every LOG_INTERVAL responses.
if (++responseCount % LOG_INTERVAL == 0) {
Serial.printf(
"DS_RESP_STAT,count=%lu,fail=%lu,last=OK,seq=%u,requester=0x%X,distance_mm=%ld,distance_m=%.3f,elapsed_ms=%lu\n",
static_cast<unsigned long>(responseCount), static_cast<unsigned long>(failCount), result.sequence,
result.requester, static_cast<long>(result.distanceMm), result.distanceM,
static_cast<unsigned long>(result.elapsedMs));
}
}
void setup()
{
Serial.begin(115200);
delay(1000);
Serial.printf("M5Stamp UWB DS-TWR ANCHOR\n");
Serial.printf("ROLE,mode=ANCHOR\n");
Serial.printf("TWR_MODE,mode=DS-TWR\n");
uwbReady = initUwb();
Serial.printf("TEST_START,result=%s\n", uwbReady ? "OK" : "FAIL");
}
void loop()
{
if (uwbReady) runResponder();
else delay(1000);
}串口输出示例:
M5Stamp UWB DS-TWR ANCHOR
ROLE,mode=ANCHOR
TWR_MODE,mode=DS-TWR
UWB_ID,dev_id=0xDECA0314,chip=QM33120/DW3720
UWB_CONFIG,result=OK,ch=5,plen=128,rate=6M8,tx_power=0xFEFEFEFE
TEST_START,result=OK
DS_RESP_STAT,count=9500,fail=0,last=OK,seq=7,requester=0x1,distance_mm=162,distance_m=0.162,elapsed_ms=98
DS_RESP_STAT,count=9520,fail=0,last=OK,seq=27,requester=0x1,distance_mm=185,distance_m=0.185,elapsed_ms=93
DS_RESP_STAT,count=9540,fail=0,last=OK,seq=47,requester=0x1,distance_mm=181,distance_m=0.181,elapsed_ms=98
DS_RESP_STAT,count=9560,fail=0,last=OK,seq=67,requester=0x1,distance_mm=167,distance_m=0.167,elapsed_ms=98
DS_RESP_STAT,count=9580,fail=0,last=OK,seq=87,requester=0x1,distance_mm=193,distance_m=0.193,elapsed_ms=98
DS_RESP_STAT,count=9600,fail=0,last=OK,seq=107,requester=0x1,distance_mm=166,distance_m=0.166,elapsed_ms=98
DS_RESP_STAT,count=9620,fail=0,last=OK,seq=127,requester=0x1,distance_mm=164,distance_m=0.164,elapsed_ms=98 #include <Arduino.h>
#include <M5Stamp_UWB.h>
static constexpr uint32_t RANGE_INTERVAL_MS = 200;
static constexpr uint32_t LOG_INTERVAL = 10;
M5Stamp_UWB uwb;
M5Stamp_UWBRangeConfig rangeConfig;
bool uwbReady = false;
uint32_t lastRangeMs = 0;
uint32_t rangeCount = 0;
uint32_t okCount = 0;
static bool initUwb()
{
// Stamp-C5 connections to the onboard QM33120 UWB transceiver.
M5Stamp_UWBConfig config;
config.pin_gp7 = 23;
config.pin_irq = 0;
config.pin_wakeup = 24;
config.pin_rst = 25;
config.pin_miso = 26;
config.pin_mosi = 27;
config.pin_cs = 11;
config.pin_sck = 12;
M5Stamp_UWBPHYConfig phy;
phy.channel = M5Stamp_UWBChannel::Channel5;
// Both devices must use the same PAN ID, addresses, and response timing.
rangeConfig.panId = 0xDECA;
rangeConfig.initiatorAddress = 0x0001;
rangeConfig.responderAddress = 0x0002;
rangeConfig.responseRxAfterTxDelayUus = 500;
rangeConfig.responseTxDelayUus = 3000;
rangeConfig.rxTimeoutUus = 6000;
rangeConfig.hostTimeoutMs = 100;
if (!uwb.begin(config, phy)) {
Serial.printf("UWB_BEGIN,result=FAIL,error=%s\n", uwb.lastErrorName());
Serial.printf("UWB_RAW_ID,dev_id=0x%08lX\n", static_cast<unsigned long>(uwb.readRawDeviceId()));
return false;
}
const uint32_t devId = uwb.deviceId();
Serial.printf("UWB_ID,dev_id=0x%08lX,chip=%s\n", static_cast<unsigned long>(devId), uwb.chipName());
if (devId != M5STAMP_UWB_QM33120_DEVICE_ID) {
Serial.printf("UWB_ID,result=FAIL,expected=0xDECA0314\n");
return false;
}
Serial.printf("UWB_CONFIG,result=OK,ch=%u,plen=%u,rate=6M8,tx_power=0x%08lX\n", static_cast<unsigned>(phy.channel),
static_cast<unsigned>(phy.preambleLength), static_cast<unsigned long>(phy.txPower));
return true;
}
static void runRanging()
{
// Start one SS-TWR exchange at the configured interval.
if (millis() - lastRangeMs < RANGE_INTERVAL_MS) return;
lastRangeMs = millis();
const M5Stamp_UWBRangeResult result = uwb.requestRange(rangeConfig);
rangeCount++;
okCount += result.success;
// Print accumulated statistics every LOG_INTERVAL attempts.
if (rangeCount % LOG_INTERVAL) return;
const uint32_t failCount = rangeCount - okCount;
if (result.success) {
Serial.printf(
"SS_RANGE_STAT,count=%lu,ok=%lu,fail=%lu,last=OK,seq=%u,distance_mm=%ld,distance_m=%.3f,elapsed_ms=%lu\n",
static_cast<unsigned long>(rangeCount), static_cast<unsigned long>(okCount),
static_cast<unsigned long>(failCount), result.sequence, static_cast<long>(result.distanceMm),
result.distanceM, static_cast<unsigned long>(result.elapsedMs));
} else {
Serial.printf("SS_RANGE_STAT,count=%lu,ok=%lu,fail=%lu,last=FAIL,seq=%u,error=%s\n",
static_cast<unsigned long>(rangeCount), static_cast<unsigned long>(okCount),
static_cast<unsigned long>(failCount), result.sequence, uwb.lastErrorName());
}
}
void setup()
{
Serial.begin(115200);
delay(1000);
Serial.printf("M5Stamp UWB SS-TWR TAG\n");
Serial.printf("ROLE,mode=TAG\n");
Serial.printf("TWR_MODE,mode=SS-TWR\n");
uwbReady = initUwb();
Serial.printf("TEST_START,result=%s\n", uwbReady ? "OK" : "FAIL");
}
void loop()
{
if (uwbReady) runRanging();
else delay(1000);
}串口输出示例:
M5Stamp UWB SS-TWR TAG
ROLE,mode=TAG
TWR_MODE,mode=SS-TWR
UWB_ID,dev_id=0xDECA0314,chip=QM33120/DW3720
UWB_CONFIG,result=OK,ch=5,plen=128,rate=6M8,tx_power=0xFEFEFEFE
TEST_START,result=OK
SS_RANGE_STAT,count=10,ok=10,fail=0,last=OK,seq=10,distance_mm=2270,distance_m=2.270,elapsed_ms=4
SS_RANGE_STAT,count=20,ok=20,fail=0,last=OK,seq=20,distance_mm=2282,distance_m=2.282,elapsed_ms=4
SS_RANGE_STAT,count=30,ok=30,fail=0,last=OK,seq=30,distance_mm=2275,distance_m=2.275,elapsed_ms=4
SS_RANGE_STAT,count=40,ok=40,fail=0,last=OK,seq=40,distance_mm=2251,distance_m=2.251,elapsed_ms=4
SS_RANGE_STAT,count=50,ok=50,fail=0,last=OK,seq=50,distance_mm=2265,distance_m=2.265,elapsed_ms=4
SS_RANGE_STAT,count=60,ok=60,fail=0,last=OK,seq=60,distance_mm=2301,distance_m=2.301,elapsed_ms=4
SS_RANGE_STAT,count=70,ok=70,fail=0,last=OK,seq=70,distance_mm=2301,distance_m=2.301,elapsed_ms=4
SS_RANGE_STAT,count=80,ok=80,fail=0,last=OK,seq=80,distance_mm=2291,distance_m=2.291,elapsed_ms=4
SS_RANGE_STAT,count=90,ok=90,fail=0,last=OK,seq=90,distance_mm=2282,distance_m=2.282,elapsed_ms=4
SS_RANGE_STAT,count=100,ok=100,fail=0,last=OK,seq=100,distance_mm=2312,distance_m=2.312,elapsed_ms=4 #include <Arduino.h>
#include <M5Stamp_UWB.h>
static constexpr uint32_t LOG_INTERVAL = 20;
M5Stamp_UWB uwb;
M5Stamp_UWBRangeConfig rangeConfig;
bool uwbReady = false;
uint32_t responseCount = 0;
uint32_t failCount = 0;
static bool initUwb()
{
// Stamp-C5 connections to the onboard QM33120 UWB transceiver.
M5Stamp_UWBConfig config;
config.pin_gp7 = 23;
config.pin_irq = 0;
config.pin_wakeup = 24;
config.pin_rst = 25;
config.pin_miso = 26;
config.pin_mosi = 27;
config.pin_cs = 11;
config.pin_sck = 12;
M5Stamp_UWBPHYConfig phy;
phy.channel = M5Stamp_UWBChannel::Channel5;
// Both devices must use the same PAN ID, addresses, and response timing.
rangeConfig.panId = 0xDECA;
rangeConfig.initiatorAddress = 0x0001;
rangeConfig.responderAddress = 0x0002;
rangeConfig.responseRxAfterTxDelayUus = 500;
rangeConfig.responseTxDelayUus = 3000;
rangeConfig.rxTimeoutUus = 6000;
rangeConfig.hostTimeoutMs = 100;
if (!uwb.begin(config, phy)) {
Serial.printf("UWB_BEGIN,result=FAIL,error=%s\n", uwb.lastErrorName());
Serial.printf("UWB_RAW_ID,dev_id=0x%08lX\n", static_cast<unsigned long>(uwb.readRawDeviceId()));
return false;
}
const uint32_t devId = uwb.deviceId();
Serial.printf("UWB_ID,dev_id=0x%08lX,chip=%s\n", static_cast<unsigned long>(devId), uwb.chipName());
if (devId != M5STAMP_UWB_QM33120_DEVICE_ID) {
Serial.printf("UWB_ID,result=FAIL,expected=0xDECA0314\n");
return false;
}
Serial.printf("UWB_CONFIG,result=OK,ch=%u,plen=%u,rate=6M8,tx_power=0x%08lX\n", static_cast<unsigned>(phy.channel),
static_cast<unsigned>(phy.preambleLength), static_cast<unsigned long>(phy.txPower));
return true;
}
static void runResponder()
{
// Wait for a Poll frame and send the delayed SS-TWR Response frame.
const M5Stamp_UWBResponderResult result = uwb.respondRange(rangeConfig);
if (!result.success) {
// Idle receive timeouts are expected and are not counted as failures.
if (result.error == M5Stamp_UWBError::RxTimeout) return;
if (++failCount % LOG_INTERVAL == 0) {
Serial.printf("SS_RESP_STAT,count=%lu,fail=%lu,last=FAIL,error=%s\n",
static_cast<unsigned long>(responseCount), static_cast<unsigned long>(failCount),
uwb.lastErrorName());
}
return;
}
// Print accumulated statistics every LOG_INTERVAL responses.
if (++responseCount % LOG_INTERVAL == 0) {
Serial.printf("SS_RESP_STAT,count=%lu,fail=%lu,last=OK,seq=%u,requester=0x%X,elapsed_ms=%lu\n",
static_cast<unsigned long>(responseCount), static_cast<unsigned long>(failCount), result.sequence,
result.requester, static_cast<unsigned long>(result.elapsedMs));
}
}
void setup()
{
Serial.begin(115200);
delay(1000);
Serial.printf("M5Stamp UWB SS-TWR ANCHOR\n");
Serial.printf("ROLE,mode=ANCHOR\n");
Serial.printf("TWR_MODE,mode=SS-TWR\n");
uwbReady = initUwb();
Serial.printf("TEST_START,result=%s\n", uwbReady ? "OK" : "FAIL");
}
void loop()
{
if (uwbReady) runResponder();
else delay(1000);
}串口输出示例:
M5Stamp UWB SS-TWR ANCHOR
ROLE,mode=ANCHOR
TWR_MODE,mode=SS-TWR
UWB_ID,dev_id=0xDECA0314,chip=QM33120/DW3720
UWB_CONFIG,result=OK,ch=5,plen=128,rate=6M8,tx_power=0xFEFEFEFE
TEST_START,result=OK
SS_RESP_STAT,count=1260,fail=0,last=OK,seq=12,requester=0x1,elapsed_ms=98
SS_RESP_STAT,count=1280,fail=0,last=OK,seq=32,requester=0x1,elapsed_ms=98
SS_RESP_STAT,count=1300,fail=0,last=OK,seq=52,requester=0x1,elapsed_ms=93
SS_RESP_STAT,count=1320,fail=0,last=OK,seq=72,requester=0x1,elapsed_ms=98
SS_RESP_STAT,count=1340,fail=0,last=OK,seq=92,requester=0x1,elapsed_ms=98 