Repository navigation
Releases: tag-designs/software
Release list
v4.0.1
Host tools for Windows, macOS (Apple silicon, signed and notarized) and Linux (x86_64). A bug-fix release for v4.0 that fixes everything found while testing v4.0 on the bench. Nothing about files, options or workflows changes.
Fixes
- Unplugging a base no longer floods the log. Unplugging a base mid-session used to log about 2,500 identical USB errors. Detaching from a running IMUTag used to log about 50. Each now logs a few lines and a count. A base that has been unplugged ends the session, so the tools and apps stop polling it.
- Tag Monitor shows itself detached after its base is unplugged, instead of staying attached to nothing.
- Tag Programmer's Tag row now shows the firmware just programmed. Before, it showed the old firmware until the next attach. If the tag does not answer after programming, the row asks you to press Attach.
- Piping a tool into
head(for exampletag-info | head) no longer makes the tool crash with status 101. It now ends quietly, as the C++ tools did. This affects macOS and Linux. tag-testnow gives the internal flash size in KB and the external flash size in bytes. Both were labelled "kb".tag-dwnlddraws its progress bar only in a terminal. When its output goes to a file or a pipe, it writes only the finished bar, instead of one line per request.
Documentation
- The Tag Monitor page now covers the BitTag NG and UIUCTag settings.
- The
dataprocessingpage now coversimu-compass-orientation, which computes orientation for IMUTag logs.
Base firmware
The base firmware now retries its connection to a tag that is waking from sleep. That protects every program that talks to a tag through a base, including Tag Programmer and tag-flash. The change is in the source but not in this release's packages. It reaches bases with the next firmware release, through base-flash.
v4.0
Host tools for Windows, macOS (Apple silicon, signed and notarized) and Linux (x86_64). In this release the Rust apps and command-line tools replace the Qt ones. The Qt apps deprecated in v3.5 are no longer in the packages, and no part of the packages uses Qt. STM32CubeProgrammer is no longer needed for anything.
The apps
| App | Executable | Replaces |
|---|---|---|
| Tag Programmer | tagprogram |
qtprogram |
| Tag Monitor | tagmonitor |
qtmonitor |
| Tag Calibrator | tagcalibrate |
qtcalibrate |
| Tag View | tagview |
sensorviz and btviz |
They read and write the same files as the Qt apps: configurations, calibrations, logs and SensorViz preference files. Each app's page in the user guide (Help menu) describes it in full.
- Tag View has no activity filter yet. It was in SensorViz's Qt layer and has not been ported. Everything else SensorViz drew, Tag View draws from the same code.
btvizis gone. Convert legacy BitTag.txtfiles withtag-txt-importand open them in Tag View.- On Linux the apps need WebKitGTK 4.1, which GNOME desktops already have. Elsewhere:
sudo apt install libwebkit2gtk-4.1-0on Ubuntu or Debian.
Command-line tools
tag-info, tag-start, tag-stop, tag-reset, tag-test, tag-cal and tag-dwnld are now Rust programs. They keep the names, options and output of the C++ tools and are installed in the same place, so scripts that call them need no changes. They also add:
tag-start --config FILEstarts a tag with a saved configuration, and with--mergecopies the file onto the tag's stored configuration instead of replacing it.- Timeouts for each step:
--start-timeout,--stop-timeout,--settle-timeout,--test-timeout. - An unknown option or a bad value exits with status 2 before the tag is touched. Any other failure exits with status 1.
New: tag-flash and base-flash. tag-flash programs, erases and reads a tag through its base. base-flash updates a base's firmware over USB through the base's bootloader. Each firmware release now includes base images. These two tools are why STM32CubeProgrammer is no longer needed.
dataprocessing and tag-txt-import are unchanged.
Fixes
- A rare failure on the first attach to an L432 tag (BitTag, PresTag, CompassTag, UIUCTag), status
0x81, is fixed. It happened when the tag woke from Shutdown at the moment the base tried to connect. The connection is now retried. The fix is in every app and tool.
For developers
The Qt apps and the C++ versions of the tools still build, with the CMake option BUILD_LEGACY_HOST=ON, which needs Qt. That option is the fallback if a Rust program fails in the field. The default build no longer needs Qt, but it now needs Rust (cargo). See decision 0042 in docs/decisions/.
Packages
- Linux: install the udev rule in
tag_tools/udev/for USB access to the base. It also covers the base's bootloader, forbase-flash. - The packages contain no GPL-licensed program. Third-party notices are in
licenses/.
Full Changelog: v3.5...v4.0
v3.5
Host tools for Windows, macOS (Apple silicon, signed and notarized) and Linux (x86_64). This release brings new builds of the four desktop apps, written in Rust and no longer built on Qt. The Qt apps are still included but are now deprecated. v3.4 was not published, so its changes are included here: Tag Programmer is rebuilt as a batch programming station, Tag Monitor's Configuration tab is now a configuration editor, and the .txt log format is retired.
New apps, without Qt
Each Qt app has a replacement, installed beside it:
| Qt app (deprecated) | Replacement | macOS app size |
|---|---|---|
Tag Programmer (qtprogram) |
Tag Programmer (tagprogram) |
53 MB → 24 MB |
Tag Monitor (qtmonitor) |
Tag Monitor (tagmonitor) |
51 MB → 18 MB |
Tag Calibrator (qtcalibrate) |
Tag Calibrator (tagcalibrate) |
160 MB → 14 MB |
SensorViz (sensorviz) |
Tag View (tagview) |
83 MB → 19 MB |
The new apps do the same jobs, with the same files: configurations, calibrations, logs and SensorViz preference files work in both. Tag View draws the same plots from the same code as SensorViz. Each app's page in the user guide (Help menu) lists what is different, for example Export PDF in place of Print in Tag View.
On Linux the new apps need WebKitGTK 4.1, which GNOME desktops already have. Elsewhere, install it once: sudo apt install libwebkit2gtk-4.1-0 on Ubuntu or Debian. Windows 10 and 11 and macOS need nothing extra.
Why we are moving off Qt
- Licensing. Some of the Qt apps contain parts that are licensed only under the GPL: SensorViz and
btvizuse QCustomPlot, and Tag Calibrator uses Qt Quick 3D and Qt Quick Timeline. So, as distributed, those apps are licensed under GPL version 3. The new apps contain no GPL-only parts, and they are distributed under the project's MIT License. Their third-party notices are inlicenses/. - Size. On macOS each Qt app carries its own copy of Qt. Together the four apps above take 347 MB as Qt builds and 75 MB as new builds. Removing Qt will also make the downloads much smaller and the builds simpler.
Deprecation
The Qt apps (qtprogram, qtmonitor, qtcalibrate, sensorviz and btviz) are deprecated and will be removed in a future release. Please switch to the new apps now, and report anything they don't do that you rely on. btviz reads only legacy BitTag .txt files and has no replacement. Convert those files with tag-txt-import, then open them in Tag View.
The command-line tools are unchanged and do not use Qt.
Tag Programmer
Tag Programmer is now a station for programming a batch of tags from a published firmware release.
Which firmware to use: choose the latest release qualified for the tag type you are programming. Each release's notes have a qualification table, and Tag Programmer warns when the chosen release is not qualified for the chosen tag type. The latest release is not always qualified for every tag type.
- Firmware from GitHub. It lists the published
fw-v*releases and downloads one when you choose it. Each image is checked against its build manifest before it can be programmed. - Qualification warning. If the chosen release has not been bench-qualified for the chosen tag type, the Firmware line turns amber and the Program dialog says so. You can still program, for bench work, but do not fly those tags.
- Finds everything itself. It finds the base and the tag.
qtprogramalso finds STM32CubeProgrammer (on a Mac, the app under/Applications,~/Applicationsor~/Software). If it cannot find STM32CubeProgrammer, Choose… takesSTM32CubeProgrammer.appon a Mac, orSTM32_Programmer_CLIitself. - Bases. A single base is used without further setup, including ST-LINK/V2 probes with old firmware. With several, it asks which; Station ▸ Choose Base… switches at any time.
- One button per tag. Program stops the tag, has its firmware erase its log, erases flash as the image requires, writes it, sets the clock, runs the self-tests, starts and stops the tag once, and erases it again. The tag is left IDLE, with its clock set.
- A record of every tag. Each attempt adds a line to a CSV: label, UUID, firmware release, commit, image SHA-256, self-test result and more.
- Retest runs the self-tests again without reprogramming. It also reads the tag's flash back and compares it with the release the tag reports, so the record names that release, or
MISMATCHif the flash is something else. - Clock. The Tag panel shows how far the tag's clock is from this computer's, amber beyond 2 s, with Sync to set it.
- Older releases. Images from releases before fw-v0.7 are programmed with a full erase.
- STM32CubeProgrammer is needed only by the Qt build (
qtprogram; tested with 2.22 and 2.23). The newtagprogramprograms the tag directly through the base and does not need it. It is also faster.
Tag Monitor
- Menus and a status strip replace the row of buttons. The strip, visible on every tab, shows the tag's type, state, clock offset, voltage and self-test result.
- Configuration editor. The Configuration tab holds one configuration for one tag type: New, Open, Open Recent, Save, Save As and Reset to Defaults, with or without a tag attached. Each tag type shows only the settings its firmware accepts. A saved file may list only some settings; the rest take the tag type's defaults.
- Start checks. Before starting a tag, Start checks that its clock is within 2 s, its self-test passed and Approved is ticked, and asks before starting anyway.
- Schedule. Every schedule time is a whole hour in UTC, and each hibernation row has an In use checkbox.
- Choosing a base. The base chooser names each base, and an attach to a base with no tag says so.
- More reliable with L432 tags (BitTag, PresTag, CompassTag, UIUCTag): requests to a sleeping tag no longer fail in bursts. This fix is in every app and command-line tool that talks to a tag.
SensorViz and Tag View
- One y axis per sensor group, with a legend.
- Faster with large logs: plots are drawn from a decimated view of the log.
- Gaps in the data are shown as gaps, not joined by a line.
Logs and command-line tools
- SQLite is the only download format.
tag-dwnldand Tag Monitor no longer write.txtlogs;--format textis refused before the tag is touched. - New:
tag-txt-importconverts existing BitTag and PresTag.txtlogs to SQLite. Each conversion is checked line by line against the original before the.db3is kept. dataprocessingstores compass orientation as a quaternion. Heading, pitch and roll are calculated by the program that displays them. Files written by earlier versions keep theiryaw,pitchandrollcolumns.
Packages
- Linux: install the udev rule in
tag_tools/udev/for USB access to the base. The tarball carries Qt's TLS support, which the Qt Tag Programmer needs to reach GitHub. - macOS: the apps carry Qt's TLS support, for the same reason.
Full Changelog: v3.3...v3.5
v3.3
fw-v0.7
Tag firmware for the distributed targets. Each image ships with its build manifest, which records the commit, the ChibiOS commit and branch, the toolchain and nanopb versions, and the SHA-256 of the image itself. CI does not bench-test power behaviour; qualification is a separate step before flight, and the table below records it.
CompassTagAT25 is qualified against this release. The other four targets are not yet qualified on fw-v0.7. The CompassTagAT25 figures come from the published image, not a local rebuild, and its session logs are attached as an asset.
Qualification
Supply voltage is given with every current because the targets do not share one: the L432 tags run unregulated from a 2.5 V cell and IMUTag runs from 3.7 V.
| Target | Qualified | Supply | Idle | Running | Finished |
|---|---|---|---|---|---|
| CompassTagAT25 | 2026-10-15 | 2.496 V | 0.23 uA | 1.97 uA @ 30 s | 0.23 uA |
| IMUTagNandBmp581 | not yet | ||||
| UIUCTag | not yet | ||||
| BitTag | not yet | ||||
| PresTag | not yet |
CompassTagAT25
- Board and image. Board
203633324B4250060022005E. The tag reports githash169cda2c(fw-v0.7).CompassTagAT25.elfsha2561e8a891f35967f1dc6d97b235b11f8533e18478117d582a4887d61a4fc628e06, measured with--skip-build. - Self-test.
tag-testreturnedALL_PASSED. - Idle. Four trials read 0.2286, 0.2282, 0.2282 and 0.2285 uA.
- Life cycle. Idle 0.23 uA, running 1.97 uA over a 900 s window, finished 0.23 uA, and idle after a full cycle 0.23 uA. The download held 30 activity rows from the run.
- No attach storms. The harness now skips them on L432 targets, because these tags sleep in standby between events and an attach just resets them.
Running current is 1% above fw-v0.6 on the same board and supply: 1.95 uA before, 1.97 uA now. That is consistent with the one change on the logging path, which lengthens the magnetometer power-up wait from 1 ms to 2 ms on each 30 s sample. Idle moved from 0.21 to 0.23 uA.
What is in this release
- CompassTag calibration is pinned to the top L432 flash page.
.calibrationused to follow the code to the next page boundary, so whenever the image crossed one, a calibrated board read an unwritten page as its constants. It now sits at0x0803f800whatever the image size. Other L432 targets link to byte-identical images..persistentstill floats.flash_release.py --keep-calibrationerases everything below the calibration page and programs, in one invocation. - The accelerometer zero-g offset is stored on the tag. CompassTag and IMUTag calibration slots now hold 12 magnetometer floats and 3 accelerometer floats (layout version 2). The host tools apply the offset wherever logs are read. This changes the calibration record format, so upgrading a calibrated tag from
fw-v0.6needs one mass erase (flash_release.py --erase) and recalibration. After that, later upgrades can use--keep-calibration. - The CompassTag magnetometer gets 2 ms to power up, up from 1 ms. A tag on a run-down cell logged corrupted magnetometer and accelerometer reads. The longer wait adds margin; it has not been proven to fix the fault.
- The IMUTagNandBmp581 magnetometer is mapped into the host's axis frame, using the per-board mounting given in
custom.h.
Known limitations
tag_release_check.pydoes not pass--measure-pythonto the life-cycle check. SetJOULESCOPE_PYTHONto an interpreter that can importpyjoulescope_driver, or the life-cycle step aborts without measuring anything.
v3.2
v3.1
What's Changed
- Release: build the macOS package on CI, sign it locally with sign-latest by @geoffreymbrown in #1
New Contributors
- @geoffreymbrown made their first contribution in #1
Full Changelog: v3.0.1...v3.1
fw-v0.6
Tag firmware for the five distributed targets. Each image ships with its build manifest, recording the commit, the ChibiOS commit and branch, the toolchain and nanopb versions, and the SHA-256 of the image. CI does not bench-test power behaviour — qualification is a separate step before flight, and the table below is the record of it.
All five targets are qualified against this release, each measured from the published image rather than a local rebuild, and each with its session logs attached as an asset.
Qualification
Supply voltage is stated with every current because the targets do not share one: the L432 tags run unregulated from a 2.5 V cell and IMUTag from 3.7 V. A current figure means nothing without it.
| Target | Qualified | Supply | Idle | Running | Finished |
|---|---|---|---|---|---|
| IMUTagNandBmp581 | 2026-10-07 | 3.693 V | 6.43 uA | 665.41 uA @ 400 Hz | 6.43 uA |
| CompassTagAT25 | 2026-10-06 | 2.496 V | 0.21 uA | 1.95 / 1.9478 uA @ 30 s | 0.21 uA |
| UIUCTag | 2026-10-08 | 2.496 V | 0.1572 uA | 0.5620 uA @ 300 s | 0.1598 uA |
| BitTag | 2026-10-07 | 2.496 V | 0.1185 uA | 0.4941 uA @ default (1 bit/5 min); 0.5073 / 0.5068 @ 1 bit/s | 0.1178 uA |
| PresTag | 2026-10-07 | 2.496 V | 0.1118 uA | 0.3894 / 0.3908 uA @ 60 s | 0.1108 uA |
No power regression
Every distributed image changed in this release — shared ADC code, a verify added to every tag's configuration-write path, and four boards' LSE configuration — so nothing carried forward from fw-v0.5. Against the previous qualified figures on the same boards and supplies:
| Target | Running | Change |
|---|---|---|
| IMUTagNandBmp581 | 662 → 665.41 uA | +0.5% |
| CompassTagAT25 | 1.96 → 1.95 uA | −0.5% |
| BitTag | 0.508 → 0.5073 uA @ 1 bit/s | −0.14% |
IMUTag is the target that matters most here: at 665 uA it draws three orders of magnitude more than the others, so it alone decides whether a deployment makes its battery life, and it is the part where image layout has moved idle current by 150x in the past. Its firmware contribution is isolated — the outgoing image was measured on the same board in the same session before flashing, giving idle 6.2881 → 6.4137 uA (+2.3%) and running +0.5%.
IMUTagNandBmp581
Board 00303143433650090059002E at 3.6932 V. The full harness ran, including three attach-storm sets: six rounds, none aborted, data intact. Attach storms are meaningful for this target and only this one — IMUTag stays awake through a run and must restart collection after an attach, whereas the L432 tags collect on events and sit in standby between them.
Idle 6.4144 / 6.4382 / 6.428 / 6.4355 uA over four trials; life-cycle idle 6.43, running 665.41 @ 400 Hz, finished 6.43, idle after a full cycle 6.45 uA; download sane; tag-test ALL_PASSED.
CompassTagAT25
Board 203633324B4250060022005E, CompassTagAT25.elf sha256 ba99e8534395963d635c0c7f8ab123d4f40cb39768b22336634c7fbbb012078f, measured with --skip-build so the harness could not substitute a rebuild.
Four idle trials 0.2181 / 0.2144 / 0.2139 / 0.2137 uA; life cycle idle 0.21, running 1.95, finished 0.21, idle-after-cycle 0.21 uA; a second 900 s running window at 1.9478 uA, 0.1% from the first; three storm sets, 240 attach/detach cycles, none aborted; the 30 s sample interval exact in all six stored round databases; tag-test ALL_PASSED.
Calibration survived the flash byte-identically, verified either side. __calibration_start__ was confirmed still at 0x0800b000 — the address this board was calibrated under — before the flash was allowed. Headroom between the image end and that page is now 200 bytes, down from 936; when it closes, .calibration moves and a calibrated board silently reads an unwritten page.
UIUCTag
Board 2036354B3032500800520028. A 31-minute run from 16:22:35Z stored 7 pressure and 7 temperature samples at exactly 300 s and 30 activity buckets at exactly 60 s, the first on the first minute boundary after the start, with no slot missed. Idle 0.1572, running 0.5620, finished 0.1598, idle after a full cycle 0.1598 uA; tag-test ALL_PASSED.
Running current is 26% below the 0.76 uA measured on this board at bring-up. Lower is not a budget concern; the likeliest cause is accelerometer configuration, since that session was still resolving an activity-pegged ADXL367.
tag-test is intermittent on this target — roughly four attempts in six return a result, the failures being host-side link timeouts at the first transaction rather than device-test failures.
BitTag
Board 2035374D303150190059002F. Both logging configurations were measured. At 1 bit/s, two 30-minute windows agreed to 0.1% (0.5073 and 0.5068 uA), both within 0.25% of this tag's fw-v0.5 figure; the run stored 1860 activity records with every interval exactly 1 s. At the shipped default of 1 bit per 5 minutes the run draws 0.4941 uA.
The logging rate is nearly free: 300x less logging buys 2.6%. BitTag's run current is set by the accelerometer watching for activity, not by how often a bit is written, so battery life must not be estimated from the record rate.
Activity values are zero throughout because the tag was stationary. Activity sensitivity (Phase D of its test plan) has never been run, in this qualification or fw-v0.5's.
PresTag
Board 203330503641500400490058. The first PresTag release qualification — the 2026-09 campaign was explicitly not one, because the stored-configuration write was unchecked, which this release fixes.
Two independent 30-minute runs agreed to 0.36% (0.3894 and 0.3908 uA at the 60 s period) and each stored 31 consecutive samples exactly 60 s apart with no gap or drift. Idle 0.1118, finished 0.1108, idle after a full cycle 0.1108 uA; four idle trials 0.1227 / 0.1142 / 0.1139 / 0.1137 uA; tag-test ALL_PASSED.
What is in this release
- The internal ADC.
adcVDD()indexedSMPR2with(channel - 9)rather than(channel - 10), writing the sampling time to the wrong channel's field, and sampled the internal channels without the settling time VREFINT and the temperature sensor need. A UIUCTag reported 3.77–3.96 V and 92.5–111.4 C against a 2.4960 V rail. Both defects date to the initial commit; the margin was board-dependent, which is why the same code read correctly elsewhere for a long time. - The stored-configuration write is verified. It was programmed and its result ignored on seven of eight implementations. Flash programming only clears bits, so a write into a region that was not erased yields the bitwise AND of old and new — the tag runs a configuration nobody asked for while the host prints the one it sent. A failure now aborts the start with
EVENT_CONFIGERROR. This makes the failure loud; it does not make the write succeed. - LSE bypass on every distributed board. All clock LSE from an RV-3028, which drives a clock signal rather than a crystal, but only PresTagv3 was configured for it. The others carried a boot that hangs forever in
__early_init, with a monitor-attach timeout as the only symptom. All five boards have now been powered and qualified on this image, so the change is confirmed on every one. - BitTag no longer samples VDD and temperature on every wake — 35 conversions per stored value at the default format — and PresTag's default sample period moves from 90 s to 60 s.
Known limitations
tag_release_check.pycannot drive BitTag, PresTag or UIUCTag, aborting withMonitor attach failed: initial DEMCR read failed— the attach-from-sleep problem. Those three were qualified by running the phases by hand. The harness aborts; the tags do not, and no run in any session reachedABORTED.tag-testis intermittent on UIUCTag, as above.BitTag-legacydoes not build, atfw-v0.5as well as here:main.c:289passes too few arguments toStateMachine().- CompassTagAT25 has 200 bytes of headroom before its calibration page, as above.
fw-v0.5
Tag firmware for the distributed targets. Each image ships with its build manifest, which records the commit, the ChibiOS commit and branch, the toolchain and nanopb versions, and the SHA-256 of the image itself. CI does not bench-test power behaviour; that is a separate step before flight, and the table below says which targets have had it.
Power qualification
Measured on hardware, from this release's images, per the release procedure. Supply voltage is stated because it is not the same for every tag: BitTag is unregulated and runs directly from its cell, so its currents have no meaning at any other voltage.
| Target | Qualified | Supply | Idle | Running | Finished |
|---|---|---|---|---|---|
| BitTag | 2026-10-03 | 2.496 V | 0.122 µA | 0.508 µA | 0.121 µA |
| CompassTagAT25 | 2026-10-04 | 2.496 V | 0.23 µA | 1.95 µA | 0.22 µA |
| IMUTagNandBmp581 | not qualified | ||||
| PresTag | not qualified | ||||
| UIUCTag | not qualified |
BitTag — PASS. Board BitTag V6, UUID 2035374D303150190059002F, config bittag-bitpersec.json (one activity bit per second). Four resting states agree within 1.1%, 40× below the 5 µA gate; running reproduced to 0.04% across two 1200 s windows with the state confirmed RUNNING. A 1440-row activity table downloaded with monotonic timestamps. tag-test ALL_PASSED. Per mAh of cell: 342 days resting, 82 days recording. Evidence attached as qualification-BitTag-20261003.tar.gz.
CompassTagAT25 — PASS. Board CompassTagv1, UUID 203633324B4250060022005E, shipped default config, 30 s compass sample period. Four resting states agree within 10 nA, including one taken after a different attach pattern; idle and finished match to 0.01 µA, as they must, sharing one terminal-sleep path. Running measured over three 900 s windows across two harnesses — 1.96, 1.95 and 1.9482 µA, a 0.60% spread. Each run downloaded 30 activity rows with every sample interval exactly 30 s. tag-test ALL_PASSED. Per mAh of cell: 187 days resting, 21.3 days recording. Evidence attached as qualification-CompassTagAT25-20261004.tar.gz.
Two caveats worth carrying. FINISHED had never been measured on a CompassTag before this session. And the figures agree with the 2026-09-24 production unit (…004A005D: idle 0.2346/0.2317 µA, running 1.9538 µA) on a different board and from a release image rather than a local build — the first board-to-board agreement CompassTag has had. Read that older running figure as carrying 17% of its own alignment error, though: it came from a 180 s window, six ticks of a 30 s period.
IMUTagNandBmp581 is marked not qualified deliberately. It has a full set of measurements at this commit, but from a locally built image rather than from this release, so it does not meet the procedure's requirement that the image measured is the image that ships. It will be re-run against the release.
A target that is not flying this round does not need qualifying. A target that is needs it again after any change to its image — which, the build being reproducible, means after any change to the commit it is built from.
fw-v0.0.3
Tag firmware for the distributed targets. Each image ships with its build manifest, which records the commit, the ChibiOS commit and branch, the toolchain and nanopb versions, and the SHA-256 of the image itself. These images have not been bench-tested for power behaviour by CI; that is a separate step before flight.