Skip to content

Releases: cvhviz/CAIRN

Cairn 115

Choose a tag to compare

@cvhviz cvhviz released this 12 Oct 19:54

Cairn 115 is mostly about audio and how things look. The L2 Pro's microphone works, the music player gets a five-band EQ, an edge-to-edge visualizer and its options as cards, and every chooser on the touch and key boards is now a page of cards instead of a list. Packet details are redesigned around the sender, the map draws a node's route, and nodes get an actions page. The clock no longer shows a stale time after a power cut (#18), older T-Deck keyboards type again (#17), and the T-Display SF32 no longer restarts when you switch emoji tabs in a message.

Highlights:

  • L2 Pro microphone: the recorder works on the L2 Pro.
  • EQ (L2 Pro, T-Deck Plus): five bands with Flat, Voice, Bright, Warm and Custom presets, shaped live over the music's spectrum.
  • Now playing is a full-screen visualizer. Tap it for the controls.
  • Choosers are cards: button actions, settings lists and action menus use the Settings card style.
  • Packet details lead with who sent it: the name, its type, distance and key, then the signal and route as large values.
  • Map routes and node actions: tap a node on the map for its route; a node's "…" opens Discover path, Reset route, Favorite, Share contact, Admin and Remove as cards.
  • Clock: after a power cut the clock shows "--:--" until GPS, the app or Wi-Fi sets it, instead of the time the board was switched off.
  • T-Display SF32: no more restart on the second * in a message, a faster Nodes tab, and card titles in Title Case.

Update

  • On Cairn 108 to 114 (L2 Pro, Heltec V4 touch, ThinkNode M9, T-Deck, T-Display SF32): Settings › System › Firmware update: Check, Download, Install. The new build starts on trial and goes back to the old one by itself if it fails to start. On the T-Display SF32, dock the core on the keyboard board first (it carries the Wi-Fi chip). A tester's 115beta build is offered this release too.
  • Wio Tracker L1 Pro, DV1, L1 e-ink and the T096: double-tap Reset and copy the board's .uf2 onto the drive that appears (UF2 guide). Updating keeps your identity, contacts, channels and settings.
  • T-Display SF32 without Cairn, or to recover: unzip the SF32 -install.zip, plug the core in over USB-C and run Install on Mac.command, Install on Windows.bat or ./install-linux.sh (SF32 install guide).
  • A board without Cairn: write the board's -full.bin (ESP32 install guide), or copy the .uf2 for the L1 Pro family and the T096 (UF2 guide).

Downloads

Board File Use
✅ Seeed Wio Tracker L2 Pro MeshCore-WioL2Pro-Cairn115-2026-10-12-full.bin first install or recovery over USB, at 0x0
MeshCore-WioL2Pro-Cairn115-2026-10-12-update.bin update over USB at 0x10000, or over Wi-Fi
✅ Heltec V4 + Expansion Kit (touch) MeshCore-HeltecV4Touch-Cairn115-2026-10-12-full.bin first install or recovery over USB, at 0x0
MeshCore-HeltecV4Touch-Cairn115-2026-10-12-update.bin update over USB at 0x10000, or over Wi-Fi
✅ Elecrow ThinkNode M9 (beta) MeshCore-ThinkNodeM9-Cairn115-2026-10-12-full.bin first install or recovery over USB, at 0x0
MeshCore-ThinkNodeM9-Cairn115-2026-10-12-update.bin update over USB at 0x10000, or over Wi-Fi
✅ LilyGO T-Deck / T-Deck Plus (beta) MeshCore-TDeckPlus-Cairn115-2026-10-12-full.bin first install or recovery over USB, at 0x0
MeshCore-TDeckPlus-Cairn115-2026-10-12-update.bin update over USB at 0x10000, or over Wi-Fi
✅ LilyGO T-Display SF32 + keypad MeshCore-TDisplaySF32-Cairn115-2026-10-12-install.zip first install or recovery over USB, with the included installer
MeshCore-TDisplaySF32-Cairn115-2026-10-12-update.bin update over Wi-Fi (what Firmware update fetches)
MeshCore-TDisplaySF32-Cairn115-2026-10-12-update.bin.sha256 its checksum, for the updater
✅ Seeed Wio Tracker L1 Pro MeshCore-WioL1Pro-Cairn115-2026-10-12.uf2 companion, SH1106 OLED (the main L1 build)
MeshCore-DV1-Cairn115-2026-10-12.uf2 companion, 2.42" SSD1309 OLED on the Grove port
MeshCore-WioL1Eink-Cairn115-2026-10-12.uf2 companion, 2.13" e-ink
MeshCore-WioL1RoomServer-Cairn115-2026-10-12.uf2 room server
✅ Heltec Mesh Node T096 (companion) MeshCore-HeltecT096-Cairn115-2026-10-12-ble.uf2 the main build (phone app over Bluetooth)
MeshCore-HeltecT096-Cairn115-2026-10-12-usb.uf2 the app over USB serial

The compact repeater files are listed under Compact repeaters.

SHA256SUMS.txt covers every file: shasum -a 256 -c SHA256SUMS.txt. The preview boards' files are in Cairn 107 preview.

Touch and key boards: what's new

L2 Pro, Heltec V4 touch, ThinkNode M9 and T-Deck.

Audio (L2 Pro and T-Deck Plus)

  • L2 Pro microphone: the recorder is back on and records. The mic needed the speaker chip's analogue side powered while recording.
  • EQ: five bands (150 Hz, 400 Hz, 1 kHz, 2.5 kHz, 6 kHz, ±6 dB) and the presets Flat, Voice, Bright, Warm and Custom. The EQ page shows the live spectrum of the music after the EQ with a handle on each band: drag one to shape Custom. It applies to music and recordings.
  • Now playing fills the screen: 24 bars with peak caps, the level, peak and loudest frequency, the EQ in use, and the time. Tap it and the controls slide in (title, Library, Options, volume, progress and transport) without covering the bars; tap the bars to put them away.
  • Player options (⋯) are cards: Repeat, Shuffle, Volume, EQ, Screen off and Alerts.
  • The Audio page has shorter Player and Recorder cards with Library and Options cards under them.
  • Music no longer stutters when a message, contact or setting is saved while it plays, and no longer fails with "Not enough memory" after Wi-Fi has connected.
  • Alert tones no longer interrupt music. Alerts in Player options turns them back on.
  • L2 Pro: a louder speaker (tones about +9 dB, music about +6 dB) through a smoother limiter.
  • The User button can now open the Player or the Recorder, or Play / pause.

Cards everywhere

  • Every chooser is a page of cards in the Settings style: button actions (grouped General, Mesh, Tools, Audio), setting lists, and the action menus on Nodes, Chats, the map and the recorder.
  • A node's "…" opens its actions as cards: Discover path, Reset route, Favorite, Share contact, Admin / Log in and Remove contact. Discover path finds a route to a contact (or logs in to a repeater or room to learn one).
  • The Quick panel's Theme button is now SD Card, which opens Files.

Packet details

  • Tap a packet in Traffic: the sender comes first with its name large, and under it who it was sent to, its type, how far away it is and its key. When it was heard sits at the top right.
  • RSSI, SNR and margin, then route, size and airtime, are shown as large values with small labels, followed by the relay it came through and the packet type.

Map

  • Tap a node on the GPS map to see its route: a line from you through each repeater on its path that has a position. A hop that is not on the map is drawn dashed, and a node with no known route gets a dashed straight line.
  • The map remembers your last GPS fix across restarts.

Clock (#18)

  • After a power cut or a reset, the clock shows "--:--" until GPS, the phone app or Wi-Fi gives it the real time. Before, it showed the time from the newest contact, which on the ThinkNode M9 was the moment it was switched off. Messages you send still carry timestamps that move forward.
  • A soft restart or waking from sleep keeps a clock that was already set.

Other

  • T-Deck (#17): keyboards with older firmware type again. They ignored the request for raw key mode that the Mic key needs; Cairn now notices and switches back.
  • Heltec V4 touch: the LoRa / Wi-Fi / Bluetooth tabs are easier to tap.
  • Settings › Connect › Wi-Fi shows the IP address once connected.

T-Display SF32: what's new

  • No restart on the second * in a message. The first * opens the symbol picker and the second switched to an emoji tab, which ran out of memory. Emoji tabs now take almost no memory, and all of each tab's emoji show.
  • Faster Nodes tab: it opens in about 0.2 s (was 0.5 s), and switching Recent and Contacts takes about 20 ms (was 0.4 s).
  • Card titles in Title Case on every card ("Ping Last", "Time Sync", "IR Remote").
  • The clock follows the same rule as the other boards when it has no trusted time.
  • The firmware is about 58 KB smaller, leaving room for future updates in the Wi-Fi update slot.

Other boards

The Wio Tracker L1 Pro family, the T096 companion and the compact repeaters are rebuilt as Cairn 115 with no changes of their own.

Compact repeaters

Board File Use
✅ Heltec Mesh Node T096 (beta) MeshCore-HeltecT096-Cairn115-2026-10-12-repeater.uf2 copy onto the board's USB drive
🧪 Heltec T114 MeshCore-HeltecT114-Cairn115-2026-10-12-repeater.uf2 copy onto the board's USB drive
🧪 Heltec V2 MeshCore-HeltecV2-Cairn115-2026-10-12-repeater-full.bin first install at 0x0
MeshCore-HeltecV2-Cairn115-2026-10-12-repeater-update.bin update at 0x10000
🧪 Heltec V3 MeshCore-HeltecV3-Cairn115-2026-10-12-repeater-full.bin first install at 0x0
MeshCore-HeltecV3-Cairn115-2026-10-12-repeater-update.bin update at 0x10000
🧪 Heltec V4 MeshCore-HeltecV4-Cairn115-2026-10-12-repeater-full.bin first install at 0x0
MeshCore-HeltecV4-Cairn115-2026-10-12-repeater-update.bin update at 0x10000
🧪 Heltec V4-R8 `MeshCore-Helte...
Read more

Cairn 114

Choose a tag to compare

@cvhviz cvhviz released this 12 Oct 06:35

Cairn 114 brings LoRa, Wi-Fi and Bluetooth together on one Radios page with tabs, and adds a full-screen Traffic log with a details window for every packet. The touch and key boards get a new Storage page with a file manager, and the T-Deck Plus and L2 Pro can now act as a USB drive for their microSD card. The card gets tidy top-level folders (Maps, Music, Recordings, Sounds, Pictures). Audio on the L2 Pro is cleaner and its volume matches the alert tones, and the T-Deck's Mic key opens the recorder. The T-Display SF32 gets the same Radios tabs and Traffic log, a Sensors card and a clean startup tone.

Highlights:

  • Radios page with tabs: LoRa (with its frequency), Wi-Fi and Bluetooth side by side. The scanners moved here from the Network page. On the T-Deck and the SF32, keys 1, 2 and 3 pick a tab.
  • Traffic log: tap the traffic card on the LoRa tab for a full-screen list of the last hour's packets (up to 2048), with a stats strip. Tap a packet for its details as tiles.
  • Storage page and Files (Settings › Hardware › Storage): the card's status, a file manager, Eject, Re-scan and Format in one place.
  • USB drive (T-Deck Plus and L2 Pro): the microSD card shows up as a disk on your computer, so you can copy maps and music without taking the card out.
  • Card folders: Maps, Music, Recordings, Sounds and Pictures at the top of the card. Maps in the old cairn/maps folder move by themselves.
  • L2 Pro audio: music no longer distorts at high volume, and its loudness now matches the alert tones.
  • T-Deck Plus: the Mic key opens the recorder.

Update

  • On Cairn 108 to 113 (L2 Pro, Heltec V4 touch, ThinkNode M9, T-Deck, T-Display SF32): Settings › System › Firmware update: Check, Download, Install. The new build starts on trial and goes back to the old one by itself if it fails to start. On the T-Display SF32, dock the core on the keyboard board first (it carries the Wi-Fi chip).
  • Wio Tracker L1 Pro, DV1, L1 e-ink and the T096: double-tap Reset and copy the board's .uf2 onto the drive that appears (UF2 guide). Updating keeps your identity, contacts, channels and settings.
  • T-Display SF32 without Cairn, or to recover: unzip the SF32 -install.zip, plug the core in over USB-C and run Install on Mac.command, Install on Windows.bat or ./install-linux.sh (SF32 install guide).
  • A board without Cairn: write the board's -full.bin (ESP32 install guide), or copy the .uf2 for the L1 Pro family and the T096 (UF2 guide).

Downloads

Board File Use
✅ Seeed Wio Tracker L2 Pro MeshCore-WioL2Pro-Cairn114-2026-10-12-full.bin first install or recovery over USB, at 0x0
MeshCore-WioL2Pro-Cairn114-2026-10-12-update.bin update over USB at 0x10000, or over Wi-Fi
✅ Heltec V4 + Expansion Kit (touch) MeshCore-HeltecV4Touch-Cairn114-2026-10-12-full.bin first install or recovery over USB, at 0x0
MeshCore-HeltecV4Touch-Cairn114-2026-10-12-update.bin update over USB at 0x10000, or over Wi-Fi
✅ Elecrow ThinkNode M9 (beta) MeshCore-ThinkNodeM9-Cairn114-2026-10-12-full.bin first install or recovery over USB, at 0x0
MeshCore-ThinkNodeM9-Cairn114-2026-10-12-update.bin update over USB at 0x10000, or over Wi-Fi
✅ LilyGO T-Deck / T-Deck Plus (beta) MeshCore-TDeckPlus-Cairn114-2026-10-12-full.bin first install or recovery over USB, at 0x0
MeshCore-TDeckPlus-Cairn114-2026-10-12-update.bin update over USB at 0x10000, or over Wi-Fi
✅ LilyGO T-Display SF32 + keypad MeshCore-TDisplaySF32-Cairn114-2026-10-12-install.zip first install or recovery over USB, with the included installer
MeshCore-TDisplaySF32-Cairn114-2026-10-12-update.bin update over Wi-Fi (what Firmware update fetches)
MeshCore-TDisplaySF32-Cairn114-2026-10-12-update.bin.sha256 its checksum, for the updater
✅ Seeed Wio Tracker L1 Pro MeshCore-WioL1Pro-Cairn114-2026-10-12.uf2 companion, SH1106 OLED (the main L1 build)
MeshCore-DV1-Cairn114-2026-10-12.uf2 companion, 2.42" SSD1309 OLED on the Grove port
MeshCore-WioL1Eink-Cairn114-2026-10-12.uf2 companion, 2.13" e-ink
MeshCore-WioL1RoomServer-Cairn114-2026-10-12.uf2 room server
✅ Heltec Mesh Node T096 (companion) MeshCore-HeltecT096-Cairn114-2026-10-12-ble.uf2 the main build (phone app over Bluetooth)
MeshCore-HeltecT096-Cairn114-2026-10-12-usb.uf2 the app over USB serial

The compact repeater files are listed under Compact repeaters.

SHA256SUMS.txt covers every file: shasum -a 256 -c SHA256SUMS.txt. The preview boards' files are in Cairn 107 preview.

Touch and key boards: what's new

L2 Pro, Heltec V4 touch, ThinkNode M9 and T-Deck.

Radios page with tabs

  • The sidebar's Radio tab is now Radios, with three tabs across the top: LoRa (its label shows the frequency), Wi-Fi and Bluetooth. On the T-Deck's keyboard, 1, 2 and 3 pick a tab.
  • The Wi-Fi and Bluetooth scanners moved here from Home › Network and Settings › Connect. Those pages keep the Wi-Fi and Bluetooth settings.
  • Home's radio card is now titled LORA and opens the LoRa tab.
  • The scanners' status ("Scanning", "Scan · 12", "N found", "BT off") moved into the status bar, so the plot and the bars are taller.
  • The scanner's controls fit in one row: one button switches between Plot and List ('#' on the keys), Active and Passive sit next to it, and Start is the widest button.

Traffic log and packet details

  • The LoRa tab is laid out again around a traffic card. Tap it for Traffic: a full-screen list of the packets heard over about the last hour (up to 2048), with a stats strip: packets, per minute, average RSSI and SNR.
  • Tap a packet for a window with its fields as tiles: type, route and hops, signal, margin over the spreading factor's limit, from (or channel), to, heard via, size and when it was heard. Open node jumps to the sender's page.

Storage and Files

  • Settings › Hardware › Storage is one page: the card's status first, then USB drive and Files, then Eject and Re-scan, then Format. While a delete runs, a Deleting tile shows its progress.
  • Files is a file manager for the card: browse folders, see sizes, and delete a file or a whole folder. A folder delete moves it aside first, so it is quick and survives a restart.
  • A card that fails to start at boot is tried again at the same speed before the slower ones.

USB drive (T-Deck Plus and L2 Pro)

  • Storage › USB drive turns the board into a USB disk for your computer: the microSD card mounts on the Mac or PC, and you can copy files to it and from it. Eject it on the computer, then tap Done: the board restarts and goes back to normal.
  • The radio and the app stay off while it is a drive. Measured speeds: about 0.7 MB/s on the L2 Pro and 0.5 MB/s on the T-Deck Plus (its card shares a bus with the screen and the radio).
  • Not on the Heltec V4 touch or the ThinkNode M9: their USB ports cannot act as a drive.

Card folders

  • The card now uses five folders at the top: Maps, Music, Recordings, Sounds and Pictures. The same layout is used on every Cairn board, so a card moves between them.
  • Offline maps in cairn/maps move to Maps by themselves the first time the card is read. Only folder names change: nothing is copied or deleted.
  • The Library shows subfolders in Music, up to two levels deep (for example Music/Artist/Album).

Music player

  • Now playing has a taller spectrum with its 24 bars. The volume buttons sit over the right side of the spectrum with the level in %. Tap the spectrum to hide or show them.
  • Repeat and shuffle moved to the player's options sheet (⋯).
  • L2 Pro: music no longer clips or sounds harsh at high volume (a low-cut filter, more headroom and a limiter), and the volume now follows the same scale as the alert tones, so they match.

T-Deck Plus

  • The keyboard's Mic key opens the recorder. On the recorder it starts and stops a recording.
  • A fix for crackling static in recordings (the microphone's data slipped by one bit now and then).

T-Display SF32: what's new

  • Radios page with tabs: 1 LoRa, 2 Wi-Fi and 3 BLE, picked by touch or by keys 1, 2 and 3. The scanners moved here from the Network page.
  • Traffic log and packet details, as on the touch boards.
  • The scanners' status moved to the status bar, so the plot is taller. One button switches between Plot and List, Active and Passive have their own buttons, and Start is the widest. Keys: 4 Active, 5 Passive, 6 the view, 7 Start.
  • Home: the GPS card is gone (the sidebar already shows the GPS). Climate and Steps are now one Sensors card, with the step count as its main value.
  • Startup tone: it no longer stutters while the board starts up. The tone now plays at a higher priority than the start-up work.

Other boards

The Wio Tracker L1 Pro family, the T096 companion and the compact repeaters are rebuilt as Cairn 114 with no changes of their own.

Compact repeaters

Board File Use
✅ Heltec Mesh Node T096 (beta) MeshCore-HeltecT096-Cairn114-2026-10-12-repeater.uf2 copy onto the board's USB drive
🧪 Heltec T114 MeshCore-HeltecT114-Cairn114-2026-10-12-repeater.uf2 copy onto the board's USB drive
🧪 Heltec V2 MeshCore-HeltecV2-Cairn114-2026-10-12-repeater-full.bin first install at 0x0
MeshCore-HeltecV2-Cairn114-2026-10-12-repeater-update.bin update at 0x10000
🧪 Heltec V3 `MeshCore-HeltecV3-Cairn114-2026-10-12-re...
Read more

Cairn 113

Choose a tag to compare

@cvhviz cvhviz released this 12 Oct 00:59

Cairn 113 brings Wi-Fi and Bluetooth scanners, with a full-screen radar and Find, to the Wio Tracker L2 Pro, Heltec V4 touch, ThinkNode M9, T-Deck and T-Display SF32. The T-Deck Plus gets a music player and a voice recorder, and the L2 Pro gets the music player. The touch and key boards also get a new Battery page and ask before Wi-Fi, Bluetooth or GPS switch. The Wio Tracker L1 Pro family moves up from Cairn 107 with sent-message sync, a GPS data view and Settings in categories. The T096 companion gets a regrouped Settings list, and the compact repeaters pick up fixes from upstream MeshCore.

Highlights:

  • Wi-Fi and Bluetooth scanners (Home › Network): a live plot and a list of the networks or devices around you. The Bluetooth radar opens full screen, and Find helps you walk towards a device.
  • Music player (T-Deck Plus and L2 Pro): play MP3 or WAV files from the microSD card. Voice recorder (T-Deck Plus): record voice notes with the built-in microphone. The L2 Pro's recorder is off for now.
  • Battery page: time left, the rate, today's range and a graph over 1 hour to 7 days, with markers for plug-in, unplug and full.
  • Asks before switching: turning Wi-Fi, Bluetooth or GPS on or off from the board asks first, so a stray touch can't drop the app.
  • GPS icon in the sidebar is green while the GPS is on and red while it is off.
  • Wio Tracker L1 Pro: messages sent from the phone show on the L1, plus a GPS data view, Path hash and Units settings, a TX/RX line and Settings in six categories.
  • Heltec V4-R8: better reception (the receive amplifier is now switched on).

Update

  • On Cairn 108 to 112 (L2 Pro, Heltec V4 touch, ThinkNode M9, T-Deck, T-Display SF32): Settings › System › Firmware update: Check, Download, Install. The new build starts on trial and goes back to the old one by itself if it fails to start. On the T-Display SF32, dock the core on the keyboard board first (it carries the Wi-Fi chip).
  • Wio Tracker L1 Pro, DV1, L1 e-ink and the T096: double-tap Reset and copy the board's .uf2 onto the drive that appears (UF2 guide). Updating keeps your identity, contacts, channels and settings.
  • T-Display SF32 without Cairn, or to recover: unzip the SF32 -install.zip, plug the core in over USB-C and run Install on Mac.command, Install on Windows.bat or ./install-linux.sh (SF32 install guide).
  • A board without Cairn: write the board's -full.bin (ESP32 install guide), or copy the .uf2 for the L1 Pro family and the T096 (UF2 guide).

Downloads

Board File Use
✅ Seeed Wio Tracker L2 Pro MeshCore-WioL2Pro-Cairn113-2026-10-11-full.bin first install or recovery over USB, at 0x0
MeshCore-WioL2Pro-Cairn113-2026-10-11-update.bin update over USB at 0x10000, or over Wi-Fi
✅ Heltec V4 + Expansion Kit (touch) MeshCore-HeltecV4Touch-Cairn113-2026-10-11-full.bin first install or recovery over USB, at 0x0
MeshCore-HeltecV4Touch-Cairn113-2026-10-11-update.bin update over USB at 0x10000, or over Wi-Fi
✅ Elecrow ThinkNode M9 (beta) MeshCore-ThinkNodeM9-Cairn113-2026-10-11-full.bin first install or recovery over USB, at 0x0
MeshCore-ThinkNodeM9-Cairn113-2026-10-11-update.bin update over USB at 0x10000, or over Wi-Fi
✅ LilyGO T-Deck / T-Deck Plus (beta) MeshCore-TDeckPlus-Cairn113-2026-10-11-full.bin first install or recovery over USB, at 0x0
MeshCore-TDeckPlus-Cairn113-2026-10-11-update.bin update over USB at 0x10000, or over Wi-Fi
✅ LilyGO T-Display SF32 + keypad MeshCore-TDisplaySF32-Cairn113-2026-10-11-install.zip first install or recovery over USB, with the included installer
MeshCore-TDisplaySF32-Cairn113-2026-10-11-update.bin update over Wi-Fi (what Firmware update fetches)
MeshCore-TDisplaySF32-Cairn113-2026-10-11-update.bin.sha256 its checksum, for the updater
✅ Seeed Wio Tracker L1 Pro MeshCore-WioL1Pro-Cairn113-2026-10-11.uf2 companion, SH1106 OLED (the main L1 build)
MeshCore-DV1-Cairn113-2026-10-11.uf2 companion, 2.42" SSD1309 OLED on the Grove port
MeshCore-WioL1Eink-Cairn113-2026-10-11.uf2 companion, 2.13" e-ink
MeshCore-WioL1RoomServer-Cairn113-2026-10-11.uf2 room server
✅ Heltec Mesh Node T096 (companion) MeshCore-HeltecT096-Cairn113-2026-10-11-ble.uf2 the main build (phone app over Bluetooth)
MeshCore-HeltecT096-Cairn113-2026-10-11-usb.uf2 the app over USB serial

The compact repeater files are listed under Compact repeaters.

SHA256SUMS.txt covers every file: shasum -a 256 -c SHA256SUMS.txt. The preview boards' files are in Cairn 107 preview.

Touch and key boards: what's new

L2 Pro, Heltec V4 touch, ThinkNode M9 and T-Deck.

Wi-Fi and Bluetooth scanners

  • Home › Network (and Settings › Connect) opens with two new tools at the top, Wi-Fi scan and BT scan, with the Wi-Fi and Bluetooth settings under them. Each works like the LoRa band scan: a Start button, a live plot and a List view.
  • Wi-Fi scan shows the 2.4 GHz band, channels 1 to 14: one arc per network at its signal strength, each in its own colour, the four strongest named, and your own network's channel highlighted. Under the plot, a bar per channel shows how many networks reach it. Tap the plot or a bar for a readout ("ch 6 · 2437 MHz · HomeNet -62 dBm · WPA2 · 3 APs").
  • The Wi-Fi list names every network, strongest first, hidden ones too, with its channel, security and a Connected or Saved tag. A tap offers Details, Show on plot or Join….
  • BT scan shows the Bluetooth LE devices in range: a bar per device over a half radar with -60 / -80 / -100 dBm rings, MeshCore nodes marked and named in the accent colour. Tap a bar or a dot for a readout ("MeshCore-Chris · -58 dBm · pk -52 · Apple · 3 s ago"). The list gives each device's name, signal and peak, address, maker and when it was last heard, and keeps a steadier order, so neighbours a couple of dB apart no longer swap places every few seconds.
  • Tap the radar (or press Enter on it) for a full-screen radar. It shows distance only, never a direction. The trackball or d-pad moves to the nearest dot in the direction you press, and a small chip follows the selected dot with its name and signal.
  • Find: pick a device and walk towards it. The page shows its signal in big figures, one word for the last 10 s (Closer, Farther, Steady or Lost) and the last minute as a line. While you walk with a GPS course, a ring marks the way you were facing when the signal was strongest. An optional Tone ticks faster as the signal climbs.
  • Details open as cards in the Settings style, with live values while the scan runs: for Wi-Fi the signal, channel, security, standards and BSSID; for Bluetooth the signal, maker, address, services and transmit power. A network or device that goes quiet says so.
  • Active asks for names (Wi-Fi probe requests, about 2.5 s a scan; Bluetooth name requests). Passive only listens and sends nothing.
  • A scan only starts when you tap Start. It stops by itself when you leave the page, when the screen goes dark, after 3 minutes without a touch, or when an update starts. One scanner runs at a time. Both use battery while they run. Nothing is sent on the mesh and LoRa keeps working. BT scan works with Bluetooth switched off and while the app is connected.

Music player (T-Deck Plus and L2 Pro) and voice recorder (T-Deck Plus)

  • On these two boards an Audio card takes the GPS card's place on Home. The GPS tab in the sidebar still shows the satellites.
  • Player: put MP3 or WAV files in a Music folder on the microSD card. The Library lists Music and Recordings. Now playing has a live spectrum, a seek bar, previous / -10 s / play-pause / +10 s / next, volume, repeat (off, all, one) and shuffle. Music keeps playing with the screen dark, unless you set "Pause at screen off". A message alert pauses the music, and it carries on after the tone. Hold a file to delete it.
  • Plays MP3 (CBR or VBR) and WAV (8, 16 or 24-bit, mono or stereo), 8 to 48 kHz. Stereo plays as mono on the one speaker.
  • Recorder (T-Deck Plus): records voice notes to a Recordings folder as WAV at 16 or 8 kHz, with mic gain, a time limit, voice start (waits for sound and keeps the half second before it) and marks. Files are named by date and time once the clock is set. A recording keeps going with the screen dark and is kept safe if the power or the card goes. The microphone is only on while you record or the Record view is open, and the status bar shows MIC or REC whenever it is.
  • L2 Pro: the Audio card opens the Player. Its recorder is off for now: our unit's microphone gives only silence, and Seeed lists it as reserved. It stays off until we know more.
  • The folders match the T-Display SF32's player, so a card moves between the devices.
  • On the T-Deck the card shares its bus with the radio and the screen, so very large files (CD-quality WAV) are refused with "Too fast for this card: use MP3".

Battery page

  • Home's Battery card opens a new page: a header with the percent, the voltage and one status line ("On battery · 9h 40m left", "Charging · ~45m to full", "USB power").
  • Six cells: time left (or to full), the rate, how long it has been in this state, today's low and high, the last full charge and the screen-on share. On the M9 the last cell shows Battery saver while it is on.
  • A graph over 1 hour, 6 hours, 24 hours or 7 days, in % or volts, with markers for plug-in, unplug, full, warnings, restarts and Battery saver changes, and...
Read more

Wio L1 Pro build 98 (archive)

Pre-release

Choose a tag to compare

@cvhviz cvhviz released this 10 Oct 19:08

Archived from the old WioL1Pro-CVHBuild repository (first published 2026-09-05). These are the Wio Tracker L1 Pro builds from before Cairn; current builds are in the latest Cairn release.

CVHBuild 98 — messages that survive a reboot, and a Nodes page

A Seeed Wio Tracker L1 Pro fork of MeshCore
(nRF52840, SH1106 128×64 OLED, 5-way joystick, buzzer, L76K GNSS), on upstream v1.17.1.

Six builds' worth of work since the last release, all cut the same day: messages now survive a
power cycle, messages you send from the device finally appear in its own list, the Recent page
grew into a Nodes page that also browses saved contacts, and the page indicator is legible.

Build 92 was a DV1-only diagnostic image and was never released, so every target moves from
91 straight to 98.

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.


Downloads

File Target
MeshCore-WioL1Pro-CVHBuild98-2026-09-05.uf2 Companion, SH1106 OLED — the main build
MeshCore-WioL1Eink-CVHBuild98-2026-09-05.uf2 Companion, Waveshare 2.13" B/W e-ink
MeshCore-DV1-CVHBuild98-2026-09-05.uf2 Companion, 2.42" SSD1309 OLED on the Grove connector
MeshCore-WioL1RoomServer-CVHBuild98-2026-09-05.uf2 Room server, SH1106 OLED

Flashing: double-tap reset to expose the TRACKER L1 volume and copy the .uf2 onto it. The
"could not copy extended attributes" error is normal, and the volume ejecting itself means the
flash succeeded.

Time the copy. A flash that finishes in about a second with "Device not configured" is a
truncated write and leaves the app bricked — a good flash takes several seconds (≈6 s for the
OLED images, ≈16 s for e-ink). If it was quick, double-tap reset and copy it again.


Added

The un-synced message queue now survives a power-off or reboot. (build 93)

The companion's offline queue holds up to 256 messages that have arrived but that the phone app has
not pulled yet. It lived only in RAM, so switching the node off discarded all of them — including
in the case the queue exists for, a node collecting messages unattended with no app connected.

It is now snapshotted to /offline_q on the way down and restored at the next boot. On the L1 that
file lands on the 2 MB QSPI flash alongside contacts and channels. Records are length-prefixed, so
a queue holding three messages writes a few hundred bytes rather than the whole 45 KB array, and a
write cut short by power loss reads back as fewer messages instead of garbage.

The snapshot is deliberately one-shot — the loader deletes the file after reading it. Queue reads
are destructive (the app takes each message into its own database), so a file left behind would
replay messages the app already had the next time power was lost ungracefully, showing up as
duplicates. Deleting at load means only never-synced messages ever come back.

Hooked into every path that ends in SYSTEM OFF or a reset: the menu power-off and the menu
restart, the app's reboot command, and the serial CLI reboot.

Scope: this survives a clean shutdown, not a battery pull, a brownout or a crash. Closing that
gap needs a debounced write-through, which would put blocking flash I/O in the packet-receive
callback. The device's own on-screen message history is a separate RAM-only list and is still lost
on reboot; only the app-sync queue is restored.


Fixed

A message sent from the device never appeared in its own Messages list. (build 94)

The message store only ever recorded received messages, so a message composed on the device went
out over the radio and left no trace on screen — the conversation showed only the other side of it.

Both send paths now record on success, keyed by the peer rather than by us. That is what threads
it correctly: an outgoing DM stores the recipient's key and name, so it joins that peer's
conversation instead of opening a new one titled with your own node name. Your own messages never
count toward the unread badge, thread rows mark them with >, and the read view titles them
To <peer> rather than showing the peer as if they had sent it.

Sent messages now share the 24-entry ring, so a busy conversation ages received messages out
somewhat sooner than before. That is inherent to showing a conversation rather than an inbox.

The Nodes page age column read 49707d for saved contacts. (build 97)

Caught on hardware the moment the contact list was first shown against a real mesh. The age is
now - last_advert_timestamp in unsigned arithmetic. A node with a drifting clock genuinely
advertises timestamps ahead of yours, and a contact carries whatever that node claimed — so the
subtraction underflowed and wrapped to just under 2^32 seconds, which divides out to about 49707
days. The contacts in question were roughly 3.3 days ahead, exactly the observed wrap.

It could not have shown up on the recently-heard list, where every entry is by definition something
this node received in its own past. Reaching the saved contact list is what made future timestamps
displayable for the first time.

Elapsed time is now clamped at zero, and anything more than a minute ahead of our clock renders as
-, the same dash a never-heard contact gets. Past clock jitter there is no age that can honestly
be stated, and now would have been a confident lie about a node last heard days ago.


Changed

The Recent page is now the Nodes page — recently-heard adverts or saved contacts. (build 95)

Same tab position and icon. The list gains a mode row at the top naming which list is on screen
and how long it is, with Enter switching between them:

> Recent                    7      <- Enter switches to Contacts
  BaseCamp                 2m
  KG7XYZ                  14m
  Repeater-N               1h
                Back:exit Enter:ping

The cursor model extends the old one rather than replacing it. With no cursor, Enter still opens the
zero-hop/flood advert chooser exactly as before. Row 0 is the mode row. Rows 1 and beyond are nodes,
where Enter pings. Each position gets its own hint line — at 128px the annotation row holds about 21
characters, so a combined hint was never going to fit.

The mode row scrolls off once the cursor moves down into the list, so it does not permanently cost a
row of a four-row viewport. The cursor wraps through 1 + count rows, so the mode row stays
reachable and an empty Recent list can still be switched over to Contacts.

The contact list is windowed, not materialised — only the rows about to be drawn are fetched, so
350 contacts cost the same four rows of RAM the recently-heard table already used. Ping needed no
new logic: contacts render into the same row type the advert list uses, so both feed one render path
and one ping. Ages now roll over to days as well, since a contact can be weeks stale where a heard
advert never is.

The active page indicator is an inverted block instead of a 1px underline. (build 96)

The underline sat on the bottom row of the panel, where it was hard to tell from the panel edge and
near-impossible to place among six identical icons at a glance. The active icon is now knocked out
of a filled block — the same idiom as the inverted title bar, so the top and bottom of the frame
read alike.

The title bar names the page, not the node. (build 98)

It carried the node's own name on every page — the one thing that never changes — while the
sub-views, which you can get lost in, had no label at all. It now reads Messages, Recent,
Contacts, Radio, Traffic, Spectrum, GPS, Compass, Battery or Settings.

The clock face is the exception: the giant clock already says which page it is, so the bar carries
the node name there. That is also the page the device rests on, so the name stays glanceable
without changing pages.

The Nodes page loses its Recent/Contacts mode row. (build 98)

The title bar names the list now, so the row was saying it twice and costing a row of a four-row
viewport. With it gone the switch needed a home, and every gesture on that page was already spoken
for. The one that freed up cleanly was Enter — the send-advert chooser it used to open also lives
on the Radio page, reachable in the same two keypresses, so moving it off Nodes costs nothing:

  • Not in the list — Up/Down switches Recent ↔ Contacts, the same gesture that switches the
    sub-view on Clock, Radio and GPS. Enter steps into the list.
  • In the list — Up/Down moves the cursor, Enter pings, Back steps back out.

The page now behaves like every other page when idle, and like a list once you are in one.


Still open

  • The SSD1309 back-button report — Back on the message-reading screen sleeps the display instead
    of navigating out — is not fixed here. The input-latch mechanism that looked like the culprit
    turned out to be inert on every OLED build, so that lead is closed. The symptom means the key
    reaches the screen and is not consumed, so the reading screen's own cancel handling is where to
    look next.
  • No add or remove action on contacts. Auto-add is on by default, so heard nodes are already
    saved, and the recently-heard table keeps only a 7-byte key prefix — not enough to encrypt to
    anyone. Manual promotion would need the full 32-byte key retained (~400 bytes).

Memory

Build Flash RAM
Companion (SH1106 OLED) 468 KB / 692 KB (67.6%) 150 KB / 230 KB (65.4%)
Companion (e-ink) 488 KB / 692 KB (70.6%) 154 KB / 230 KB (67.1%)
Companion (DV1 / SSD1309) 475 KB / 692 KB (68.6%) ...
Read more

Wio L1 Pro build 97 (archive)

Pre-release

Choose a tag to compare

@cvhviz cvhviz released this 10 Oct 19:08

Archived from the old WioL1Pro-CVHBuild repository (first published 2026-09-05). These are the Wio Tracker L1 Pro builds from before Cairn; current builds are in the latest Cairn release.

CVHBuild 97 — messages that survive a reboot, and a Nodes page

A Seeed Wio Tracker L1 Pro fork of MeshCore
(nRF52840, SH1106 128×64 OLED, 5-way joystick, buzzer, L76K GNSS), on upstream v1.17.1.

Five builds' worth of work since the last release, all cut the same day: messages now survive a
power cycle, messages you send from the device finally appear in its own list, the Recent page
grew into a Nodes page that also browses saved contacts, and the page indicator is legible.

Build 92 was a DV1-only diagnostic image and was never released, so every target moves from
91 straight to 97.

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.


Downloads

File Target
MeshCore-WioL1Pro-CVHBuild97-2026-09-05.uf2 Companion, SH1106 OLED — the main build
MeshCore-WioL1Eink-CVHBuild97-2026-09-05.uf2 Companion, Waveshare 2.13" B/W e-ink
MeshCore-DV1-CVHBuild97-2026-09-05.uf2 Companion, 2.42" SSD1309 OLED on the Grove connector
MeshCore-WioL1RoomServer-CVHBuild97-2026-09-05.uf2 Room server, SH1106 OLED

Flashing: double-tap reset to expose the TRACKER L1 volume and copy the .uf2 onto it. The
"could not copy extended attributes" error is normal, and the volume ejecting itself means the
flash succeeded.

Time the copy. A flash that finishes in about a second with "Device not configured" is a
truncated write and leaves the app bricked — a good flash takes several seconds (≈6 s for the
OLED images, ≈16 s for e-ink). If it was quick, double-tap reset and copy it again.


Added

The un-synced message queue now survives a power-off or reboot. (build 93)

The companion's offline queue holds up to 256 messages that have arrived but that the phone app has
not pulled yet. It lived only in RAM, so switching the node off discarded all of them — including
in the case the queue exists for, a node collecting messages unattended with no app connected.

It is now snapshotted to /offline_q on the way down and restored at the next boot. On the L1 that
file lands on the 2 MB QSPI flash alongside contacts and channels. Records are length-prefixed, so
a queue holding three messages writes a few hundred bytes rather than the whole 45 KB array, and a
write cut short by power loss reads back as fewer messages instead of garbage.

The snapshot is deliberately one-shot — the loader deletes the file after reading it. Queue reads
are destructive (the app takes each message into its own database), so a file left behind would
replay messages the app already had the next time power was lost ungracefully, showing up as
duplicates. Deleting at load means only never-synced messages ever come back.

Hooked into every path that ends in SYSTEM OFF or a reset: the menu power-off and the menu
restart, the app's reboot command, and the serial CLI reboot.

Scope: this survives a clean shutdown, not a battery pull, a brownout or a crash. Closing that
gap needs a debounced write-through, which would put blocking flash I/O in the packet-receive
callback. The device's own on-screen message history is a separate RAM-only list and is still lost
on reboot; only the app-sync queue is restored.


Fixed

A message sent from the device never appeared in its own Messages list. (build 94)

The message store only ever recorded received messages, so a message composed on the device went
out over the radio and left no trace on screen — the conversation showed only the other side of it.

Both send paths now record on success, keyed by the peer rather than by us. That is what threads
it correctly: an outgoing DM stores the recipient's key and name, so it joins that peer's
conversation instead of opening a new one titled with your own node name. Your own messages never
count toward the unread badge, thread rows mark them with >, and the read view titles them
To <peer> rather than showing the peer as if they had sent it.

Sent messages now share the 24-entry ring, so a busy conversation ages received messages out
somewhat sooner than before. That is inherent to showing a conversation rather than an inbox.

The Nodes page age column read 49707d for saved contacts. (build 97)

Caught on hardware the moment the contact list was first shown against a real mesh. The age is
now - last_advert_timestamp in unsigned arithmetic. A node with a drifting clock genuinely
advertises timestamps ahead of yours, and a contact carries whatever that node claimed — so the
subtraction underflowed and wrapped to just under 2^32 seconds, which divides out to about 49707
days. The contacts in question were roughly 3.3 days ahead, exactly the observed wrap.

It could not have shown up on the recently-heard list, where every entry is by definition something
this node received in its own past. Reaching the saved contact list is what made future timestamps
displayable for the first time.

Elapsed time is now clamped at zero, and anything more than a minute ahead of our clock renders as
-, the same dash a never-heard contact gets. Past clock jitter there is no age that can honestly
be stated, and now would have been a confident lie about a node last heard days ago.


Changed

The Recent page is now the Nodes page — recently-heard adverts or saved contacts. (build 95)

Same tab position and icon. The list gains a mode row at the top naming which list is on screen
and how long it is, with Enter switching between them:

> Recent                    7      <- Enter switches to Contacts
  BaseCamp                 2m
  KG7XYZ                  14m
  Repeater-N               1h
                Back:exit Enter:ping

The cursor model extends the old one rather than replacing it. With no cursor, Enter still opens the
zero-hop/flood advert chooser exactly as before. Row 0 is the mode row. Rows 1 and beyond are nodes,
where Enter pings. Each position gets its own hint line — at 128px the annotation row holds about 21
characters, so a combined hint was never going to fit.

The mode row scrolls off once the cursor moves down into the list, so it does not permanently cost a
row of a four-row viewport. The cursor wraps through 1 + count rows, so the mode row stays
reachable and an empty Recent list can still be switched over to Contacts.

The contact list is windowed, not materialised — only the rows about to be drawn are fetched, so
350 contacts cost the same four rows of RAM the recently-heard table already used. Ping needed no
new logic: contacts render into the same row type the advert list uses, so both feed one render path
and one ping. Ages now roll over to days as well, since a contact can be weeks stale where a heard
advert never is.

The active page indicator is an inverted block instead of a 1px underline. (build 96)

The underline sat on the bottom row of the panel, where it was hard to tell from the panel edge and
near-impossible to place among six identical icons at a glance. The active icon is now knocked out
of a filled block — the same idiom as the inverted title bar, so the top and bottom of the frame
read alike.


Still open

  • The SSD1309 back-button report — Back on the message-reading screen sleeps the display instead
    of navigating out — is not fixed here. The input-latch mechanism that looked like the culprit
    turned out to be inert on every OLED build, so that lead is closed. The symptom means the key
    reaches the screen and is not consumed, so the reading screen's own cancel handling is where to
    look next.
  • No add or remove action on contacts. Auto-add is on by default, so heard nodes are already
    saved, and the recently-heard table keeps only a 7-byte key prefix — not enough to encrypt to
    anyone. Manual promotion would need the full 32-byte key retained (~400 bytes).

Memory

Build Flash RAM
Companion (SH1106 OLED) 468 KB / 692 KB (67.6%) 150 KB / 230 KB (65.4%)
Companion (e-ink) 488 KB / 692 KB (70.6%) 154 KB / 230 KB (67.1%)
Companion (DV1 / SSD1309) 475 KB / 692 KB (68.6%) 150 KB / 230 KB (65.4%)
Room server 417 KB / 792 KB (52.6%) 37 KB / 230 KB (16.1%)

Testing

All four environments build and the native suite passes 48/48 (40 core + 8 KISS modem). Every image
was verified to report CVHBuild 97 / 5 Sep 2026 against its firmware.elf.

Build 96 was run on a DV1, which is where the age-column wrap above was caught and fixed. Every
page renders correctly there.

Still unconfirmed: power the node off and back on with unread messages queued and check the app
still pulls them; send a message from the device and check it threads into the conversation; and
scroll a contact list longer than four rows on the Nodes page, since that is where the windowing
arithmetic lives.

Full changelog: see CHANGELOG.md, entries [cvhbuild-v93] through [cvhbuild-v97].

Wio L1 Pro build 91 (archive)

Pre-release

Choose a tag to compare

@cvhviz cvhviz released this 10 Oct 19:08

Archived from the old WioL1Pro-CVHBuild repository (first published 2026-08-28). These are the Wio Tracker L1 Pro builds from before Cairn; current builds are in the latest Cairn release.

CVHBuild 91 — the GPS Eco freeze

A Seeed Wio Tracker L1 Pro fork of MeshCore
(nRF52840, SH1106 128×64 OLED, 5-way joystick, buzzer, L76K GNSS), on upstream v1.17.1.

One fix, and it is the reason nodes were going dark: a node running GPS Eco froze for good at its
first ten-minute re-wake.
Everything else is build 90.

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.


Downloads

File Target
MeshCore-WioL1Pro-CVHBuild91-2026-08-25.uf2 Companion, SH1106 OLED — the main build
MeshCore-WioL1Eink-CVHBuild91-2026-08-25.uf2 Companion, Waveshare 2.13" B/W e-ink
MeshCore-DV1-CVHBuild91-2026-08-25.uf2 Companion, 2.42" SSD1309 OLED on the Grove connector
MeshCore-WioL1RoomServer-CVHBuild91-2026-08-25.uf2 Room server, SH1106 OLED

All four images carry the fix — it is not specific to one panel.

Flashing: double-tap reset to expose the TRACKER L1 volume and copy the .uf2 onto it. The
"could not copy extended attributes" error is normal, and the volume ejecting itself means the
flash succeeded.

Time the copy. A flash that finishes in about a second with "Device not configured" is a
truncated write and leaves the app bricked — a good flash takes several seconds (≈6 s for the
OLED images, ≈16 s for e-ink). If it was quick, double-tap reset and copy it again.


Fixed

GPS Eco stopped killing the node ten minutes in.

EnvironmentSensorManager::start_gps() opened with an unconditional Serial1.end(), on the
reasoning that closing first is harmless whether or not the port was already open. It is not
harmless. This core's Uart::end() has no _begun guard: it clears EVENTS_TXSTOPPED and
EVENTS_RXTO, fires TASKS_STOPRX/TASKS_STOPTX, then spins on

while ( !(nrfUart->EVENTS_TXSTOPPED && nrfUart->EVENTS_RXTO) ) yield();

A disabled UARTE does not execute tasks, so on an already-closed port neither event ever arrives and
the call never returns. yield() is taskYIELD(), so the loop task spins at its own priority for
ever: display frozen or dark, joystick dead, node off the mesh — while the SoftDevice and TinyUSB
keep running on their own tasks, which is why the USB serial port stays enumerated through it and
the node looks asleep rather than crashed. Nothing recovers it; only a reset does.

Eco mode walks straight into it. stop_gps() closes the port at the end of each fix window, and
GPS_ECO_INTERVAL_MS (10 minutes) later the eco loop calls start_gps() — the first eco re-wake
is the hang.
The same path is reachable by hand in any GPS mode: Settings → GPS On → Off → On
freezes on the second On, which is the thirty-second reproduction and the way to confirm the fix on
a flashed device.

Both Serial1.end() calls are now guarded with if (Serial1) — Uart::operator bool() returns
_begun, so the port is only closed when it is actually open.

Present since build 73 (the "GPS UART closes with the GPS" change, 2026-08-13) in every L1
image, not just the SH1106 one. It only fires on a device that is in Eco, or that has had GPS
toggled off and on again — which is why it read as intermittent.


Memory

Build Flash RAM
Companion (SH1106 OLED) 466 KB / 692 KB (67.3%) 150 KB / 230 KB (65.4%)
Companion (e-ink) 486 KB / 692 KB (70.3%) 154 KB / 230 KB (67.1%)
Companion (DV1 / SSD1309) 473 KB / 692 KB (68.3%) 150 KB / 230 KB (65.4%)
Room server 417 KB / 792 KB (52.6%) 37 KB / 230 KB (16.1%)

Testing

All four environments build and the native suite passes 48/48 (40 core + 8 KISS modem). The
mechanism is certain from the core's source rather than inferred from symptoms, but the fix has
not yet been confirmed on hardware
— Settings → GPS On → Off → On is the check to run first.

Full changelog: see CHANGELOG.md, [cvhbuild-v91].

Wio L1 Pro build 83 (archive)

Pre-release

Choose a tag to compare

@cvhviz cvhviz released this 10 Oct 19:08

Archived from the old WioL1Pro-CVHBuild repository (first published 2026-08-15). These are the Wio Tracker L1 Pro builds from before Cairn; current builds are in the latest Cairn release.

CVHBuild 83 — an e-ink that rests, a third panel, and MeshCore v1.17.1

A Seeed Wio Tracker L1 Pro fork of MeshCore
(nRF52840, SH1106 128×64 OLED, 5-way joystick, buzzer, L76K GNSS).

Builds 68–83. The theme is e-paper: the panel now stops refreshing when nobody is reading it, and
it draws values and annotation in different faces so a 250×122 screen reads like one. There is a
third panel option, a 2.42" SSD1309 OLED. The base moves from upstream v1.17.0 to v1.17.1.

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.


Downloads

File Target
MeshCore-WioL1Pro-CVHBuild83.uf2 Companion, SH1106 OLED — the main build
MeshCore-WioL1Eink-CVHBuild83.uf2 Companion, Waveshare 2.13" B/W e-ink
MeshCore-WioL1-1309-CVHBuild83.uf2 Companion, 2.42" SSD1309 OLED
MeshCore-WioL1RoomServer-CVHBuild83.uf2 Room server, SH1106 OLED

Flashing: double-tap reset to expose the TRACKER L1 volume and copy the .uf2 onto it. The
"could not copy extended attributes" error is normal, and the volume ejecting itself means the
flash succeeded.

Upgrading from CVHBuild 67 keeps your settings — the preferences format is unchanged across this
span. One room-server nuance is noted under Upstream v1.17.1.

The tri-colour e-ink variant is no longer produced. Every refresh on a three-colour panel is a
blocking ~15 s full flash, which makes live pages snapshots rather than displays. The environment
and its EINK_3C paths remain in the tree and still build.


The e-ink panel finally rests

E-paper holds its image with no power, so the cost of a screen is not being on — it is
refreshing. Four separate things were refreshing an idle panel, each for its own reason, and none
of them was the screen-timeout setting:

  • The Radio page redrew every 30 s forever. Spectrum sampling lives inside render(), so one
    column is one redraw. That changed the frame, which defeated the driver's CRC gate, which cost a
    refresh on a page nobody was reading. Sampling is now gated on recent input and the readouts
    hold their captured values, so the frame goes byte-identical and the refresh is skipped.
  • The clock asked for a redraw on every minute boundary before consulting the timeout — the
    comment said "always". That was the periodic refresh that could not be switched off.
  • The message preview repainted every 10 seconds, ungated. It appears by itself when a message
    lands and never dismisses itself, and drew a seconds-resolution age — six refreshes in the first
    minute, then one a minute for as long as it stayed up.
  • The screen timeout was compiled out entirely on e-ink, on the reasoning that there is no
    panel to blank. On e-paper that is the wrong question. One predicate, isScreenLive(), now
    governs both builds: live when the timeout is Never, live while plugged in with keep-on-USB set,
    otherwise live until the timeout expires since the last input. It replaced three hardcoded
    60-second windows, so a bench node on USB keeps a live screen and a pocket node goes quiet.

The clock says SLEEPING rather than freezing. Simply stopping the clock would have been
worse than refreshing it: e-paper holds the last thing drawn, so a clock that stops ticking does
not go quiet, it goes wrong, and drifts further from the truth every minute it sits there.
SLEEPING costs one refresh to put up, after which every frame is identical and the CRC gate
suppresses all of them. Any key brings the real clock back on the next frame.

One honest caveat: the status bar is still drawn while asleep, so an arriving message can still
change the unread badge and cost a refresh. That is wanted — the count beside SLEEPING is
useful — but a busy mesh is not perfectly silent.


E-ink typography

The e-ink build draws the same 128×64 logical canvas as the OLED, so the bigger panel makes
everything larger, not roomier. What buys space is that its font is proportional and narrower
per glyph than the OLED's fixed 6 px advance.

  • Values are drawn in bold, labels and units in a smaller face. Differentiating by weight
    turned out to be the better lever: FreeSansBold9pt7b shares its metrics and its origin with the
    regular face, so there is no second baseline to reconcile and no risk of a value overrunning its
    column. There is no font one point above the built-in 6×8 cell — the whole bundled ladder below
    9pt is Tiny3x3, TomThumb, Picopixel and Org_01, all smaller — so an intermediate size would
    have meant generating and bundling a 7pt face. Bold sidesteps the question.
  • The treatment runs across the whole UI, not just the Radio page: clock and battery, GPS and
    compass, the message list, thread, read view, quick reply and both compose stages, contacts,
    settings and telemetry. Anything that names or qualifies a value — counters, ages, column
    headers, keybind hints, units — drops to the small face, and column values right-justify onto a
    shared rule.
  • Units get a reserved column the width of the widest unit on the page, rather than being
    appended. MHz, kHz, dBm and dB are all different widths, so appending would push each
    row's digits left by a different amount and unstack the column that reserving keeps aligned.
  • Header badge digits shrank. They annotate 7-row icons but were drawn in the value font,
    standing a head taller than the glyph they belong to.
  • The read-view footer moving to the small cell is a clipping fix as well as a restyle: at 9pt
    a glyph on row 56 descends past row 64.

Every one of these helpers carries a guarded contract — off e-ink, or on a board without a usable
small face, each is exactly the call it replaced, so the OLED build is byte-for-byte unaffected.


New: 2.42" SSD1309 build

WioTrackerL1_1309_companion_radio_ble drives a 2.42" SSD1309 OLED. It wears the e-ink
typography while behaving in every other respect like an OLED — isEink() stays false, so
blanking, brightness, the fast refresh cadence and every non-e-ink path apply unchanged.

That combination forced a real fix: the UI had been gating its layout on
UI_HAS_SMALL_FACE && isEink(), which conflates "this panel has a second, smaller face" with
"this panel is e-paper". Four defaulted DisplayDriver virtuals now separate them —
hasSmallFace(), smallFaceDrop(), smallFaceGap() and hasBoldFace(). Bold is a separate
capability from small precisely because this panel has one and not the other.

What the 128×64 panel would and would not take:

  • TomThumb (3×5) for annotation, chosen over Picopixel and Org_01 by pixel-identity test across
    all 62 alphanumerics: Picopixel collides 0/O, and Org_01 collides 0/O, 1/l, 2/Z and
    5/S — on a page printing SF 5 and CR 2, that matters.
  • No bold. Emulating weight by 1-px dilation lands on the right ink density but pushes 60 of 68
    glyphs into their single inter-glyph gap — RSSI becomes one continuous bar — and 20 lose their
    counters. At 5×7 the stem, counter and sidebearing are each already one pixel wide.
  • Baseline drop is 2 here rather than the e-ink's 3, and the unit gap is 0 rather than 2, because
    the fixed 6-px cell already carries a blank trailing column that a proportional face does not.

Upstream v1.17.1

Everything in upstream's release. What reaches this hardware:

  • Fixed upstream: replies were dropped when flood.max.unscoped was low. A scoped reply was
    being held to the unscoped hop limit, so on a mesh configured to damp unscoped flooding, the
    answer to a request could die before reaching the asker. Reply-routing moved into a shared
    RoutingPolicy.h with 14 unit tests behind it — the native suite goes from 33 cases to 40.
  • The nRF52 hardware crypto engine is initialised once, at board startup, instead of on every
    call; nRFCrypto.begin()/.end() used to bracket each individual hash, encrypt and decrypt.
  • The CC310 random number generator is mixed with the radio's entropy rather than trusted alone.
  • FEM gain preferences arrive for boards with an external front end, and they land differently
    in the two NodePrefs. The companion's copy has them #if 0'd upstream until they can actually
    be set, so it writes nothing new; the shared prefs used by the room server, repeater and sensor
    carry both keys live. Your ui settings are untouched either way — the serialiser is named-key,
    so added keys are additive.
  • Fixed upstream: fem_rxgain was aliased to the wrong field in the shared prefs. The key
    claimed to describe the external front end while actually reading and writing the SX126x
    boosted-gain setting. If you run the room server, note that it stops applying a stored
    fem_rxgain to rx_boosted_gain — the correctly-named rxgain key still carries that, and both
    had been written with the same value, so your setting survives.

On this hardware the new FEM calls are no-ops regardless — virtuals on mesh::MainBoard defaulting
to false, which WioTrackerL1Board overrides neither of.

The merge itself was cheap: one conflict, and not a disagreement — upstream added its two FEM gain
defaults at exactly the point this fork sets its five UI prefs defaults. Both blocks kept.


Security

Defects in the upstream v1.17.0 base, fixed here and still unfixed upstream:

  • Utils::decrypt rejects a ciphertext length that is not a multiple of the...
Read more

Wio L1 Pro build 67 (archive)

Pre-release

Choose a tag to compare

@cvhviz cvhviz released this 10 Oct 19:08

Archived from the old WioL1Pro-CVHBuild repository (first published 2026-08-10). These are the Wio Tracker L1 Pro builds from before Cairn; current builds are in the latest Cairn release.

CVHBuild 67 — now on MeshCore v1.17.0, plus a room server build

A Seeed Wio Tracker L1 Pro fork of MeshCore
(nRF52840, SH1106 128×64 OLED, 5-way joystick, buzzer, L76K GNSS).

Rebased from upstream v1.16.0 onto v1.17.0 (320 upstream commits), and adds a room server
build that uses the same paged UI as the companion instead of a single static screen.

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.


Downloads

File Target
MeshCore-WioL1Pro-CVHBuild67.uf2 Companion, SH1106 OLED — the main build
MeshCore-WioL1Eink-CVHBuild67.uf2 Companion, Waveshare 2.13" B/W e-ink
MeshCore-WioL1RoomServer-CVHBuild67.uf2 Room server, SH1106 OLED

Flashing: double-tap reset to expose the TRACKER L1 volume and copy the .uf2 onto it. The
"could not copy extended attributes" error is normal, and the volume ejecting itself means the
flash succeeded.

The tri-colour e-ink variant is no longer produced. Every refresh on a three-colour panel is a
blocking ~15 s full flash, which makes live pages snapshots rather than displays. The environment
and its EINK_3C paths remain in the tree and still build.


New: room server build

WioTrackerL1_room_server previously drew one static screen — node name, frequency, bandwidth —
with no pages and no joystick handling, even though the hardware inputs were already wired up for
any build with a display. It now has five pages, chosen for what a server that sits in one place
actually has rather than by copying the companion:

Page Contents
Clock clock face, date, client count; sub-view for battery, uptime and post counters
Posts the 32 stored posts, scrollable, Enter opens the full text word-wrapped
Clients the ACL — key, admin flag, hops, unsynced posts, last seen; Enter opens a detail view
Radio freq/BW/SF/CR grid, live RSSI spectrum, traffic monitor, fullscreen spectrum
Settings advert, GPS, set clock, TX power, brightness, screen timeout, clock style

Left/Right changes page, Up/Down scrolls or switches sub-view, Enter acts, hold Back locks.

Setting the clock. A room server has no companion app to push the time down, so previously the
only route was time <epoch> over the serial or admin CLI. There is now a GPS on/off setting —
the NMEA provider is constructed with the RTC, so a fix sets the clock — and a manual editor with
year/month/day/hour/minute cells for when there is no fix. Time is entered as local and stored as
UTC.

Identities are public-key prefixes (e.g. A3F2). The room server stores no names: neither
ClientInfo nor PostInfo has one, so that is the whole of what the device knows. The client
detail view shows eight bytes when you need to identify a specific node.


Upstream v1.17.0

Everything in upstream's release, plus these fork-specific consequences:

  • Preferences moved to named-key JSON. Upstream replaced the byte-offset preferences struct
    with a serialiser. This fork's eight UI settings moved into their own ui group, and the legacy
    reader still parses them at their former offsets — so a device upgrading from CVHBuild 56
    keeps its settings
    , and the first save migrates the file.
  • Colours come from a semantic palette rather than an abstract enum the driver reinterpreted.
    No visible change on the OLED; on e-ink it removes the light/dark inversion hack, because the
    palette carries the inversion instead.
  • Fixed: the contact list dropped its last eight entries. Upstream reserved eight slots at the
    head of the contact array and made getNumContacts() exclude them, while getContactByIdx()
    still indexes the raw array. Three loops here paired the two, so they walked eight reserved slots
    at the start and stopped eight short of the end — silently hiding the most recent contacts from
    the compose picker, the recipient count and room re-login.

Security

  • The destination bound on Utils::decrypt() now also covers upstream's new CC310 hardware AES
    path. Both write whole 16-byte blocks, so both can overrun a short destination; the check sits
    above the branch rather than inside one arm of it. Upstream still carries no bound of its own.
  • Upstream independently fixed the BLE receive-queue race, which arrives with this merge.

Upstream defects found

Both break the build only on macOS/libc++, which is why upstream's Linux CI is green:

  • ConfigSerializer.cpp calls atoi/atol/atof without including <stdlib.h>.
  • test/mocks/Utils.h declares MACThenDecrypt() with the old unbounded signature.

E-ink companion

  • The clock face is drawn, not typed. 24pt was the largest font available and pixel-doubling a
    1-bit face stair-steps every edge, so the digits are now seven-segment shapes built from
    rectangles: crisp at any size, and no flash cost. 46 panel rows tall, against 25 before.
  • The clock page became a resting screen. A reflective panel is on permanently and refreshes
    about once a minute, so it now carries what you would otherwise change pages to read: date and
    uptime with battery percentage and voltage, then unread and node counts with GPS state and die
    temperature. Each row measures itself and drops its least important token rather than letting
    two strings overlap.
  • Fixed: auto lock never engaged. It was implemented inside the screen-blanking path, and
    e-paper never blanks — it holds its image with no power — so the whole block was compiled out
    while the setting still read On. Locking is about a device in a pocket, not about the panel.
  • The screen timeout and keep-awake-on-USB settings now read n/a where nothing blanks, instead
    of accepting values that nothing acted on.

Memory

Build Flash RAM
Companion (OLED) 467 KB / 692 KB (67.5%) 150 KB / 230 KB (65.3%)
Companion (e-ink) 483 KB / 692 KB (69.9%) 154 KB / 230 KB (67.1%)
Room server 416 KB / 792 KB (52.5%) 37 KB / 230 KB (16.1%)

Testing

The OLED companion and the B/W e-ink companion are running on hardware. The room server has been
run on hardware and its Clock, Radio and Settings pages exercised; its Posts and Clients pages
cannot be fully validated until real clients log in and post
, so those layouts are verified
arithmetically rather than visually. The tri-colour e-ink variant has never been run.

Upgrading from CVHBuild 56 migrates the preferences file on first save. The old file is left in
place, so reflashing 56 gets you back to a device that reads the original.

Full changelog: see CHANGELOG.md, builds 57–67.

Wio L1 Pro build 56 (archive)

Pre-release

Choose a tag to compare

@cvhviz cvhviz released this 10 Oct 19:08

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. 000000 means 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_PIN and the set pin serial 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 NodePrefs byte 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 reads n/a on 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 single sprintf(), so
    BW 250.00 versus BW 7.80 shifted CR sideways.
  • 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() accepts 0–999999 rather than 0 or 100000–999999. A BLE passkey is a
    six-character field, so 012345 is 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 before powerOff(), 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_Y was 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-tuned EINK_Y_OFFSET that 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_H report the canvas the UI was drawn for, so anything
    sizing itself off height() — 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 msg setting — 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 off is now Screen, 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 nor MACThenDecrypt() knew how large the destination was.
    Both now take a dest_size and refuse to write when the blo...
Read more

Wio L1 Pro build 40 (archive)

Pre-release

Choose a tag to compare

@cvhviz cvhviz released this 10 Oct 19:08

Archived from the old WioL1Pro-CVHBuild repository (first published 2026-08-06). These are the Wio Tracker L1 Pro builds from before Cairn; current builds are in the latest Cairn release.

Initial Release. Please view the ReadMe