github liquidraver/ZephCore 1.16.6-zephcore
Firmware 1.16.6-zephcore

6 hours ago

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

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 paced by that same clock — deterministic, confirmed dead on hardware. It has been replaced
    everywhere by a two-clock beat source: counting CPU cycles across fixed intervals of an
    independent low-frequency oscillator, whose physical drift is genuinely random. The same mechanism
    now runs uniformly on ESP32, nRF52 and MG24. (nRF was never in danger — its RNG is radio-independent —
    this adds an equally strong second source there for defence in depth.)

This was not signed off by inspection. A new on-device self-test (tools/rng_selftest) generated 1024
identities per platform through the real generation path
: zero duplicate keys, and pairwise-difference
statistics matching ideal randomness — including a torture run with the hardware RNG deliberately
disabled
, where the beat source alone still produced fully diverse keys. A node whose hardware RNG
silently dies no longer means a predictable identity.

Tip

Upgrading does not regenerate your identity — the key made at first boot stays. If your node is an
ESP32 board and its identity was generated by an earlier release, that key was created under the old,
weaker entropy. If you want one generated under the new code, factory-reset / format the node (or
erase-flash and reflash) — the next boot creates a fresh identity through the hardened path. The cost is
real: a new public key, so your contacts must re-add you, and contacts, channels and bonds start clean.
Whether that trade is worth it is your call — for a casual node, probably not; for a node whose identity
matters (an admin key, a well-known repeater), it is worth considering. nRF boards have no reason to
regenerate.

GPS standby now actually saves power

CONFIG_PM_DEVICE (device power management) is enabled for the first time, carefully scoped: system-managed
PM stays off, nothing suspends automatically, and every PM call is made explicitly from the main thread.
This buys two things:

  • The GNSS UART is suspended while GPS is off or in standby. An armed nRF UARTE receiver keeps the
    high-frequency clock running — roughly 0.5–1 mA continuously on nRF52840 — even with the GPS module
    itself powered down. Nodes with GPS disabled in prefs were paying this for their entire uptime. The UART
    is resumed before every wake, so no NMEA is lost.
  • GNSS drivers that boot suspended now get resumed. gnss-nmea-generic initializes suspended under
    device PM and never opens its data pipe until told to — this was the old "enabling PM breaks GPS" trap,
    now handled once at boot with retries.

The UART suspend requires an nRF UARTE port and a board that can power the GPS module down. Boards
without power control keep the UART running and forgo the saving: suspending the port while the module is
still streaming can wedge the main thread, leaving the radio deaf and the CLI answering -> busy. This
affects RAK3401 1W and GAT562 30S. Other UART drivers are left alone entirely — the saving is
UARTE-specific.

Removed: Adaptive Power Control

APC tried to save battery by lowering TX power when neighbors reported more signal margin than they
needed. It has been removed entirely, because the saving it chased is not one most nodes actually pay.

TX power is only spent at the instant of a transmission. A companion that sits idle for days and sends a
handful of messages spends almost nothing on transmit to begin with — its drain is BLE advertising, the
radio sitting in receive, and the MCU. APC could not meaningfully reduce that. On the nodes where transmit
volume is high enough to matter, the feature never got a clean run: its measurement was structurally
confounded (the echo it measured comes from the neighbor's own fixed-power transmission and does not
respond to your reduction), and the staleness timer that ramped power back up could not tell "our link
degraded" from "the mesh was quiet." Rather than ship another round of tuning on a feature with no
demonstrated payoff and a real downside — a node that reduces too far goes silent, and cannot detect that
it has — it's gone.

What changes for you:

  • set tx apc and set/get apc.margin are removed. set tx <dbm> sets a fixed power, as it always
    did, and get tx now answers with a plain number (> 22) exactly like upstream Arduino MeshCore.
  • Nodes that had APC enabled now transmit at their configured tx power. If you had turned it on,
    check that your configured power is what you actually want — it is now used verbatim.
  • The APC line is gone from the on-device radio display, and the TX row now shows a single power
    value instead of an effective/max pair — there is only one number now, so it is only printed once.
  • Your saved settings are safe. APC's two prefs bytes are kept reserved at their original offsets, so
    no stored configuration shifts and nothing else is misread on upgrade.

New boards

muzi works R1 Neo

A pocket LoRa node — nRF52840 + SX1262 (RAK4630 stamp on a custom muzi baseboard), with GNSS, buzzer,
battery-backed RTC and a single user button. No display, so the UI is button actions only. It reuses the
RAK4631 flash layout (SoftDevice s140 v6.1.1, app @ 0x26000), so the stock UF2 bootloader works as-is —
no bootloader reflash needed. The board's soft-power design is handled in devicetree: the DCDC rail latches
on at boot and is released on shutdown, so long-press power-off works properly.

Two things still want confirming against real hardware: the battery divider multiplier is an estimate, and the GNSS module identity is
unconfirmed. Reports welcome.

Thanks to @WillyJL, who requested this port and provided the hardware details / most of the code
it is based on.

ThinkNode M9 — experimental, source build only

