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>
This commit is contained in:
Carlos Valdes
2026-05-25 14:05:46 -05:00
committed by GitHub
co-authored by GitHub Claude Sonnet 4.6 Ben Meadors
parent 5bce26d9b7
commit 5b7a5b2c22
6 changed files with 300 additions and 1 deletions
@@ -0,0 +1,150 @@
# Raspberry Pi Pico 2 + W5500 + E22-900M30S — Meshtastic Variant
Meshtastic support for a **Raspberry Pi Pico 2** (RP2350, 4 MB flash) with an external **W5500** Ethernet module and an **EBYTE E22-900M30S** LoRa module.
---
## Required Hardware
| Component | Model | Notes |
| --------- | ------------------- | ---------------------------------------- |
| MCU | Raspberry Pi Pico 2 | RP2350 @ 150 MHz, 512 KB RAM, 4 MB flash |
| Ethernet | W5500 module | Any WIZnet W5500 breakout board |
| LoRa | EBYTE E22-900M30S | SX1262 + 30 dBm PA, 868/915 MHz |
---
## Pinout
### System pins (Pico 2, fixed)
| GPIO | Function |
| ---- | --------------------------------------- |
| GP24 | VBUS sense — HIGH when USB is connected |
| GP25 | User LED (heartbeat) |
| GP29 | ADC3 — VSYS/3, measures supply voltage |
### W5500 Ethernet (SPI0)
| W5500 signal | Pico 2 GPIO |
| ------------ | ----------- |
| MISO | GP16 |
| CS / SCS | GP17 |
| SCK | GP18 |
| MOSI | GP19 |
| RST | GP20 |
| INT | — (nc) |
| VCC | 3.3V |
| GND | GND |
> SPI0 is reserved for the W5500.
### E22-900M30S LoRa (SPI1)
| E22 signal | Pico 2 GPIO | Notes |
| ---------- | ----------- | ------------------------------------------ |
| SCK | GP10 | SPI1 clock |
| MOSI | GP11 | SPI1 TX |
| MISO | GP12 | SPI1 RX |
| NSS / CS | GP13 | Chip select |
| RESET | GP15 | Active LOW reset |
| DIO1 | GP14 | IRQ interrupt |
| BUSY | GP2 | Module busy indicator |
| RXEN | GP3 | LNA enable — held HIGH permanently |
| TXEN | ← DIO2 | See wiring note below |
| VCC | 3.3V | Add a 100 µF capacitor close to the module |
| GND | GND | — |
> See `wiring.svg` in this directory for the full connection diagram.
---
## Special wiring: DIO2 → TXEN bridge on the E22 module
The E22-900M30S does **not** connect DIO2 to the TXEN pin of its PA internally. They must be bridged with a short wire or solder bridge **on the module itself**:
```text
E22 DIO2 pin ──┐
├── wire / solder bridge on the module
E22 TXEN pin ──┘
```
With this bridge in place, `SX126X_DIO2_AS_RF_SWITCH` causes the SX1262 to drive DIO2 HIGH automatically during TX, enabling the PA without needing an RP2350 GPIO for TXEN.
**Without this bridge the module will not transmit.**
---
## Build
```bash
pio run -e pico2_w5500_e22
```
### Flash — BOOTSEL mode
1. Hold the **BOOTSEL** button on the Pico 2.
2. Connect USB to the PC — it appears as a `RPI-RP2` storage drive.
3. Copy the `.uf2` file:
```text
.pio/build/pico2_w5500_e22/firmware-pico2_w5500_e22-*.uf2
```
Or directly with picotool:
```bash
pio run -e pico2_w5500_e22 -t upload
```
---
## Network usage
This board uses Ethernet (no Wi-Fi). From the Meshtastic app:
- **Enable Ethernet** under `Config → Network → Ethernet Enabled`
- **DHCP** by default; static IP can also be configured
Services available once connected:
| Service | Details |
| ------- | --------------------------- |
| NTP | Time synchronization |
| MQTT | Messages to external broker |
| API | TCP socket on port 4403 |
| Syslog | Remote logging (optional) |
---
## Technical notes
### LoRa — RF control
| Define | Effect |
| ------------------------------ | ----------------------------------------------------------- |
| `SX126X_ANT_SW 3` | GP3 (RXEN) driven HIGH at init and never toggled again |
| `SX126X_DIO2_AS_RF_SWITCH` | SX1262 drives DIO2 HIGH during TX → enables TXEN via bridge |
| `SX126X_DIO3_TCXO_VOLTAGE 1.8` | E22 TCXO controlled by DIO3 |
| `-D EBYTE_E22_900M30S` | Sets `TX_GAIN_LORA=7`, max power 22 dBm |
> RXEN and TXEN may both be HIGH simultaneously during TX — this is safe for the E22 RF switch.
### Ethernet
- Library: `arduino-libraries/Ethernet@^2.0.2` (supports W5100/W5200/W5500 auto-detection).
- SPI0 is explicitly initialized with pins GP16/18/19 before `Ethernet.init()`.
- DHCP timeout is set to 10 s (instead of the default 60 s) to avoid blocking LoRa startup.
### HW_VENDOR
Mapped to `meshtastic_HardwareModel_PRIVATE_HW` — no dedicated model exists in the Meshtastic protobuf for this hardware combination yet.
---
## Memory usage (reference build)
| Resource | Used | Total | % |
| -------- | ------ | ------- | ----- |
| RAM | 94 KB | 512 KB | 18% |
| Flash | 964 KB | 3.58 MB | 26.3% |
@@ -0,0 +1,25 @@
[env:pico2_w5500_e22]
extends = rp2350_base
board = rpipico2
board_level = community
upload_protocol = picotool
build_flags =
${rp2350_base.build_flags}
-ULED_BUILTIN # avoid "LED_BUILTIN redefined" warnings from framework common.h
-I variants/rp2350/diy/pico2_w5500_e22
-D HW_SPI1_DEVICE
-D EBYTE_E22_900M30S # selects the EBYTE E22-900M30S module config, including TCXO voltage support and TX gain / max power settings
# Re-enable Ethernet and API source paths excluded in rp2350_base
build_src_filter = ${rp2350_base.build_src_filter} +<mesh/eth/> +<mesh/api/> +<mqtt/>
lib_deps =
${rp2350_base.lib_deps}
${networking_base.lib_deps}
${networking_extra.lib_deps}
# Standard WIZnet Ethernet library — supports W5100/W5200/W5500 auto-detect
arduino-libraries/Ethernet@^2.0.2
debug_build_flags = ${rp2350_base.build_flags}, -g
debug_tool = cmsis-dap
@@ -0,0 +1,88 @@
// Raspberry Pi Pico 2 + external W5500 Ethernet module + EBYTE E22-900M30S
// RP2350 (4 MB flash) — wire modules to the GPIO pins listed below
//
// LoRa (SX1262 / E22-900M30S) on SPI1:
// SCK=GP10 MOSI=GP11 MISO=GP12 CS=GP13
// RST=GP15 DIO1/IRQ=GP14 BUSY=GP2 RXEN=GP3
// TXEN: bridge E22_DIO2 → E22_TXEN on the module (no RP2350 GPIO needed)
//
// W5500 Ethernet on SPI0:
// MISO=GP16 CS=GP17 SCK=GP18 MOSI=GP19 RST=GP20
//
// See wiring.svg in this directory for a complete connection diagram.
// Community/DIY board — no dedicated Meshtastic HardwareModel
#define PRIVATE_HW
#define ARDUINO_ARCH_AVR
// Onboard LED (GP25 on Pico 2)
#define LED_POWER PIN_LED
// Power monitoring
// GP24: VBUS sense HIGH when USB is present (digital read)
// GP29: ADC3 measures VSYS/3 (200 kΩ / 100 kΩ divider, same as standard Pico 2)
#define EXT_PWR_DETECT 24
#define BATTERY_PIN 29
#define ADC_MULTIPLIER 3.0
#define BATTERY_SENSE_RESOLUTION_BITS 12
// No real battery — suppress false "battery at 100%" while USB powers VSYS
#define NO_BATTERY_LEVEL_ON_CHARGE
// Optional user button — connect a button between GP6 and GND
// #define BUTTON_PIN 6
// #define BUTTON_NEED_PULLUP
// GPS on UART1 (Serial2) — GP8 TX, GP9 RX
// GP8/GP9 belong to UART1, so we must use Serial2 (not the default Serial1/UART0).
// GP0/GP1 (UART0 defaults) are free but the firmware treats pin 0 as "not configured".
// GP4/GP5 occupied by I2C (SCL/SDA for BMP-280).
#define HAS_GPS 1
#define GPS_TX_PIN 8
#define GPS_RX_PIN 9
#define GPS_BAUDRATE 38400
#define GPS_SERIAL_PORT Serial2
// ---- EBYTE E22-900M30S on SPI1 -----------------------------------------
#define USE_SX1262
#undef LORA_SCK
#undef LORA_MISO
#undef LORA_MOSI
#undef LORA_CS
#define LORA_SCK 10
#define LORA_MOSI 11
#define LORA_MISO 12
#define LORA_CS 13
#define LORA_DIO0 RADIOLIB_NC
#define LORA_RESET 15
#define LORA_DIO1 14 // IRQ
#define LORA_DIO2 2 // BUSY
#define LORA_DIO3 RADIOLIB_NC
#ifdef USE_SX1262
#define SX126X_CS LORA_CS
#define SX126X_DIO1 LORA_DIO1
#define SX126X_BUSY LORA_DIO2
#define SX126X_RESET LORA_RESET
// GP3 = RXEN: driven HIGH at init and held there (LNA always enabled).
// SX1262 drives DIO2 HIGH during TX → TXEN via bridge on E22 module.
#define SX126X_ANT_SW 3
#define SX126X_DIO2_AS_RF_SWITCH
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
#endif
// ---- W5500 Ethernet on SPI0 --------------------------------------------
#define HAS_ETHERNET 1
// Use the arduino-libraries/Ethernet stack (W5500) instead of RAK13800_W5100S
#define USE_ARDUINO_ETHERNET 1
#define ETH_SPI0_MISO 16
#define ETH_SPI0_SCK 18
#define ETH_SPI0_MOSI 19
#define PIN_ETHERNET_RESET 20
#define PIN_ETHERNET_SS 17
#define ETH_SPI_PORT SPI