Skip to content

Releases: smellyspice/MeshCore

Trifecta bridge — beta 12

Pre-release

Choose a tag to compare

@smellyspice smellyspice released this 08 Sep 01:50

What's new since beta11

  • WiFi modem sleep is now explicitly disabled on every WiFi/ESP-NOW code
    path
    (esp_wifi_set_ps(WIFI_PS_NONE)), matching Espressif's own
    performance guidance for latency-sensitive WiFi/ESP-NOW use. Previously
    this was never set anywhere in the fork, meaning every board was left on
    the default modem-sleep power-save mode — which adds RX-window latency
    and jitter to every incoming ESP-NOW packet. Confirmed via extensive live
    RF field testing (RSSI/power-sweep rig, thousands of samples across
    S3-Zero, C3 SuperMini, and XIAO S3 boards).
  • Per-board WIFI_TX_POWER overrides reset to the 20dBm code default on
    every board except the XIAO.
    Beta11 lowered several boards to 11-15dBm
    as a single fleet-wide precaution against a suspected brownout/collapse
    issue. Follow-up field testing (solo runs, physically-separated runs, and
    a large-sample run) traced that original anomaly to near-field
    desense/proximity interference between co-located test radios, not a
    real TX-power brownout on those boards — so the reduction wasn't
    buying anything and was just cutting range. The one board with a
    confirmed, reproducible TX-side brownout (downlink collapses at
    17-20dBm even as RSSI improves) is the XIAO acting as base/repeater; it
    keeps a reduced value, raised slightly from 11dBm to 15dBm to sit just
    above the measured collapse point.

Full technical writeup of the field testing behind both changes is kept in
this fork's own working notes, not part of this repo's tracked history.

Trifecta bridge — beta 11

Pre-release

Choose a tag to compare

@smellyspice smellyspice released this 05 Sep 02:49

What's new since beta10

  • WiFi/ESP-NOW tx power is now a per-board override, not a hardcoded
    80 (20dBm) at every call site.
    A standalone ESP-NOW RSSI/power-sweep
    field test found that some boards show a success-rate collapse at high
    tx power even though RSSI keeps improving — a brownout signature, not a
    range problem — while others just keep improving all the way to 20dBm
    with no downside. Based on that: C3 SuperMini and S3-Zero (companion
    envs) are lowered to 11dBm, Heltec V4's ESP-NOW+IP bridge env to
    15dBm (a stability/range midpoint — that unit also had ESP-NOW
    connect/range problems, so it trades back some power). Everything else
    keeps the prior effective default of 20dBm. Not yet a fleet-wide policy —
    each board type needs to be confirmed safe at high power individually,
    not assumed.
  • New: companion build for real-LoRa XIAO S3+WIO boards
    (Xiao_S3_WIO_companion_radio_wifi) — WiFi-pairing companion (phone app
    connects over your LAN instead of BLE), alongside the existing no-LoRa
    C3 SuperMini / S3-Zero companion builds.
  • New tools/monitor.py (not part of firmware, for the RSSI power-sweep
    test rig) — not relevant to normal Trifecta use.

Repeaters (Xiao S3 WIO, Heltec V3, Heltec V4)

Which file to flash:

  • -merged.bin — for a blank/fresh board. Flash at offset 0x0:
    esptool.py --chip esp32s3 write_flash 0x0 <file>-merged.bin
  • plain .bin (no -merged suffix) — app-only, for upgrading a board
    that's already running MeshCore. Preserves identity, WiFi credentials,
    and BLE bonds. Flash at offset 0x10000:
    esptool.py --chip esp32s3 write_flash 0x10000 <file>.bin

Debug builds (_debug) have full logging on for troubleshooting; the
plain envs are the same code with logging compiled out for regular use.

After flashing, configure over the standard MeshCore serial CLI
(set bridge.channel, set bridge.secret, set wifi.ssid, set wifi.pwd,
set ip.host, etc.) — no custom tooling needed.

Room server, no LoRa (Waveshare ESP32-S3-Zero, ESP32-C3 Super Mini)

Same flashing pattern as above (-merged.bin at 0x0 for blank, plain
.bin at 0x10000 to upgrade; use --chip esp32c3 for the C3 board).
If changing an existing board's role, erase flash fully first —
esptool.py erase_flash — before flashing the merged image, since a role
change doesn't touch the SPIFFS partition otherwise and stale config from
the old role can survive.

Configure the ESP-NOW join the same as a repeater (set bridge.channel/
set bridge.secret), then optionally set room.channel <name> <psk> for
the public-channel bridge.

Companion, no LoRa (Waveshare ESP32-S3-Zero, ESP32-C3 Super Mini)

