The console keeps its stock kernel palette - black background, classic PC hues - instead of the custom setvtrgb remap. MESH_THEME_16 is re-tuned against the stock slots, which hands back the map's own language for free: clients wear bright pink (13), repeaters purple (5), rooms cyan (6), sensors brown (3). The cost is one dark grey - muted, faint and track all collapse onto slot 8. Heat quantizes on plain human units (15 min / 1 h / 1 day). The custom design is archived, not deleted: theme._VT_SLOTS_CUSTOM + MESHTERM_CUSTOM_PALETTE=1 on the font script reinstalls it, with the parity test keeping the two in sync. Undoing the remap on the device exposed a trap: setvtrgb overwrites the kernel's DEFAULT colormap, so the OSC palette-reset "resets" straight back to the custom values - the script's default mode now programs the stock palette explicitly (verified live; specimen screenshot in .claude/plans/p3-specimen-stock.png). The header also follows JP's spec: the handheld brands the app - "MeshTerm vX" - instead of naming a soldered port that never changes, and the activity pulse takes whatever room remains. Also root-causes the intermittent suite flake: Rich memoizes a parsed Style's ANSI codes on first render (Style.parse is globally cached), so a raw hex style first rendered by a truecolor console replays 38;2 verbatim on a standard console later in the process. The specimen's nodes row now wears the type.* theme styles - which is also just correct: the marks now match the names row. Six consecutive clean full-suite runs. Deferred at JP's request: the glyph substitution map (he will redo it), the F-key lane design (awaits his explicit spec), the CLI fold gap. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
scripts
Device-side helpers for running MeshTerm on real hardware. Independent, self-contained targets live here:
- Calculinux device (Luckfox Lyra / ClockworkPi PicoCalc) — one-time bring-up and the console font.
- XIAO nRF52840 UART radio (
xiao-radio/) — wire a Seeed XIAO + Wio-SX1262 to the Lyra's hardware UART and run MeshTerm against it. Full build → flash → setup guide with scripts. This is the way to give a PicoCalc a real MeshCore radio. - SPI-attached radio bridge (
meshterm-spi-bridge) — expose a bare SPI LoRa chip to MeshTerm as if it were a companion device (uConsole AIO, Waveshare HATs).
Calculinux device (Luckfox Lyra / ClockworkPi PicoCalc)
Helpers for running MeshTerm on a Luckfox Lyra / ClockworkPi PicoCalc under Calculinux (the bare-framebuffer terminal target). They run on the device as root, not on your dev machine.
| Script | What it does |
|---|---|
calculinux-setup.sh |
Complete one-time bring-up: opkg python packages, the Wi-Fi boot-scan kick, the deploy user, the read-only clone, the venv + pip install -e ., the login PATH, and the console font. Idempotent. |
calculinux-console-font.sh |
Just the console font: builds/installs/persists the 512-glyph meshterm PSF (Terminus + braille + node glyphs + rounded frame corners + the list cursor). Called by the setup script; also runnable on its own after a MeshTerm update. |
Usage
Copy the scripts/ folder to the device and, as root:
sh calculinux-setup.sh
Two prerequisites the setup script checks for but cannot embed:
- Deploy key — place the read-only GitHub deploy key at
$HOME/.ssh/id_ed25519(mode 600) before running; the clone uses it over SSH. - Wi-Fi credentials — the kick only nudges a network iwd already knows. Either connect
once by hand (
iwctl station wlan0 connect <SSID>) or exportWIFI_SSIDandWIFI_PSKso the script writes the iwd config (secrets stay in your environment, never in the repo).
Both scripts are POSIX sh, idempotent, and safe to re-run.
XIAO nRF52840 UART radio — give a PicoCalc a real radio
A Seeed XIAO nRF52840 + Wio-SX1262 wired to the Luckfox Lyra's hardware UART1, running
MeshCore companion firmware over Serial1 (D6/D7). MeshTerm talks to it directly on
/dev/ttyS1 — no bridge process. Everything (parts, wiring, firmware build/flash, device
setup, the why) is in xiao-radio/README.md, with scripts for each step:
| Script | Runs on | What it does |
|---|---|---|
xiao-radio/build-firmware.sh |
dev machine | clone + patch MeshCore, build the _usb env, emit the .uf2 |
xiao-radio/flash.py |
dev machine | flash the .uf2 to the XIAO via its UF2 bootloader |
xiao-radio/lyra-setup.sh |
the Lyra (root) | boot-persistent UART1→GP4/GP5 mux service, dialout, default MeshTerm profile |
xiao-radio/uart1-mux.py |
the Lyra | the matrix-IO register poke that routes UART1 to the header pads |
Two things this depends on, both handled by the patch/guide: the firmware must bind the
companion to Serial1 and move I²C off D6/D7 (MeshCore maps it there and it steals the
UART pins); and MeshTerm must include the platform-UART liveness fix (serial_port_present
treats a soldered /dev/ttyS1 as present).
meshterm-spi-bridge — expose an SPI-attached radio
Some boards wire the LoRa chip straight to the host's SPI bus — the ClockworkPi
uConsole with the hackergadgets AIO, Waveshare LoRa HATs, and similar. There is
no companion microcontroller and no firmware to flash: the mesh node runs in
software on the host via the pymc_core
library (on the uConsole this arrives with the meshcore-uconsole apt package,
which also ships the meshcore-console GUI).
MeshTerm, however, speaks the MeshCore companion protocol over serial / BLE /
TCP and expects to talk to a node, not to drive a bare radio. This bridge fills
the gap: it runs the pymc_core node itself and puts the standard companion
protocol in front of it on a local TCP port, so that
meshterm --tcp 127.0.0.1:5000
connects exactly as if a real companion device were plugged in.
Usage
Copy meshterm-spi-bridge onto the radio host and run it with any Python 3 —
it finds the pymc_core runtime and re-execs the bridge under it for you:
./meshterm-spi-bridge # interactive setup menu
./meshterm-spi-bridge --check # run prerequisite checks and exit
./meshterm-spi-bridge --run # run the bridge now, in this terminal
The menu offers:
- Run now — runs in the foreground; Ctrl+C stops it.
- Install as a service — a
systemd --userunit that starts on boot and restarts on failure, running as you (so it uses your node identity). - Uninstall — removes only the bridge service + its helper copy. It never
touches the
meshcore-console/pymc_corepackages.
Caveats
- Only one program may use the radio at a time. Run either the
meshcore-consoleGUI or this bridge — never both. The bridge preflight warns you when the GUI is running. - The bridge and MeshTerm are two cooperating processes; keep the bridge running while you use MeshTerm.
- Same identity as the GUI: when the
meshcore-consolestack is present, the bridge loads the node key from~/.local/share/meshcore-uconsole/identity.key, so MeshTerm sees the same node. On a genericpymc_core-only install it mints and persists its own key under~/.local/share/meshterm-spi-bridge/. - Trace is not wired yet. Messaging, contacts, adverts and config work today; the node's trace / path-discovery replies aren't yet pushed to the companion server, so MeshTerm's Trace screen won't populate until that's added.
Overriding hardware pins
Defaults match the hackergadgets uConsole AIO. For other SPI boards, set env
vars before launching (read by the direct-pymc_core path):
MESHCORE_BUS_ID, MESHCORE_CS_ID, MESHCORE_RESET_PIN, MESHCORE_BUSY_PIN,
MESHCORE_IRQ_PIN, MESHCORE_FREQUENCY, MESHCORE_TX_POWER,
MESHCORE_SPREADING_FACTOR, MESHCORE_BANDWIDTH, MESHCORE_CODING_RATE. To
force a specific interpreter, set MESHTERM_PYMC_PYTHON.