Not part of this release's prebuilt binaries or the web configurator — bring-up is still in progress,
so it has been held out of the automated release matrix for now. If you have the hardware, build and flash
it yourself (west build -b thinknode_m9/esp32s3/procpu zephcore --pristine); everything below describes
that source build, not a download.

Consider this a preview, not a finished port. It builds, flashes and runs, and it is worth testing if
you have the hardware and don't mind rough edges — but several things have not been confirmed on a real
device yet, and a few are knowingly missing.

It is ZephCore's first ESP32-S3 + LR1110 board, so a fair amount of it is new ground rather than a copy of
an existing port. Currently unverified: display init parameters (inherited from another ST7789V board — if
the screen is mirrored or upside down, that's a known one-line fix), GPS output from the CC1167Q module,
battery calibration, and whether the USB-C port reaches the native USB pads at all (BLE is the companion
path meanwhile).

The M9 has no user button in hardware — its keypad is the only input — so it now runs the full companion UI
driven by a new driver for the STC8H keypad MCU on I2C. Enter, back, home, menu and page prev/next are wired
up, and typed characters pass straight through. The arrow keys are not mapped yet: no reference we could
find documents their codes, so rather than guess, the driver logs any key it doesn't recognise along with its
hex value. Press the arrows once with a serial console attached and they can be bound in a one-line change.
Until then you can move between pages but not up and down within a list.

Also worth knowing on this board specifically: the SD slot shares the radio's SPI bus with the LR1110
and the display. An inserted card was confirmed to break LR1110 radio-firmware flashing on this board — the
write reports success and the radio then runs nothing. To keep a card from disturbing normal operation, the
firmware now puts any inserted card into SPI idle at startup, before the radio driver initialises, so it
stays quiet until something actually addresses it. If the M9 misbehaves in a way you can't explain, try
it once with the card removed
— that is still the fastest way to rule the slot out.

Feedback from anyone with an M9 is genuinely useful here — the bring-up list in the board's README says
exactly what to look at.

Other fixes and improvements

  • Fixed: Heltec V3 failed to build from source once device PM was enabled. The upstream Zephyr board
    definition force-enables the GPIO power-domain driver without the devicetree guard that normally comes
    with it, and ZephCore's overlay deliberately replaces both of that board's power domains with plain
    regulators — so the driver was being compiled for hardware that no longer existed in the devicetree, and
    tripped over a helper that only exists under a config it never turned on. The power-domain subsystem is
    now explicitly off for V3, which is simply the truth about that board. Binary releases were unaffected;
    this only bit source builds. (V4 / V4.3 use ZephCore-local board definitions and were never affected.)
  • Air530z GPS driver patch comments corrected to match the new PM strategy.
  • ESP32 BLE controller heap raised from 32 KB to 64 KB (all ESP32 boards except T-Beam). The Espressif
    BLE controller allocates 30 KB from this pool in a single call during bt_enable(); at 32 KB that left
    almost no margin for whatever else had allocated before BLE came up. Found during ThinkNode M9 bring-up,
    where the extra display, keypad and LR1110 init pushed past it and the blob asserted instead of failing
    gracefully — every ESP32 board was one allocation away from the same failure. T-Beam is held at the old
    32 KB (only 768 bytes of DRAM free) since it can't absorb the increase; that narrower margin is tracked
    separately.
  • Fixed: companion battery calibration (adc_multiplier) reset itself every reboot. It was applied at
    boot but never written back to prefs, so any calibration you set silently reverted to the board default
    on the next power cycle. Now persisted.

Sidenote: LR1110 radio firmware updater

Not part of this release and not something you need — but if you have an LR1110 board and feel like it,
you can now update the radio chip's own firmware to Semtech's current version (0x0402, the H1_2026
security release). This is the LR1110's internal firmware, not ZephCore. Prebuilt tools for T1000-E and
ThinkNode M9 are in LR1110_updater/
— flash one, watch it over serial, then flash ZephCore again afterwards.

Worth reading the README
first. Two things in particular: the chip bootloader update it performs is one-way, and on the M9 you
must take the SD card out (it shares the radio's SPI bus and silently corrupts the write — the tool
checks and refuses to start, but still). Boards on older firmwares work perfectly well with
ZephCore and are fine left alone.

Recommended upgrade checklist

  1. Set an admin password on v1.16.5? Try the all-lowercase form if you are locked out; otherwise
    re-set it after upgrading.
  2. ESP32-S3 / ESP32-C boards that skipped v1.16.5: flash -merged.bin once over USB/serial. Data survives.
  3. Everything else, from v1.16.2 onward: just flash — bonds and data survive.
  4. From v1.16.1 or older: just flash — self-migrates on first boot; re-bond once.
  5. From Official MeshCore: just flash — auto-formats on first boot.
  6. ESP32 node whose identity you care about? Consider a one-time factory reset so the key is
    regenerated under the hardened entropy — see the tip in the identity-randomness section. Optional,
    and it changes your public key.
  7. Anything odd? Format first.

Don't miss a new ZephCore release

NewReleases is sending notifications on new releases.