Offband Mesh · Releases
from GitHub · updated 2026-07-2117 releases
v1.2.0 # 15 days ago · 2026-07-21 06:48 UTC
Adds MeshSmith Photon‑1W support (both MCU flavors) on the MeshCore 1.16.0 base, and receive-sensitivity recovery on Heltec V4 (external FEM LNA control, which stock MeshCore leaves bypassed), plus the ESP32‑C6 I2C‑scan boot‑hang fix found bench‑validating it, a NimBLE build fix, and flashing‑docs corrections.
Added
- MeshSmith Photon‑1W support — ESP32‑C6 + nRF52 (#193, #194) — vendors MeshSmith's MIT Photon
variants (
meshsmith_photon_esp32c6,meshsmith_photon_nrf52; Seeed XIAO ESP32‑C6 / XIAO nRF52840 + Ebyte E22‑900M30S 1W radio) and wires all roles into CI + the release pipeline. ESP32‑C6 needed minimal MeshCore‑consistent base edits (antenna‑switch virtuals, protected_gps_serial, NimBLE dep); nRF52 needed none. The ESP32‑C6 companion + repeater are bench‑verified on hardware; the nRF52 variant is not yet bench‑verified — review findings tracked as bench checkpoints on #193/#194. - External FEM LNA control on Heltec V4 companions (#298). MeshCore leaves the Heltec V4
front-end module's LNA bypassed at boot, so V4 companions ran with degraded receive
sensitivity. Offband now enables it at companion boot from a persisted
radio_fem_rxgainpreference (default on), on the boards whose FEM exposes an independent LNA line (heltec_v4,heltec_tracker_v2,heltec_t096). A new companion-API command (0xC3, SET/GET) plus a capability bit lets the client show a user toggle, gated on the runtime-detected FEM chip.FIRMWARE_VER_CODE15 -> 16. Verified end-to-end on a KCT8103L (V4.3) board. - Model string names the detected FEM part (#327) — reads
Heltec V4 OLED (KCT8103L)or(GC1109), so a V4.3 (independent LNA control) is distinguishable from a V4.2 in the app.
Fixed
- Photon‑1W ESP32‑C6 hung at boot, dead on the mesh (#294). The C6 variant declares its I2C bus,
but
EnvironmentSensorManager::begin()ran the blind I2C scan regardless; on the C6 the scan wedges the I2C peripheral (hangs at addr0x0d) and never returns, sosetup()never reachedloop(). The scan is now skipped on boards that setENV_SKIP_I2C_SENSOR_SCAN(guard vendored verbatim from MeshSmith's fork); every other board is unchanged. - CLI accepted garbage after a valid key (#299).
get radio foobarreturned the radio settings, andset radio 910.525,62.5,7,5,junksilently applied the first four values and dropped the rest. Keys now require a whole-token match; extraset radioparameters are rejected. - 64 ESP32 BLE companion environments could not build (#199, #90). The NimBLE dependency was
declared per-variant with no shared source, and the greedy source filter pulled the BLE
interface into non-BLE (
usb/wifi) builds too. Factored into a shared config; restores boards silently absent from prior releases, includingHeltec_v2_companion_radio_usbandXiao_C3_companion_radio_usb. A CI invariant now guards every ESP32 env. (The #89 fix below is the first, single-env instance of this class.) - FEM auto-detect source comment corrected (#318, #321). The Heltec V4 FEM type is set by a
board strap, not chip-internal pulls as the code claimed; documented against schematics + the
GC1109 datasheet. A boot detection probe is available behind
-D FEM_DEBUG_PROBE(off by default). pio-flashidentified devices by USB port-path, not identity (#336, #323). Boards were misidentified after any USB port or device swap, and a no-discriminator registry entry wildcard-matched its whole chip family. Now keys on the device-unique USB serial and refuses ambiguous matches; hardware-verified across colliding same-VID:PID boards. Bench tooling, not shipped firmware.pio-flashbootstrap parses the ESP32-C6/H2 base MAC and anchors its MAC regexes (#290, #292). Bench tooling.Xiao_S3_WIO_companion_radio_usbbuild (#89) — excludeSerialBLEInterface.cppfrom the USB companion env (it has no BLE), resolving the `NimBLEDe …
- MeshSmith Photon‑1W support — ESP32‑C6 + nRF52 (#193, #194) — vendors MeshSmith's MIT Photon
variants (
v1.1.2 # 1 month ago · 2026-07-03 00:31 UTC
Companion GPS auto-baud + on-demand GPS status, plus the wedge/time fixes found while validating it on real hardware, and a safer first-flash default — a fresh Observer no longer auto-publishes to a preset MQTT broker. Still on the MeshCore 1.16.0 base.
Added
- GPS auto-baud detection — the companion now detects the GPS modem's baud rate at runtime (probing 115200 then 9600, validating by a checksum-good NMEA sentence), so one image reads either a standard 9600 module or a 115200 one with no rebuild. It re-probes on a GPS enable and keeps trying if a modem is slow to start at boot. (#216, #233)
- On-demand GPS status to the app (
0xC1) — the client can query live GPS state (enabled / detected / fix / baud / lat / lon / alt / sats / time) instead of only seeing position at connect. (#216) - On-device build identity — an optional build tag shows on the app device-info field and the OLED splash, so a specific build is identifiable without a serial console. (#222)
Changed
- Fresh flashes no longer auto-publish to OKIMesh CoreScope. A newly-flashed Observer previously seeded the CoreScope (Dayton) broker enabled, so a device flashed anywhere in the world immediately fed MQTT to OKIMesh CoreScope tagged as a Dayton node. On a fresh flash every broker now ships disabled — the operator opts in per slot. (#262)
- Default region is now
XYZ(a non-geographic placeholder) instead ofHAO(Dayton), so an out-of-region device stops mislabeling itself until the operator sets its region viamqtt iata. Fresh / NVS-erased devices only; existing devices keep their stored config. (#262)
Fixed
- GPS at high baud could wedge BLE. An unbounded GPS read loop let a fast modem (e.g. 115200, multi-constellation) monopolize the main loop and starve BLE — the app would slog or stall. GPS ingestion is now bounded per loop. (#231)
- GPS time showed garbage before the date was acquired. A position fix can arrive
before the calendar date; the device now reports
time=0("acquiring") until a real date is parsed, instead of a wrapped-garbage timestamp. (#232)
Internal
- Build/governance tooling + GPS design & diagnostic records — no firmware-behavior change. (#214, #217–#219)
Which file do I download?
File What it is When to use it *-merged.bin(ESP32 — Heltec V3/V4, XIAO)Full image — bootloader + partition table + app in one, flashed at 0x0after a chip erase. Self-contained, works on a blank chip.First install / clean setup. In a web flasher this is the "Full Firmware" option. *.bin(ESP32)App only — flashed at the app offset ( 0x10000); the bootloader must already be on the chip.Updating an existing node — OTA / "Update Only." Keeps the device identity + WiFi/MQTT config. *.uf2(nRF52 — RAK, T-Echo, XIAO nRF52)Complete self-contained image. First install and updates — double-tap reset, then drag-drop onto the USB drive. (nRF52 has no merged/app split.) ⚠️ ESP32: the app-only
*.binwill not boot if flashed at0x0— use*-merged.binfor a fresh install. A full erase / "Full Firmware" wipes the device's identity + saved config, so use it only for a first install or recovery, never a routine update.What's Changed
- fix(#239): round CLI frequency output to 3 decimals (ftoa3) by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/240
- docs(#244): block-user firmware<->app architectural contract by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/245
- feat(#241): Block/Ignore Users — firmware half (BlockStore + 0xC2 sync + DM-drop) by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/247
- chore: sync pre-commit to canonical (§0.6) + pin DW_PROJECT=Crosswire by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/249
- fix(#251): pio-flash ESP32-S3 USB-Serial/JTA …
v1.1.2-rc1 # Pre-release 1 month ago · 2026-06-28 08:18 UTC
Companion GPS auto-baud + on-demand GPS status, plus the wedge/time fixes found while validating it on real hardware. Still on the MeshCore 1.16.0 base.
Added
- GPS auto-baud detection — the companion now detects the GPS modem's baud rate at runtime (probing 115200 then 9600, validating by a checksum-good NMEA sentence), so one image reads either a standard 9600 module or a 115200 one with no rebuild. It re-probes on a GPS enable and keeps trying if a modem is slow to start at boot. (#216, #233)
- On-demand GPS status to the app (
0xC1) — the client can query live GPS state (enabled / detected / fix / baud / lat / lon / alt / sats / time) instead of only seeing position at connect. (#216) - On-device build identity — an optional build tag shows on the app device-info field and the OLED splash, so a specific build is identifiable without a serial console. (#222)
Fixed
- GPS at high baud could wedge BLE. An unbounded GPS read loop let a fast modem (e.g. 115200, multi-constellation) monopolize the main loop and starve BLE — the app would slog or stall. GPS ingestion is now bounded per loop. (#231)
- GPS time showed garbage before the date was acquired. A position fix can arrive
before the calendar date; the device now reports
time=0("acquiring") until a real date is parsed, instead of a wrapped-garbage timestamp. (#232)
Internal
- Build/governance tooling + GPS design & diagnostic records — no firmware-behavior change. (#214, #217–#219)
Which file do I download?
File What it is When to use it *-merged.bin(ESP32 — Heltec V3/V4, XIAO)Full image — bootloader + partition table + app in one, flashed at 0x0after a chip erase. Self-contained, works on a blank chip.First install / clean setup. In a web flasher this is the "Full Firmware" option. *.bin(ESP32)App only — flashed at the app offset ( 0x10000); the bootloader must already be on the chip.Updating an existing node — OTA / "Update Only." Keeps the device identity + WiFi/MQTT config. *.uf2(nRF52 — RAK, T-Echo, XIAO nRF52)Complete self-contained image. First install and updates — double-tap reset, then drag-drop onto the USB drive. (nRF52 has no merged/app split.) ⚠️ ESP32: the app-only
*.binwill not boot if flashed at0x0— use*-merged.binfor a fresh install. A full erase / "Full Firmware" wipes the device's identity + saved config, so use it only for a first install or recovery, never a routine update.What's Changed
- fix(hooks): track canonical commit-binding hooks so worktrees resolve the right task (#213) by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/214
- Epic #216: Companion GPS autobaud + 0xC1 + wedge/time fixes by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/234
- fix(#233): autobaud re-probes (no 9600 give-up) + CHANGELOG [1.2.0] by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/236
- chore(#237): retitle CHANGELOG [1.2.0] -> [1.1.2] for the GPS release by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/238
Full Changelog: https://github.com/OffbandMesh/meshcore-firmware/compare/offband-v1.1.1...offband-v1.1.2-rc1
v1.1.1 # 1 month ago · 2026-06-26 05:24 UTC
Urgent patch: the RAK3401 1W companion radio was dead on v1.1.0. Still on the MeshCore 1.16.0 base.
Fixed
- RAK3401 1W companion radio dead on Offband (no TX/RX). Offband's GPS support (#104) probed a pin that doubles as the radio's power-enable on the RAK3401; with no GPS attached it left the radio unpowered, so it couldn't transmit or receive. GPS is now disabled on the RAK3401 companion build until the probe is fixed. (#211)
Which file do I download?
File What it is When to use it *-merged.bin(ESP32 — Heltec V3/V4, XIAO)Full image — bootloader + partition table + app in one, flashed at 0x0after a chip erase. Self-contained, works on a blank chip.First install / clean setup. In a web flasher this is the "Full Firmware" option. *.bin(ESP32)App only — flashed at the app offset ( 0x10000); the bootloader must already be on the chip.Updating an existing node — OTA / "Update Only." Keeps the device identity + WiFi/MQTT config. *.uf2(nRF52 — RAK, T-Echo, XIAO nRF52)Complete self-contained image. First install and updates — double-tap reset, then drag-drop onto the USB drive. (nRF52 has no merged/app split.) ⚠️ ESP32: the app-only
*.binwill not boot if flashed at0x0— use*-merged.binfor a fresh install. A full erase / "Full Firmware" wipes the device's identity + saved config, so use it only for a first install or recovery, never a routine update.What's Changed
- docs(#192): Photon-1W (MeshSmith) support design-of-record by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/195
- chore(#197): CLAUDE.md -- record no-upstream-merge policy by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/198
- fix(#89): exclude SerialBLEInterface.cpp from Xiao_S3_WIO USB companion by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/200
- fix(rak3401): companion GPS probe floats WB_IO2, killing the 1W radio (#211) by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/212
Full Changelog: https://github.com/OffbandMesh/meshcore-firmware/compare/offband-v1.1.0...offband-v1.1.1
v1.2.0-beta1 # Pre-release 1 month ago · 2026-06-25 04:37 UTC
Adds MeshSmith Photon‑1W support (both MCU flavors) plus a NimBLE build fix, on the MeshCore 1.16.0 base. Shipping in
-betauntil the Photon‑1W is bench‑verified on hardware.Added
- MeshSmith Photon‑1W support — ESP32‑C6 + nRF52 (#193, #194) — vendors MeshSmith's MIT Photon
variants (
meshsmith_photon_esp32c6,meshsmith_photon_nrf52; Seeed XIAO ESP32‑C6 / XIAO nRF52840 + Ebyte E22‑900M30S 1 W radio) and wires all roles into CI + the release pipeline. ESP32‑C6 needed minimal MeshCore‑consistent base edits (antenna‑switch virtuals, protected_gps_serial, NimBLE dep); nRF52 needed none. Not yet bench‑verified — vendored drivers kept for round‑1; review findings tracked as bench checkpoints on #193/#194.
Fixed
Xiao_S3_WIO_companion_radio_usbbuild (#89) — excludeSerialBLEInterface.cppfrom the USB companion env (it has no BLE), resolving theNimBLEDevice.hregression from the #288 NimBLE migration.
Changed
- CLAUDE.md: no‑upstream‑merge policy (#197) — Offband does not merge from upstream MeshCore; the
upstreamremote stays fetch‑only for reference. Keep MeshCore nomenclature/coding‑standard consistency for clean rebasing.
Which file do I download?
File What it is When to use it *-merged.bin(ESP32 — Heltec V3/V4, XIAO)Full image — bootloader + partition table + app in one, flashed at 0x0after a chip erase. Self-contained, works on a blank chip.First install / clean setup. In a web flasher this is the "Full Firmware" option. *.bin(ESP32)App only — flashed at the app offset ( 0x10000); the bootloader must already be on the chip.Updating an existing node — OTA / "Update Only." Keeps the device identity + WiFi/MQTT config. *.uf2(nRF52 — RAK, T-Echo, XIAO nRF52)Complete self-contained image. First install and updates — double-tap reset, then drag-drop onto the USB drive. (nRF52 has no merged/app split.) ⚠️ ESP32: the app-only
*.binwill not boot if flashed at0x0— use*-merged.binfor a fresh install. A full erase / "Full Firmware" wipes the device's identity + saved config, so use it only for a first install or recovery, never a routine update.What's Changed
- docs(#192): Photon-1W (MeshSmith) support design-of-record by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/195
- chore(#197): CLAUDE.md -- record no-upstream-merge policy by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/198
- fix(#89): exclude SerialBLEInterface.cpp from Xiao_S3_WIO USB companion by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/200
Full Changelog: https://github.com/OffbandMesh/meshcore-firmware/compare/offband-v1.1.0...offband-v1.2.0-beta1
- MeshSmith Photon‑1W support — ESP32‑C6 + nRF52 (#193, #194) — vendors MeshSmith's MIT Photon
variants (
v1.1.0 # 1 month ago · 2026-06-24 04:41 UTC
The headline is Epic F: the companion-API config command — a WiFi observer can now be configured entirely from the app (WiFi, the full MQTT broker pool, display) over BLE — plus the observer config/NVS-layer hardening that came out of bench-validating it. Still on the MeshCore 1.16.0 base.
Added
- Companion-API config command (Epic F, #159) — configure a WiFi observer from the
MeshCore/Offband app over BLE: WiFi credentials, the full MQTT broker pool (per-slot
url / port / transport / auth / JWT claims / CA-cert / topic-prefix / IATA),
enable / disable / clear a slot, and display settings. Wholly observer-gated and surfaced
behind a new
WIFI_OBSERVER_SUPPORTcapability bit +FIRMWARE_VER_CODE 14, so stock companions are unaffected. (#160–#169) - Per-broker live state in the broker read (#172) — each slot now reports its live
connection state (
connecting/up/backoff/held…) + last-error class, so the app shows whether a broker actually connected, not just that it's enabled. - Resolved-default hints (#173, #186) — when
jwt_owner/iata_overrideare unset, the read shows the value the device will actually use at connect (its own pubkey / the global IATA) as a placeholder instead of a confusing blank — in both the pool dump and the per-field read.
Changed
- Honest observer config writes (SAFELANE §6, #181) — the config / NVS write path used to silently swallow failures (a broker disable could ACK success while nothing changed). Every writer, the live-reload path, and the crash logger itself now surface + log failures with context. This is what root-caused #179.
- Compact broker config storage + seamless migration (#182) — broker config is stored as small per-key entries (no blank fields) rather than one ~1 KB blob, so writes fit a near-full NVS; a one-time invisible boot migration reclaims space on upgrade, so the operator never sees a transient write error.
- CoreScope (okimesh) default broker →
mqtt://mqtt1.okimesh.org:1883(#170). - Offband tell in MQTT (#174) — observer nodes append a 📡 to their display name in MQTT payloads, so feeds / maps can flag Offband observers.
Fixed
- #179 — a broker disable ACK'd success while the dump still reported
enabled=1. Root-caused as a near-full-NVS write failure the old code lied about; fixed by #181 (honest write) + #182 (storage that fits). Verified on hardware. - Observer capped at 1 concurrent TLS broker (#171) — a heap guard stops the OOM reboot when a 2nd TLS/wss broker's ~60 KB mbedTLS context exhausts the Heltec V3 heap. Measured on hardware (1 wss ≈ 63 KB free, 2 wss ≈ crash).
- BLE connect survives the client's reconnect-thrash (#178) — a deeper ESP32 BLE frame queue (4 → 12) + stream terminators on reconnect, so a mid-stream reconnect can't leave the app's contact / settings sync hung. Affects companion and observer. (The root — the client's uncapped reconnect retry — is paced client-side in meshcore-client.)
- Crash log reliable for the whole boot (#183) — the 1 Hz heartbeat was flooding the 4 KB RTC crash-ring (wrapped in ~50 s) and evicting real crash diagnostics; the heartbeat now writes the ring every 30 s / on a sharp heap drop, so a post-reboot dump keeps the evidence that explains a crash past boot+50 s.
CI / build
- Heltec T114 (nRF52) companion added to the CI matrix (#185) — first nRF52-companion build coverage (complements the existing RAK4631 repeater).
Which file do I download?
File What it is When to use it *-merged.bin(ESP32 — Heltec V3/V4, XIAO)Full image — bootloader + partition table + app in one, flashed at 0x0after a chip erase. Self-contained, works on a blank chip.First install / clean setup. In a web flasher this is the "Full Firmware" option. *.bin(ESP32)App only — flashed at the app offset ( 0x10000); the bootloader m… - Companion-API config command (Epic F, #159) — configure a WiFi observer from the
MeshCore/Offband app over BLE: WiFi credentials, the full MQTT broker pool (per-slot
url / port / transport / auth / JWT claims / CA-cert / topic-prefix / IATA),
enable / disable / clear a slot, and display settings. Wholly observer-gated and surfaced
behind a new
v1.1.0-rc1 # Pre-release 1 month ago · 2026-06-24 04:24 UTC
The headline is Epic F: the companion-API config command — a WiFi observer can now be configured entirely from the app (WiFi, the full MQTT broker pool, display) over BLE — plus the observer config/NVS-layer hardening that came out of bench-validating it. Still on the MeshCore 1.16.0 base.
Added
- Companion-API config command (Epic F, #159) — configure a WiFi observer from the
MeshCore/Offband app over BLE: WiFi credentials, the full MQTT broker pool (per-slot
url / port / transport / auth / JWT claims / CA-cert / topic-prefix / IATA),
enable / disable / clear a slot, and display settings. Wholly observer-gated and surfaced
behind a new
WIFI_OBSERVER_SUPPORTcapability bit +FIRMWARE_VER_CODE 14, so stock companions are unaffected. (#160–#169) - Per-broker live state in the broker read (#172) — each slot now reports its live
connection state (
connecting/up/backoff/held…) + last-error class, so the app shows whether a broker actually connected, not just that it's enabled. - Resolved-default hints (#173, #186) — when
jwt_owner/iata_overrideare unset, the read shows the value the device will actually use at connect (its own pubkey / the global IATA) as a placeholder instead of a confusing blank — in both the pool dump and the per-field read.
Changed
- Honest observer config writes (SAFELANE §6, #181) — the config / NVS write path used to silently swallow failures (a broker disable could ACK success while nothing changed). Every writer, the live-reload path, and the crash logger itself now surface + log failures with context. This is what root-caused #179.
- Compact broker config storage + seamless migration (#182) — broker config is stored as small per-key entries (no blank fields) rather than one ~1 KB blob, so writes fit a near-full NVS; a one-time invisible boot migration reclaims space on upgrade, so the operator never sees a transient write error.
- CoreScope (okimesh) default broker →
mqtt://mqtt1.okimesh.org:1883(#170). - Offband tell in MQTT (#174) — observer nodes append a 📡 to their display name in MQTT payloads, so feeds / maps can flag Offband observers.
Fixed
- #179 — a broker disable ACK'd success while the dump still reported
enabled=1. Root-caused as a near-full-NVS write failure the old code lied about; fixed by #181 (honest write) + #182 (storage that fits). Verified on hardware. - Observer capped at 1 concurrent TLS broker (#171) — a heap guard stops the OOM reboot when a 2nd TLS/wss broker's ~60 KB mbedTLS context exhausts the Heltec V3 heap. Measured on hardware (1 wss ≈ 63 KB free, 2 wss ≈ crash).
- BLE connect survives the client's reconnect-thrash (#178) — a deeper ESP32 BLE frame queue (4 → 12) + stream terminators on reconnect, so a mid-stream reconnect can't leave the app's contact / settings sync hung. Affects companion and observer. (The root — the client's uncapped reconnect retry — is paced client-side in meshcore-client.)
- Crash log reliable for the whole boot (#183) — the 1 Hz heartbeat was flooding the 4 KB RTC crash-ring (wrapped in ~50 s) and evicting real crash diagnostics; the heartbeat now writes the ring every 30 s / on a sharp heap drop, so a post-reboot dump keeps the evidence that explains a crash past boot+50 s.
CI / build
- Heltec T114 (nRF52) companion added to the CI matrix (#185) — first nRF52-companion build coverage (complements the existing RAK4631 repeater).
Which file do I download?
File What it is When to use it *-merged.bin(ESP32 — Heltec V3/V4, XIAO)Full image — bootloader + partition table + app in one, flashed at 0x0after a chip erase. Self-contained, works on a blank chip.First install / clean setup. In a web flasher this is the "Full Firmware" option. *.bin(ESP32)App only — flashed at the app offset ( 0x10000); the bootloader m… - Companion-API config command (Epic F, #159) — configure a WiFi observer from the
MeshCore/Offband app over BLE: WiFi credentials, the full MQTT broker pool (per-slot
url / port / transport / auth / JWT claims / CA-cert / topic-prefix / IATA),
enable / disable / clear a slot, and display settings. Wholly observer-gated and surfaced
behind a new
v1.0.0 # 2 months ago · 2026-06-18 00:46 UTC
First production-stable Offband release — companion, observer, and repeater roles all working and hardware-verified. Built on the MeshCore 1.16.0 base.
Base
- MeshCore 1.16.0 base-update (#126) — the fork is rebased onto upstream MeshCore 1.16.0, smoke-verified across all three active roles (Companion, Observer, Repeater) on Heltec V3/V4 + RAK3401.
Added
- RAK3401 (WisMesh 1W) GPS (#104) — the RAK12500 (u-blox ZOE-M8Q) I²C GPS now works in Slot D. Companion and repeater acquire a position fix. (Position only; the I²C path does not sync the clock.)
- Display always-on toggle (#141) —
display always onkeeps a USB/mains-powered observer's screen lit;display normalrestores the 15 s timeout. Persists across reboots, applies immediately. Heltec V3, V4 OLED, V4 TFT observers. - Display rotation (0/180) (#148) —
display rotate 0/180/display flipover the_syschannel; persists, applies immediately. Verified on the OLED observers (Heltec V3, V4 OLED). Displays without a verified rotation driver (the V4 TFT) reportrotation not supported on this displayrather than silently no-op'ing; TFT rotation is tracked separately.
Known issues
- Heltec V4 observer GPS position unverified (#149) — an attached UART GPS doesn't yet surface a position on the V4 observer (reads 0,0). Observer time (NTP/SNTP) and all other function are unaffected; GPS only adds the device's own map-position dot.
Which file do I download?
File What it is When to use it *-merged.bin(ESP32 — Heltec V3/V4, XIAO)Full image — bootloader + partition table + app in one, flashed at 0x0after a chip erase. Self-contained, works on a blank chip.First install / clean setup. In a web flasher this is the "Full Firmware" option. *.bin(ESP32)App only — flashed at the app offset ( 0x10000); the bootloader must already be on the chip.Updating an existing node — OTA / "Update Only." Keeps the device identity + WiFi/MQTT config. *.uf2(nRF52 — RAK, T-Echo, XIAO nRF52)Complete self-contained image. First install and updates — double-tap reset, then drag-drop onto the USB drive. (nRF52 has no merged/app split.) ⚠️ ESP32: the app-only
*.binwill not boot if flashed at0x0— use*-merged.binfor a fresh install. A full erase / "Full Firmware" wipes the device's identity + saved config, so use it only for a first install or recovery, never a routine update.What's Changed
- epic(#126): MeshCore 1.16.0 base-update (integration) by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/134
Full Changelog: https://github.com/OffbandMesh/meshcore-firmware/compare/offband-v0.18.1...offband-v1.0.0
v0.19.0-rc1 # Pre-release 2 months ago · 2026-06-16 04:06 UTC
Display
_sysenhancements (RC1 — for testing)Two new observer/companion display controls over the
_syschannel:- Display always-on (#141) —
display always onkeeps the screen lit;display normalrestores the 15 s blank. (Verified: V3/V4 OLED + V4 TFT.) - Display rotation 0/180 (#148) —
display rotate 0/display rotate 180/display flipflip the screen for upside-down mounting. (Verified: V3/V4 OLED.)
⚠️ TFT 180° rotation is UNVERIFIED — this is the thing to test. On the Heltec V4 TFT (
heltec_v4_tft_companion_observer_wifi),display rotate 180uses a best-guess MADCTL combo (flipScreenVertically). Please confirm it gives a clean 180° flip (upside-down but readable), not a mirror or garbled image.display rotate 0should return to normal;display flipshould toggle. Report back — it's a one-line fix if the combo is wrong.TFT flash:
heltec_v4_tft_companion_observer_wifi-v0.19.0-rc1-14bc32d-merged.bin(full image — web-flasher "Full Firmware" for a fresh install), or the.bin(app-only) to update an existing node and keep its identity/config.
Which file do I download?
File What it is When to use it *-merged.bin(ESP32 — Heltec V3/V4, XIAO)Full image — bootloader + partition table + app in one, flashed at 0x0after a chip erase. Self-contained, works on a blank chip.First install / clean setup. In a web flasher this is the "Full Firmware" option. *.bin(ESP32)App only — flashed at the app offset ( 0x10000); the bootloader must already be on the chip.Updating an existing node — OTA / "Update Only." Keeps the device identity + WiFi/MQTT config. *.uf2(nRF52 — RAK, T-Echo, XIAO nRF52)Complete self-contained image. First install and updates — double-tap reset, then drag-drop onto the USB drive. (nRF52 has no merged/app split.) ⚠️ ESP32: the app-only
*.binwill not boot if flashed at0x0— use*-merged.binfor a fresh install. A full erase / "Full Firmware" wipes the device's identity + saved config, so use it only for a first install or recovery, never a routine update.Full Changelog: https://github.com/OffbandMesh/meshcore-firmware/compare/offband-v0.18.1...offband-v0.19.0-rc1
- Display always-on (#141) —
v0.18.1 # 2 months ago · 2026-06-15 03:08 UTC
Fixed
- WiFi password confirmation wording —
set wifi.pwdnow replieswifi.pwd set (N chars entered)instead ofwifi.pwd set (length=N), which was being misread as a 17-character maximum. The reply reports the length of what was entered (never the secret PSK); it is not a cap. WiFi passwords accept the full WPA2 range (8–63 chars).
Which file do I download?
File What it is When to use it *-merged.bin(ESP32 — Heltec V3/V4, XIAO)Full image — bootloader + partition table + app in one, flashed at 0x0after a chip erase. Self-contained, works on a blank chip.First install / clean setup. In a web flasher this is the "Full Firmware" option. *.bin(ESP32)App only — flashed at the app offset ( 0x10000); the bootloader must already be on the chip.Updating an existing node — OTA / "Update Only." Keeps the device identity + WiFi/MQTT config. *.uf2(nRF52 — RAK, T-Echo, XIAO nRF52)Complete self-contained image. First install and updates — double-tap reset, then drag-drop onto the USB drive. (nRF52 has no merged/app split.) ⚠️ ESP32: the app-only
*.binwill not boot if flashed at0x0— use*-merged.binfor a fresh install. A full erase / "Full Firmware" wipes the device's identity + saved config, so use it only for a first install or recovery, never a routine update.What's Changed
- fix(observer): wifi.pwd confirmation wording (0.18.1) by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/139
Full Changelog: https://github.com/OffbandMesh/meshcore-firmware/compare/offband-v0.18.0...offband-v0.18.1
- WiFi password confirmation wording —
v0.18.0 # 2 months ago · 2026-06-15 02:56 UTC
Added
- Heltec V4 TFT observer build (
heltec_v4_tft_companion_observer_wifi) — the observer role on the TFT (ST7789) display variant, switched to the NimBLE stack the observer requires. Added to the CI build matrix and the release env set so it always builds and ships.
Changed
- Firmware download clarity — a "Which file?" table in the README and a static
footer appended to every GitHub Release, explaining
-merged.bin(first install) vs.bin(update) vs.uf2(nRF52).
Fixed
pio-flashdevice matching —find_in_registryprefers an exact DeviceID-instance match over a class-only label, so a registered chip is no longer shadowed by a same-class device with null discriminators (fixes nRF52/ESP32 mislabels where two boards share a VID:PID).
Internal
- Wire a gitignored
HARDWARE.local.md(symlink to the LoRa hardware inventory) plus a CLAUDE.md "read before any hardware work" pointer.
Which file do I download?
File What it is When to use it *-merged.bin(ESP32 — Heltec V3/V4, XIAO)Full image — bootloader + partition table + app in one, flashed at 0x0after a chip erase. Self-contained, works on a blank chip.First install / clean setup. In a web flasher this is the "Full Firmware" option. *.bin(ESP32)App only — flashed at the app offset ( 0x10000); the bootloader must already be on the chip.Updating an existing node — OTA / "Update Only." Keeps the device identity + WiFi/MQTT config. *.uf2(nRF52 — RAK, T-Echo, XIAO nRF52)Complete self-contained image. First install and updates — double-tap reset, then drag-drop onto the USB drive. (nRF52 has no merged/app split.) ⚠️ ESP32: the app-only
*.binwill not boot if flashed at0x0— use*-merged.binfor a fresh install. A full erase / "Full Firmware" wipes the device's identity + saved config, so use it only for a first install or recovery, never a routine update.What's Changed
- docs(#127): MeshCore 1.16.0 base-update merge plan by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/133
- feat: Heltec V4 TFT observer build (always-build) + flash docs & tooling by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/138
Full Changelog: https://github.com/OffbandMesh/meshcore-firmware/compare/offband-v0.17.0...offband-v0.18.0
- Heltec V4 TFT observer build (
v0.18.0-rc1 # Pre-release 2 months ago · 2026-06-14 22:46 UTC
(No CHANGELOG entry for v0.18.0-rc1.)
Which file do I download?
File What it is When to use it *-merged.bin(ESP32 — Heltec V3/V4, XIAO)Full image — bootloader + partition table + app in one, flashed at 0x0after a chip erase. Self-contained, works on a blank chip.First install / clean setup. In a web flasher this is the "Full Firmware" option. *.bin(ESP32)App only — flashed at the app offset ( 0x10000); the bootloader must already be on the chip.Updating an existing node — OTA / "Update Only." Keeps the device identity + WiFi/MQTT config. *.uf2(nRF52 — RAK, T-Echo, XIAO nRF52)Complete self-contained image. First install and updates — double-tap reset, then drag-drop onto the USB drive. (nRF52 has no merged/app split.) ⚠️ ESP32: the app-only
*.binwill not boot if flashed at0x0— use*-merged.binfor a fresh install. A full erase / "Full Firmware" wipes the device's identity + saved config, so use it only for a first install or recovery, never a routine update.What's Changed
- docs(#127): MeshCore 1.16.0 base-update merge plan by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/133
- feat: Heltec V4 TFT observer build (always-build) + flash docs & tooling by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/138
Full Changelog: https://github.com/OffbandMesh/meshcore-firmware/compare/offband-v0.17.0...offband-v0.18.0-rc1
v0.17.0 # 2 months ago · 2026-06-14 07:31 UTC
First release under OffbandMesh/meshcore-firmware. Bundles the 0.16.0 observer work below (which landed on
firmware-basebut was never separately tagged) with the Crosswire→Offband rebrand and the OffbandMesh org cutover. (Version pending owner confirmation; the tag is hardware-gated per VERSIONING.md.)Changed
- Rebranded the fork from Crosswire to Offband (GitHub org
OffbandMesh): the C++ namespace, build macros, embedded identity blob, version prefix (offband-v*), MQTT / flash-audit identity fields (offband_*), brand strings (serial banner,versioncommand, OLED splash, Home Assistant manufacturer), and the WiFi setup-AP SSID (Offband-Observer-). Historicalcrosswire-v*release tags are preserved; the_sysPSK domain separator and the MeshCore interop topic namespace are intentionally unchanged. (#100) - Repo / board / working-dir cutover to OffbandMesh — repo
OffbandMesh/meshcore-firmware, OffbandMesh org Projects board, and the preflight / CLAUDE.md / label-sync workflow re-pointed; removed the stale upstreamCNAME. (#107, #111)
Docs
- Finished the rebrand reference cleanup across docs + code comments. (#113, #114)
- Release-readiness pass: README getting-started + multi-role positioning, the
docs index surfaces the observer guides, and observer
_sysCLI reference corrections. (#117)
What's Changed
- feat: observer MQTT connectivity (crosswire-v0.15.0) — #53 #48 #63 #68 by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/72
- docs: fix README license link (LICENSE.txt -> license.txt) — #73 by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/74
- docs: 2026-06-10 observer-MQTT session handoff — #75 by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/76
- chore: relocate to C:/Dev/Crosswire — canonical hook sync + AM repin by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/78
- docs(#79): correct stale active-agents line in CLAUDE.md by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/80
- Observer: time arbiter (#69) + position in /status (#31) + _sys CLI grammar (#45) by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/91
- Observer follow-ups: arbiter decouple + held-state (#87) + /status radio from runtime prefs (#88) by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/92
- docs: README/CHANGELOG 0.16.0 (#93) + observer instructions (#94) by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/99
- #95: observer MQTT broker pre-config + per-device auth by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/105
- #98: mqtt view + mqtt clear (broker config inspection) by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/106
- Offband rebrand: Crosswire -> Offband (code + docs) by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/108
- chore(#107): finish OffbandMesh local cutover (preflight, CLAUDE.md, board #1) by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/109
- chore(#111): remove stale upstream CNAME (docs.meshcore.nz) by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/112
- docs(#113): finish rebrand — Strycher/Crosswire refs + CLAUDE.md PROJECT_PAT note by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/115
- chore(#114): update stale Strycher/Crosswire# refs in code comments -> #N by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/116
- epic(#117): Offband release-readiness — docs, observer reference, CHANGELOG prep by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/125
Full Changelog: https://github.com/OffbandMesh/meshcore-firmware/compare/crosswire-v0.14.0...offband-v0.17.0
- Rebranded the fork from Crosswire to Offband (GitHub org
v0.17.0-rc1 # Pre-release 2 months ago · 2026-06-14 07:15 UTC
(No CHANGELOG entry for v0.17.0-rc1.)
What's Changed
- feat: observer MQTT connectivity (crosswire-v0.15.0) — #53 #48 #63 #68 by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/72
- docs: fix README license link (LICENSE.txt -> license.txt) — #73 by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/74
- docs: 2026-06-10 observer-MQTT session handoff — #75 by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/76
- chore: relocate to C:/Dev/Crosswire — canonical hook sync + AM repin by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/78
- docs(#79): correct stale active-agents line in CLAUDE.md by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/80
- Observer: time arbiter (#69) + position in /status (#31) + _sys CLI grammar (#45) by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/91
- Observer follow-ups: arbiter decouple + held-state (#87) + /status radio from runtime prefs (#88) by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/92
- docs: README/CHANGELOG 0.16.0 (#93) + observer instructions (#94) by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/99
- #95: observer MQTT broker pre-config + per-device auth by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/105
- #98: mqtt view + mqtt clear (broker config inspection) by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/106
- Offband rebrand: Crosswire -> Offband (code + docs) by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/108
- chore(#107): finish OffbandMesh local cutover (preflight, CLAUDE.md, board #1) by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/109
- chore(#111): remove stale upstream CNAME (docs.meshcore.nz) by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/112
- docs(#113): finish rebrand — Strycher/Crosswire refs + CLAUDE.md PROJECT_PAT note by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/115
- chore(#114): update stale Strycher/Crosswire# refs in code comments -> #N by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/116
- epic(#117): Offband release-readiness — docs, observer reference, CHANGELOG prep by @Strycher in https://github.com/OffbandMesh/meshcore-firmware/pull/125
Full Changelog: https://github.com/OffbandMesh/meshcore-firmware/compare/crosswire-v0.14.0...offband-v0.17.0-rc1
v0.14.0 # 2 months ago · 2026-06-10 23:45 UTC
Added
- CI release pipeline (epic #14): dev-channel firmware artifacts on every
firmware-basepush / PR (ci.yml), and arelease.ymlworkflow that builds the curated community board set fromcrosswire-v*tags and publishes a GitHub Release (pre-release for-rc*, "Latest" for stable). Curated env set in.github/release-envs.txt(72 envs;heltec_v4_repeater_telemetrygated on #20). Design of record:docs/architecture/2026-06-06-ci-release-pipeline.md. (#15, #16, #17)
Changed
- Reconciled inherited CI workflows: removed 7 dead/superseded
(
pr-build-check,auto-promote,github-pages, the three upstream-tagbuild-*-firmwares,branch-cleanup); keptbuild-safeboot-firmwares+sync-labels-to-board. (#18)
What's Changed
- fix(#27): pio-flash firmware_dir -> repo root + --firmware-dir override by @Strycher in https://github.com/Strycher/Crosswire/pull/28
- pio-flash artifact-flash: flash CI release artifacts through the identity gate (#29, #34) by @Strycher in https://github.com/Strycher/Crosswire/pull/35
- fix(#33): OLED splash carries pre-release identifier (v0.14.0-rc1, not v0.14.0+0) by @Strycher in https://github.com/Strycher/Crosswire/pull/37
- chore(#38): gitignore pycache/ by @Strycher in https://github.com/Strycher/Crosswire/pull/40
- feat(#42): strip observer companion to minima + delete dead ring buffer by @Strycher in https://github.com/Strycher/Crosswire/pull/50
- docs: pin Crosswire project identity (Citadel + Agent Mail keys) (#55) by @Strycher in https://github.com/Strycher/Crosswire/pull/56
- docs(#32): position-to-map pipeline architecture draft by @Strycher in https://github.com/Strycher/Crosswire/pull/57
- docs: MeshCore 1.16.0 base-update impact assessment (spike #54) by @Strycher in https://github.com/Strycher/Crosswire/pull/58
- chore(#59): port /work + session-state.py compaction-recovery hook into Crosswire by @Strycher in https://github.com/Strycher/Crosswire/pull/60
- docs(#61): correct CLAUDE.md build/flash + migration-status after meshcore-firmware retire by @Strycher in https://github.com/Strycher/Crosswire/pull/62
Full Changelog: https://github.com/Strycher/Crosswire/compare/crosswire-v0.14.0-rc1...crosswire-v0.14.0
- CI release pipeline (epic #14): dev-channel firmware artifacts on every
v0.15.0 # 2 months ago · 2026-06-10 23:43 UTC
Observer MQTT connectivity -- hardware-validated against three brokers (CoreScope / W8OOF tcp-anon, eastme.sh wss/jwt, LetsMesh-US wss/jwt).
Added
- Owner broker registry: seed the default 6-slot broker set with an
iata=HAOdefault; per-broker GTS Root R4 + ISRG Root X2 CA certificates added and mapped, registry cert-names corrected. (#48) - Per-broker JWT identity claims
jwt_owner/jwt_email(set mqtt.broker.<N>.jwt_owner|jwt_email), surfaced inmqtt status. (#63) - Multi-frame
mqtt statusover the BLE_syschannel: the per-slot broker table spans multiple frames instead of truncating. (#48)
Fixed
- BLE
_syscommand channel no longer hangs whenset mqtt.broker.*runs. The blockingesp_mqttlifecycle ops (connect / destroy) moved offloopTaskto a dedicatedmqtt_workertask with a per-broker lock and a per-slot reconcile flag. (#53) - wss/JWT broker authentication: send the MQTT CONNECT username
v1_<UPPERCASE pubkey>(was a null username) so eastme.sh / LetsMesh accept the connection -- the broker verifies the token'spublicKeyclaim against it and rejects a null username (CONNACK rc=5) even with an otherwise-valid token. (#68)
What's Changed
- fix(#27): pio-flash firmware_dir -> repo root + --firmware-dir override by @Strycher in https://github.com/Strycher/Crosswire/pull/28
- pio-flash artifact-flash: flash CI release artifacts through the identity gate (#29, #34) by @Strycher in https://github.com/Strycher/Crosswire/pull/35
- fix(#33): OLED splash carries pre-release identifier (v0.14.0-rc1, not v0.14.0+0) by @Strycher in https://github.com/Strycher/Crosswire/pull/37
- chore(#38): gitignore pycache/ by @Strycher in https://github.com/Strycher/Crosswire/pull/40
- feat(#42): strip observer companion to minima + delete dead ring buffer by @Strycher in https://github.com/Strycher/Crosswire/pull/50
- docs: pin Crosswire project identity (Citadel + Agent Mail keys) (#55) by @Strycher in https://github.com/Strycher/Crosswire/pull/56
- docs(#32): position-to-map pipeline architecture draft by @Strycher in https://github.com/Strycher/Crosswire/pull/57
- docs: MeshCore 1.16.0 base-update impact assessment (spike #54) by @Strycher in https://github.com/Strycher/Crosswire/pull/58
- chore(#59): port /work + session-state.py compaction-recovery hook into Crosswire by @Strycher in https://github.com/Strycher/Crosswire/pull/60
- docs(#61): correct CLAUDE.md build/flash + migration-status after meshcore-firmware retire by @Strycher in https://github.com/Strycher/Crosswire/pull/62
- feat: observer MQTT connectivity (crosswire-v0.15.0) — #53 #48 #63 #68 by @Strycher in https://github.com/Strycher/Crosswire/pull/72
Full Changelog: https://github.com/Strycher/Crosswire/compare/crosswire-v0.14.0-rc1...crosswire-v0.15.0
- Owner broker registry: seed the default 6-slot broker set with an
v0.14.0-rc1 # Pre-release 2 months ago · 2026-06-07 21:22 UTC
(No CHANGELOG entry for v0.14.0-rc1.)
What's Changed
- docs: adopt versioning + CHANGELOG + release-channel discipline (#11) by @Strycher in https://github.com/Strycher/Crosswire/pull/12
- chore: roll CHANGELOG to v0.13.2 + fix em-dashes to ASCII (#11) by @Strycher in https://github.com/Strycher/Crosswire/pull/13
- epic(#14): CI release pipeline -- dev artifacts + crosswire-v* releases by @Strycher in https://github.com/Strycher/Crosswire/pull/24
- docs(#25): roll CHANGELOG to [0.14.0] (CI release pipeline) by @Strycher in https://github.com/Strycher/Crosswire/pull/26
New Contributors
- @Strycher made their first contribution in https://github.com/Strycher/Crosswire/pull/12
Full Changelog: https://github.com/Strycher/Crosswire/commits/crosswire-v0.14.0-rc1