Archived from the old WioL1Pro-CVHBuild repository (first published 2026-08-09). These are the Wio Tracker L1 Pro builds from before Cairn; current builds are in the latest Cairn release.
Changelog
Notable changes in this fork of MeshCore, targeting the
Seeed Wio Tracker L1 Pro (nRF52840, SH1106 128×64 OLED, joystick, buzzer, L76K GNSS).
Based on upstream MeshCore v1.16.0 (companion). Format follows
Keep a Changelog. This fork versions by build number —
CVHBuild N, shown on the splash screen — so a device can be matched to a tag from its own display.
Build environments:
| Env | Target |
|---|---|
WioTrackerL1_companion_radio_ble
| SH1106 OLED (primary) |
WioTrackerL1Eink_companion_radio_ble
| Waveshare 2.13" B/W e-ink |
WioTrackerL1Eink3C_companion_radio_ble
| Waveshare 2.13" tri-colour e-ink |
cvhbuild-v56 — 2026-08-09
Builds 48–56. Only build 48 was flashed and tested on hardware; 49–56 are staged.
Added
Bluetooth
- The BLE pairing pin can be set on the device. The Settings row was previously hardcoded
read-only, so the pin could only be changed from the phone app. Six digit cells: Left/Right
walks them, Up/Down cycles the one under the caret through 0–9.000000means auto, where
the device picks a pin each session. - Pin changes apply without a reboot, via a new
BaseSerialInterface::setPin()implemented
for nRF52 and ESP32. Devices already bonded keep their keys and stay paired — the pin gates
new pairings only. - The app's
CMD_SET_DEVICE_PINand theset pinserial command now route through the same
MyMesh::setBLEPin(), so all three paths validate, persist and apply identically.
Display
- Brightness setting, 10/25/50/75/100 %, persisted at
NodePrefsbyte 146. The SH1106 has
no backlight, so this drives the contrast register (0x81) that sets segment current — a
real power lever, not just a comfort one. Applied live while stepping; Back restores.
Percentages are perceived brightness, squared on the way to the register (10 % → 2,
50 % → 63, 100 % → 255), because eye response to luminance is a power law and a linear map
leaves every visible step crowded into the bottom fifth of the range. - New
DisplayDriver::hasBrightness()/setBrightness()virtuals, defaulting to no-ops. The
row readsn/aon e-ink, which is reflective and has nothing to turn up.
Radio page
- Third sub-view: fullscreen spectrum. Up/Down now cycles settings+spectrum → traffic →
fullscreen. 24 px graph with a dotted peak-hold rule, live channel RSSI and noise floor, and
the peak/low of the window in dBm. - Enter sends an advert from any Radio sub-view, using the Recent page's existing zero-hop vs.
flood chooser. Nothing transmits until the choice is confirmed.
Battery view
- Discharge graph replacing the fill-bar, with a dotted 20 % reference line and an
estimated time remaining. The bar only restated a percentage already printed twice on the
same page; the trend is the part a number cannot give you. - The buffer is self-scaling: 63 slots, 126 bytes, allocated once. Sampling starts at 20 s
spacing and each time the buffer fills it discards every other sample in place and doubles
the spacing — 21 min of window at first, ~11 h after five compactions, ~90 h after eight. It
never grows, wraps or resets.
Changed
- Key lock is two gestures instead of one toggle: hold Back to lock, double-click Back to
unlock. Double-click no longer locks.UITask::toggleLock()was removed so nothing can
reintroduce the toggle. Triple-click still unlocks, for a fumbled double. - Radio page values are laid out on a real column grid — labels on a left rule, values
right-aligned onto a common rule, split by a vertical divider — so digits stack vertically.
Units were dropped from the values, which is what makes the numbers align rather than merely
the fields. Previously the second column was padded inside a singlesprintf(), so
BW 250.00versusBW 7.80shiftedCRsideways. - Spectrum samples are captured at the fullscreen graph's resolution and divided down for the
compact band, instead of being captured at the compact 7 px resolution and blown up. setBLEPin()accepts0–999999rather than0or100000–999999. A BLE passkey is a
six-character field, so012345is a real pin; both backends already format to six digits.
Fixed
- The GPS was left running through hibernate. nRF52 System OFF retains GPIO levels, so a
GPS enabled at power-off kept acquiring at tens of milliamps on a device that looked off.
It is now stopped beforepowerOff(), without touching prefs, so the chosen mode returns on
the next boot. start_gps()/stop_gps()are idempotent. Re-selecting the mode already active leaked a
ref-counted claim on the shared peripheral rail that the matching stop could never undo, and
re-ran_location->reset(), forcing a cold start on boards with a GPS reset pin.- E-ink layout occupied roughly two thirds of the panel.
EINK_SCALE_Ywas 1.28 for a
250×122 panel showing a 128×64 canvas, mapping 64 rows onto 82 pixels and leaving the bottom
third blank, while crushing the 9 px row pitch to 11.5 physical px against a font needing 18.
Now 1.90625 (122 ÷ 64). - E-ink text baselines are derived from each font's ascent (
EINK_TEXT_BASELINE_AUTO) instead
of a hand-tunedEINK_Y_OFFSETthat could only line up for one text size. Adafruit_GFX
places custom fonts on the baseline while every caller passes the top-left corner. - New
EINK_LOGICAL_W/EINK_LOGICAL_Hreport the canvas the UI was drawn for, so anything
sizing itself offheight()— the alert popup — is no longer given 128 on a 64-row layout.
cvhbuild-v47 — 2026-08-06
First tagged release. Everything below is relative to upstream v1.16.0.
Added
Display & text
- UTF-8 text folding for the CP437 bitmap fonts, replacing the previous
"every non-ASCII byte becomes a solid block" behaviour. Typographic punctuation folds to
ASCII (don’t→don't,…→..., en/em dashes →-), accented Latin folds to base
letters (café→cafe,straße→strasse), and ~40 emoji map to real font glyphs
(❤ → ♥, 🙂 → ☺, 🎵 → ♫, 👍 →+1, arrows →← → ↑ ↓). - Cyrillic and Greek romanisation —
Привет→Privet,Ретранслятор→Retranslyator,
Αθήνα→Athina. Covers the main Cyrillic block, the Ukrainian/Serbian/Macedonian
extensions, and Greek including final sigma and accented vowels. The table is Russian-style,
so Ukrainianиromanises as i rather than y. - Context-dependent handling for emoji with no font glyph: dropped from names (a marker is
just noise in front of the word), shown as a bullet in message bodies (silently dropping
what a sender wrote would change the message). Names fall back to blocks only when a name
would otherwise render empty.
UI pages
- Clock page with 12h / 24h / analogue styles and a persisted UTC offset, plus a battery
sub-view (percentage, charge bar, millivolts, power source, uptime, CPU die temperature). - Recent page listing heard nodes, with per-node ping, a zero-hop/flood advert chooser, and a
confirm-guarded clear. - Radio page with a live channel-RSSI spectrum anchored to the noise floor, and a traffic
monitor sub-view tabulating the last frames by type, route, hops, RSSI, SNR and age. - GPS page with satellite bar, speed, altitude, and a course-over-ground compass sub-view.
- Settings page consolidating Bluetooth, GPS, tones, screen, clock, region, TX power, BLE pin,
auto-lock and hibernate, with in-place editing and confirm-on-commit for destructive changes. - Node-to-node ping from the UI, over telemetry request for known chat contacts and trace
otherwise, reporting round-trip time and SNR.
GPS
- Eco mode — a third GPS state that duty-cycles the receiver (10 min interval, 90 s fix window,
15 s settle) instead of running it continuously. No preferences are written per cycle. - Cached last-known fix, so the GPS page shows coordinates and the fix time while the receiver
is powered down between eco cycles, rather than a countdown. - Speed and course over ground added to
LocationProvider(getSpeedKnotsX1000(),
getCourseDegX1000()), defaulting to unsupported for providers that cannot supply them.
Messaging
- Conversation threading, a message read view with scrolling, and quick-reply presets.
Wake on msgsetting — whether an incoming message may light the screen. Messages never
take over the display during active use, and a screen switched off deliberately with Back
stays off.
Power & display
- Configurable screen timeout, keep-awake-while-USB-powered, and auto-lock on timeout.
E-ink
- Two e-ink variants (B/W and tri-colour) with activity-aware refresh pacing and periodic
full refreshes to clear ghosting.
Changed
- Settings item
Screen offis nowScreen, a submenu covering timeout, USB behaviour and
message wake. - Altitude in feet, speed in mph, temperature in °F, battery as a percentage.
Fixed
- Contact names in the compose recipient picker were rendered without UTF-8 filtering, sending
raw bytes to a CP437 font and producing mojibake. Every other name path was already filtered.
Security
These harden upstream code and are not specific to this board.
- Decryption wrote past the destination buffer.
Utils::decrypt()always writes whole
16-byte blocks, but neither it norMACThenDecrypt()knew how large the destination was.
Both now take adest_sizeand refuse to write when the block-rounded length will not fit. - Attacker-supplied embedded paths were trusted. A malformed
path_lenin a received PATH
packet pushed the read cursor past the decrypted data, underflowingextra_len. The path
length is now validated and the cursor bounds-checked before use. - Unused channel slots matched anything. Empty slots are all zeroes, so they matched hash 0
with a secret any attacker could guess. Slots with an all-zero secret are now skipped. - BLE queue races.
recv_queueis produced on the BLE callback task and consumed on the
loop task; dequeue-and-shift, frame enqueue and buffer clearing were unsynchronised, so a
preempting callback could splice two frames together or corrupt the queue length. These are
now critical sections, reads happen outside them, and enqueue is bounded by the queue size. - Off-air lengths were used unclamped when building serial frames; they are now clamped to
what the serial layer accepts.
Build history
Per-build detail, newest first. Builds up to and including 48 were flashed and tested on
hardware; 49–56 are staged but unflashed. All predate the repository's commit history.
| Build | Change |
|---|---|
| 56 | Battery graph made self-scaling; time-remaining gated on elapsed time, not sample count |
| 55 | Battery discharge graph replaces the fill-bar |
| 54 | Brightness corrected to perceived scale; Radio page column divider |
| 53 | Brightness setting; Radio page column grid |
| 52 | Fullscreen spectrum sub-view |
| 51 | Hold Back to lock, double-click to unlock |
| 50 | Enter sends an advert from the Radio page |
| 49 | Pin editor: six digit cells, full 0–9, leading zeros allowed |
| 48 | BLE pin editable at all; e-ink geometry; GPS powered down on hibernate |
| 47 | Emoji marker split by context: dropped in names, bullet in message bodies |
| 46 | Cyrillic + Greek romanisation |
| 45 | Unmappable emoji dropped rather than blocked; fixed unfiltered compose picker |
| 44 | GPS eco sleep shows cached coordinates and fix time instead of a countdown |
| 43 | UTF-8 folding: punctuation, accents, emoji-to-glyph mapping |
| 42 | Wake on msg simplified to a plain on/off toggle
|
| 41 | Wake on msg setting added
|
| 40 | E-ink refresh pacing; tri-colour variant |
| 39 | Spectrum framing and scale refinements |
| 33–38 | Battery view expansion, advert chooser, GPS compass sub-view, status bar cleanup |
| 28–32 | GPS eco mode, message-wake suppression during active use, clock settings moved |
| 23–27 | Traffic monitor table, spectrum capture fixes, hybrid ping, Back-to-off |
| 19–22 | Satellite bar, speed row, spectrum anchored to noise floor |
| 13–18 | UI ping from Recent, live RSSI spectrum, traffic monitor |