Brings in the external FEM gain preferences (fem_txgain, PR #3137 plus the
companion-side port), the AGC reset rxgain fix, the LR2021 preamble/IRQ
timeout logic, and assorted variant fixes (T096, T-Echo Card TCXO, promicro
pinmap, minewsemi, R1 Neo).
Conflict resolutions:
- SH1106Display: both sides fixed T-Beam Supreme startup independently. Kept
our _initialized guard and DISPLAY_ADDRESS_ALT override, took upstream's
SA0-pair fallback and its unconditional display.begin() so the frame buffer
is allocated even when no panel answers.
- MyMesh/SensorMesh/CommonCLI: took upstream's fem_txgain default and wiring,
kept our comments and the observer-side prefs layout.
Also fixes CustomLLCC68Wrapper, which upstream missed when sx126xResetAGC
gained its rx_boost_gain parameter. No variant builds that wrapper today, so
neither tree failed to compile.
Setting SPI pins to values that are OOB of the g_ADigitalPinMap[] array causes OOB reads. This variant.h has pin 0 as 0xFF which passes through as NRFX_SPIM_PIN_NOT_USED.
Documents why the largest allocatable block on a non-PSRAM observer walks down in
16 KiB steps at every TLS reconnect, and what to do about it.
The framework builds mbedTLS with the symmetric buffer configuration
(CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN 16384), so each broker slot holds two 16 KiB
record buffers in internal DRAM and two slots cost 64 KiB on a board with about
80 KiB free. Confirmed against the sdkconfig the Heltec env actually links, not
the IDF 5.3 libs package that belongs to the esp32c6 env.
Rebuilding only the mbedTLS archives with an asymmetric outbound buffer of 4 KiB
is ABI-safe, because in_buf/out_buf are heap pointers sized in ssl_setup() and no
public struct embeds a CONTENT_LEN-sized array. Dropping KEEP_PEER_CERTIFICATE is
not, since it changes mbedtls_ssl_session layout, so it is excluded.
Also records two traps worth not rediscovering: esp32-arduino-lib-builder's
release/v4.4 clones its dependencies at master and no longer resolves, and
pointing platform_packages at a file:// framework copy overwrites the shared
package for every other project on the machine.
Introduced consistent preferences for external LoRa FEM RX and TX gain settings in NodePrefs. Updated companion MyMesh to apply these settings during initialization and transmission. Added unit tests to verify the round-trip serialization of these new preferences.
Nine commits reducing the MQTT bridge's internal-DRAM footprint, plus four
fixes that rode with them (invalid path encodings, stale-JWT scan after a
clock correction, setup-retry interval measured from the failure, retried
setup consuming the reconnect allowance).
Touches no prefs surface -- nothing in NodePrefs, MQTTPrefs, or
ConfigSerializer -- so /prefs.json layout is unaffected and there is no
fleet config risk.
Soak evidence: every soak branch already contained this work in full. Device 1
has run it 69.6 h with 325,852 publishes, 0 errors and 0 reboots. The caveat
worth carrying: that long-duration evidence is all on the reduced-TLS
framework (OUT_CONTENT_LEN 4096). Device 3 is now soaking it on the stock
framework, which is where the allocation-ordering interaction with the full
16 KiB record buffers actually gets exercised.
Both channels move together: the beta workflow's FIRMWARE_VERSION must equal
production's, since a node compares its embedded base against its own channel's
manifest and a mismatch would read as permanently behind.
Resets the per-base build counter to N=1 on both channels. Nodes still on
v1.16.0.N take the diff_base branch in ESP32Board's OTA comparison ('different
base version is always an update'), so the counter going backwards does not
strand them; the build-number arithmetic only runs when the base matches.
The flasher repo's config.json version key still needs the matching bump.
Picks up upstream MeshCore 1.17.0 and the dev commits that followed it.
Notable upstream content:
- 1.17.0 version/build-date bump in the example MyMesh headers.
- anon_req hardening: reply_path_len is now uint8_t with an isValidPathLen()
bounds check and a 0xFF sentinel; reply_path_hash_size is gone.
- LR2021 support (Meshnology W12, Seeed MeshTracker X1) incl. side detectors,
multi-SF and the new `extra.sf` get/set CLI.
- CustomLFS 0.2.2 -> 0.2.3 (GD25Q64C support).
- kiss_modem envs for several nRF52 variants; ThinkNode M6 GPS/flash fixes.
Conflicts resolved:
- platformio.ini: took upstream's CustomLFS 0.2.3, kept our lib_ignore for
the vendored PsychicMqttClient.
- CommonCLI.h: kept both upstream's USE_LR2021 configSideDetectors() hook and
our fault-alert callbacks.
NodePrefs gains upstream's extra_sf[4], which is not registered with
ConfigSerializer, so /prefs.json layout is unchanged.
Verified: 275/275 native host tests pass; Heltec_v3_repeater_observer_mqtt,
Heltec_v3_repeater and heltec_v4_repeater_observer_mqtt all build.
The flasher's /webconfig page frames webui/index.html — the real page this
firmware serves — and runs it against a browser simulator, so visitors drive the
actual product rather than looking at screenshots of it. That only stays true if
the page travels with the docs, so it joins the .md files this workflow already
syncs, and the flasher's build-webconfig-demo.py re-injects the simulator.
Triggers on webui/index.html alongside the existing markdown paths. Still gated
on observer-firmware, so nothing publishes until the portal CLI reaches the
release channel at v1.17.0; the flasher carries a hand-copied page until then.
LilyGo_TLora_V2_1_1_6 observer builds failed to link:
undefined reference to `WebConfigServer::MAX_BATCH'
in handleStatus and handleCliPost
An in-class initialiser is only a declaration under C++11, which is what the
xtensa-esp32 toolchain builds with. Every previous use of MAX_BATCH was a
comparison, which reads the value and needs no symbol. Reporting it as
status.max_cmds, and naming it in the "too many commands" error, passes it to
ArduinoJson — which takes `const T&` — and binding a reference odr-uses it.
It linked on most targets because the compiler folded the reference away, and
failed on the ones where it did not. A cast at the two call sites would have
silenced it just as narrowly; defining the symbols is what stops the next use
from depending on the same luck. MAX_BODY and STOP_WARN_MS get the same
treatment for the same reason, before they are the next to be passed by
reference.
A worktree checked out inside the repo, caught by a `git add -A` and recorded
as a gitlink. Same problem df070833 fixed for .build-wt-028a5dca, and the same
fix: untrack it and widen the ignore rule, which only covered .build-wt-*.
It is not harmless. The gitlink pointed at a different commit than the worktree
had checked out, so `git status` was permanently dirty — which is how it nearly
got re-committed here, and it makes CI checkouts warn about a submodule path
with no .gitmodules entry.
The worktree directory is gone; branch fix/station-g3-ota-manifest-base and its
commit are untouched. That branch still carries one commit not on prod —
5000391c, which adds OTA_MANIFEST_BASE to the station_g3 variant. It belongs on
prod, whose build.sh does not yet inject that flag, and must NOT come to dev,
where build.sh does and an .ini declaration cannot be overridden (SCons reorders
-U/-D) — it would pin dev and beta builds to the production manifest.
Adding build_date to the WebConfigServer constructor broke every
*_room_server_observer_mqtt target: simple_room_server constructs the portal as
well, and only simple_repeater was updated. Nothing caught it because every
build run to that point had been a repeater target.