Close
0%
0%

W5500-EVB-Pico2 + E22P: a one-cable PoE LoRa node

A carrier for the WIZnet W5500-EVB-Pico2: 1 W LoRa, PoE power and Ethernet over a single cable, for Meshtastic and APRS.

Public Chat
Similar projects worth following
0 followers
One Ethernet cable up the mast carries both power and data. This open-source carrier board takes a WIZnet W5500-EVB-Pico2 (an RP2350 with the W5500 Ethernet controller already on board), adds an IEEE 802.3af PoE module and a 1 W Ebyte E22P LoRa module, and turns it into a Meshtastic node, an MQTT gateway or a LoRa APRS iGate/digipeater. The board is supported in upstream Meshtastic firmware, including firmware updates over Ethernet. Schematic and Gerbers are released under the GPL-2.0.

Why

A node on a roof or a mast needs power and, for a gateway, a network connection. With PoE both come down the same Ethernet cable, and the node can be managed and updated from the LAN without climbing up to it.

Hardware (V2.0)

  • WIZnet W5500-EVB-Pico2: RP2350 (dual Cortex-M33 / Hazard3 RISC-V) with an on-board W5500 hardwired TCP/IP Ethernet controller, so there is no separate Ethernet module to wire.
  • WIZnet WIZPoE-P1: IEEE 802.3af PoE module, Mode A and Mode B.
  • Ebyte E22P-868M30S: SX1262 LoRa module, 30 dBm. The 433 MHz E22P-433M30S uses the same footprint and pins (that is the one for LoRa APRS).
  • Sensirion SHT4x temperature and humidity sensor on board.
  • STEMMA QT / Qwiic connector for more I2C sensors, level-shifted, with a jumper to select its supply voltage.
  • GPS, one of two options: a 5-pin JST SH header (5 V, TX, RX, PPS, GND) for an external receiver, or an on-board GPS02-UBX module (u-blox M10) with a u.FL connector. GPS power goes through a TI TPS22917 load switch on GP22, so the firmware can switch it off.
  • Optional NXP EdgeLock SE050 secure element.

Pin mapping

  • LoRa (SPI1): SCK GP10, MOSI GP11, MISO GP12, CS GP13, RST GP15, DIO1 GP14, BUSY GP2, RXEN GP3.
  • TXEN: solder jumper JP3 ties it to DIO2 by default; rebridge it and TXEN goes to GP6 instead.
  • Ethernet (SPI0, on the EVB-Pico2): MISO GP16, CS GP17, SCK GP18, MOSI GP19, RST GP20.
  • I2C: SDA GP4, SCL GP5. SE050 enable: GP7.
  • GPS (UART1): TX GP8, RX GP9, PPS GP26, power enable GP22, reset GP27 (on-board module only).

Firmware

