Nothing has ever read it. The slot count comes from two other places: the
persisted array size (MAX_MQTT_SLOTS / RUNTIME_MQTT_SLOTS in MQTTPresets.h) and
the runtime cap MQTTBridge::getMaxActiveSlots(), which answers 5 with PSRAM and
2 without. A flag reading MAX_MQTT_BROKERS=3 sitting in every observer env
implies a third, lower limit that does not exist — it cost me a wrong answer
about how many MQTT slots a Heltec V4 exposes.
36 lines across 15 variants, all of them =3. Removing an unread -D cannot change
code: heltec_v4_repeater_observer_mqtt builds to a byte-identical size before and
after (1647744). The image checksum does differ, but so does it between two
clean builds of untouched source — ESP-IDF stamps the app descriptor with the
build time — so size is the meaningful comparison here.
All 606 envs still parse; heltec_v4, heltec_v3, station_g2, rak3112 and
xiao_s3_wio observer targets all build.
Neighbors publication was gated on BOARD_HAS_PSRAM. Removing that gate alone
was not enough: the feature built but was inert without PSRAM, because three
allocation sites asked for MALLOC_CAP_SPIRAM (which returns null with no
PSRAM), the bridge's persistent buffer was allocated inside a BOARD_HAS_PSRAM
block, and neighborDiscoverReady() rejected every pass at runtime on
psramFound(). The entry table also did not fit: finishNeighborDiscover put
pubkey_hex[50][65] plus entries[50] on the stack, a 4752-byte frame against
the mesh loop task's 8 KB.
- Gate on MAX_NEIGHBOURS plus PSRAM or an explicit per-variant
MQTT_NEIGHBORS_WITHOUT_PSRAM opt-in.
- Move the entry table and its hex strings into one heap block sized to the
pass; the frame drops from 4752 to 304 bytes.
- Prefer PSRAM and fall back to internal DRAM in the mesh-side allocations
and the ArduinoJson pool; hoist the bridge's persistent buffer out of the
BOARD_HAS_PSRAM block (psram_malloc already falls back).
- Keep the runtime psramFound() check only where the buffers are sized for
PSRAM, so a board whose PSRAM failed to init still refuses.
- Size for internal DRAM without PSRAM: 4 KB text buffer and 20 entries per
publish, keeping the pool to a single block and the peak near 13 KB rather
than ~35 KB. Oversized tables truncate and report total_neighbors as before.
Enabled on the ESP32-S3 observer envs (Heltec V3/WSL3, RAK3112, Heltec
Tracker v1.1/v2). Left off for the classic ESP32 T-LoRa V2.1-1.6, which is
already limited to one active TLS slot.
Costs ~7.4 KB static DRAM on repeaters and ~9.6 KB on room servers. The
prefs layout is unchanged, so this is neutral for existing devices.
Absorbs 106 upstream commits. Seven files conflicted; the substantive one
was upstream's new JSON ConfigSerializer (PR #2982), which replaces the
binary /com_prefs layout with /prefs.json and makes NodePrefs a
ConfigSerializer subclass.
Prefs migration
- Adopt upstream's ConfigSerializer. writeCommonPrefsImage() and its
documented offsets (0-294) are deleted, along with the now-unreachable
saveCommonPrefsImageAtomically()/CommonPrefsFileStore atomic rename path.
- Load order is /prefs.json, then /com_prefs, then /node_prefs. Upstream
dropped the /node_prefs fallback; it is restored here so devices that
never advanced past that filename keep their config.
- Legacy files are never removed, so migration cannot destroy its own
source and a deferred or failed save simply retries next boot.
- /com_prefs is treated as a format migration only, not an "upgrade", so
it does not trip the bridge.source tx->rx flip on existing nodes.
- The MQTTPrefsAtomicStore legacy gate is retained: the observer tail
recovered from an old-format file still commits to /mqtt_prefs before
/prefs.json is written.
- MQTTPrefs and /mqtt_prefs are untouched; savePrefs keeps its save_mqtt
parameter and now returns upstream's bool.
Fixes to upstream code
- RadioPrefs::structure() bound both "rxgain" and "fem_rxgain" to
rx_boosted_gain, so radio_fem_rxgain was never persisted. Bound to the
correct field.
- discovery_mod_timestamp was dropped from structure(); it gates
'since'-filtered DISCOVER replies and is set on every config change, so
losing it would silently stop discovery responses after a reboot. Added
as "disc_mod".
Merge artifacts repaired
- Restored bblanchon/ArduinoJson to the native test env; a clean but wrong
auto-merge at the lib_deps block boundary dropped it and broke all 19
host test suites.
- Migrated the fork's WebConfig UITask screens off the removed
DisplayDriver::Color enum to upstream's UIColor element types.
- Removed duplicate getCADEnabled() definitions in companion MyMesh.cpp
and simple_sensor SensorMesh.cpp that both sides had added.
- Dropped memset(&_prefs, 0, ...) in the four example meshes; NodePrefs now
has a vtable. guard gains an initializer that memset used to provide.
Other resolutions
- simple_room_server keeps both the fork's discover.* commands and
upstream's new room.post.
- docs/payloads.md taken from upstream, undoing content earlier merges had
reverted (Control data section, split login tables).
Verified: 273/273 host tests pass across native and native_kiss_modem;
Heltec v3 repeater, repeater_observer_mqtt, room_server_observer_mqtt,
sensor, and companion_radio_ble all build clean.
Add support for neighbor discovery and management in the MyMesh class.
This includes structures for neighbor information, methods for handling
neighbor advertisements, and control data reception. The new features
allow for dynamic neighbor management, enhancing the mesh network's
capabilities. Additionally, the maximum number of neighbors is set to
50 across various platform configurations.
Three real regressions from the 2026-07-19 upstream merge, all invisible to the
two prescribed smoke builds:
- heltec_tracker_v2/HeltecTrackerV2Board.cpp: the FEM trio
(setLoRaFemLnaEnabled/canControlLoRaFemLna/isLoRaFemLnaEnabled) was duplicated
verbatim by auto-merge, and upstream's new powerOff() (35f654ce) used
P_LORA_PA_POWER unguarded — a macro defined only for the tracker_v2 envs, while
heltec_tracker_v1_1 compiles the same board file. Guarded it the same way
LoRaFEMControl.cpp already guards that macro.
- SimpleMeshTables.h: the tracker variants pull in TFT_eSPI, whose
TFT_eSPI_ESP32_S3.h defines FS_NO_GLOBALS. That suppresses FS.h's own
'using fs::File', so File never reached global scope and every TU routed
through it failed with "'File' has not been declared" — here and at
simple_repeater/MyMesh.h:158. Restore the using when FS_NO_GLOBALS is set.
Explicit fs::File is not an option: File is also the global type on the
nRF52/RP2040 paths, which have no fs namespace.
- ST7735Display.cpp: upstream's HSPI fix (d30d8ed7) guarded on
HELTEC_LORA_V3 || HELTEC_TRACKER_V2. heltec_tracker_v1_1 matches neither and
fell through to &SPI1, which is not instantiated on ESP32. Added it to the guard.
Also parks LilyGo_TLora_V2_1_1_6_{repeater,room_server}_observer_mqtt with a
trailing underscore (the nibble_screen_connect convention from b8f1fad6), which
also excludes them from the workflows' enumeration regex. That board does NOT
fit and never did: 2,069,397 / 1,966,080 = 105.3% on flex, with no webconfig and
no upstream merge. It has been failing on production all along — the release
ships 30 envs, not 32 — hidden because build.sh does not propagate pio's exit
code. Dropping webconfig would recover ~46 KB of a ~101 KB deficit, so that is
not a fix. Rationale and options in .scratch/tlora-v2-oversize.md.
First upstream merge since the 2026-06-06 base (191 upstream commits). 14 files
conflicted; resolutions below.
Fleet-critical check (Constraint 1): upstream reordered NodePrefs members
(rx_boosted_gain / path_hash_mode moved to the struct tail) but did NOT change
/com_prefs. Persistence is written field-by-field at explicit offsets, so member
order is in-memory only. Verified the fork's writeCommonPrefsImage() is
byte-identical to upstream's inline writer at every offset (79 pad, 121, 122,
290-294). No migration needed.
Resolutions:
- CommonCLI.h: kept the fork's NodePrefs (superset) and adopted upstream's
setRxBoostedGain(bool)->bool signature change, which CommonCLI.cpp now uses to
report unsupported. Corrected a stale comment claiming rx_boosted_gain lives at
offset 79 (it is a pad; the field is at 290).
- CommonCLI.cpp: kept the fork's legacy /com_prefs migration and the extracted
writeCommonPrefsImage() call.
- UITask.cpp: three-way merge - upstream's drawTextCentered + powering-off
screen, plus the fork's WITH_WEBCONFIG portal/reboot screens.
- ESP32Board.cpp, MeshCore.h, platformio.ini: kept both sides (fork OTA additions
alongside upstream powerOff/enterDeepSleep and Packet.cpp).
- MicroNMEALocationProvider.h: took upstream's claim/release and added the
_claims member they depend on.
- MyMesh.cpp/.h (repeater + room server): kept the fork's superset defaults.
- Removed duplicate declarations auto-merge produced: RadioLibWrapper::_cad_enabled
and MyMesh::getCADEnabled().
Verification: native suite 15/15 (incl. upstream's new test_mesh_tables), both
MQTT smoke builds green, ArduinoJson pin check passes. Hardware validation next.
Replace direct JSON construction in MQTTMessageBuilder with calls to
MQTTPayloadBuilder for building status, packet, and raw messages.
This change improves code maintainability and reduces duplication by
centralizing message formatting logic. Additionally, update platformio.ini
to include ArduinoJson dependency for JSON handling.
Introduce a web configuration portal for easier node management and
provisioning without serial CLI. Enhance MQTT functionality with
improved IATA code validation, dynamic slot management, and
background NTP synchronization. Update web UI elements for better
user experience and security notes regarding open AP usage.
Credit: @yellowcooln (PR #12). Adds Wireless Tracker v1.1 board
def and MQTT observer envs for v1.1/v2; gate FEM control so v1.1
builds without KCT8103L pins.
companion_radio has its own NodePrefs without radio_fem_rxgain (matching
upstream, which doesn't wire companion either), so the startup call added in
e905451d broke every companion build:
MyMesh.cpp:973: error: 'struct NodePrefs' has no member named 'radio_fem_rxgain'
It went unnoticed behind the pre-existing companion build failures.
Also port upstream's FEM LNA overrides for heltec_t096 and heltec_tracker_v2
verbatim (board overrides + isLNAEnabled/const getters), so all three boards
upstream wires now match instead of reporting unsupported. Both variant files
compile; those targets still fail overall on the pre-existing non-observer
MQTT-source build-config issue (unchanged with this work stashed).
Match upstream pin map (KCT8103L CSD/CTX, I2C 6/17, LORA_TX_POWER=9) and restore meshcore LoRaFEMControl.cpp and HeltecTrackerV2Board.cpp. Run ESP32 at 240MHz for MQTT/WiFi bridge headroom. Sensor env drops fork-only ENV_PIN overrides to match upstream.
Made-with: Cursor
Upstream adds LoRaFEMControl for a KCT8103L pinout; this variant uses GC1109 (P_LORA_PA_*). Reintroduce the class with the same API and wire HeltecTrackerV2Board through it so FEM logic matches platformio.ini.
Made-with: Cursor
Reverted prior dev merge, then merged current meshcore-dev dev. Resolved
CommonCLI by restoring MQTT prefs flow with upstream com_prefs layout
(rx_boosted_gain at offset 79), rxgain CLI, poweroff/shutdown, and FEM
variant files from upstream.
Made-with: Cursor
Enable GPS time synchronization across all variants by passing
&rtc_clock to MicroNMEALocationProvider. When GPS gets a valid
fix, the RTC clock is now updated automatically every 30 minutes.
Updated 16 variants: rak4631, lilygo_tbeam_SX1262, rak_wismesh_tag,
lilygo_tbeam_supreme_SX1262, thinknode_m3, heltec_v4, thinknode_m1,
lilygo_tbeam_SX1276, meshadventurer, nano_g2_ultra, heltec_v3,
promicro, xiao_c3, heltec_tracker_v2, keepteen_lt1, heltec_mesh_solar.
The GC1109 FEM needs its VFEM_Ctrl pin held HIGH during deep sleep
to keep the LNA active, enabling proper RX sensitivity for
wake-on-packet. Without this, the LNA is unpowered during sleep
and RX wake sensitivity is degraded by ~17dB.
Release RTC holds in begin() after configuring GPIO registers
(not before) to ensure glitch-free pin transitions on wake.
Trade-off: ~6.5mA additional sleep current for significantly
improved wake-on-packet range.
Resolve conflict in heltec_tracker_v2/platformio.ini: keep
SX126X_REGISTER_PATCH=1 from upstream and retain GC1109 comment.
Co-authored-by: Cursor <cursoragent@cursor.com>