No text CLI (speaks the binary phone-app protocol) — configure the
ESP-NOW join with the bundled script instead:

python3 set_bridge_params.py list
python3 set_bridge_params.py <port> set <channel 1-14> <secret>

Companion, real LoRa radio (XIAO S3+WIO)

Xiao_S3_WIO_companion_radio_wifi pairs over WiFi/TCP instead of BLE —
useful if you'd rather not deal with BLE pairing, or want to reach the
board from a phone/app on the same LAN. Same flashing pattern as above
(-merged.bin at 0x0 blank, plain .bin at 0x10000 upgrade,
--chip esp32s3). The shipped binary has placeholder WiFi credentials
baked in at build time — set your real network over the CLI/companion
protocol (set wifi.ssid, set wifi.pwd) after first boot, it does not
require reflashing.

Monitoring multiple boards at once

python3 watch_boards.py

Auto-detects every connected board, connects to all of them, and streams
their debug output live in one terminal. Press n to dump neighbors.all,
b to dump bridge status, c to toggle watching companion boards, from
every connected board at once. See the script's own --help for details.

Full writeup, use cases, and setup details: https://smellyspice.github.io/MeshCore/


A personal MeshCore fork, unmerged upstream, developer release only — do not run on a mesh you depend on.

Trifecta bridge — beta 10

Pre-release

Choose a tag to compare

@smellyspice smellyspice released this 02 Sep 01:35

