Tag command-line tools (Rust)
Rust ports of the tools in ../commandline,
built on the Rust tag interface in
../libraries/tagcore-rs. Each tool
keeps its C++ name, options, messages and exit status, so scripts can use
either. The crate is not wired into the CMake build.
cargo build --release # binaries in target/release/
cargo run --bin tag-info -- --json
Every tool that talks to a tag takes the shared options from
src/lib.rs, the port of options.cc: -d/--debug, and
-b/--base bus:device, which is required when more than one base is
connected. Attaching resets the tag, as with the C++ tools.
Ported
| Tool | C++ source | What it does |
|---|---|---|
tag-info |
tag-info.cc |
Information, configuration, status and state log; --json for recording |
tag-start |
tag-start.cc |
Program a configuration (--config, --merge) and start an IDLE tag |
tag-stop |
tag-stop.cc |
Stop a running tag and wait for a stopped state |
tag-reset |
tag-reset.cc |
Stop if needed, erase, and wait for IDLE |
tag-test |
test.cc |
Run a firmware self-test and report the result |
tag-test-example |
test-example.cc |
Set the RTC and report the clock error |
tag-cal |
tag-calibrate.cc |
Stream raw calibration data from a tag in calibration mode |
tag-cal-write |
tag-cal-write.cc |
Write identity calibration constants so a reflashed compass tag will start |
tag-peek |
tag-peek.cc |
Read words of tag memory between monitor RPCs |
tag-side-channel-check |
tag-side-channel-check.cc |
Hardware check of memory access, keep-alive and threading |
tag-attach-cycle |
tag-attach-cycle.cc |
Attach and detach repeatedly, for attach-path testing |
tag-monitor-test |
tag-monitor-test.cc |
Drive the L4 monitor handshake directly through LinkAdapt |
qtmonitor-fixture-capture |
qtmonitor-fixture-capture.cc |
Capture a tag's info, config and status as a qtmonitor test fixture |
tag-dwnld |
dwnld.cc |
Download a stopped tag's log to SQLite, through the C++ writer (below) |
tag-dwnld and the C++ writer
tag-dwnld reads the tag with tagcore-rs but writes the database with the
C++ SqliteTagLogWriter, through
taglogwriter and the tagcore_c
library, so its file is the one the C++ tag-dwnld writes. It is built only
with the sqlitelog feature, and needs that library built by CMake first:
cmake -DBUILD_TAGCORE_C=ON <build-dir>
cmake --build <build-dir> --target tagcore_c_static # tagcore_c off macOS
TAGCORE_C_LIB_DIR=<build-dir>/Release/lib \
cargo build --release --features sqlitelog --bin tag-dwnld
On macOS it links libtagcore_c.a, and the executable needs nothing at run
time. On Linux and Windows (and with TAGCORE_C_LINK=dylib) it links the
shared library, one copy of which serves every tool in the install
directory, and finds it through an rpath to that directory. Messages and exit
status follow the C++; the format check that names an unsupported tag type
reports the writer's own message ("Could not open output: SQLite log output
does not support tag type ...").
Not ported yet
These need a library that the Rust crate does not have yet:
tag-capture,tag-xflash,tag-sramcallandtag-rebuild: the SWD capture and recovery library intagcore/recovery/.tag-log-importandtag-txt-import:sqlitelogandtxtlogs.dataprocessing: the sensoranalysis core and SQLite.
Differences from the C++
- Option errors exit with status 2 (clap) rather than 1 (cxxopts).
- Text-format output omits the randomised
goo.gle/debugprotoline that recent C++ protobuf releases add toDebugString(). - Log lines keep the
log.clayout (HH:MM:SS LEVEL file:line: message), with Rust source paths.
Porting turned up a set of C++ faults, now fixed in both versions together
(b2c907ef on main for the C++):
- Failures that exited 0 now exit 1:
tag-startwhen the tag refuses the start,tag-calon a tag that is not IDLE,tag-testfor an unknown test, a failed status read or a tag not IDLE, andtag-test-examplewhen attach fails. tag-resetalso stops a CONFIGURED tag, polls for the stop (--stop-timeout), and exits 1 unless the tag ends IDLE, so--set-rtcis sent only to an IDLE tag.tag-monitor-testfollowsTagMonitor's L4 attach and call sequence, ends its session with MONITORSTOP, and attaches again.tag-peekrejects an address it cannot parse;tag-side-channel-checkrejects--idlewith--interval 0;qtmonitor-fixture-captureprints bus:address as numbers and logs with--debug;tag-calwaits 100 ms between calibration-log reads.
qtmonitor-fixture-capture --capture-default-config still does nothing: it
names the default, as its help says.
How they were checked
On 2026-10-26, against a BitTag V6 (STM32L432, firmware 56e5e6a0) on a tag
programmer base, each Rust tool was run immediately after its C++ build from
the same source tree, and the two outputs were compared:
- Same output apart from live readings (clock, voltage, temperature,
timings and loop counts):
tag-info(text and--json),tag-peek,tag-test(self-tests passed),tag-test-example,tag-cal(refused, as BitTag has no calibration mode),tag-side-channel-check,tag-attach-cycle,qtmonitor-fixture-capture(the fixture files too), andtag-monitor-test. - The state tools:
tag-start --set-rtc,tag-stopandtag-reseteach took the tag through CONFIGURED, FINISHED and back to IDLE, with each step printing the same output as the C++ tool. After the fixes,tag-calrefused the started tag with exit 1, andtag-resetstopped it, waited for FINISHED and erased it to IDLE, in both versions; the argument checks andtag-monitor-testprinted the same in both.
The same day, on an IMUTagNandBmp581 (STM32U375, the shared-block
transport, firmware 8f1350da), each tool again matched its C++ build:
tag-info, tag-peek, tag-side-channel-check (including the
monitor_shared write guard and keep-alive across a 12 s idle),
tag-attach-cycle, qtmonitor-fixture-capture, the start, stop and reset
cycle, and tag-cal, which streamed samples and stopped on Ctrl-C.
tag-monitor-test refuses an STM32U3, as the C++ now does.
Not checked on hardware: --config/--merge (the merge itself is covered by
tagcore-rs unit tests), the opt-in write, start and unplug checks of
tag-side-channel-check, and tag-test on the IMUTag, whose
external-flash test can write the NAND bad-block map.