Documentation / Interfaces / USB serial

USB serial protocol

USB CDC at 115200 baud. Commands and responses are UTF-8, newline-delimited records.

Open the normal serial device, not STM32 DFU. Send INFO\n after connection to identify the role and start the current TWR host stream. Device events and measurements share the same stream; parse by the first token and ignore unknown records.

Distance commands

CommandRolesResponse
PINGI, RPONG
INFOI, RINFO, one or more DIAG lines, then PHY
RESET_STATSI, ROK RESET_STATS
CALIB <i32>ROK CALIB <i32>

Stream records

Distance

D <seq> <millimeters> <exchange_us> <rssi_tenths_dbm>
D 42 3046 16174 -748
seqUnsigned 8-bit exchange sequence, wrapping at 256. It is not a timestamp or device ID; long losses and resets cannot be reconstructed reliably from this field alone.
millimetersCorrected two-way range. The current firmware clamps negative corrected results to zero; zero is not proof of contact.
exchange_usResponder-side processing interval from receipt of the poll to range computation. It excludes the full cycle's inter-range pause and is not end-to-end host latency.
rssi_tenths_dbmFirst-path signal power from the final packet in tenths of dBm; divide by 10. The legacy field name says RSSI; it is not total received power or an NLOS confidence score.

Older images may emit only D <seq> <millimeters>. Treat omitted exchange time and signal power as unavailable, not zero.

Miss

MISS <seq> <reason>
MISS 193 no-final

Initiator reasons include no-resp, bad-resp-id, resp-seq-N, and final-tx. Responder reasons include no-final, bad-final-id, final-seq-N, and compute-fail. Not every failure produces a MISS line; inspect DIAG counters too.

INFO response

INFO mode=R calib=12255 fw=twr_resp-5 id=... target_hz=40 drops=0
DIAG polls=2602 no_final=1 bad_final=4 seq_final=0
DIAG rx_err=0 resp_tx_err=0
PHY br=850 pre=128 sfd=ieee ch=9

target_hz is a configured target, not measured throughput. drops counts host-output queue drops, not all radio failures. INFO and DIAG lines are asynchronous and are not one atomic snapshot. Unknown commands return ERR unknown; malformed calibration values return ERR bad-int.

CALIB is volatile.

The command changes the active responder offset in RAM. Reapply it after a reset or power cycle. Browser and Python saved values are separate; see persistence.

Location records and commands

The mobile location tag uses the same newline-delimited USB transport, but a different command set. It does not implement the distance role's PING, RESET_STATS, or CALIB commands.

INFO mode=TDOA dimensions=3 anchors=4 biases=0,0,0
P <seq> <x_mm> <y_mm> <z_mm> <raw10> <raw20> <raw30>
MISS <seq> mask=<received_anchor_bitmask>

Position coordinates are signed millimetres in the surveyed frame. The raw fields are signed, master-clock-corrected arrival-time differences relative to A0, in device-time units, before per-anchor bias correction and median filtering. In 2D the third raw field is unused and Z is the configured tag height. Other MISS reasons include geometry and solve.

CommandPurpose / response
INFOReturns mode, dimensions, anchor count, and active biases.
BIAS b10 b20 [b30]Sets signed DTU biases in RAM; returns OK BIAS b10 b20 b30.
CAL START x_mm y_mm z_mm samplesCalibrates at a surveyed point using 10–500 samples. Returns OK CAL START ..., then CAL result b10 b20 b30 n=... when complete.
CAL STOPStops collection; returns OK CAL STOP.

Configuration and calibration persistence are described in the Location quickstart. Do not send distance calibration commands to a location tag.

Minimal Python reader

import serial

with serial.Serial("/dev/tty.usbmodem1201", 115200, timeout=1) as tag:
    tag.write(b"INFO\n")
    while True:
        line = tag.readline().decode(errors="replace").strip()
        if line:
            print(line)