develop
8
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2c1fd6b7ea | fix(ScanI2C): fetch the SE050 probe response with requestFrom() (#11133) | ||
|
|
e64d20548c |
feat(eth): port mesh/http to RP2350 + W5500 (HTTP/80 + HTTPS/443) (#10573)
* add watchdog to rp2xx0
* feat(eth-api): phase 0 skeleton — HTTP API server on TCP/80 (503 only)
First step of porting mesh/http/ to RP2350 + W5500 (today ESP32-only).
Phase 0 stands up the listener over the existing arduino-libraries/Ethernet
stack (no Mongoose — its built-in TCP/IP would conflict with EthernetServer
and break OTA/MQTT/NTP). Skeleton parses request line, logs it, replies 503.
Real handlers (/api/v1/{info,fromradio,toradio}) come in later phases.
- New mesh/eth/ethApiServer.{h,cpp} gated by HAS_ETHERNET_API
- Wired into ethClient init+loop in parallel with existing ethHttpOTA (port 4244)
- Enabled in both wiznet_5500_evb_pico2_e22p and pico2_w5500_e22 variants
- Build impact on wiznet: +~9 KB flash (63.0% used), <1 KB RAM
* feat(eth-api): phase 1 — implement /api/v1/{fromradio,toradio} with PhoneAPI
Replace phase 0 skeleton with the real handlers that bridge the HTTP transport
to PhoneAPI, mirroring mesh/http/ContentHandler (ESP32) semantics.
- EthHttpAPI : public PhoneAPI (api_type=TYPE_HTTP, checkIsConnected=true),
outside the MESHTASTIC_EXCLUDE_WEBSERVER gate so it builds on RP2350.
- Minimal HTTP parser: method/path/query/Content-Length, no allocations beyond
Arduino String, capped at 32 header lines x 256 bytes (anti-DoS).
- OPTIONS preflight -> 204 with CORS + X-Protobuf-Schema.
- GET /api/v1/fromradio[?all=true]: stream-write up to 64 protobufs from
webAPI.getFromRadio(), Connection: close framing (HTTP/1.0 style).
- PUT /api/v1/toradio: read Content-Length bytes (<=512), call
webAPI.handleToRadio(), echo body back.
- 404 default for unknown paths, 405 for wrong method, 400 for bad body.
Validated e2e against wiznet @ 192.168.1.143:
- OPTIONS /api/v1/fromradio -> 204 + correct CORS headers
- PUT ToRadio{want_config_id=1} (2 bytes) -> 200 + echo
- GET /api/v1/fromradio?all=true -> 200 + 3476 bytes (config+channels+nodeinfo)
- GET /api/v1/nonexistent -> 404 unknown endpoint
- OTA HTTP on port 4244 untouched and still responsive
Build impact on wiznet_5500_evb_pico2_e22p: +2 KB flash (63.1%), +1.8 KB RAM
(17.6%) vs phase 0.
* fix(eth-api): move accept loop to dedicated OSThread (20ms tick)
The API server was being polled from the Ethernet client Periodic which runs
every 5s. Measured impact before this fix on wiznet @ 192.168.1.143:
req 1: ttfb=6.458s total=6.509s (matches the 5s tick + handler overhead)
req 2: connection refused (W5500 sockets exhausted)
req 3: connection refused
After: same hardware, same network, back-to-back requests:
req 2: ttfb=0.287s
req 3: ttfb=0.015s
req 4: ttfb=0.022s
req 5: ttfb=0.020s
The web client (meshtastic/web served from localhost) was visibly stalling
mid-handshake — it had pulled 109 nodes + 19 messages but channels and device
info never arrived. With the periodic-only polling, every request takes 5s
and the W5500's 4 hardware sockets fill up under the burst.
EthApiServerThread mirrors the WebServerThread pattern from ESP32
mesh/http/WebServer.cpp: adaptive interval — 20ms when there's recent
traffic, 100ms after 5s of idle, 500ms after 30s. Auto-registers with the
OSThread scheduler on construction.
ethHttpOTA still ticks from the periodic; left unchanged because OTA is one
large transfer that tolerates the latency, and minimizing scope here to one
behaviour change at a time.
* refactor(eth-api): extract handlers to shared module via IStreamReadWrite
Phase 2.0 of the TLS port — separate transport from request handling so
the upcoming HTTPS server can reuse the exact same parser + routing logic.
- New ethApiHandlers.{h,cpp}: Request, parser, CORS helpers, fromradio/toradio
handlers, EthHttpAPI : PhoneAPI subclass. All driven by a single
IStreamReadWrite interface that inherits Print (so client.print(...) keeps
working transparently).
- ethApiServer.cpp slimmed to ~90 LOC: now just the EthernetServer(80) +
OSThread + an EthernetClientStream adapter that forwards reads/writes to
the underlying EthernetClient. No behaviour change.
Validated on wiznet @ 192.168.1.143: 5 curl requests TTFB 18-110ms (same
as pre-refactor), protobuf round-trip PUT 200 + GET ?all=true 3499 bytes.
Flash impact: +200 bytes (63.2%); RAM unchanged (17.6%).
* feat(eth-tls): ECDSA P-256 self-signed cert generation for HTTPS API
mbedTLS-based cert generation module that produces a SAN=IP self-signed
ECDSA P-256 server certificate, persisted under LittleFS so subsequent
boots reuse the same key. Generation runs once on a dedicated OSThread
so the ECDSA keygen path (~430 ms) never blocks the Periodic stack or
the Ethernet reconnect loop.
- mbedTLS 3.6.2 sources compiled in via scripts/add_mbedtls_sources.py
(BuildSources of pico-sdk/lib/mbedtls/library/*.c, all 108 files).
MBEDTLS_USER_CONFIG_FILE injected as CPPDEFINES tuple in the script —
build_flags shell escape mangles the embedded quotes on Windows.
- src/mbedtls_user_config.h undefs MBEDTLS_HAVE_TIME, HAVE_TIME_DATE,
TIMING_C, NET_C, FS_IO, PSA_ITS_FILE_C, PSA_CRYPTO_STORAGE_C, and
defines MBEDTLS_NO_PLATFORM_ENTROPY (entropy_poll.c uses a raw
platform check, not gated by a flag).
- src/mesh/eth/ethCert.{h,cpp}: ECDSA P-256 keypair + cert with
SAN(IP=current) using pico-sdk get_rand_64() directly as f_rng,
bypassing mbedtls_entropy. DER buffers heap-allocated to keep the
OSThread stack within budget. Cert + key + ip persisted under
/prefs/eth_*.der so subsequent boots reuse the same identity.
- Gated by HAS_ETHERNET_TLS_API. Standalone phase: generation only;
HTTPS server (TCP/443) wired up in the follow-up commit.
* feat(eth-tls-api): phase 2.2 — HTTPS server on TCP/443 reusing handlers
Brings up an mbedTLS server on port 443 that defers to the ECDSA P-256
self-signed cert produced by [[ethCert]]. Reuses the HTTP request /
response handlers from [[ethApiHandlers]] via the IStreamReadWrite
interface — there is exactly one code path for routing / CORS / PhoneAPI
integration regardless of whether the transport is plain TCP or TLS.
EthTlsApiServerThread (OSThread):
- Phase A: poll isEthCertReady() every 500 ms while the cert worker
runs. Once true, parse cert chain + key, build ssl_config (TLS server,
stream transport, default preset, VERIFY_NONE since we are the cert
issuer), install own cert, run ssl_setup, bind tlsServer on 443.
- Phase B: standard adaptive accept loop (20 / 100 / 500 ms tick),
identical to the plain-HTTP server.
Per-connection flow:
1. session_reset on the static ssl context (1 in-flight session — multi-
session pool is Phase 3 if needed)
2. set_bio routes mbedtls I/O to two C callbacks (netSend / netRecv)
that bridge to the live EthernetClient via the void* ctx
3. handshake loop (sync, blocking, with 10 s recv timeout) — mbedTLS
errors are logged with mbedtls_strerror
4. wrap (ssl, client) in MbedTlsStream → handleApiClient(stream)
5. close_notify + client.stop
Stack budget continues the Phase 2.1-bis discipline: every large buffer
(ssl context, cert chain, pk_key, ssl_config) lives in BSS as a static
global. The OSThread stack only holds the per-connection EthernetClient
adapter and small mbedtls return codes.
Validated on wiznet 192.168.1.143:
- cert load-from-FS path: 13 ms (regen skipped on second boot)
- cert gen path: 290 ms (first boot only)
- ssl_setup chain: parse cert, parse key, ssl_config_defaults,
conf_own_cert, ssl_setup all return 0
- end-to-end: curl -k https://192.168.1.143/api/v1/fromradio → 200 OK
with application/x-protobuf, CORS, X-Protobuf-Schema headers, server
initiates close_notify cleanly
Footprint vs 2.0:
- flash 70.5% → 87.3% (+220 KB for mbedtls_ssl + x509 server-side code)
- RAM static 17.6% → 19.8% (+11 KB BSS for ssl_context + cert chain)
- heap at runtime adds ~32 KB per active session (mbedtls in/out
record buffers, default MBEDTLS_SSL_*_CONTENT_LEN=16384)
Next: validate from Firefox direct (https://192.168.1.143 → warning
accept → JSON visible), then from client.meshtastic.org hosted to
confirm the mixed-content block is gone.
* fix(eth-tls): add KeyUsage + EKU serverAuth and cap TLS 1.2 for browser compat
Two browser-compat fixes that surfaced in Firefox validation:
1. Cert v1 only had Basic Constraints + SAN. NSS / Firefox refuse to
treat a cert as a TLS server cert without an Extended Key Usage
extension naming id-kp-serverAuth since 2023 — the error surfaces as
a non-overridable 'Secure Connection Failed' with no 'Accept the
Risk' path. Add KeyUsage(digitalSignature + keyEncipherment, critical)
and ExtendedKeyUsage(serverAuth, critical). Bump cert/key/ip file
paths to '_v2' so live boards regenerate on next start instead of
loading a v1 cert that the browser silently refuses.
2. pico-sdk mbedtls defines MBEDTLS_SSL_PROTO_TLS1_3 in its default
config, but the server-side 1.3 plumbing in this vendored build is
incomplete: Firefox and openssl-3.5's s_client default to 1.3 and
the handshake dies 4 ms in with MBEDTLS_ERR_ERROR_GENERIC_ERROR
(-0x0001). curl/SChannel happened to default to 1.2 so it masked the
issue earlier. Cap min/max_tls_version to TLS 1.2 — clients downgrade
transparently and we keep the ECDHE-ECDSA + AES-GCM / CHACHA20-POLY1305
suites that already work end-to-end.
Validated on wiznet 192.168.1.143:
- cert v2 dumps with the 4 extensions visible (openssl x509 -text):
BasicConstraints CA:FALSE, KeyUsage(critical) digitalSignature +
keyEncipherment, ExtendedKeyUsage(critical) serverAuth, SAN IP.
- openssl s_client (no version flag): downgrades to TLS 1.2, handshake
completes, verify_return=18 (self-signed, expected).
- Firefox: warning self-signed -> Advanced -> Accept Risk -> handshake
OK in 632 ms with CHACHA20-POLY1305, request reaches handleApiClient
and returns the protobuf body.
* perf(eth-api): HTTP/1.1 keep-alive — one handshake per session, not per request
Before this change /fromradio used 'Connection: close' framing (HTTP/1.0
style with no Content-Length), forcing each poll to redo the full TLS
handshake. client.meshtastic.org needs ~80 sequential /fromradio polls
during initial sync (one per packet from the config-replay state
machine: MyInfo, channels, every Config_*, every ModuleConfig_*, every
NodeInfo), so the user-visible load time was ~50 s of pure handshake
overhead (80 requests * ~625 ms ECDSA P-256 each).
Three coordinated changes:
1. handleApiClient() now loops on the same connection until the peer
closes or parseRequest hits its 3 s idle timeout. requestsServed
counter keeps the 'bad/timeout request' debug log from firing on
the natural idle close after a keep-alive sequence.
2. handleFromRadio() buffers all packets into a std::vector before
writing, so it can emit a real Content-Length and 'Connection:
keep-alive'. Buffer is dynamic — common 1-packet response only
allocates ~256 B; ?all=true keeps the 64-packet cap which tops out
around 16 KB. handleToRadio + sendPreflight switched to keep-alive
too (they already had real Content-Length). sendError keeps close —
errors are terminal.
3. ethTlsApiServer netRecv RECV_TIMEOUT_MS dropped from 10 s to 3 s so
mbedtls_ssl_read can't outlast the handler's idle deadline (a quiet
browser leaving the socket open would otherwise wedge the OSThread
for 10 s past the natural close).
Measured on wiznet 192.168.1.143 against client.meshtastic.org:
- one TLS handshake (641 ms, CHACHA20-POLY1305)
- ~80 requests pipelined over the same session
- full config + 40 NodeInfos in ~6-8 s (vs ~50 s before)
- per-request latency post-handshake: ~10-25 ms
- curl -kv with two URLs: 'Reusing existing https: connection',
server returns 'Connection: keep-alive' explicitly.
* fix(eth-tls): watchdog + Chrome compat — handshake stability across browsers
Phase 3 keep-alive shipped a working Firefox flow but client.meshtastic.org
loops + Chrome 'Test Connection' both rebooted the board. Four distinct
issues; collectively they kept the OSThread inside serveClient() too long
or spin-looping without yielding, and pico-sdk mbedtls' TLS 1.3 code path
choked on Chrome's modern ClientHello.
1. Watchdog reset during keep-alive idle. Once a client drained the
replay queue and entered 3 s poll mode, the OSThread sat inside
netRecv()'s busy-wait waiting for the next request. Two consecutive
3 s waits plus prior handler time crossed the 8 s RP2350 hardware
watchdog. Pet the watchdog inside netRecv()'s poll loop (every 2 ms)
so a quiet client can never starve the watchdog. Same fix in the
ethApiHandlers per-request yield path.
2. Cap session at 64 requests + yield() between. Defense-in-depth: a
pathological client can't monopolize serveClient indefinitely; after
the cap it just re-handshakes (~625 ms), still vastly cheaper than
the per-request handshake we had before keep-alive.
3. TLS 1.3 code compiled out of mbedtls entirely
(#undef MBEDTLS_SSL_PROTO_TLS1_3 in mbedtls_user_config). Capping
max_tls_version=TLS1_2 at runtime is enough for Firefox / openssl
(they downgrade cleanly), but Chrome's ClientHello carries TLS 1.3
extensions — post-quantum key shares, Encrypted ClientHello,
etc. — that the vendored mbedtls 1.3 parser crashes on before the
downgrade decision happens. Removing the 1.3 sources sidesteps the
parser; ServerHello just announces TLS 1.2 and Chrome accepts.
4. netSend infinite WANT_WRITE spin. When W5500's TX buffer momentarily
filled mid-handshake (Chrome draining slower than Firefox during
ServerKeyExchange), EthernetClient::write() returned 0, our netSend
returned MBEDTLS_ERR_SSL_WANT_WRITE without delay, mbedtls retried
immediately, repeat at ~180k iter/sec until ... well, until the
board's other threads got nothing done. Log signature: ret=-0x6880
tight-looping in the handshake iter trace. Rewrite netSend to block
with delay(2) + watchdog_update() and a 3 s timeout — same shape as
netRecv. Return MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY on disconnect
(was incorrectly returning WANT_WRITE).
Also added granular per-iter handshake logging gated on first 20 iters
+ every 50th after that, so any future regression localizes itself in
COM9 without RTT JLink.
Validated on wiznet 192.168.1.143:
- Firefox: client.meshtastic.org full sync + idle poll stable (no
reset during the previously-crashing 'replay drain complete' phase)
- Chrome: 'Test Connection' accepts the cert prompt and connects
- Edge: same as Chrome
- openssl s_client default + tls1_2 forced: both negotiate TLS 1.2
with ECDHE-ECDSA + AES-GCM / CHACHA20-POLY1305, verify=18 (self-
signed, expected)
* chore(eth-tls): drop verbose debug logs from cert + TLS init paths
The cert pipeline + TLS context init had step-by-step LOG_INFOs and
ubiquitous Serial.flush() that were essential while diagnosing the
Phase 2.1-bis stack overflow, the Chrome handshake crash, and the
keep-alive watchdog reset. Once those bugs were fixed the logs just
clutter COM9 on every boot.
Kept on hand:
- cert: 'loaded from FS', 'generating ...', 'generated N B in T ms',
'persisted to LittleFS', plus all LOG_ERROR / LOG_WARN paths
- tls: 'server listening on TCP port 443', 'client connected from',
'handshake OK in N ms ciphersuite=', 'handshake failed -0xXXXX (...)',
plus all init LOG_ERRORs
Dropped:
- cert: 'step 1/8 pk_setup' through 'step 8/8 copy key DER', 'thread
woke', 'pipeline OK' (now silent on success)
- tls: 'cert is ready, initializing', 'parsing cert chain', 'parsing
key', 'ssl_config_defaults', 'conf_own_cert', 'ssl_setup',
'server worker scheduled', and the per-iter handshake trace
- obsolete 'Optional mbedtls debug bridge' commented-out stub
- all Serial.flush() that were added defensively for the
debug-the-crash phase
Bin shrinks ~40 KB (logs + format strings). Validated on wiznet
192.168.1.143: HTTP 200 round-trip works post-flash, no regression.
* fix(eth): harden API/TLS build guards (review #10573)
- ethApiHandlers.{h,cpp}: gate on (HAS_ETHERNET_API || HAS_ETHERNET_TLS_API)
so the shared handleApiClient/IStreamReadWrite still compile for an
HTTPS-only variant (the TLS server depends on them).
- ethTlsApiServer.{cpp,h}, ethCert.{cpp,h}: require ARCH_RP2040 (they use Pico
SDK get_rand_64()/watchdog_update()); the ethClient.cpp TLS include + call
site are tightened to match, so enabling the flag on a non-RP2040 Ethernet
target is a clean no-op instead of a cryptic build break.
- clang-format (16) the touched eth files to satisfy Trunk Check.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(eth): address review — listener/cert recovery + keep-alive framing
- Rebind HTTP/80 and HTTPS/443 after a W5500 chip reset. reconnectETH() now
tears the API/TLS servers down (deInitEthApiServer/deInitEthTlsApiServer) so
the restart path recreates them; the singleton guards previously left both
bound to dead sockets until reboot. The workers are kept (only the listener
is dropped) so nothing is deleted from another thread's runOnce.
- Stop the keep-alive loop after a handler error. /fromradio and /toradio
return whether the connection may stay open; a 400/405/408 response
(Connection: close) now ends the loop so unread body bytes can't be parsed
as the next request.
- Validate the cached cert/key before trusting it (parse both DERs), and clear
the IP commit-marker before rewriting the pair and write it last, so a reset
mid-persist regenerates instead of handing the TLS server a truncated or
mismatched pair (which would hard-disable HTTPS).
- Track a cert generation counter so the TLS server reloads and rebinds when a
DHCP lease change regenerates the cert for a new IP (the SAN must follow, or
browsers reject the new address). The cert worker is no longer one-shot.
- Silence the SCons F821/E402 lint on add_mbedtls_sources.py to match the other
extra_scripts.
Validated on a W5500 board: a forced chip reset rebinds 80 and 443 without an
MCU reboot; a truncated cached cert regenerates and HTTPS recovers; a simulated
lease change reissues the cert with the new IP in the SAN.
* fix(eth): clear cert readiness on regen failure + verify cached pair
Follow-up to the review:
- Reset ready_/certIp_ when ensureCertForIp() fails, so isReady() no longer
reports true with empty material — otherwise a later TLS reload fails
initTlsContext() and stays disabled instead of retrying on the next poll.
- certKeyParse() now also checks the cached cert and key form a matching pair
(mbedtls_pk_check_pair), rejecting an independently-parseable but mismatched
cert/key (e.g. if the IP commit-marker survived a failed clear).
Validated on a W5500 board: a valid cached pair still loads from FS, and a
regenerated cert reloads cleanly.
---------
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
3501f620a3 |
fix: restore fixed position into localPosition on boot (#10670)
* NodeDB: restore fixed position into localPosition at boot Fixed-position nodes without a GPS stop broadcasting their position (and publishing MQTT map reports) after a reboot. On boot localPosition is empty, and NodeDB::hasValidPosition() for the local node only inspects localPosition, not the persisted node position. PositionModule therefore never sends a position, so the lazy localPosition backfill in getPositionPacket() never runs and localPosition stays at (0,0) indefinitely. Restore the configured fixed position into localPosition during NodeDB construction so position broadcasts and map reports resume automatically after a reboot. * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Ben Meadors <benmmeadors@gmail.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> |
||
|
|
4f0e2dde98 |
feat: add Ethernet OTA support for RP2350/W5500 boards (#10136)
* feat: add Raspberry Pi Pico 2 + W5500 + E22-900M30S variant Adds community variant for Raspberry Pi Pico 2 (RP2350, 4 MB flash) with external WIZnet W5500 Ethernet module and EBYTE E22-900M30S LoRa module (SX1262, 30 dBm PA, 868/915 MHz). Key details: - LoRa on SPI1: GP10/11/12/13 (SCK/MOSI/MISO/CS), RST=GP15, DIO1=GP14, BUSY=GP2, RXEN=GP3 (held HIGH via SX126X_ANT_SW) - W5500 on SPI0: GP16/17/18/19/20 (MISO/CS/SCK/MOSI/RST) - SX126X_DIO2_AS_RF_SWITCH: DIO2→TXEN bridge on module handles PA - SX126X_DIO3_TCXO_VOLTAGE 1.8: TCXO support via EBYTE_E22 flags - DHCP timeout reduced to 10 s to avoid blocking LoRa startup - GPS on UART1/Serial2: GP8 TX, GP9 RX - Reuses WIZNET_5500_EVB_PICO2 code paths for Ethernet init Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * pico2_w5500_e22: rename define and address review feedback Rename WIZNET_5500_EVB_PICO2 to PICO2_W5500_E22 so the variant-specific define matches the variant directory name and isn't confused with an on-board EVB SKU. Review fixes from PR #10135: - Gate the 10 s Ethernet DHCP timeout behind PICO2_W5500_E22 so other Ethernet builds keep the default 60 s behavior; apply the same timeout to reconnectETH() for consistency. - Drop the unused -D EBYTE_E22 flag; EBYTE_E22_900M30S already selects TX_GAIN_LORA / SX126X_MAX_POWER in src/configuration.h. - Rewrite "on-board W5500" comments to describe the external module. - Correct README TX_GAIN_LORA value (7, not 10) and drop the EBYTE_E22 row. * fix(pico2_w5500_e22): drop DEBUG_RP2040_PORT=Serial The arduino-pico framework hooks _write() when DEBUG_RP2040_PORT=Serial is set and dumps raw debug bytes onto USB CDC, corrupting any binary protobuf stream sent through StreamAPI (e.g. `meshtastic --port COMx`). The variant excludes BT and WiFi, so the primary client transport is Ethernet TCP via ethServerAPI — unaffected — but users who configure the node over USB serial would see protobuf decode failures from debug-byte interleaving. Removing the flag restores clean USB CDC. Debug output can still be enabled per-build by adding -D DEBUG_RP2040_PORT=Serial1 to redirect to UART0 instead of USB CDC. * feat: add Ethernet OTA support for RP2350/W5500 boards Adds over-the-air firmware update capability for RP2350-based boards with a WIZnet W5500 Ethernet module (e.g. pico2_w5500_e22). Protocol (MOTA): - SHA256 challenge-response authentication with a configurable PSK (override via USERPREFS_OTA_PSK; default key ships in source) - 12-byte header: magic "MOTA" + firmware size + CRC32 - Firmware received in 1 KB chunks, verified with CRC32, written via Updater (picoOTA), then device reboots to apply - Constant-time hash comparison prevents timing attacks on auth - 30s inactivity timeout + 5s cooldown after failed auth - Response codes 0x00-0x08 map 1:1 to OTAResponse enum Firmware side: - ethOTA.cpp / ethOTA.h: OTA TCP server on port 4243 - ethClient.cpp: wire initEthOTA/ethOTALoop into reconnect loop - main-rp2xx0.cpp: hardware watchdog (8s, paused during debug) - pico2_w5500_e22/platformio.ini: HAS_ETHERNET_OTA flag, filesystem_size bumped to 0.75m for OTA staging Host side: - bin/eth-ota-upload.py: Python uploader with progress and full result-code mapping (matches OTAResponse 0x00-0x08) * style(eth): clang-format ethOTA.cpp per repo .clang-format Reformat to the repo trunk clang-format config (IndentWidth 4, ColumnLimit 130). Resolves the Trunk Check 'Incorrect formatting' failure on PR #10136. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
ce7444c1a1 |
feat: add WIZnet W5500-EVB-Pico2 + E22P-868M30S variant (#10552)
* feat: add WIZnet W5500-EVB-Pico2 + E22P-868M30S variant Adds a community variant for the WIZnet W5500-EVB-Pico2 development board (https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico2) paired with an external EBYTE E22P-868M30S LoRa module. This is the hardware twin of variants/rp2350/diy/pico2_w5500_e22 — same LoRa pinout, same W5500 pin mapping — and reuses its variant.h verbatim via `-I variants/rp2350/diy/pico2_w5500_e22`. No duplicated pinout file. Two differences vs pico2_w5500_e22 justify the separate env: 1. Board target: `wiznet_5500_evb_pico2` (2 MB flash) instead of `rpipico2` (4 MB flash). The WIZnet PCB ships with a smaller Q-SPI chip than a stock Pi Pico 2. Selecting the correct board makes the linker fail fast on overflow instead of producing a UF2 that gets silently truncated when flashed to the device. 2. W5500 PHY is integrated on the PCB (hard-wired SPI0 GP16-20) rather than supplied as an external module the user wires manually. The E22P-868M30S uses a single combined RFEN pin (LNA + PA enable) instead of the older E22-900M30S's split RXEN/TXEN pins. RFEN is held HIGH at all times via `SX126X_ANT_SW 3`; TX switching still uses the on-module DIO2 → TXEN bridge through `SX126X_DIO2_AS_RF_SWITCH`. Build verified: wiznet_5500_evb_pico2_e22p SUCCESS (RAM 19.2%, Flash 65.6% of 1.5 MB partition / 2 MB chip). * style(wiznet-evb-pico2-e22p): prettier-format README tables --------- Co-authored-by: Ben Meadors <benmmeadors@gmail.com> |
||
|
|
5b7a5b2c22 |
feat: add Raspberry Pi Pico 2 + W5500 + E22-900M30S variant (#10135)
* feat: add Raspberry Pi Pico 2 + W5500 + E22-900M30S variant Adds community variant for Raspberry Pi Pico 2 (RP2350, 4 MB flash) with external WIZnet W5500 Ethernet module and EBYTE E22-900M30S LoRa module (SX1262, 30 dBm PA, 868/915 MHz). Key details: - LoRa on SPI1: GP10/11/12/13 (SCK/MOSI/MISO/CS), RST=GP15, DIO1=GP14, BUSY=GP2, RXEN=GP3 (held HIGH via SX126X_ANT_SW) - W5500 on SPI0: GP16/17/18/19/20 (MISO/CS/SCK/MOSI/RST) - SX126X_DIO2_AS_RF_SWITCH: DIO2→TXEN bridge on module handles PA - SX126X_DIO3_TCXO_VOLTAGE 1.8: TCXO support via EBYTE_E22 flags - DHCP timeout reduced to 10 s to avoid blocking LoRa startup - GPS on UART1/Serial2: GP8 TX, GP9 RX - Reuses WIZNET_5500_EVB_PICO2 code paths for Ethernet init Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * pico2_w5500_e22: rename define and address review feedback Rename WIZNET_5500_EVB_PICO2 to PICO2_W5500_E22 so the variant-specific define matches the variant directory name and isn't confused with an on-board EVB SKU. Review fixes from PR #10135: - Gate the 10 s Ethernet DHCP timeout behind PICO2_W5500_E22 so other Ethernet builds keep the default 60 s behavior; apply the same timeout to reconnectETH() for consistency. - Drop the unused -D EBYTE_E22 flag; EBYTE_E22_900M30S already selects TX_GAIN_LORA / SX126X_MAX_POWER in src/configuration.h. - Rewrite "on-board W5500" comments to describe the external module. - Correct README TX_GAIN_LORA value (7, not 10) and drop the EBYTE_E22 row. * fix(pico2_w5500_e22): drop DEBUG_RP2040_PORT=Serial The arduino-pico framework hooks _write() when DEBUG_RP2040_PORT=Serial is set and dumps raw debug bytes onto USB CDC, corrupting any binary protobuf stream sent through StreamAPI (e.g. `meshtastic --port COMx`). The variant excludes BT and WiFi, so the primary client transport is Ethernet TCP via ethServerAPI — unaffected — but users who configure the node over USB serial would see protobuf decode failures from debug-byte interleaving. Removing the flag restores clean USB CDC. Debug output can still be enabled per-build by adding -D DEBUG_RP2040_PORT=Serial1 to redirect to UART0 instead of USB CDC. * style(pico2_w5500_e22): apply trunk fmt — fixes Trunk Check - variant.h: clang-format 16.0.3 (drop manual #define alignment) - README.md: prettier + add `text` language to fenced code blocks (markdownlint MD040) - wiring.svg: svgo optimization Resolves the Trunk Check Runner failure on this PR (3 unformatted files + 8 markdownlint issues). No functional changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(pico2_w5500_e22): address review — move to rp2350/diy, generic guards Maintainer feedback from NomDeTom on PR #10135: - Move the variant from variants/rp2350/ to variants/rp2350/diy/ to distinguish DIY from prebuilt boards (matches the variants/*/diy/ pattern; still discovered via the existing variants/*/diy/*/platformio.ini glob in the root platformio.ini). - Replace the board-name macro PICO2_W5500_E22 in shared code with a generic capability macro USE_ARDUINO_ETHERNET, defined from variant.h. DebugConfiguration.h / ethServerAPI.h / ethClient.cpp no longer reference a board name. - Drop the architecture.h hook entirely: variant.h now defines PRIVATE_HW, which the existing `#elif defined(PRIVATE_HW)` branch already handles. No functional change. Build verified: pico2_w5500_e22 SUCCESS (RAM 19.2%, Flash 28.1%). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(pico2_w5500_e22): drop unoptimized wiring.svg to fix trunk fmt check --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Ben Meadors <benmmeadors@gmail.com> |
||
|
|
e10e13226d |
nrf54l15: fix SHT4x libdep -- arduino-sht, not Adafruit_SHT4X (#10515)
SHTXXSensor (the SHT4x driver) includes <SHTSensor.h>, gated by __has_include(<SHTSensor.h>). That header ships in Sensirion/arduino-sht. Adafruit_SHT4X ships Adafruit_SHT4X.h and has no consumer anywhere in src/, so the SHT40 driver was silently excluded from the build -- the nRF54L15 variant could not read an SHT4x sensor as committed in #10193. Replace the dead Adafruit_SHT4X libdep with arduino-sht v1.2.6. Validated on nRF54L15-DK: SHT40-AD1B @0x44, 24.3h soak, 0 reboots, temperature/humidity telemetry stable end-to-end. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Ben Meadors <benmmeadors@gmail.com> |
||
|
|
4a1ff18f57 |
feat: add Nordic nRF54L15-DK variant (Zephyr + BLE + LoRa) (#10193)
* feat: add Nordic nRF54L15-DK variant (Zephyr + BLE + LoRa)
Adds a community hardware variant for the Nordic nRF54L15-DK (PCA10156)
with an external EBYTE E22-900M30S (SX1262) LoRa module. First Meshtastic
port running on the Zephyr RTOS; all other Nordic targets use the nRF5
SoftDevice stack.
Scope
-----
- New Zephyr-based platform layer under src/platform/nrf54l15/ providing
Arduino-compatible shims (Arduino.h, SPI, Wire, Print, Stream) over the
Zephyr APIs plus a LittleFS-backed InternalFileSystem on SPIM20.
- Bluetooth LE peripheral (NRF54L15Bluetooth.*) built on the Zephyr BT
host stack, exposing the Meshtastic GATT service with legacy
connectable advertising, just-works pairing, dynamic MTU exchange
(up to 247 bytes), and iOS connection-parameter tweaks.
- Variant directory variants/nrf54l15/nrf54l15dk/ with pin map for the
E22 module on connector J1, PlatformIO env (nrf54l15dk), Zephyr
DT overlay and a wiring README.
- Zephyr project config (zephyr/prj.conf + board overlay) tuned for
BT + LoRa: 16 KB main stack, 4 KB BT RX thread, RTT logging in
immediate mode, newlib-nano heap sized to leave room for the GATT
pools while still allowing ATT MTU=247.
- extra_scripts/nrf54l15_linker.py works around a PlatformIO + old Ninja
issue where Zephyr's two-pass linker script generation does not run
automatically; the post-script parses build.ninja and invokes the
gcc -E step directly before the final link.
- boards/nrf54l15dk.json board definition (PlatformIO needs it for the
DK; the Seeed platform only ships the XIAO variants).
- variants/rp2350/rp2350.ini excludes platform/nrf54l15/ from RP2350
build_src_filter so the shared platform tree does not leak between
targets.
- .gitignore: add nRF J-Link / RTT debug artifacts (flash.jlink,
rtt_*.txt).
Shared source changes
---------------------
- src/main.{cpp,h}, src/RedirectablePrint.cpp, src/FSCommon.{cpp,h},
src/mesh/{Channels,NodeDB,RadioLibInterface,MeshService,PhoneAPI}.cpp,
src/mesh/RadioLibInterface.h, src/modules/AdminModule.cpp: add small
guards / helpers so the Zephyr build compiles alongside the Arduino
targets. Behavior on existing boards is unchanged.
Hardware model
--------------
HW_VENDOR maps to meshtastic_HardwareModel_PRIVATE_HW until a dedicated
protobuf enum value is assigned upstream. The variant declares
custom_meshtastic_hw_model = 132 so the maintainers can wire the new
enum value through the protobufs repo after merge.
Hardware note
-------------
The E22-900M30S does not connect its DIO2 pin to TXEN internally — a
wire/solder bridge between DIO2 and TXEN on the module is required for
TX to work. Details and full pin map are in the variant README.
Validation
----------
Built clean against develop. On real hardware (April 2026) the port
passes end-to-end: iOS companion app pairs and connects, configuration
round-trip works, LoRa TX/RX reaches a canonical tbeam on the same mesh
channel, NodeDB updates propagate both ways, and traceroute completes.
* fix(nrf54l15): use atomic fs_rename instead of copy fallback
Zephyr LittleFS on nrf54l15 supports fs_rename natively, so route it
through the same atomic path as ESP32. The previous copyFile+remove
fallback truncated the destination before copying, leaving 0-byte files
if interrupted mid-write.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(nrf54l15): expand storage_partition from 36KB to 700KB
LittleFS on the default 9-block (36KB) storage_partition ran out of
space during copy-on-write of config.proto, causing fs_write to return
ENOSPC and pb_encode to surface "io error" when saving configuration
via the mobile app.
Reclaim slot1_partition (the MCUboot secondary slot — unused since we
flash directly via J-Link) and grow storage_partition to span
0xb6000..0x165000 (~175 blocks).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(nrf54l15): drop USERPREFS_LORACONFIG_* so LoRa config stays mutable
NodeDB rewrites LoRa config from USERPREFS_LORACONFIG_* on every boot,
which prevented reconfiguration via the BLE/serial app. Drop the
variant-level defaults; users configure region and modem preset through
the app like every other variant.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(nrf54l15): enforce MITM passkey pairing on GATT service
- Add MESH_PERM_READ/MESH_PERM_WRITE macros (READ_AUTHEN/WRITE_AUTHEN)
on all mesh service characteristics so clients must complete passkey
exchange before accessing fromNum/fromRadio/toRadio/logRadio.
- Wire FIXED_PIN mode to bt_passkey_set() so the device advertises a
known PIN (config.bluetooth.fixed_pin); RANDOM_PIN keeps default
per-pairing random passkey.
- Reduce BleDeferredThread HARD_WATCHDOG_MS from 3min to 1min.
- prj.conf: CONFIG_BT_SMP_ENFORCE_MITM=y, CONFIG_BT_FIXED_PASSKEY=y,
CONFIG_BT_SMP_SC_PAIR_ONLY=n (legacy fallback for clients that abort
SC pairing with reason 0x01 within 150ms).
* fix(nrf54l15): resolve develop-merge conflict + cppcheck warnings
The `Merge branch 'develop'` left two ~RadioLibInterface() declarations
in src/mesh/RadioLibInterface.h: the inline version added upstream by
PR #10254 (which independently applied the same UAF guard this PR was
carrying) and the out-of-line version this PR introduced. GCC rejects
the duplicate, breaking every platform build. Drop the out-of-line
declaration + definition; keep upstream's inline form.
Also silence the 13 cppcheck low warnings introduced by the new
nrf54l15 Arduino shim — Arduino's `String`/`SPISettings` API contract
relies on implicit single-arg constructors used pervasively by
existing Meshtastic code, so suppress `noExplicitConstructor` inline
with a comment instead of breaking the API. The few mechanical wins
(`const tmp[2]`, `const uint32_t *sp`) are applied directly.
* fmt: fix Trunk Check lint issues on nrf54l15-port
- extra_scripts/nrf54l15_linker.py: move regular imports above
Import("env") to silence E402, add trunk-ignore-all(F821) for the
PIO/SCons SConstruct injection (matches esp32_pre.py / nrf52_extra.py
convention)
- src/platform/nrf54l15/NRF54L15Bluetooth.cpp: clang-format 16.0.3
- boards/nrf54l15dk.json + variants/nrf54l15/nrf54l15dk/README.md:
prettier 3.8.3 (also resolves markdownlint MD060 on README tables)
No behavior change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(nrf54l15): address Copilot review comments + correct clang-format style
Six review threads from the 2026-04-30 Copilot review:
- src/platform/nrf54l15/nrf54l15_main.cpp: validate PSP against the nRF54L15
SRAM range (0x20000000..0x20040000) and 4-byte alignment before walking the
faulting thread's stack, and clamp the walk so it never reads past the end
of RAM. Prevents a second fault inside the fatal handler when PSP is
corrupted (common in real faults).
- src/platform/nrf54l15/nrf54l15_arduino.cpp: gate the bring-up printk traces
in digitalWrite/digitalRead (CS/NRESET toggle log, BUSY-before-NRESET
snapshot, BUSY periodic timeline) behind a new -DNRF54L15_GPIO_DEBUG flag
that is off by default. The "dev NOT READY" message stays unconditional —
it indicates a genuine hardware/DTS misconfig.
- src/modules/AdminModule.cpp: don't mutate config.device.output_gpio_enabled
from handleGetConfig(). Reflect the live pin state in the response payload
only — a getter must not write back to disk-persisted state.
- src/platform/nrf54l15/InternalFileSystem.h: derive totalBytes() from
FIXED_PARTITION_SIZE(storage_partition) at compile time so it tracks the
DK overlay's ~700 KB partition instead of the stale 36 KB hard-coded value.
Updated the file header comment accordingly.
- extra_scripts/nrf54l15_linker.py: make _extract_gcc_command() handle the
POSIX Ninja COMMAND format (no `cmd.exe /C "..."` wrapper) in addition to
the Windows form, so the script doesn't hard-fail on Linux/macOS hosts.
- src/platform/nrf54l15/NRF54L15Bluetooth.cpp: clamp NO_PIN to RANDOM_PIN
with a one-shot LOG_WARN. The mesh GATT permissions are declared with
BT_GATT_PERM_*_AUTHEN and prj.conf sets CONFIG_BT_SMP_ENFORCE_MITM=y, so
NO_PIN with no auth callbacks would leave every characteristic returning
BT_ATT_ERR_AUTHENTICATION. Falling back to RANDOM_PIN keeps the link
usable instead of silently broken. Also re-formatted this file with the
project's .trunk/configs/.clang-format (Linux braces, 4-space indent,
130-col) — the previous lint-fix commit a2aca3234 accidentally used the
default LLVM style here, which CI's clang-format would have rejected.
Build verified: pio run -e nrf54l15dk passes, RAM 47.4%, Flash 28.6%.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(nrf54l15): address remaining Copilot review threads
Round 2/3 review fixes — bugs first, then docs/portability:
BLE concurrency (NRF54L15Bluetooth.cpp):
- onNowHasData / sendLog / BleDeferredThread / shutdown: acquire
active_conn under ble_mutex via new acquire_active_conn() helper so
disconnected_cb can't free the conn between the null check and
bt_conn_ref/bt_gatt_notify (use-after-unref).
- write_toradio: reject writes that exceed MAX_TO_FROM_RADIO_SIZE with
ATT_ERR_INVALID_ATTRIBUTE_LEN instead of returning success and silently
dropping the payload (would hide failed config writes from the phone).
- start_advertising: truncate the device name to fit the 31-byte legacy
scan-response limit and switch to BT_DATA_NAME_SHORTENED so
bt_le_adv_start() doesn't reject the payload when the name approaches
CONFIG_BT_DEVICE_NAME_MAX=32.
Linker / portability:
- main.h: drop the rp2040Loop() forward declaration that had no
definition and no callers — would surface as a link error if any RP2040
build added a call to the symbol.
- nrf54l15_arduino.cpp: transfer16() now uses static __aligned(4) DMA
buffers (matching transfer()), removing the EasyDMA-reachability hazard
of caller-stack buffers on this part.
Filesystem (InternalFileSystem.h):
- usedBytes(): return real usage from fs_statvfs() instead of 0 so OTA
/ range-test free-space guards work.
- rewindDirectory(): close the dir before reopening — Zephyr fs_dir_t has
no rewind, and re-fs_opendir on an open handle leaks LittleFS state.
Crash handler (nrf54l15_main.cpp):
- After the stack walk, busy-wait 50 ms to flush RTT/printk and call
sys_reboot(SYS_REBOOT_COLD) directly so the saved_crash record is
actually reported on the next boot. Default Zephyr config has
RESET_ON_FATAL_ERROR=n, so the previous k_fatal_halt() spun forever.
Generalization / config:
- PhoneAPI.cpp: replace the NRF54L15_DK ifdef with a
MESHTASTIC_EXCLUDE_FILES_MANIFEST capability flag (defined in the
nrf54l15dk env) so future variants can opt in/out without touching
shared code.
- variants/nrf54l15/nrf54l15.ini: parameterize libdeps include paths via
${PIOENV} so additional nRF54L15 envs sharing nrf54l15_base don't break.
- prj.conf: drop the stale "36 KB storage_partition" comments — the DK
overlay reclaims slot1 to ~700 KB and runtime size comes from
FIXED_PARTITION_SIZE.
- nrf54l15dk overlay: remove the zephyr,console / zephyr,shell-uart
chosen entries that conflicted with CONFIG_UART_CONSOLE=n + RTT
console; keep uart30 enabled so swapping the console is one Kconfig
flip away.
Build: nrf54l15dk SUCCESS (flash 28.6%, RAM 47.4%); wiznet_5500_evb_pico2
SUCCESS (verifies the shared main.h change).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(nrf54l15dk): use PRIVATE_HW (255) for custom_meshtastic_hw_model
Per @thebentern's review: the nRF54L15-DK is a development kit, not a
canonical Meshtastic SKU, so it falls under HardwareModel::PRIVATE_HW
(255) — the same enum value already used at runtime via HW_VENDOR. The
placeholder 132 is removed; no dedicated enum number will be assigned
for DK boards. Slug stays NRF54L15_DK as a human-readable identifier.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(nrf54l15): unstarve bt_long_wq so SC pairing completes
bt_pub_key_gen() runs the ECC P256 key generation on bt_long_wq. At default
prio=10 (preemptible) and stack=1400 it gets starved by Meshtastic app
threads at boot — sc_public_key stays NULL for minutes, smp_public_key()
defers with SMP_FLAG_PKEY_SEND, and every SC pairing attempt stalls right
after the public-key exchange. iOS shows "Connecting…" forever with no
PIN prompt; bleak/CLI fails the first CCC notify write with
"Protocol Error 0x05: Insufficient Authentication".
Set CONFIG_BT_LONG_WQ_PRIO=0 (highest preemptible, ties with main) and
CONFIG_BT_LONG_WQ_STACK_SIZE=4096 (margin for the P256M driver frames).
Validated E2E with iOS Meshtastic app: bt_smp_pkey_ready fires within ~40 s
of boot, 20 SC Passkey Entry rounds complete with matching pcnf/cfm,
encrypt 0x01 / sec_level 0x04 (Authenticated MITM), bonded=1.
* feat(nrf54l15): hardware I2C bus via TWIM30 + sensor telemetry
Adds the Arduino TwoWire layer for the nRF54L15-DK so Meshtastic's
sensor drivers can talk to external I2C devices over the hardware
TWIM30 peripheral.
Bus binding:
- &uart30 disabled in the board overlay (peripheral instance 30 is
shared between UARTE30 / TWIM30 / SPIM30 — pick one). Console stays
on RTT via CONFIG_RTT_CONSOLE.
- New i2c30_default / i2c30_sleep pinctrl with SDA=P0.03 / SCL=P0.04.
External 4.7 kOhm pull-ups required on both lines.
- &i2c30 enabled at I2C_BITRATE_FAST (400 kHz).
- button_3 (SW3, P0.04) deleted from DTS so the pad can be claimed by
i2c30 pinctrl; SW3 is still wired to the pad on the DK, do not press
it during I2C use or it will short SCL to GND.
Arduino layer:
- src/platform/nrf54l15/Wire.cpp resolves the DT node at compile time
via DEVICE_DT_GET(DT_NODELABEL(i2c30)) and dispatches Arduino's
beginTransmission / write / endTransmission / requestFrom to
i2c_write / i2c_write_read / i2c_read. Buffer is sized to 256 bytes
for forward compatibility with the SE050 secure element on the
custom PCB.
- Wire.h drops the prior compile-only stubs and exposes the real
TwoWire surface.
- Arduino.h: BitOrder becomes an enum (not #define) so Adafruit_BusIO's
`typedef BitOrder BusIOBitOrder;` compiles.
Variant + build flags:
- nrf54l15.ini flips HAS_WIRE / HAS_SENSOR / HAS_TELEMETRY from 0 to 1
and cherry-picks the sensor libs Meshtastic needs (BusIO, Sensor,
BMP280, BME280, INA219/226/260/3221, SHT4X). The full
environmental_base group is avoided because it pulls
Adafruit_SSD1306 / Adafruit_GFX which rely on Arduino pin macros the
Zephyr shim does not implement.
- nrf54l15dk variant.h defines PIN_WIRE_SDA / PIN_WIRE_SCL for parity
with the Arduino convention used by other variants. The actual bus
wiring is fixed by the overlay pinctrl above.
Validated 2026-05-14/15 on the DK with BMP280 @ 0x76 (temperature +
barometric pressure) and INA3221 @ 0x42 (rail voltage / current);
EnvironmentTelemetry / PowerTelemetry packets transmit successfully
over LoRa.
Footprint cost on nrf54l15dk: +45 KB flash, +1.7 KB RAM.
* feat(nodedb): honor USERPREFS for environment telemetry on first boot
installDefaultConfig() now respects two new compile-time prefs:
USERPREFS_CONFIG_ENV_TELEM_UPDATE_INTERVAL
USERPREFS_CONFIG_ENVIRONMENT_MEASUREMENT_ENABLED
The mobile apps enforce a 30 min floor on environment_update_interval
in the settings UI, which makes short-interval bring-up testing of new
sensor hardware painful — you have to wait half an hour for the first
LoRa packet to confirm wiring + driver. With these prefs baked into
the variant, the firmware can ship a freshly-flashed device that
broadcasts on a shorter cadence (e.g. 900 s) the moment storage_partition
is empty.
Both prefs are gated on #ifdef so the behavior is unchanged for any
variant that does not opt in. Documented in userPrefs.jsonc with the
existing telemetry-interval pref block.
* fix(nrf54l15): allow multiple bonded BLE peers
CONFIG_BT_MAX_PAIRED defaults to 1, so once the first peer (e.g. an
iOS phone) has paired and bonded, every subsequent pairing attempt
from a different MAC fails inside bt_keys_get_addr() with no free
key slot — the host returns BT_SECURITY_ERR_KEY_DOES_NOT_EXIST and
the second peer never gets past SMP.
Raise the slot count to 4 so the device can simultaneously hold an
iOS phone, a Windows host, a Linux host, and one spare bond. Add
BT_KEYS_OVERWRITE_OLDEST so that once the table fills, the LRU peer
is evicted on the next pairing rather than rejecting the new peer.
This matches the behavior other Meshtastic ports already provide
(nRF52 uses CONFIG_BT_PERIPHERAL_PRIO_CONN with similar semantics).
Discovered while bringing up the Python CLI on Windows alongside
the existing iOS bond.
* fix(nrf54l15): zero-initialize TwoWire buffers + clang-format Wire
cppcheck on every CI target (esp32s3, rp2040, rp2350, nrf52840, ...) was
failing the build with two `uninitMemberVar` warnings on TwoWire's
constructor: `txBuf` and `rxBuf` (256-byte arrays) were not initialized.
Even though the buffers are only read after txLen/rxLen is set, leaving
them uninitialized is a footgun if any future caller bypasses the
len-set step. Use C++11 value-initialization in the member initializer
list — costs ~512 B of memset at boot, gains a clean cppcheck pass and
defensive-against-future-changes semantics.
Also reformat Wire.{cpp,h} with the project's `.trunk/configs/.clang-format`
config so the Trunk Check Runner passes — clang-format moved the
`<errno.h>` include before the Zephyr-namespaced ones in Wire.cpp and
collapsed two inline overloads to single lines in Wire.h.
* fix(AdminModule): remove dead OUTPUT_GPIO_PIN/GpioOutputModule references
OUTPUT_GPIO_PIN is never defined and modules/GpioOutputModule.h doesn't
exist in the codebase; all #ifdef OUTPUT_GPIO_PIN branches were dead code
introduced by the nRF54L15-DK variant commit. Strips the include, the
output_gpio_enabled OFF→ON/ON→OFF transition logic in handleSetConfig(),
and the digitalRead() reflection in handleGetConfig().
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
|