New targets:
make provision-twatch_ultra — provisions EEPROM + sets firmware hash
make flash-twatch_ultra-full TWATCH_PORT=/dev/ttyACM0 — full flash
TWATCH_PORT defaults to /dev/ttyACM4, override for remote Pi:
make provision-twatch_ultra TWATCH_PORT=/dev/ttyACM0
Root cause found via loop profiling: two functions called every loop
iteration on unprovisioned devices consumed 950ms:
1. beacon_update() called startRadio() every loop when GPS had fix
but device wasn't provisioned (hw_ready=false). startRadio() does
full radio init with SPI commands and delays (~600ms). Fix: gate
beacon_update() on hw_ready.
2. stopRadio() called LoRa->end() (SPI sleep + SPI.end()) every loop
in the !hw_ready path (~99ms). Fix: only call when radio_online
is true (stop once, not repeatedly).
Result: loop time 950ms → 0.3ms (3200x improvement).
Also added:
- Main loop profiling (radio/serial/display/pmu/gps/bt/imu timing)
- Build timestamp in metrics command for version verification
- Visible-tile-only label updates (GPS float formatting skipped
when GPS screen not shown)
- Reverted to hwcdc USB mode (TinyUSB adds ~900ms/loop overhead)
- USB MSC SD card code preserved but inactive (needs TinyUSB)
SharedSPI.h: FreeRTOS mutex protecting the shared SPI bus (pins
33/34/35) used by LoRa (SX1262), SD card, and future NFC.
sx126x.cpp: All 6 SPI transaction blocks wrapped with
shared_spi_mutex acquire/release. LoRa uses portMAX_DELAY
(always gets access, just delayed by SD if needed).
USBSD.h: USB MSC SD card access uses shared_spi_mutex with
200ms timeout. Deferred init (3s after boot) for SPI bus
readiness. Metrics: sd_ready, sd_reads, sd_fails counters.
IMULogger.h, Gui.h: SD card writes wrapped in shared_spi_mutex.
Status: SD card correctly reports 29.7GB via USB MSC. Reads
work intermittently (~50% success). Root cause: main loop
takes ~700ms per iteration, causing mutex timeout for MSC
callbacks. Needs loop time investigation to achieve reliable
USB mass storage.
The CO5300 display uses a separate SPI3 bus — not affected.
LVGL 9.5.0 integration replacing direct framebuffer rendering.
Watch face with time, date, status bar (mode/battery), and three
complications (LoRa/GPS/Steps). Tileview swipe navigation between
five screens: watch face, radio status, GPS, messages, settings.
Haptic feedback on screen transitions via DRV2605.
Radio status screen shows frequency, LoRa params, RSSI bar gauge,
channel utilization, BLE state, and packet counts. GPS screen shows
coordinates, fix quality, altitude/speed, and beacon status.
Serial screenshot tool (scripts/screenshot.py) captures display
contents over USB CDC by sending trigger bytes and receiving the
shadow framebuffer as raw RGB565.
Build changes:
- FlashSize=16M in FQBN (bootloader embeds flash size, defaults 4MB)
- PSRAM=enabled for LVGL draw buffers and screenshot shadow buffer
- Custom 8MB app partition (partition_twatch.csv) for 16MB flash
- flash-twatch_ultra-full make target for full bootloader+partition+app flash
Architecture notes in code:
- display_init() must run BEFORE xl9555_init() (display power gate
defaults high at power-on, reordering causes black screen)
- LVGL draw buffers use separate swap buffer for RGB565 byte-order
conversion to avoid corrupting LVGL's internal buffer state
- Touch input registered via function pointer to decouple Gui.h from
touch library include order
Minimal I2C driver for the DRV2605 ERM vibration motor. 117 built-in
effects via the ERM effect library. Named constants for watch use
cases (click, bump, alert, buzz, tick, etc.).
Boot: sharp click on startup. Sleep: soft bump before entering deep
sleep. XL9555 DRV_EN and DISP_EN now explicitly enabled at boot.
Updated Makefile upload-twatch_ultra to set firmware hash after JTAG flash.
Standalone GPS beacon mode: when no KISS host is connected for 15s,
the RNode transmits position and battery telemetry over LoRa.
Two beacon paths:
- LXMF (recommended): encrypted per-packet messages with announces,
compatible with Sideband and any LXMF application. Supports IFAC
network authentication.
- Legacy JSON: plaintext or encrypted raw packets for simple collectors.
Key changes:
- GPS support for T-Beam Supreme S3 (L76K) and Heltec V4 (external)
- SX1262 radio fixes: IQ polarity, DCD preamble lockup, RX reliability
- LXMF identity management with NVS-backed Ed25519/X25519 keys
- IFAC authentication (CMD_IFAC_KEY 0x89) for private networks
- Per-channel serial isolation (USB, BLE, WiFi)
- GPS status page in OLED display rotation
- Provisioning via rnlog: provision-lxmf, provision-ifac
- Documentation in Documentation/BEACON.md