Meshtastic: the board variant (wiznet_5500_evb_pico2_e22p) and Ethernet OTA updates for RP2350/W5500 boards are both merged upstream (meshtastic/firmware #10552 and #10136, June 2026). The pre-built images linked from the repo come from my fork, which adds an HTTP/HTTPS API so the Meshtastic web client can talk to the node over Ethernet, plus a few fixes still under review upstream.

LoRa APRS: I ported CA2RXU's LoRa APRS iGate/Digipeater firmware to the RP2350 + W5500 (pull request open upstream). Pre-built binaries are linked from the repo.

Reticulum (experimental): a standalone Reticulum transport node with its identity keys generated inside the SE050. It was developed on the sibling board (a plain Pico 2 with a separate W5500 module), which uses the same pin mapping.

Things I learned on the way

  • The E22's RXEN pin enables the LNA. If the radio library drives it as an RX-enable line, it sits low when the radio is idle and the node goes deaf. RXEN is now held high permanently and the PA is switched by DIO2 through the TXEN jumper.
  • On the W5500-EVB-Pico2, GP21 is the W5500 interrupt line. The previous revision had the GPS PPS on GP21; V2.0 moves it to GP26.
  • Without a DHCP server, the Ethernet library blocks for 60 s at boot and LoRa does not start until it gives up. The firmware now uses a 10 s timeout.
  • The hardest bug so far was a stack overflow: one admin command from the web client rebooted the node every time. That story gets its own project log.

Status

  • Tested: everything except the GPS PPS input (GP26) and the TXEN-on-GP6 option (jumper open). Both are still to be checked.
  • V2.0: designed in KiCad, DRC clean, Gerbers in the repo. The boards are on their way from the fab; photos of the assembled board will follow in the project logs.
  • The previous revision, with external sensor and GPS modules, is built; there is a photo in the repo README.

Meshtastic is a trademark of Meshtastic LLC. This board is an independent design, not affiliated with or endorsed by Meshtastic LLC.

Gerber_W5500_evb_Pico2-E22P.zip

Zip Archive - 277.90 kB - 10/14/2026 at 21:18

Download

  • 1 × WIZnet W5500-EVB-Pico2
  • 1 × WIZnet WIZPoE-P1 IEEE 802.3af PoE module
  • 1 × Ebyte E22P-868M30S
  • 1 × Sensirion SHT4x temperature/humidity sensor
  • 1 × TI TPS22917 load switch (GPS power)

View all 10 components

  • One web command, one reboot: hunting a 480-byte stack overflow on the RP2350

    Carlos Valdes • 24 minutes ago • 0 comments

    My rooftop Meshtastic node hung twice, once in September and again in October, and the watchdog reset it. No crash message and no fault output: the log just stopped, and about 15 seconds later the node booted again.

    The rooftop node is the sibling of this board: a plain Pico 2 with a separate W5500 module. It runs the same firmware with the same RP2350 build settings, and the fix at the end applies to both.

    Making it happen on demand

    Both hangs came right after admin messages from the Meshtastic web client, sent through the node's HTTP/HTTPS API (my fork adds that API so the web client can reach the node over Ethernet). One of those messages was "set fixed position". I sent that same command from a script, and the node hung every time: once after 22 hours of uptime, and again 83 seconds after it came back. So it had nothing to do with how long the node had been running.

    Then the same command through three different doors:

    • HTTPS (port 443): hangs.
    • Plain HTTP (port 80): hangs. So it is not TLS.
    • The stock Meshtastic TCP API (port 4403): works.

    Where it stops

    The last line in the log was always Use AES128 key. What comes next in the code is the node checking the signature on a local copy of its own outgoing packet: an XEdDSA verify, about 90 ms of curve25519 maths and a lot of stack. In a healthy send, the next log line appears about 90 ms later.

    A red herring

    The HTTP API handler kept a 512-byte buffer on the stack. Making it static was my first fix. The node still hung.

    Measuring instead of guessing

    So I added marks that print the free stack of the running task (from the stack base that vTaskGetInfo() reports and the current stack pointer), right before and after the verify, only for the node's own packets:

    • TCP API: it entered the verify with 2,080 bytes free, and the lowest point inside was 232 bytes. The verify needs about 1,850 bytes.
    • HTTP API: it entered with 1,368 bytes free, about 480 bytes short. The "after" mark never printed.

    Root cause

    In FreeRTOS mode, arduino-pico runs setup() and loop(), and with them the whole Meshtastic packet path, inside a task called "CORE0" that it creates with a fixed stack of 1024 words: 4 KB, with no build option to change it. That line is still the same in arduino-pico master today (freertos-main.cpp, line 150).

    Stock Meshtastic builds for the RP2350 also run arduino-pico in FreeRTOS mode, so they have the same 4 KB. Through the TCP API they get through the verify with 232 bytes to spare.

    The fix, without patching the core

    The linker can redirect every call to a function: with -Wl,--wrap=xTaskCreate, the framework's call to xTaskCreate lands in __wrap_xTaskCreate, which can still reach the original as __real_xTaskCreate. The wrapper bumps the CORE0 task to 8 KB and creates every other task as asked:

    extern "C" BaseType_t __wrap_xTaskCreate(TaskFunction_t pxTaskCode, const char *const pcName,
                                             const configSTACK_DEPTH_TYPE uxStackDepth, void *const pvParameters,
                                             UBaseType_t uxPriority, TaskHandle_t *const pxCreatedTask)
    {
        configSTACK_DEPTH_TYPE depth = uxStackDepth;
        if (pcName && strcmp(pcName, "CORE0") == 0 && depth < MESHTASTIC_CORE0_STACK_WORDS) // 2048 words = 8 KB
            depth = MESHTASTIC_CORE0_STACK_WORDS;
        return __real_xTaskCreate(pxTaskCode, pcName, depth, pvParameters, uxPriority, pxCreatedTask);
    }
    • A 41-hour soak afterwards: no reboots, and the lowest free stack in 5,769 samples was 3,504 bytes.

    That last number is worth a second look: 3,504 bytes free out of 8 KB means 4,688 bytes used at the deepest point, more than the whole old stack.

    What this does not show

    • Long runs on the V2.0 board: the boards are not here yet. Its firmware build includes the same wrapper.
    • Whether this explains every hang. I have seen another one with the same last log line and a different trigger, and I have not proven it is the same bug.

    The fix is in my firmware fork: core0Stack.cpp and the commit.

View project log

Enjoy this project?

Share

Discussions

Does this project spark your interest?

Become a member to follow this project and never miss any updates