What's new since beta9

  • ESP-NOW zero-hop adverts now actually broadcast. They have no path and
    no specific destination -- "whoever's nearby," same as their inherently
    broadcast behavior on LoRa. The ESP-NOW bridge was unicasting them like
    any other direct-route packet once a peer's MAC was known, silencing them
    to every other peer on the same segment. Fixed on both the repeater and
    peer side, scoped precisely to advert + direct-route + empty-path packets
    -- every other packet type keeps its existing unicast-with-retry behavior.
  • ESP-NOW peer limit raised 6 → 19 (the real safe max: ESP-NOW's hard
    20-peer limit, minus one slot for the broadcast address). The old value
    had no documented rationale and looks like it was modeled on ESP-NOW's
    encrypted-peer limit, which doesn't apply here since peers are always
    registered unencrypted.
  • New: WiFi-companion envs for both the C3 SuperMini and S3-Zero, as an
    alternative to BLE pairing.
  • Fixed a real ESP32-C3 companion crash — BLE controller init reliably
    failed at contact/channel/queue capacity values copied unmodified from
    the S3-Zero variant, since the C3 has less total SRAM. Bisected safe
    values on real hardware for both BLE-only and USB+BLE dual envs.
  • MQTT bridge is now built into every standard Trifecta repeater build
    (Heltec V3/V4) — no separate binary needed.
    set mqtt.enabled on|off
    set mqtt.server <url>       # mqtt://, mqtts://, ws://, or wss://
    set mqtt.username/password  # optional, if your broker needs auth
    set mqtt.iata <code>        # optional, short site code included in the topic
    
    Off by default. Broker must be on your own LAN/VPN — TLS is not
    certificate-verified. Single-threaded, polled from the main loop — no
    separate task, no cross-core state to get wrong.
  • Room servers now persist their post history to flash. Previously the
    post backlog clients sync against lived in RAM only — a reboot wiped it
    and any client syncing afterward got nothing for that gap. It's now
    mirrored to flash on every new post and restored on boot.
  • Fixed: a contact explicitly set to the Read-Only ACL role could still
    post.
    The posting check only excluded the Guest role; Read-Only fell
    through to the same path as Read-Write. Also reported upstream as
    meshcore-dev/MeshCore#3340.
  • Room server config note: if allow.read.only is enabled with a
    blank guest password, a blank-password login currently gets read/write
    access instead of read-only — a known upstream issue
    (meshcore-dev/MeshCore#940,
    fix pending in PR #2995).
    Until that lands, set a real (non-blank) guest password that you never
    hand out — any anonymous/wrong-password login then correctly falls
    through to read-only.

Flashing

  • Blank/fresh board: flash the -merged.bin file at address 0x0.
  • Upgrading an already-configured board: flash the plain .bin file at
    address 0x10000 — this preserves identity, radio config, WiFi/BLE
    bonds, and all CLI settings; it does not erase the board.
esptool.py --chip esp32s3|esp32c3 write_flash 0x0 <file>-merged.bin        # blank board
esptool.py --chip esp32s3|esp32c3 write_flash 0x10000 <file>.bin           # upgrade in place

Trifecta bridge — beta 9

Pre-release

Choose a tag to compare

@smellyspice smellyspice released this 31 Aug 21:38

What's new since beta8

  • Advert-timer jitter, all roles. Identically-configured boards (e.g.
    several room servers attached to the same repeater's ESP-NOW bridge) were
    flood-adverting in perfect sync — the timer is a fixed offset from
    boot/last-fire time, so boards flashed/configured together stayed
    locked together indefinitely. Both the flood-advert timer (+/-10 min) and
    the local/zero-hop advert timer (+/-2 min) now apply a random offset,
    re-rolled every reschedule, so nodes drift apart instead of firing
    together. Applied identically to the repeater, room server, and
    companion/sensor code paths that share this timer pattern.

Flashing

  • Blank/fresh board: flash the -merged.bin file at address 0x0.
  • Upgrading an already-configured board: flash the plain .bin file at
    address 0x10000 — this preserves identity, radio config, WiFi/BLE
    bonds, and all CLI settings; it does not erase the board.
esptool.py --chip esp32s3|esp32c3 write_flash 0x0 <file>-merged.bin        # blank board
esptool.py --chip esp32s3|esp32c3 write_flash 0x10000 <file>.bin           # upgrade in place

Debug vs non-debug repeater/room-server builds

The _debug envs have full debug logging compiled in (BRIDGE_DEBUG,
MESH_DEBUG, etc.) — small timing/CPU cost, meant for pairing with
watch_boards.py while troubleshooting. Non-debug envs are the same code
with logging compiled out — quieter, for regular use once you're done
troubleshooting.

Tools

  • watch_boards.py — multi-board serial monitor.
  • set_bridge_params.py — set bridge.channel/bridge.secret from the
    command line.

Reference site with the full breakdown of every piece (ESP-NOW companions,
room servers/Echo Boards, the IP bridge, and the safety mechanisms behind
it): https://smellyspice.github.io/MeshCore/


This is an unmerged personal fork — developer release only. Please don't
run it on a mesh you depend on.

Trifecta bridge — beta 8

Pre-release

Choose a tag to compare

@smellyspice smellyspice released this 30 Aug 14:52

Trifecta repeater firmware (real LoRa + ESP-NOW bridge + IP bridge, all at once) — beta 8.

Built from commit 7ee4ca44 on the espnow-ip-bridge branch. Covers the companion, room server, and repeater roles on Heltec V3, Heltec V4, Xiao S3 WIO, Waveshare ESP32-S3-Zero, and — newly confirmed on real hardware this release — the ESP32-C3 "Super Mini".

Two builds per repeater/room-server board, same code, different tradeoff:

  • _debug filesBRIDGE_DEBUG/MESH_DEBUG/ESPNOW_DEBUG_LOGGING (plus MESH_PACKET_LOGGING/WIFI_DEBUG_LOGGING on the real-LoRa repeaters) all on. Noisier serial output, and debug logging has a real (small) timing/CPU cost, but gives full visibility into bridge/routing decisions. Use this for testing, troubleshooting, or pairing with watch_boards.py below.
  • Plain files (no _debug suffix) — functionally identical, debug logging stripped out. Quieter and slightly leaner/faster. Use this once you're done actively debugging and just want the board running.
    (Companion firmware has no _debug build — its serial line is the binary phone-app protocol, and debug text would collide with it.)

What's new since beta 7

  • New: ESP-NOW time-sync beacon. A dual-bridge repeater (ESP-NOW + IP bridge together, e.g. Heltec V3/V4 with the IP bridge configured) already gets real NTP time. It now broadcasts that time to its ESP-NOW segment every 5 minutes, authenticated purely by the shared bridge.secret (same trust boundary as the rest of the bridge protocol). Any no-LoRa ESP-NOW client (companion, repeater-role, or room server) picks it up and sets its own clock — but only once per 12-hour cooldown, so a steady stream of beacons only actually moves the clock about twice a day. Fixes these boards' clocks drifting to a bogus default with no other time source available. No CLI action needed on either side — it's automatic once the repeater has WiFi/NTP configured.
  • Confirmed working on real hardware: the ESP32-C3 "Super Mini" (companion/repeater/room-server roles), first shipped untested in beta 7. Found and fixed a real bug in the process: the generic esp32-c3-devkitm-1 PlatformIO board definition doesn't enable native USB CDC the way the S3-Zero's board definition does, so the serial CLI was completely silent on a fresh C3 board even though flashing worked fine. Fixed by adding ARDUINO_USB_MODE/ARDUINO_USB_CDC_ON_BOOT explicitly.
  • New: ESP32_C3_SuperMini_room_server_bridge_espnow_debug and ESP32_S3_Zero_room_server_bridge_espnow_debug — the room server role had no debug build until now (needed one to diagnose the two issues above and a wrong-room-password rejection on a test board).
  • Changed: room server's channel-bridge CLI command renamed room.channel <name> <psk>set room.channel <name> <psk>, for consistency with the rest of the CLI's set/get convention (get room.channel unchanged).
  • Fixed: ESPNowBridge's bridge→client retry ceiling raised 3→12, matching the client→bridge direction's existing ceiling (raised in beta 4 for the same reason). Both directions cross the same physical, sometimes-marginal ESP-NOW link — there was no reason retries should help only one direction.

Repeaters (Xiao S3 WIO, Heltec V3, Heltec V4)

Which file to flash:

  • -merged.bin — for a blank/fresh board. Flash at offset 0x0:
    esptool.py --chip esp32s3 write_flash 0x0 <file>-merged.bin
  • plain .bin (no -merged suffix) — app-only, for upgrading a board that's already running MeshCore. Preserves identity, WiFi credentials, and BLE bonds. Flash at offset 0x10000:
    esptool.py --chip esp32s3 write_flash 0x10000 <file>.bin

After flashing, configure over the standard MeshCore serial CLI (set bridge.channel, set bridge.secret, set wifi.ssid, set wifi.pwd, set ip.host, etc.) — no custom tooling needed. A repeater with both bridge.channel/bridge.secret and the IP bridge (wifi.ssid/ip.host or ip.port) configured will automatically start broadcasting time to its ESP-NOW segment once WiFi/NTP land — nothing extra to turn on.

Room server, no LoRa (Waveshare ESP32-S3-Zero, ESP32-C3 Super Mini)

Same flashing pattern as above (-merged.bin at 0x0 for blank, plain .bin at 0x10000 to upgrade; use --chip esp32c3 for the C3 board). If changing an existing board's role (e.g. this same physical board was previously a companion), erase flash fully first — esptool.py erase_flash — before flashing the merged image. A role change doesn't touch the separate SPIFFS partition otherwise, so stale config from the old role can survive and cause boot issues.

Configure the ESP-NOW join the same as a repeater (set bridge.channel/set bridge.secret, matching whatever real repeater it should join), then optionally set room.channel <name> <psk> for the public-channel bridge.

Companion, no LoRa (Waveshare ESP32-S3-Zero, ESP32-C3 Super Mini)

No text CLI (speaks the binary phone-app protocol) — configure the ESP-NOW join with the bundled script instead:

python3 set_bridge_params.py list
python3 set_bridge_params.py <port> set <channel 1-14> <secret>

Monitoring multiple boards at once

python3 watch_boards.py

Auto-detects every connected board, connects to all of them, and streams their debug output live in one terminal. Press n to dump neighbors.all, b to dump bridge status, c to toggle watching companion boards, from every connected board at once. See the script's own --help for details.

Full writeup, use cases, and setup details: https://smellyspice.github.io/MeshCore/


A personal MeshCore fork, unmerged upstream, developer release only — do not run on a mesh you depend on.

Trifecta bridge — beta 7

Pre-release

Choose a tag to compare

@smellyspice smellyspice released this 30 Aug 03:17

Trifecta repeater firmware (real LoRa + ESP-NOW bridge + IP bridge, all at once) — beta 7.

Built from commit a4e94a33 on the espnow-ip-bridge branch. Covers the companion, room server, Heltec V3, Heltec V4, and Xiao S3 WIO.

Two builds per repeater board, same code, different tradeoff:

  • _debug filesBRIDGE_DEBUG, MESH_DEBUG, MESH_PACKET_LOGGING, WIFI_DEBUG_LOGGING all on. Noisier serial output, and debug logging has a real (small) timing/CPU cost on a busy repeater, but gives full visibility into bridge/routing decisions. Use this for testing, troubleshooting, or pairing with watch_boards.py below.
  • Plain files (no _debug suffix) — functionally identical, debug logging stripped out. Quieter and slightly leaner/faster. Use this once you're done actively debugging and just want the repeater running.

What's new since beta 6

  • New: room server can now join a Trifecta repeater's mesh with zero LoRa hardware, same no-LoRa ESP-NOW bridge pattern the companion already uses (Waveshare S3-Zero). New env: ESP32_S3_Zero_room_server_bridge_espnow.
  • New: that room server can passively listen on one public group channel (#hashtag-style) and feed its text messages straight into the room's post feed — CLI: room.channel <name> <psk> (accepts either hex or base64, whichever your app shows you), get room.channel to read it back. Deliberately one channel per board, not several — keeps the full post-length budget available and needs no channel-name prefix. See get bridge.channel/set bridge.channel/set bridge.secret to join the ESP-NOW side first, same as the companion.
  • Fixed: a genuinely unconfigured ESPNOW_BRIDGE_RADIO board (companion, repeater, or room server) boot-looped. The mesh auto-sends a self-advert on boot regardless of whether the ESP-NOW radio has been configured yet; the send path reached an XOR-keying step that divides by the configured secret's length, which is zero pre-configuration — instant crash, every boot, on any board that hadn't been set up yet.
  • Fixed: a companion/room-server-role board joined to a repeater's ESP-NOW bridge could never receive anything relayed back out that same bridge. A shared dedup table meant a packet that arrived over ESP-NOW was already marked "seen" by the time the repeater tried to mirror it back out over ESP-NOW to reach a different peer on the same bridge — silently dropped, every time, regardless of RF conditions. Real-world symptom: an ESP-NOW-joined room server never receiving channel messages sent by an ESP-NOW-joined companion through the same repeater, while the identical message from a real LoRa companion worked fine.
  • Fixed: once a repeater's ESP-NOW bridge had learned even one peer, it stopped broadcasting entirely — every subsequent send became unicast-only to already-known peers, permanently excluding any peer that never transmits unprompted (a passive room server, by design). FLOOD-route traffic (channel messages, self-adverts) now gets one broadcast after the normal known-peer unicast fan-out, so a passive listener is reachable too; DIRECT traffic's existing unicast-with-retry behavior is unchanged.
  • New, untested (no hardware yet): an ESP32-C3 "Super Mini" variant spec. Companion/repeater/room-server envs all compile clean against the same chip-agnostic radio code the S3-Zero uses — nothing hardware-specific has been verified yet (onboard LED pin deliberately left unset rather than guessed).

Repeaters (Xiao S3 WIO, Heltec V3, Heltec V4)

Which file to flash:

  • -merged.bin — for a blank/fresh board. Flash at offset 0x0:
    esptool.py --chip esp32s3 write_flash 0x0 <file>-merged.bin
  • plain .bin (no -merged suffix) — app-only, for upgrading a board that's already running MeshCore. Preserves identity, WiFi credentials, and BLE bonds. Flash at offset 0x10000:
    esptool.py --chip esp32s3 write_flash 0x10000 <file>.bin

After flashing, configure over the standard MeshCore serial CLI (set bridge.channel, set bridge.secret, set wifi.ssid, set wifi.pwd, set ip.host, etc.) — no custom tooling needed.

Room server, no LoRa (Waveshare ESP32-S3-Zero)

Same flashing pattern as above (-merged.bin at 0x0 for blank, plain .bin at 0x10000 to upgrade). If changing an existing board's role (e.g. this same physical board was previously a companion), erase flash fully first — esptool.py erase_flash — before flashing the merged image. A role change doesn't touch the separate SPIFFS partition otherwise, so stale config from the old role can survive and cause boot issues.

Configure the ESP-NOW join the same as a repeater (set bridge.channel/set bridge.secret, matching whatever real repeater it should join), then optionally room.channel <name> <psk> for the public-channel bridge.

Monitoring multiple boards at once

python3 watch_boards.py

Auto-detects every connected board, connects to all of them, and streams their debug output live in one terminal. Press n to dump neighbors.all, b to dump bridge status, c to toggle watching companion boards, from every connected board at once. See the script's own --help for details.

Full writeup, use cases, and setup details: https://smellyspice.github.io/MeshCore/


A personal MeshCore fork, unmerged upstream, developer release only — do not run on a mesh you depend on.

Trifecta bridge — beta 6

Pre-release

Choose a tag to compare

@smellyspice smellyspice released this 29 Aug 03:05

Trifecta repeater firmware (real LoRa + ESP-NOW bridge + IP bridge, all at once) — beta 6.

Built from commit 2d9324e7 on the espnow-ip-bridge branch. Covers the companion, Heltec V3, Heltec V4, and Xiao S3 WIO.

Two builds per board, same code, different tradeoff:

  • _debug filesBRIDGE_DEBUG, MESH_DEBUG, MESH_PACKET_LOGGING, WIFI_DEBUG_LOGGING all on. Noisier serial output, and debug logging has a real (small) timing/CPU cost on a busy repeater, but gives full visibility into bridge/routing decisions. Use this for testing, troubleshooting, or pairing with watch_boards.py below.
  • Plain files (no _debug suffix) — functionally identical, debug logging stripped out. Quieter and slightly leaner/faster, since nothing is spending time formatting and pushing log lines out over serial every packet. Use this once you're done actively debugging and just want the repeater running.

What's new since beta 5

  • The IP bridge's reconnect no longer retries at a flat interval forever. On a real connect failure it now backs off (10s → 20s → 40s → 60s, capped) instead of hammering every 5 seconds indefinitely. The cap is deliberately short (60s, not minutes) — this bridge only ever has one peer, so there's no shared server to be polite to, and the priority is noticing the peer come back quickly.
  • Fixed: that backoff wasn't actually escalating past 20s. The periodic DNS re-resolve (kicks in after a couple of failures, in case the peer's address genuinely changed) was resetting the failure counter on every successful lookup — DNS succeeding says nothing about whether the peer itself is reachable, so this silently capped the backoff at 20s regardless of how long the real outage was. Now the counter only resets on an actual successful connection.
  • The backoff distinguishes "no WiFi at all" from "WiFi's up but the peer isn't answering." A WiFi outage isn't the peer's fault and shouldn't cost the same escalating penalty — while WiFi itself is down, retries stay at a short flat interval (persistent, not growing) so reconnecting happens quickly the moment WiFi actually comes back, rather than potentially sitting out a long backoff window that had nothing to do with the peer at all.
  • The board's clock now re-syncs via NTP every 12 hours, not just once at boot. These boards have no battery-backed RTC, so the clock was previously set once (at boot or WiFi reconnect) and then left to drift for the rest of the uptime. Same "only if WiFi is actually configured" guard as the original sync.

Repeaters (Xiao S3 WIO, Heltec V3, Heltec V4)

Which file to flash:

  • -merged.bin — for a blank/fresh board. Flash at offset 0x0:
    esptool.py --chip esp32s3 write_flash 0x0 <file>-merged.bin
  • plain .bin (no -merged suffix) — app-only, for upgrading a board that's already running MeshCore. Preserves identity, WiFi credentials, and BLE bonds. Flash at offset 0x10000:
    esptool.py --chip esp32s3 write_flash 0x10000 <file>.bin

After flashing, configure over the standard MeshCore serial CLI (set bridge.channel, set bridge.secret, set wifi.ssid, set wifi.pwd, set ip.host, etc.) — no custom tooling needed.

Monitoring multiple boards at once

python3 watch_boards.py

Auto-detects every connected board, connects to all of them, and streams their debug output live in one terminal. Press n to dump neighbors.all, b to dump bridge status, c to toggle watching companion boards, from every connected board at once. See the script's own --help for details.

Full writeup, use cases, and setup details: https://smellyspice.github.io/MeshCore/

This is an unmerged personal fork, not reviewed by MeshCore maintainers — a developer/beta release for testers who want to help shake it out, not something to run on a mesh anyone actually depends on, debug logging on or off. If your mesh matters to you or to other people using it, wait for something further along than this. See the writeup for known limitations before flashing.

Trifecta bridge — beta 5

Pre-release

Choose a tag to compare

@smellyspice smellyspice released this 27 Aug 19:04

Trifecta repeater firmware (real LoRa + ESP-NOW bridge + IP bridge, all at once) — beta 5.

Built from commit 7ca989a8 on the espnow-ip-bridge branch. Covers the companion, Heltec V3, Heltec V4, and Xiao S3 WIO.

Two builds per board, same code, different tradeoff:

  • _debug filesBRIDGE_DEBUG, MESH_DEBUG, MESH_PACKET_LOGGING, WIFI_DEBUG_LOGGING all on. Noisier serial output, and debug logging has a real (small) timing/CPU cost on a busy repeater, but gives full visibility into bridge/routing decisions. Use this for testing, troubleshooting, or pairing with watch_boards.py below.
  • Plain files (no _debug suffix) — functionally identical, debug logging stripped out. Quieter and slightly leaner/faster, since nothing is spending time formatting and pushing log lines out over serial every packet. Use this once you're done actively debugging and just want the repeater running.

What's new since beta 4

  • The IP bridge now runs over TCP+TLS-PSK instead of UDP+DTLS-PSK. UDP gave no delivery guarantee — a dropped datagram on the IP hop was silently invisible on both ends, with no way to tell it apart from a real failure elsewhere. TCP's own retransmit/ordering now covers that. mbedtls_net_connect() has no non-blocking TCP variant, so the client-side connect is done manually (non-blocking socket()/connect(), polled to completion) to keep the bridge's synchronous, single-threaded design intact — no new FreeRTOS task.
  • A new incoming connection can no longer preempt an active session just by connecting. A bare TCP connect() (a port scanner, or any random connection attempt) now has to complete its own TLS-PSK handshake — proving it holds the real shared secret — before it's allowed to replace the currently active peer. This also fixes a real operational issue found during testing: previously, if a peer's IP changed (e.g. after a network switch), the other side needed a manual reboot to accept the new connection; now a legitimately reconnecting peer gets back in automatically once it re-authenticates.
  • Added TCP-level keepalive (30s idle, 10s probe interval, 3 probes) as a backstop alongside the existing app-level ping/pong heartbeat — catches some dead-link cases the app-level heartbeat alone might be slower to notice, and incidentally keeps NAT/router connection-tracking state alive on a port-forwarded link.
  • If you're reaching a repeater through port-forwarding / DDNS for the IP bridge, the forwarded port now needs to allow TCP, not just UDP. This is the one real deployment-facing change — update your router's forwarding rule for the bridge port if you're using this over the internet rather than a local network.
  • Housekeeping: binaries no longer embed the build machine's local file paths (a standard but easily-avoidable side effect of compiling, unrelated to any of the above). Also added the companion firmware and set_bridge_params.py to this release — every file ever shipped across any beta now lives in every new one, so nothing requires digging through older releases.

Repeaters (Xiao S3 WIO, Heltec V3, Heltec V4)

Which file to flash:

  • -merged.bin — for a blank/fresh board. Flash at offset 0x0:
    esptool.py --chip esp32s3 write_flash 0x0 <file>-merged.bin
  • plain .bin (no -merged suffix) — app-only, for upgrading a board that's already running MeshCore. Preserves identity, WiFi credentials, and BLE bonds. Flash at offset 0x10000:
    esptool.py --chip esp32s3 write_flash 0x10000 <file>.bin

After flashing, configure over the standard MeshCore serial CLI (set bridge.channel, set bridge.secret, set wifi.ssid, set wifi.pwd, set ip.host, etc.) — no custom tooling needed.

Monitoring multiple boards at once

python3 watch_boards.py

Auto-detects every connected board, connects to all of them, and streams their debug output live in one terminal. Press n to dump neighbors.all, b to dump bridge status, c to toggle watching companion boards, from every connected board at once. See the script's own --help for details.

Full writeup, use cases, and setup details: https://smellyspice.github.io/MeshCore/

This is an unmerged personal fork, not reviewed by MeshCore maintainers — a developer/beta release for testers who want to help shake it out, not something to run on a mesh anyone actually depends on, debug logging on or off. If your mesh matters to you or to other people using it, wait for something further along than this. See the writeup for known limitations before flashing.

Trifecta bridge — beta 4

Pre-release

Choose a tag to compare

@smellyspice smellyspice released this 26 Aug 20:50

Trifecta repeater firmware (real LoRa + ESP-NOW bridge + IP bridge, all at once) — beta 4.

Built from commit 18599bb3 on the espnow-ip-bridge branch. Covers Heltec V3, Heltec V4, and Xiao S3 WIO.

This build has debug logging enabled (BRIDGE_DEBUG, MESH_DEBUG, MESH_PACKET_LOGGING, WIFI_DEBUG_LOGGING) — noisier serial output than a normal release, in exchange for full visibility into bridge routing decisions. Pairs with watch_boards.py below.

What's new since beta 3

  • Much better delivery reliability for ESP-NOW-only companions (e.g. the LoRa-less S3-Zero) with a marginal RF link to their paired repeater. Root-caused via live timing instrumentation: at normal desk range, some exchanges needed several retries to get a MAC-layer ACK back — a real RF margin issue, not a routing bug. The ESP-NOW send retry ceiling is raised from 4 to 12 attempts (with a matching timeout budget) — each retry is cheap (~15-20ms), so a slower success now replaces what used to be a visible timeout error.
  • Fixed: the IP bridge's DTLS handshake could hang forever if the peer disappeared mid-handshake (network switch, IP address change), requiring a manual reboot to recover. Now times out after 30s and retries automatically, same as any other connection failure.
  • New: the IP bridge's heartbeat ping now defers by a tick when the ESP-NOW bridge has been active recently, on dual-bridge boards — avoids the heartbeat competing with an inbound ESP-NOW frame's ACK timing for the shared radio.
  • New: get bridge.channel now shows the radio's actual live WiFi channel alongside the configured value, flagging a mismatch explicitly — useful since ESP-NOW and any WiFi-STA use (the IP bridge) share one physical radio.
  • watch_boards.py: new b hotkey dumps IP/ESP-NOW bridge status (WiFi status, IP status, live channel) from every connected board at once; new c hotkey toggles watching companion boards too (skipped by default since they speak a different protocol) — useful for seeing a companion's own debug output live, at the cost of some undecodable binary noise mixed in.

Repeaters (Xiao S3 WIO, Heltec V3, Heltec V4)

Which file to flash:

  • -merged.bin — for a blank/fresh board. Flash at offset 0x0:
    esptool.py --chip esp32s3 write_flash 0x0 <file>-merged.bin
  • plain .bin (no -merged suffix) — app-only, for upgrading a board that's already running MeshCore. Preserves identity, WiFi credentials, and BLE bonds. Flash at offset 0x10000:
    esptool.py --chip esp32s3 write_flash 0x10000 <file>.bin

After flashing, configure over the standard MeshCore serial CLI (set bridge.channel, set bridge.secret, set wifi.ssid, set wifi.pwd, set ip.host, etc.) — no custom tooling needed.

Monitoring multiple boards at once

python3 watch_boards.py

Auto-detects every connected board, connects to all of them, and streams their debug output live in one terminal. Press n to dump neighbors.all, b to dump bridge status, c to toggle watching companion boards, from every connected board at once. See the script's own --help for details.

Full writeup, use cases, and setup details: https://smellyspice.github.io/MeshCore/

This is an unmerged personal fork, not reviewed by MeshCore maintainers — a developer/beta release for testers who want to help shake it out, not something to run on a mesh anyone actually depends on. This build specifically also ships with debug logging left on, which is noisier and slightly heavier than a real release build. If your mesh matters to you or to other people using it, wait for something further along than this. See the writeup for known limitations before flashing.

Trifecta bridge — beta 3

Pre-release

Choose a tag to compare

@smellyspice smellyspice released this 25 Aug 23:51

Trifecta repeater firmware (real LoRa + ESP-NOW bridge + IP bridge, all at once) — beta 3.

Built from commit 467c10f1 on the espnow-ip-bridge branch. Covers Heltec V3, Heltec V4, and Xiao S3 WIO.

This build has debug logging enabled (BRIDGE_DEBUG, MESH_DEBUG, MESH_PACKET_LOGGING, WIFI_DEBUG_LOGGING) — noisier serial output than a normal release, in exchange for full visibility into bridge routing decisions. Pairs with watch_boards.py below.

What's new since beta 2

  • Fixed: a repeater's own broadcast traffic (adverts, flood replies) could get misrouted to a single bridge instead of reaching everyone. The heuristic that redirects a reply straight back out the bridge it came from was also catching FLOOD-route sends and zero-hop self-adverts — both of which are meant to reach everyone, not bounce back to one peer. Found live: a repeater's own self-advert would silently fail to reach a peer repeater over the bridge whenever it happened to fire shortly after any unrelated bridge traffic. Fixed for both cases; always falls back to broadcasting to everyone whenever it isn't confidently a genuine reply.
  • New: RF-preference delay now covers first contact, not just already-established replies. Previously only the path-return reply got a brief hold-back (giving a real RF path a head start before mirroring over the IP bridge) — the very first message between two new peers had no such protection, so the IP link could win the race and become baked into the learned route even when a working RF path existed. Now the whole first-contact message family (requests, responses, text messages, admin logins) gets the same treatment.
  • New: that delay skips entirely when the destination is already known to be bridge-only reachable (e.g. an ESP-NOW-only companion with no LoRa radio at all) — so the two main use cases for this feature (LoRa-less companion, indoor repeater backhaul) aren't taxed with pointless latency; only genuinely redundant RF-vs-bridge topologies pay the delay.
  • Changed: bridge-neighbour tracking now recognizes companions, not just other repeaters — needed for the above to actually detect a bridge-only companion as bridge-only. The stock, RF-only neighbors CLI table is unchanged. Table size is now matched to the RF-neighbour table instead of a fixed, unexamined cap of 8.

Repeaters (Xiao S3 WIO, Heltec V3, Heltec V4)

Which file to flash:

  • -merged.bin — for a blank/fresh board. Flash at offset 0x0:
    esptool.py --chip esp32s3 write_flash 0x0 <file>-merged.bin
  • plain .bin (no -merged suffix) — app-only, for upgrading a board that's already running MeshCore. Preserves identity, WiFi credentials, and BLE bonds. Flash at offset 0x10000:
    esptool.py --chip esp32s3 write_flash 0x10000 <file>.bin

After flashing, configure over the standard MeshCore serial CLI (set bridge.channel, set bridge.secret, set wifi.ssid, set wifi.pwd, set ip.host, etc.) — no custom tooling needed.

Monitoring multiple boards at once

python3 watch_boards.py

Auto-detects every connected board, connects to all of them, and streams their debug output live in one terminal — press n to dump neighbors.all from every connected repeater at once. See the script's own --help for details.

Full writeup, use cases, and setup details: https://smellyspice.github.io/MeshCore/

This is an unmerged personal fork, not reviewed by MeshCore maintainers — a developer/beta release for testers who want to help shake it out, not something to run on a mesh anyone actually depends on. This build specifically also ships with debug logging left on, which is noisier and slightly heavier than a real release build. If your mesh matters to you or to other people using it, wait for something further along than this. See the writeup for known limitations before flashing.