Skip to content

feat: add Ethernet OTA support for RP2350/W5500 boards#10136

Open
cvaldess wants to merge 16 commits into
meshtastic:developfrom
cvaldess:feature/eth-ota
Open

feat: add Ethernet OTA support for RP2350/W5500 boards#10136
cvaldess wants to merge 16 commits into
meshtastic:developfrom
cvaldess:feature/eth-ota

Conversation

@cvaldess
Copy link
Copy Markdown
Contributor

Summary

  • Adds over-the-air firmware update via Ethernet (W5500) for RP2350-based boards
  • New TCP server on port 4243 using the MOTA protocol: SHA256 challenge-response auth + CRC32-verified transfer
  • Python upload tool (bin/eth-ota-upload.py) — stdlib-only, auto GZIP-compresses firmware before upload
  • Watchdog (8s) enabled in rp2040Loop() so the device recovers if an OTA write stalls

Depends on: #10135 (Raspberry Pi Pico 2 + W5500 + E22-900M30S variant) — please review that PR first.

Protocol

  1. Device sends a 32-byte random nonce
  2. Client responds with SHA256(nonce || PSK) — constant-time comparison on device side
  3. Client sends 12-byte header: "MOTA" magic + firmware size (LE u32) + CRC32 (LE u32)
  4. Firmware transferred in 1 KB chunks; device verifies CRC32 and calls Update.end() (picoOTA)
  5. Device reboots — bootloader applies the update from LittleFS

The PSK defaults to meshtastic_ota_default_psk_v1!!! and can be overridden per-build via USERPREFS_OTA_PSK.

Files changed

File Change
src/mesh/eth/ethOTA.cpp New — OTA TCP server implementation
src/mesh/eth/ethOTA.h New — public API: initEthOTA(), ethOTALoop()
bin/eth-ota-upload.py New — Python upload tool
src/mesh/eth/ethClient.cpp Hook initEthOTA() after connect, ethOTALoop() every 5s
src/platform/rp2xx0/main-rp2xx0.cpp Enable watchdog 8s in rp2040Loop()
variants/rp2350/pico2_w5500_e22/platformio.ini filesystem_size=0.75m, -D HAS_ETHERNET_OTA

Usage

# Build
pio run -e pico2_w5500_e22

# Upload OTA (device must be connected to Ethernet with IP assigned)
python bin/eth-ota-upload.py --host 192.168.1.100 .pio/build/pico2_w5500_e22/firmware-*.bin

# With custom PSK
python bin/eth-ota-upload.py --host 192.168.1.100 --psk mySecretKey firmware.bin

Test plan

  • Build succeeds with HAS_ETHERNET_OTA (pio run -e pico2_w5500_e22)
  • OTA upload completes and device reboots with new firmware
  • CRC mismatch correctly rejected
  • Wrong PSK correctly rejected (with cooldown)
  • Build without HAS_ETHERNET_OTA (other boards) unaffected — all code guarded

🤖 Generated with Claude Code

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Ethernet-based OTA update support for RP2350 + W5500 boards in the Meshtastic firmware, integrating a new OTA TCP server into the existing Ethernet client flow and providing a companion upload tool and RP2350 variant configuration.

Changes:

  • Introduces a new Ethernet OTA server (TCP/4243) using the MOTA protocol with PSK-based SHA256 auth and CRC32 verification.
  • Adds a stdlib-only Python uploader that GZIP-compresses firmware before transfer.
  • Adds/updates RP2350 Pico 2 + W5500 + E22-900M30S variant configs/docs and enables an RP2xxx watchdog loop.

Reviewed changes

Copilot reviewed 10 out of 12 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/mesh/eth/ethOTA.cpp Implements Ethernet OTA server, auth, transfer, CRC verification, and reboot staging.
src/mesh/eth/ethOTA.h Declares OTA server init/loop API behind feature guards.
bin/eth-ota-upload.py Implements client-side MOTA upload with GZIP + SHA256 challenge-response.
src/mesh/eth/ethClient.cpp Hooks OTA init after Ethernet connect and polls OTA loop periodically; adds W5500 SPI0 init path.
src/platform/rp2xx0/main-rp2xx0.cpp Enables and feeds an 8s watchdog in rp2040Loop().
src/platform/rp2xx0/architecture.h Maps WIZNET_5500_EVB_PICO2 builds to PRIVATE_HW vendor model.
src/mesh/api/ethServerAPI.h Adds conditional include of Ethernet.h for the W5500/Pico2 path.
src/DebugConfiguration.h Adds conditional include of Ethernet.h for the W5500/Pico2 path.
variants/rp2350/pico2_w5500_e22/platformio.ini Adds new env with Ethernet OTA flag, bigger filesystem, and Ethernet library dependency.
variants/rp2350/pico2_w5500_e22/variant.h Defines pins/capabilities for Pico2 + W5500 + E22 wiring.
variants/rp2350/pico2_w5500_e22/README.md Documents wiring/build/network usage and technical notes for the variant.
variants/rp2350/pico2_w5500_e22/wiring.svg Adds a wiring diagram for the Pico2/W5500/E22 setup.

Comment thread variants/rp2350/pico2_w5500_e22/README.md Outdated
Comment thread src/mesh/eth/ethOTA.cpp Outdated
Comment thread src/mesh/eth/ethOTA.cpp Outdated
Comment thread src/mesh/eth/ethOTA.cpp Outdated
@cvaldess
Copy link
Copy Markdown
Contributor Author

All four Copilot review items were addressed in e9ac820:

  • OTA_ERR_TIMEOUT moved 0x06 → 0x08 so it no longer collides with OTA_ACK
  • USERPREFS_OTA_PSK handled as char[] + sizeof-1 to strip the PlatformIO-stringify trailing NUL
  • Auth cooldown path now calls client.stop() silently instead of writing a byte that the client would mis-consume as part of the nonce
  • README TX_GAIN_LORA value corrected to 7 for EBYTE_E22_900M30S

bfd2c94 follows up on the protocol-code change on the client side: bin/eth-ota-upload.py previously mapped 0x06 → Timeout, which stopped matching once 0x08 became the timeout code. The mapping is now aligned with the full OTAResponse enum (0x00–0x08) so every firmware response prints a human-readable message.

Ready for maintainer review whenever you have a moment — happy to rebase again if needed.

@cvaldess cvaldess force-pushed the feature/eth-ota branch 3 times, most recently from f7978d6 to 417cec2 Compare April 20, 2026 10:44
@cvaldess cvaldess force-pushed the feature/eth-ota branch 2 times, most recently from c91ee22 to c4bb1f1 Compare May 13, 2026 12:41
cvaldess and others added 4 commits May 14, 2026 01:18
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>
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 meshtastic#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.
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.
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)
@thebentern thebentern requested a review from jake-b May 19, 2026 00:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request needs-review Needs human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants