ZephCore

ZephCore · Releases

from GitHub · updated 2026-08-05

20 releases

  1. v1.16.8-zephcore # 9 minutes ago · 2026-08-05 14:06 UTC

    ZephCore 1.16.8-zephcore

    [!CAUTION] This is a pre-release. ESP32 light sleep is enabled by default on Heltec repeaters and has not been validated on hardware yet — see the light-sleep section for what to check and how to build it out. It is deliberately not published to the apps.meshamerica.com configurator, which keeps offering v1.16.7; flash this one manually from the assets below if you want to help test it. The nRF based boards should be fine.

    [!IMPORTANT]

    Before you upgrade

    From v1.16.5 / v1.16.6 / v1.16.7 — clean flash, no re-bond, bonds and data survive.

    From v1.16.2 / v1.16.3 / v1.16.4 — clean flash, bonds and data survive. If you are on an ESP32-S3 / ESP32-C board and have not yet taken the v1.16.5 update, you still owe the one-time serial reflash: the app moved to flash offset 0x10000 in v1.16.5, and a board on the old layout cannot cross that update over WiFi-OTA or the browser flasher — flash the -merged.bin once over USB/serial. Affected boards: Heltec V3 / V4 / V4.3, Station G2, Wireless Tracker / V2, XIAO ESP32-S3 / C3 / C6, LilyGo T-Lora C6, ThinkNode M9. Identity, contacts, channels, prefs, and BLE bonds are preserved. nRF52, classic ESP32 (T-Beam / PICO-D4 / TTGO LoRa32), STM32WL, and native Linux are unaffected.

    From v1.16.1 or older — flash it; on first boot it clears BLE bonds automatically (identity, contacts, channels, prefs preserved). Re-bond your phone/desktop once.

    Coming from Arduino MeshCore — flash it; auto-formats on first boot (new identity, clean storage).

    Heltec ESP32 repeaters now light-sleep, and their USB CLI stops answering 10 minutes after boot as a result. Reconnecting your terminal normally resets the node and gives you another 10 minutes; if it doesn't, power-cycle. Details in the light-sleep section below.

    If your node's GPS went quiet after its first fix, this release fixes it. A wrong bit in the sleep command put u-blox modules into permanent backup mode on boards without hardware GPS power control. Recovery needed a physical power cycle — a reboot was not enough. See the GPS section.

    One CLI rename: set/get cad.probe.interval is now set/get probe.interval — the same setting, renamed because one measurement now feeds both the noise floor and the CAD probe. Your stored value carries over; only the command name changed. set/get agc.reset.interval is gone (see below).

    Take this with a grain of salt — try the formatters if anything anomalous happens with your node.


    A power and radio-measurement release. Repeaters no longer wake on a fixed 5 s tick — they sleep until their next real deadline. Every ESP32 build drops to 80 MHz, and Heltec repeaters now light-sleep between packets. The noise-floor sampler and CAD probe were unified into one measurement, the SX126x RSSI/AGC calibration is now band-correct, and the observer role got a batch of fixes for settings that silently never reached the hardware.

    Highlights

    Repeaters are now deadline-driven — the periodic housekeeping tick is gone

    Every repeater woke five times a second-and-a-bit forever: a fixed 5 s "housekeeping" timer that ran the noise-floor sample, the CAD probe, advert timers and the tempradio revert. None of that actually needs a poll — each item is either a deadline or a sampler with its own interval.

    The new mesh/Maintenance.h contract lets the event loop ask "when does maintenance next have work?" and arm one one-shot wake for exactly that moment. An idle repeater now sleeps until its soonest real deadline instead of on a fixed cadence. A hard ceiling (CONFIG_ZEPHCORE_MAINTENANCE_BACKSTOP_MS, default 60 s) clamps the wake as a safety net against a missed deadline — it should never bind in normal operation.

    Companions and room servers keep their periodic tick deliberately: the companion block contains genuine pollers (contact-dump progress, BLE adverti …

  2. v1.16.7-zephcore # 11 days ago · 2026-07-25 13:15 UTC

    ZephCore 1.16.7-zephcore

    [!IMPORTANT]

    Before you upgrade

    From v1.16.6 / v1.16.5 — clean flash, no re-bond, bonds and data survive.

    From v1.16.2 / v1.16.3 / v1.16.4 — clean flash, bonds and data survive. If you are on an ESP32-S3 / ESP32-C board and have not yet taken the v1.16.5 update, you still owe the one-time serial reflash: the app moved to flash offset 0x10000 in v1.16.5, and a board on the old layout cannot cross that update over WiFi-OTA or the browser flasher — flash the -merged.bin once over USB/serial. Affected boards: Heltec V3 / V4 / V4.3, Station G2, Wireless Tracker / V2, XIAO ESP32-S3 / C3 / C6, LilyGo T-Lora C6. Identity, contacts, channels, prefs, and BLE bonds are preserved. nRF52, classic ESP32 (T-Beam / PICO-D4 / TTGO LoRa32), STM32WL, and native Linux are unaffected.

    From v1.16.1 or older — flash it; on first boot it clears BLE bonds automatically (identity, contacts, channels, prefs preserved). Re-bond your phone/desktop once.

    Coming from Arduino MeshCore — flash it; auto-formats on first boot (new identity, clean storage).

    Take this with a grain of salt — try the formatters if anything anomalous happens with your node.


    A stability-and-hardware release. The headline is a whole-node wedge fixed in the GPS power-management path that shipped in v1.16.6, plus RX duty cycle finally working on LR1110 boards and a new safety margin that stops signal-independent duty-cycle packet drops on every radio. Rounded out by joystick-UI fixes and a color-display improvement for the Heltec T114.

    Highlights

    Fixed: a GPS node could wedge completely on standby (regression in v1.16.6)

    v1.16.6 introduced GPS-UART suspend to save power (an armed nRF UARTE receiver costs ~0.5–1 mA even with the GPS module powered down). It had a race that could hang the entire node — no LoRa, no USB, no BLE, CLI answering -> busy, recoverable only by reboot.

    The upstream nRF UARTE suspend path spins forever waiting for an RXTO event after issuing STOPRX. In interrupt-driven mode the receiver runs one byte at a time and re-arms from its ISR; if a byte happened to finish in the narrow window where the driver had already disabled the ENDRX interrupt, the receiver was left stopped-and-un-rearmed, STOPRX produced no RXTO, and the wait never returned — with the main mesh thread stuck inside it. Any nRF board that powers its GPS module down and suspends the port on standby could hit this.

    Fixed in two independent layers, so neither alone is load-bearing:

    • ZephyrGPSManager settles ~5 ms before suspending the GPS UART, letting the last in-flight byte finish and the RX ISR re-arm, so the receiver is armed-and-idle when STOPRX fires — the state that reliably yields RXTO. Standby happens at most every few minutes, so the cost is negligible.
    • New patch 0010 bounds the driver's RXTO wait to 4 ms instead of spinning forever. On timeout it falls through to the unconditional nrf_uarte_disable() that force-stops the receiver anyway — so even if a byte still lands in the race window, the node can never hang.

    RX duty cycle now works on LR1110 boards

    LR1110 boards (T1000-E, ThinkNode M9, and other LR11xx radios) now get real RX duty-cycle sniff mode instead of sitting in continuous receive — a substantial receive-power saving on battery nodes, the same mechanism SX126x boards have had. This took a significant rework of the LR11xx LoRa driver to land correctly.

    New: duty-cycle safety margin stops signal-independent packet drops

    set-able via CONFIG_ZEPHCORE_LORA_DC_MARGIN_PCT (default 15%), shared by every radio (SX126x / LR11xx / LR20xx).

    The theoretical per-cycle "deaf-time" budget that duty cycle is computed from assumes the sleep clock and the wake transition are exact. They are not: the chip's sleep timer runs on an internal RC oscillator (RC64k on SX126x, RTC on LR11xx) that drifts several …

  3. v1.16.6-zephcore # 13 days ago · 2026-07-23 15:33 UTC

    ZephCore 1.16.6-zephcore

    [!IMPORTANT]

    Before you upgrade

    From v1.16.5 — clean flash, no re-bond, bonds and data survive.

    From v1.16.2 / v1.16.3 / v1.16.4 — clean flash, bonds and data survive. If you are on an ESP32-S3 / ESP32-C board and have not yet taken the v1.16.5 update, you still owe the one-time serial reflash described there: the app moved to flash offset 0x10000 in v1.16.5, and a board on the old layout cannot cross that update over WiFi-OTA or the browser flasher — flash the -merged.bin once over USB/serial. Affected boards: Heltec V3 / V4 / V4.3, Station G2, Wireless Tracker / V2, XIAO ESP32-S3 / C3 / C6, LilyGo T-Lora C6. Identity, contacts, channels, prefs, and BLE bonds are preserved. nRF52, classic ESP32 (T-Beam / PICO-D4 / TTGO LoRa32), STM32WL, and native Linux are unaffected.

    From v1.16.1 or older — flash it; on first boot it clears BLE bonds automatically (identity, contacts, channels, prefs preserved). Re-bond your phone/desktop once.

    Coming from Arduino MeshCore — flash it; auto-formats on first boot (new identity, clean storage).

    Take this with a grain of salt — try the formatters if anything anomalous happens with your node.


    A maintenance release: an important admin-password fix, a substantial identity-key randomness hardening on ESP32, GPS standby power savings, and the removal of Adaptive Power Control — plus two new boards, muzi works R1 Neo and an experimental first cut at the ThinkNode M9 in dev.

    Highlights

    2-tap and 5-tap are switched

    After a lot of inadvertently sent adverts, the functions of the 2 and 5 clicks were switched, so a double click with the user button now only enables/disables the LEDs and no longer accidentally sends a flood advert.

    Fixed: uppercase letters in an admin password were silently lowercased

    If you set or changed an admin password on v1.16.5 and can no longer log in, this is why. v1.16.5 introduced case-insensitive CLI keywords by lowercasing the first two words of every command before matching them. That is safe for get cad — but password <value> puts the value in the second word, so password MyPass was stored as mypass, and the password you typed afterwards never matched. (set guest.password <value> has three words and was never affected.)

    The normalizer has been removed and CLI commands are case-sensitive again, matching upstream Arduino MeshCore. If you are locked out, re-flash and reconfigure, or log in with the all-lowercase form of the password you set.

    The one place autocapitalization genuinely hurts — typing commands to the V-Contact from a phone keyboard, which capitalizes the first letter of every line — is handled narrowly instead: only character 0 of a V-Contact chat line is folded. No command takes an argument at position 0, so no value can be touched.

    Identity keys are now generated with hardened, hardware-verified randomness

    Your node's identity — the Ed25519 keypair that is your node on the mesh — is generated once, on first boot, and kept for life. An investigation into how the randomness behind that key is gathered found that ESP32 boards were generating identities with far less entropy than intended. Two independent problems, both fixed in this release:

    • The ESP32 hardware RNG was running unseeded. It only produces true random numbers while the radio (WiFi/BT) is active — and identity generation runs before the radio comes up (on repeaters, the radio never comes up at all). Key generation now feeds the RNG from the chip's ADC noise source for its duration, Espressif's documented method for exactly this pre-radio situation. Verified on hardware: the RNG's output went from a fixed pattern to statistically ideal.
    • The timing-jitter backup source measured nothing on ESP32. It sampled the CPU's cycle counter against work …
  4. v1.16.5-zephcore # 20 days ago · 2026-07-16 17:38 UTC

    ZephCore 1.16.5-zephcore

    [!IMPORTANT]

    Before you upgrade

    ESP32-S3 / ESP32-C boards — one-time serial reflash required; WiFi-OTA cannot bridge this update. This release moves the application to a new flash location (0x10000). A board on the old layout can't update to this build over WiFi-OTA or the browser flasher — flash the -merged.bin once over USB/serial. Affected boards: Heltec V3 / V4 / V4.3, Station G2, Wireless Tracker / V2, XIAO ESP32-S3 / C3 / C6, LilyGo T-Lora C6. Your identity, contacts, channels, prefs, and BLE bonds are preserved. After this reflash, OTA and app-updates work again.

    nRF52, classic ESP32 (T-Beam / PICO-D4 / TTGO LoRa32), STM32WL, and native Linux are unaffected — upgrade as usual.

    From v1.16.2 / v1.16.3 / v1.16.4 — clean flash, no re-bond, bonds and data survive.

    From v1.16.1 or older — flash it; on first boot it clears BLE bonds automatically (identity, contacts, channels, prefs preserved). Re-bond your phone/desktop once.

    Coming from Arduino MeshCore — flash it; auto-formats on first boot (new identity, clean storage).

    Take this with a grain of salt — try the formatters if anything anomalous happens with your node.


    The headline of this release: Adaptive CAD, a self-tuning listen-before-talk that calibrates itself to each site; V-Contact, an in-app admin "channel" for companions; and a batch of LoRa-driver reliability fixes.

    Highlights

    Fixed: ESP32 WiFi-OTA silently reverted — upload hit 100%, then booted the old firmware

    On ESP32-S3 / ESP32-C boards, the app and MCUboot bootloader were using different flash partition tables, so each OTA update landed at an address the bootloader never checked. Every upload "succeeded" and then quietly did nothing. Both images now share one partition map, and OTA works correctly. (The XIAO C3/C6 and T-Lora C6 used the stock layout and were never affected.)

    ESP32 app slot moved to 0x10000

    The ESP32 app slot moved from 0x20000 to 0x10000 — matching the Arduino / ESP-IDF offset. This is the change that fixes OTA above, and it also enables the configurator's fast app-only update (rewrites just the app, keeps your settings). Requires the one-time serial reflash (see Before you upgrade).

    New: Adaptive CAD — listen-before-talk that tunes itself to your site

    Every transmission is preceded by a Channel Activity Detection (CAD) check that listens for other LoRa traffic and holds off if the channel is busy. How sensitive that check is has always been a single hardcoded number — but the right value depends on where the node lives. A quiet valley node and a 50-network hilltop need very different thresholds: too sensitive and the node keeps deferring for phantom traffic (wasted airtime, latency); not sensitive enough and it transmits over real packets (collisions).

    Nodes now measure their own RF environment and adjust automatically. In the background, between transmissions, the radio runs short calibration probes, tracks how often each sensitivity level produces a false "busy", and settles on the lowest false-positive threshold for that site — responding to a changing environment within an hour or two. It runs on repeaters and companions, and it is on by default; there is nothing to configure.

    • get cad — watch what it's doing: the current threshold and per-level false-positive statistics.
    • set cad.auto off — turn off the auto-adjust to observe or hand-tune with set cad.offset.

    Quiet sites end up more sensitive than the old fixed default (fewer stomped packets); noisy sites back off until the false alarms stop. CAD also now uses 4 detection symbols everywhere, which improves detection of an in-progress packet's payload (not just its preamble). Full guide, including the honest limitation that the "missed detection" side isn't locally observable, is in **[ADAPTIVE_CAD.md](https://github.com/liquidraver/ZephCore/blob/master/zephcore/ADAPTIVE

  5. v20260714.095706 # 22 days ago · 2026-07-14 09:57 UTC

    ZephCore v1.16.5-zephyr

    (re-release because vContact caused regression in settings import / update)

    [!IMPORTANT]

    Before you upgrade

    ESP32-S3 / ESP32-C boards — one-time serial reflash required; WiFi-OTA cannot bridge this update. This release moves the application to a new flash location (0x10000). A board on the old layout can't update to this build over WiFi-OTA or the browser flasher — flash the -merged.bin once over USB/serial. Affected boards: Heltec V3 / V4 / V4.3, Station G2, Wireless Tracker / V2, XIAO ESP32-S3 / C3 / C6, LilyGo T-Lora C6. Your identity, contacts, channels, prefs, and BLE bonds are preserved. After this reflash, OTA and app-updates work again.

    nRF52, classic ESP32 (T-Beam / PICO-D4 / TTGO LoRa32), STM32WL, and native Linux are unaffected — upgrade as usual.

    From v1.16.2 / v1.16.3 / v1.16.4 — clean flash, no re-bond, bonds and data survive.

    From v1.16.1 or older — flash it; on first boot it clears BLE bonds automatically (identity, contacts, channels, prefs preserved). Re-bond your phone/desktop once.

    Coming from Arduino MeshCore — flash it; auto-formats on first boot (new identity, clean storage).

    Take this with a grain of salt — try the formatters if anything anomalous happens with your node.


    The headline of this release:

    New: flash ZephCore from your browser — Mesh America configurator

    ZephCore is now a listed provider in the Mesh America Device Configurator. Pick your board and flash directly — companion (BLE and USB) or repeater — no toolchain, no drag-and-drop.

    Adaptive CAD, a self-tuning listen-before-talk that calibrates itself to each site;

    V-Contact, an in-app admin "channel" for companions; and a batch of LoRa-driver reliability fixes.

    Highlights

    Fixed: ESP32 WiFi-OTA silently reverted — upload hit 100%, then booted the old firmware

    On ESP32-S3 / ESP32-C boards, the app and MCUboot bootloader were using different flash partition tables, so each OTA update landed at an address the bootloader never checked. Every upload "succeeded" and then quietly did nothing. Both images now share one partition map, and OTA works correctly. (The XIAO C3/C6 and T-Lora C6 used the stock layout and were never affected.)

    ESP32 app slot moved to 0x10000

    The ESP32 app slot moved from 0x20000 to 0x10000 — matching the Arduino / ESP-IDF offset. This is the change that fixes OTA above, and it also enables the configurator's fast app-only update (rewrites just the app, keeps your settings). Requires the one-time serial reflash (see Before you upgrade).

    New: Adaptive CAD — listen-before-talk that tunes itself to your site

    Every transmission is preceded by a Channel Activity Detection (CAD) check that listens for other LoRa traffic and holds off if the channel is busy. How sensitive that check is has always been a single hardcoded number — but the right value depends on where the node lives. A quiet valley node and a 50-network hilltop need very different thresholds: too sensitive and the node keeps deferring for phantom traffic (wasted airtime, latency); not sensitive enough and it transmits over real packets (collisions).

    Nodes now measure their own RF environment and adjust automatically. In the background, between transmissions, the radio runs short calibration probes, tracks how often each sensitivity level produces a false "busy", and settles on the lowest false-positive threshold for that site — responding to a changing environment within an hour or two. It runs on repeaters and companions, and it is on by default; there is nothing to configure.

    • get cad — watch what it's doing: the current threshold and per-level false-positive statistics.
    • set cad.auto off — turn off the auto-adjust to observe or hand-tune with set cad.offset.

    Quiet sites end up more sensitive tha …

  6. v20260704.220543 # 1 month ago · 2026-07-04 22:06 UTC

    ZephCore v1.16.4-zephyr

    [!IMPORTANT]

    Before you upgrade

    Updating from v1.16.2 or v1.16.3 — clean flash, no re-bond required, bonds and data survive.

    Updating from v1.16.1 or older — flash the firmware; on first boot it detects the old installation and clears the BLE bond storage automatically. Your identity, contacts, channels, and prefs are preserved. You will need to re-bond your phone/desktop once.

    Coming from Arduino MeshCore — flash the firmware; it detects the incompatible filesystem on first boot and formats automatically (new identity, clean storage). No formatter tool required.

    Take this with a grain of salt though — try the formatters if anything anomalous happens with your node.


    (re-release because meshtimesync command's reply needed a facelift, UI lost uptime and had some offset quirk, nothing more)

    The headline of this release is Mesh Time Sync: an optional, off-by-default feature that lets a node fix its own clock from the mesh — no GPS, no phone. Alongside it: two new board ports, a color-TFT UI pass, a flash-wear fix on GPS nodes, and a batch of multi-byte-path-hash bug fixes.

    Highlights

    New: Mesh Time Sync — self-correcting clocks from the mesh (simulated from EmpireMesh live data)

    Your node can now listen to the timestamps inside other nodes' signed adverts and, if its own clock is clearly wrong, gently correct itself. No GPS and no phone required. It works on every role (repeater, room server, observer, companion).

    Why it matters: a node with a wrong clock shows garbage "last heard" times, and after a reboot without a time sync its own adverts can be silently ignored by every node that already knows it. On the live mesh today, almost half of all repeaters are more than an hour off — this fixes that whole class of problem automatically.

    It is OFF by default. Nothing changes unless you enable it.

    • set meshtimesync on — enable (USB serial CLI or remote admin on repeaters/room servers/observers; on companions toggle the meshtimesync custom variable from the app, or use the same command over the USB text CLI).
    • get meshtimesync — live view, even while the feature is off: how many trustworthy senders it sees, how many agree, and what correction it would make. Worth watching this dry-run for a day before (and after) you enable.

    Guardrails, in short: any clock set protects the node for 7 days — a GPS fix and a manual time/clock sync use the exact same 7-day suppression window, so a GPS-synced node is left alone just like a manually-set one (a live GPS re-arms it on every fix); it steps at most ±1 h at a time and at most once per 6 h; it does nothing without a quorum (default 6) of tenured, agreeing senders; and room servers/companions never step backward.

    Full documentation — how to switch it on, the honest limitations, and the recovery recipe — is in MESHTIMESYNC.md. Design details (consensus algorithm, security model, why the limits are what they are) are in ARCHITECTURE.md §4.9.

    New boards

    • Heltec Mesh Node T096 (nRF52840) — SX1262 + KCT8103L PA/FEM, UC6580 GNSS, ST7735S 160×80 color TFT, button, LED, battery ADC.
    • Seeed LoRa-E5 mini (STM32WLE5JC) — integrated sub-GHz radio (SX1262-class). No BLE/USB: runs as a companion + CLI over USART1 (serial companion transport).
    • TTGO LoRa32 (ESP32 PICO-D4, SX1276) — added as the SX127x reference board (loramac-node backend), USB-UART CLI.
    • LilyGo T-Impulse Plus (nRF52840) — LoRa wristband: SX1262 (S62F module, embedded 32 MHz TCXO, GPIO antenna switch), u-blox MIA-M10Q GNSS, SSD1315 128×64 OLED, TTP223 touch button, haptic vibration motor, ZD25WQ32C 4 MB QSPI flash, battery ADC. Companion + repeater. Ported from LilyGo's official sources and builds clean; on-device bring-up pending (see the board README for the hardware-verify checklist).
    • **Helte …
  7. v20260619.094253 # 2 months ago · 2026-06-19 09:43 UTC

    ZephCore v1.16.3-zephyr

    (re-re-re-release(last time I swear) with the "smearing fix on eink after the full update" and "automount error on a freshly formatted node")

    [!IMPORTANT]

    Before you upgrade

    Updating from v1.16.2 — clean flash, no re-bond required, bonds survive.

    Updating from v1.16.1 or older — flash the firmware; the firmware detects the old installation on first boot and clears the BLE bond storage automatically. Your identity, contacts, channels, and prefs are preserved. You will need to re-bond your phone/desktop once (old pairings were stored in a different location that v1.16.2 moved away from). No formatter tool required.

    Coming from Arduino MeshCore — flash the firmware; it detects the incompatible filesystem on first boot and formats automatically. Everything starts fresh (new identity, clean storage). No formatter tool required.

    Take this with a grain of salt tho… try the formatters if anything anomalous happens with your node.


    This is a GPS-focused release: it stops a battery-killing bug on GPS trackers, puts the GPS duty cycle under your control, and follows up the v1.16.2 ESP32 pairing work with a fix for recent Android phones.

    Highlights

    Fixed: ThinkNode M1 draining its battery in a day

    On the ThinkNode M1, the GPS module was never actually powered down between fixes — the reset line was mislabeled in the board definition and held permanently released, so the receiver kept drawing current even when the firmware thought it was asleep. A full battery could disappear in roughly a day with GPS enabled.

    The M1 now asserts the GPS hardware reset on standby, so the GPS genuinely stops between fixes. If you run an M1 with GPS on, this is the fix you want.

    New: configurable GPS duty cycle — set gps duty <seconds>

    You can now decide how your node handles GPS, instead of it being baked in:

    • set gps duty <seconds> — how long the GPS sleeps between fixes. Persists to flash and applies immediately.
    • set gps duty 0always-on: the GPS never sleeps. It streams fresh fixes (so telemetry is always current) and stays on long enough to download a full almanac.
    • set gps duty default — reset to the role default.
    • get gps duty — show the now-effective interval.

    This unifies repeaters and companions behind one knob. Repeaters default to 48 h (GPS wakes only for periodic time-sync) and companions to 5 minutes — both now adjustable. Existing repeaters are migrated automatically; nothing to do.

    Bad input is rejected with a usage hint, and the value is clamped to a sane range (10 s … 1 week, or 0 for always-on).

    Fixed: GPS no longer pins itself on waiting for a first fix

    Previously, after enabling GPS a companion would keep the receiver powered indefinitely until it got its first fix — so a node left somewhere without sky view (indoors, in a bag) could quietly drain its battery forever. The first cold-start acquisition is now bounded (5 minutes by default, enough for a genuine cold start), after which the node drops into its normal duty cycle whether or not it got a fix.

    Fixed: ESP32 Secure Connections pairing (Pixel 7 / recent Android)

    Following the v1.16.2 ESP32 pairing fix, SC-capable centrals — notably recent Android phones like the Pixel 7 — still failed to bond: the controller's encryption table was left uninitialized because the relevant code was guarded on an ESP-IDF symbol that never exists in a Zephyr build, so the link dropped with a MIC failure at encryption start. The guard is corrected (carried as a managed hal_espressif patch), and SC pairing now completes. Legacy-pairing devices were unaffected and continue to work. Thanks @seagull9000.

    Other fixes and improvements

    • Fixed: GPS always-on on a freshly flashed companion — a new install would default gps_interval to 0 (always-on) instead of the intended 5-minute duty cycle, causing the …
  8. v20260616.202853 # 2 months ago · 2026-06-16 20:29 UTC

    ZephCore v1.16.2-zephyr

    [!IMPORTANT]

    Before you upgrade — please read

    This release changes where devices store their BLE pairing data. The change affects all platforms, but the upgrade steps differ:

    All boards (nRF52 and ESP32): you must re-bond. Existing phone/desktop pairings will not carry over. After flashing, remove the old pairing from your device's Bluetooth settings (and from the ZephCore app), then pair again from scratch.

    nRF should update cleanly, but I recommend doing a flash erase + flash fw + import backup to be absolutely sure. Formatters can be found in the repo's formatter folder. Check your softdevice version before using one.


    Highlights

    Critical fix: ESP32 Bluetooth pairing restored

    On the updated Zephyr 4.4.1 base, ESP32 boards could advertise but never completed pairing — the Espressif Bluetooth controller would assert the moment a phone began to pair. This release restores the controller's link-layer encryption so pairing works again. If you have an ESP32-based node (Heltec, XIAO, Station G2, T-Beam, etc.), this is the fix you've been waiting for.

    Additionally, Android "connect from app" now works on ESP32. Advertising now always exposes the stable identity address, so the Android companion app's in-app connect flow succeeds while iOS pairing continues to work.

    BLE pairing data moved to its own storage area

    Bluetooth bonds previously shared the same filesystem as your identity, preferences, and contacts. They now live in a dedicated, isolated storage partition (NVS) on both nRF52 and ESP32. This means a busy pairing store can no longer corrupt your contacts or settings, and pairing writes never touch your user data.

    A factory reset (erase) now also clears stored BLE bonds, then reboots automatically so everything comes back up clean — previously bonds could survive an erase.

    New board: Heltec Wireless Tracker V1.1 (ESP32-S3 + SX1262), thanks to @seagull9000

    Full support for the Heltec Wireless Tracker V1.1 companion (ESP32-S3 + SX1262 radio + ST7735R TFT + UC6580 GPS), including:

    • Correct battery ADC routing (avoids a wiring conflict in the upstream board definition that otherwise broke all LoRa SPI reads)
    • Battery voltage/percentage readings
    • Onboard GNSS
    • Status-LED heartbeat
    • OLED/TFT display and BLE companion transport

    New board: LilyGo T-Beam v1.2 (SX1262)

    Full support for the classic-ESP32 LilyGo T-Beam v1.2 with the SX1262 radio, including:

    • AXP2101 power management (LoRa + GPS rails brought up automatically at boot)
    • Battery fuel-gauge readings over I2C — accurate battery voltage and percentage, even though this board has no battery ADC
    • Onboard GNSS (NEO-6M / NEO-M8N)
    • The user button wired up (short press = page/select, long press = action)
    • OLED display, BLE companion transport, and CLI-based repeater mode

    Note: classic-ESP32 repeaters (e.g. T-Beam) run CLI-only and do not include WiFi-AP OTA — the WiFi driver buffers plus OTA heap don't fit the classic ESP32's RAM. ESP32-S3 / C-series repeaters keep WiFi OTA.

    E-paper anti-ghosting

    E-paper displays now perform a periodic full refresh (every 8 partial updates by default) to clear the faint "ghost" images that build up where text changes most often, like the status bar. The interval is configurable build-time, and the feature is automatically ignored on OLED/TFT screens that don't ghost.

    More reliable companion connection (Linux / native transport)

    The native TCP transport now applies the same congestion control already used over Bluetooth. Instead of silently dropping data when the connection is briefly saturated (for example, an incoming message arriving during a large contact/channel import), it now back-pressures and retries, and important protocol responses are never dropped. A stalled peer that stops reading is cleanly dis …

  9. v20260613.205222 # 2 months ago · 2026-06-13 20:52 UTC

    DO NOT DOWNLOAD THIS RELEASE JUST TO UPDATE, YOU NEED A FLASH ERASE FOR IT TO WORK PROPERLY. I'LL RE-RELEASE IT SOON AS I CATCH THE BUG! (it seems devices with multiple bondings are affected only)

    So if something is broken: delete bond, erase flash and try again.

    What's New

    • RX duty cycle (power saving) reworked — now properly tested against the datasheet, gives ~40-55% radio-off time with no packet loss on up-to-date nodes. Toggle with set rxduty on/off. (Mixed old/new meshes: keep it off on infra nodes for now. Needs preamble 32 on adjacent nodes)
    • Joystick UI now shows accented characters (Hungarian/Slovak/Czech/Polish/etc. names display correctly), emojis are stripped instead of showing garbage. Unlock gesture changed to Left -> OK -> Right (easier/faster than the old Back-OK-Back).
    • set rxduty / set radio.rxgain now also accept on / off, not just 1/0.

    Fixes

    • WisMesh Tag battery percentage was reading high — calibration corrected.
    • RTC clock could get permanently stuck blank after battery loss — now recovers and re-syncs.
    • Clock source indicator (GPS/App/Local) and display now update correctly/immediately.
    • Auto-shutdown no longer triggers on a brief voltage dip during transmit (needs 3 low readings in a row now).
    • Fixed a race in MQTT "online" status reporting on repeaters/observers.
    • Several small crash/overflow hardening fixes from ongoing code audit (no user-visible behavior change).
  10. v20260608.105434 # 2 months ago · 2026-06-08 10:54 UTC

    New

    Hardware RTC auto-discovery

    Out-of-the-box nodes with a battery/cap-backed I2C RTC (DS3231, PCF8563, RV3028, RX8130CE) now restore the clock at boot — shown tagged L until the next GPS / app / CLI sync — and write the time back on every sync so it survives power-off. Boards opt in via boards/common/rtc-i2c.dtsi; on boards without it, this compiles to nothing.

    First wired up on LilyGo T-Echo and ThinkNode M1.

    Buttonless BLE DFU (legacy Nordic/Adafruit OTA jump)

    A paired phone can now trigger the jump into the bootloader's BLE OTA mode directly from the DFU Firmware Upgrade app — no button press needed (nRF52 only). The service exposes the full legacy DFU characteristic set (Control Point, Packet, Revision) so iOS's DFU library can discover and identify the device correctly.

    Companion needs disconnecting from MeshCore app first!

    This was only tested with a handful of nodes and an iOS! This change can introduce errors in other phones I'm not aware of. A BLE bond delete + re-pair should solve it.

    BLE TX power bumped to +8 dBm

    Raised from +4 dBm → +8 dBm, the max output the nRF52840 / nRF54L high-power radio front-end supports — better range and link margin for the companion connection. This is still under test, but should not cause excessive/noticeable battery drain.

    MIT license formalized

    Added zephcore/LICENSE (MIT, same as upstream MeshCore) and flipped the SPDX-License-Identifier header across the source tree from Apache-2.0 to MIT. Vendored dependencies that carry their own compatible licenses (Monocypher, Zephyr patches) are noted at the bottom of the license file.

    Fixed

    • Companion startup races — initial-advert handling and contact-table iteration were running on sysworkq while the main thread mutated the same mesh/contact state, a race that could corrupt state or wedge the contact dump. Both now run on the main thread, driven through the event loop (MESH_EVENT_CONTACT_ITER), same as the rest of the mesh state machine.

    • Hardware-RTC writes no longer race the main loopzephcore_rtc_save() is now routed through the same deferred-to-main-thread mechanism as prefs flushes (MESH_EVENT_RTC_SAVE). GPS fixes land on the GNSS modem-chat thread and just stash the latest epoch; the actual blocking I2C write happens on the main thread, coalescing multiple pending fixes into one save.

    • MAX_ANON_CONTACTS cap fixed — transient/anon contact slots (e.g. created for BLE direct-message lookups to non-contact pubkeys) were being capped by MAX_CONTACTS instead of their own larger allotment, causing premature slot exhaustion/eviction.

    • XIAO nRF54L15 build fixed.

    Internals

    • ZephyrCompanionUSB — assembled V3 frames now wake the main thread via k_event instead of k_work_submit on sysworkq, matching the BLE RX path and removing a parse-vs-loop() race.

    • Buttonless DFU now hard-depends on SOC_SERIES_NRF52 (was a soft default y). Other nRF52-only paths (bootloader-version scan, VBUS detect, formatter/updater tools) were cleaned up to drop the redundant SOC_SERIES_NRF52X checks.

    • West Update — Zephyr updated to 4.4.1-rc1

  11. v20260606.150211 # 2 months ago · 2026-06-06 15:02 UTC

    Synced with upstream vanilla MeshCore v1.16.0

  12. v20260605.132226 # 2 months ago · 2026-06-05 13:22 UTC

    New

    USB serial console for companions Configure companion devices (e.g. Wio Tracker L1) by typing commands over USB, no phone app needed. Works in the flasher.meshcore.io serial console (or any terminal), just like the repeater CLI. Stays out of the way of the Bluetooth/USB app — only one connection is active at a time.

    Battery gauge calibration (adc.multiplier) Correct the battery voltage reading so the % is accurate for your device:

    • set adc.multiplier full — calibrate at full charge (easiest)
    • set adc.multiplier target <mV> — calibrate to a multimeter reading (most accurate)
    • set adc.multiplier 0 — reset to default
    • get adc.multiplier — show current

    Per-device battery curves Battery % now uses a real, device-specific voltage curve instead of a rough estimate, so readings are far more accurate. Included for Wio Tracker L1, T1000-E, SenseCAP Solar, ThinkNode M6, and RAK WisMesh Tag (others use a sensible default).

    Low-battery auto-shutdown Battery-powered companions now power down automatically when the cell runs low (default 3.3 V, ~10% on the new curves) to protect it from over-discharge. Shows a brief "Low Battery — Shutting Down" notice first (3 s on OLED; e-paper just leaves it on screen), and is skipped while charging or on USB power so it never cuts out a plugged-in device. On by default for nRF52 companions. Tune it over the USB console:

    • set autoshutdown <mV> — set the cutoff (1–5000 mV)
    • set autoshutdown 0 — turn it off
    • get autoshutdown — show current

    Clock source indicator The time in the top bar now shows a tiny letter telling you where it was last synced from: G (GPS), A (phone app), N (network), or L (local). If the device hasn't heard from GPS or the app in the last 12 hours, it falls back to L — the clock is just free-running on the device's own crystal at that point (drift is only a few seconds a day, so it stays accurate, but you know it isn't being checked against an outside source). After a restart it shows L until the first sync.

    Fixed

    Device name no longer overlaps the clock On the home screen, a long device name used to run underneath the time and battery in the top bar, making both unreadable. The name is now trimmed to fit the space before the clock, on any screen size.

  13. v20260603.123327 # 2 months ago · 2026-06-03 12:33 UTC

    (Re-Released because of native linux bugfixes)

    New

    • Room Server (BBS) role (no auto-build, no repeater function)
    • GAT562 30S Mesh Kit board support (nRF52840 + SX1262, RAK4631 core, 30 dBm PA) — one image for both the full kit and the screenless solar repeater.
    • Production is now the default build — no more prod.conf; debug.conf is the opt-in for logging.
    • USB companion decoupled from logging — CDC ACM transport now compiles independently of CONFIG_LOG. (companions are USB+BLE hybrid from now, only one type can be connected at a time)
    • Linux native target — built and shipped as executables (companion + repeater).
    • Battery reading should be more precise with board specific curve support and a standard lipo curve for devices that does not have any data on the internet about it's curve

    Bug Fixes

    • USB cable-yank no longer strands the companion on USB and blocks BLE until reboot.
    • BLE/USB transport handoff reworked to first-come-first-served, made thread-safe.
    • Zero-delay direct/zero-hop sends over BLE/USB no longer stall.
    • Channel reply from the Unread screen now sends instead of silently failing.
    • Repeater anon reply with a malformed return path now floods (safe fallback) instead of emitting a corrupt direct packet.
    • Header-error frames now counted as receive errors (and no more zombie packet receptions triggered)
    • Airtime/LDRO estimate fixed to track the driver across every SF/BW.
    • RNG seed derivation hardened against hash failure.

    Internals

    • Crypto: orlp/ed25519 → Monocypher 4.0.2 — audited, single-file. Existing identities load/sign/verify unchanged — no re-key, no migration, full wire compatibility.
    • Bumped west / pinned Zephyr tree.
    • Synced with vanilla MeshCore dev.
    • Split RepeaterMesh into Uplink + RegionCLI; deduped app-layer response/telemetry/JSON builders.
    • Simplified the packet manager, dispatcher, and radio config paths.
  14. v20260528.133858 # 2 months ago · 2026-05-28 13:39 UTC

    Security

    • Hardened login password comparison against timing side-channel attacks
    • Fixed weak entropy at first boot — initial device identity is now properly random
    • Fixed 3 RNG edge cases that could produce predictable output under certain startup conditions

    Bug Fixes

    • Fixed repeater + observer mode combo not working together
    • USB: unified CDC ACM init across companion and repeater, removed unnecessary boot delays

    New

    • Native Linux target — initial port for desktop testing
  15. v20260522.140428 # 3 months ago · 2026-05-22 14:04 UTC

    Wio Tracker — completely new joystick UI (thanks @Calvario) — new screen lifecycle, DM retry, channel send with feedback, GPS screen with altitude, lock screen, time sync, path hash bytes setting, and more.

    Heltec T114 board support (thanks @Calvario) — with and without screen.

    PSRAM activation on capable devices — enables OTA on boards that support it.

    Sync with vanilla MeshCore dev

    Bug Fixes BLE disabled state now correctly honoured — watchdog was silently re-enabling BLE on every housekeeping tick Fixed out-of-bounds read in path-decoding (BLE + LoRa-anon paths) Null-termination fix in Add/Update Contact command Fixed overflow frame being clobbered under BLE congestion Fixed T114 long press Several BLE audit hardening fixes (error codes, length checks, buffer sizing)

  16. v20260514.205836 # 3 months ago · 2026-05-14 20:58 UTC
    • New experimental RX-busy latch: ZephCore now tracks an in-progress reception across the full packet, so the transmitter won't step on a packet that's already being decoded.
    • Improved CAD (Listen-Before-Talk) behavior: the radio re-enters listening mode faster between TX retries on busy channels, and a stuck channel-busy state recovers automatically instead of requiring a reset.
    • updated default prefs to advert less and loop detect moderate
  17. v20260511.125830 # 3 months ago · 2026-05-11 12:58 UTC
    • Channel deletion safety: fixed bug where deleting the public channel could wipe other channels after reboot.
    • LBT retry priority: preserved flood packet priority during LBT-based retransmissions.
    • CAD/LBT mode switching: fixed radio reconfiguration so mode changes are fully applied.
    • rx_boost init: set deterministic startup state to avoid undefined boot behavior.
    • Atomic contact saves (QSPI): contacts now use temp+sync+rename on external flash devices.
    • Atomic write refactor: unified temp-file atomic write flow used by datastore save paths.
    • promicro_sx1262 build support: fixed/added board build wiring and related project metadata.
    • Vanilla sync (CompanionMesh): aligned local logic with upstream baseline changes.
    • SX1262 patch updates: refined Zephyr SX126x native patch and related ignore rules.
  18. v20260507.103840 # 3 months ago · 2026-05-07 10:38 UTC

    In one of my ravings trying to shorten rx-rx and tx-rx path I accidentally disabled hardware CAD. This release just does that: re-enables it.

    Also added formatters for nRF here: https://github.com/liquidraver/ZephCore/tree/master/formatter

    Hardware CAD test:

    Nothing catched in that 2 symbol CAD check:

    sx126x_irq_work_handler: IRQ status: 0x0080 sx126x_irq_work_handler: CAD done: free sx126x_irq_work_handler: IRQ status: 0x0001 sx126x_handle_irq_tx_done: TX done lora_radio_base: TX complete, RX restarted

    Something was in our channel during CAD check:

    sx126x_irq_work_handler: IRQ status: 0x0180←[0m sx126x_irq_work_handler: CAD done: activity←[0m sx126x_lora_send_async: LBT: channel busy←[0m lora_radio_base: hwSendAsync failed: -16←[0m zephcore_dispatcher: checkSend: startSendRaw failed! re-queuing delay=240 ....same goes on for 3-4 times then: sx126x_irq_work_handler: IRQ status: 0x0080 sx126x_irq_work_handler: CAD done: free sx126x_irq_work_handler: IRQ status: 0x0001 sx126x_handle_irq_tx_done: TX done lora_radio_base: TX complete, RX restarted

  19. v20260506.102120 # 3 months ago · 2026-05-06 10:21 UTC
    • Duty cycling is now disabled by default. Further research showed that preamble 16 is NOT enough for a reliable duty cycle in noisy environments, so we'll wait for most of the mesh switching to preamble 32 to test it further

    • SX driver had some serious issues with CAD so it is recommended to update to this version as soon as possible

    • GPS will not be disabled if no GNSS message comes in (on repeaters), that was a test-code that stuck

  20. v20260505.131914 # 3 months ago · 2026-05-05 13:19 UTC

    Kistarcsa-edition

    Tested in the depths of the tarcsa-cauldron with weak/noisy/who-knows-whats-the-problem-of-that-heltecv3 repeater, optimized for the aforementioned use-case.

    • Duty cycle is 13/3 now. Slightly more battery consumption, but better RX error margin. No use for extra battery time if packets are lost in noisy/weak link environments.
    • hwconfig now has a fail-safe so no more edge-case unsaved radio settings
    • SX TX Ramp time increased to 800µs from 200µs. expect very heavy latency increase if you can sense time in a different way normal humans do! (also expect cleaner spectral mask at 22 dBm. nobody cares about it, doesn't change anything in a meaningful way, but it's a playground, we optimize even things that doesn't really need optimizing) (ask your favourite AI what are the benefits of 800µs vs 200µs if interested)
    • Companions had too much initial jitter with zero benefit, so it was tuned down to RNG20-150msec
    • Claude noticed some line ending stuff by itself, without asking, so I gave him credit (heh)