- Mesh, Dispatcher, Packet, packet pool and the core headers rebuilt from
Arduino MeshCore's text; ZephCore changes re-applied, each fenced
`// ZEPHCORE:` (diff vs upstream: 4540 -> 1147 lines)
- upstream header layout; MainBoard gains handleCommand/loop and the other
upstream members; RNG::nextInt back in Utils.cpp
- packet logging moved to the roles' log hooks (helpers/PacketLog.h)
- zero-hop direct ACKs deduplicated and length-checked as upstream
- RadioIncludes.h -> src/, LoRaConfig.h -> helpers/
- .editorconfig: shared files keep upstream formatting
Verified: 10 role builds, per-function disassembly review, bench DM/ACK
regression (T1000-E, Wio L1, XIAO S3 on 1.17.4) incl. via live repeater.
main_repeater.cpp and main_room_server.cpp applied the GPS duty interval
~55 lines before data_store.loadPrefs() ran, so getNodePrefs()->gps_interval
still held the initNodePrefs() value of 300 s.
That 300 is the *companion* default. loadPrefs() is not a pure read on these
roles: its first-boot path is the only place the 48 h repeater/room-server
default is established, and it persists that to flash so later code can treat
flash as authoritative. A stored value is likewise only in the struct after
loadPrefs() returns. Reading gps_interval before that call therefore cannot
see either the role default or the operator's setting -- only the companion
constant.
Two consequences, on every boot:
- A stock GPS-equipped repeater duty-cycled its GPS every 5 minutes instead
of every 48 hours, ~576x more wakeups than intended. On battery nodes this
is a straight power regression.
- Any operator value never reached the hardware. "set gps duty 0" (always-on)
persisted to flash correctly, and `get gps duty` reports the manager's
now-effective value, so it answered 300 -- readable as a stale default
rather than as the setting being ignored.
The GPS manager itself is correct: gps_start_acquiring(), the fix-done
callback, gps_enable() and the fresh-fix request all guard standby on
gps_duty_cycling(), so the always-on invariant holds once the manager is
actually told the interval is zero.
Same ordering class as the two bugs already documented in these files -- radio
frequency binding and the LED master switch -- where CLI readback looked right
and the hardware disagreed. main_companion.cpp has always had the correct
order (loadPrefs() at 1461, gps_set_poll_interval_sec() at 1616) and is
unaffected.
Move the interval and the mode switch that starts acquisition to just after
loadPrefs(). Callback registration stays where it was: it has no prefs
dependency and must precede acquisition.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two hardware-found fixes after on-air testing on the GAT562 kit:
- Add a 500ms push timer in main_room_server.cpp so the post-sync
engine advances at its intended cadence. ZephCore is event-driven
(no Arduino superloop), so without this the engine only ran on the
5s housekeeping tick — posts dripped out every ~5s and transmits
bunched up, causing timeouts/resends.
- Lower the post-sync hold from upstream's conservative defaults
(POST_SYNC_DELAY 6s -> 2s, PUSH_NOTIFY 2000ms -> 1000ms). These are
server-side timing only (no wire-format change), and take measured
delivery from ~6-7s down to ~2s.
Verified on hardware (910.525/62.5/SF7/CR8, two clients): normal-pace
messages deliver in ~2s with clean ACKs. Rapid-fire bursts can still
drop out-of-order messages via the per-client timestamp replay check
(unchanged from upstream) — left as-is to stay upstream-compatible.
Add the MeshCore Room Server role to ZephCore — a store-and-forward
shared message room. Clients log in with an admin or guest password
and post messages; the server pushes each new post to all other
logged-in clients (round-robin, per-client sync cursor, ACK + retry,
3-strike eviction).
Ported from upstream MeshCore's simple_room_server, structured as a
near-clone of RepeaterMesh so it reuses the proven ACL, region
filtering, CLI, adverts and telemetry; the post buffer + push engine
are the only net-new pieces:
- RoomServerMesh: PostInfo ring (MAX_UNSYNCED_POSTS=32), addPost,
pushPostToClient, getUnsyncedCount, processAck, onAckRecv, the
loop() push driver; room login (onAnonDataRecv parses the
sync_since cursor); posts/admin-CLI/keep-alive (onPeerDataRecv);
ADV_TYPE_ROOM advert; ServerStats wire layout; disable_fwd=1.
- main_room_server.cpp: event-loop entry (USB serial CLI, no BLE).
- Kconfig: ZEPHCORE_ROLE_ROOM_SERVER + ZEPHCORE_MAX_UNSYNCED_POSTS.
- CMakeLists role gating; boards/common/room_server.conf.
Post frame, SHA-256 ACK and login-reply layouts match upstream for
MeshCore app compatibility. Builds for gat562_30s
(FLASH 30.95% / RAM 32.46%). Not yet hardware-tested.