Raspberry Pi Pico + W5500
Overview
This tutorial demonstrates how Mongoose Library can be used on an RP2040-based board using the Pico-SDK and MIP (a bare-metal embedded TCP/IP stack designed specifically for Mongoose)
We implement a minimal static web server that responds "ok" to any URL
Board options
You can use either a board like the W5500-EVB-Pico, that has an RP2040 and a W5500 with an Ethernet jack, or you can use for example a Raspberry Pi Pico wired to an add-on board with the W5500.
W5500-EVB-Pico
The default SPI pin selection is just for this board:

mini W5500 add-on

With a board like this one, you have to wire the SPI interface and power connections between the boards:
W5500 | Raspberry Pi Pico | ||
---|---|---|---|
1, 2, 12 | GND | GND | 38 |
3 | MOSI | GPIO19/SPI0_TX | 25 |
4 | SCLK | GPIO18/SPI0_SCK | 24 |
5 | SCNn | GPIO17/SPI0_CSn | 22 |
7 | MISO | GPIO16/SPI0_RX | 21 |
10, 11 | 3.3V | 3V3 | 36 |
The Pico, powered from USB, will provide 3.3V to the add-on board through its built-in step-down power supply
This is the end result:

W5500 add-on

With a board like this one, you also have to wire the SPI interface and power connections between the boards:
W5500 | Raspberry Pi Pico | ||
---|---|---|---|
1, 7 | GND | GND | 38 |
2 | SCLK | GPIO18/SPI0_SCK | 24 |
4 | SCS | GPIO17/SPI0_CSn | 22 |
6 | MOSI | GPIO19/SPI0_TX | 25 |
8 | MISO | GPIO16/SPI0_RX | 21 |
10 | 3.3V | 3V3 | 36 |
With this wiring, the Pico, powered from USB, will provide 3.3V to the add-on board. You can also power this board with a 5V source, which you can also take from the Pico. However, good practices dictate that you add series resistors on every signal connection, as the RP2040 and the W5500 will have different 3.3V power supplies that will not match. So, we recommend you follow the wiring suggested above.
Other RP2040-based boards
If you choose to use any other board, either mimic the wiring above or change this line in main.c
accordingly:
Project structure
This example uses the Pico-SDK and compiles with its standard toolchain:
main.c
- main application file, contains Mongoose logic, SPI low-level code, and pin initialization codemongoose.c
- Mongoose Library source codemongoose.h
- Mongoose Library header fileCMakeLists.txt
- A cmake file that selects the source files and compilation optionsMakefile
- mainly pulls the SDK from its Github repo and calls cmake
Build the project
- Start a terminal in the project directory; clone the Mongoose Library repo, and run the
make
command:$ git clone https://github.com/cesanta/mongoose $ cd mongoose/examples/rp2040/pico-w5500/ $ make
- Once the build succeeds, boot your Pico in USB Mass Storage Mode by holding down the
BOOTSEL
button while you plug it into the USB port. Once it is connected, then release theBOOTSEL
button. It should automatically mount as a new USB disk in your machine. Now either manually copy or drag and drop the generatedexample.uf2
file into this new USB disk device. - The Pico will flash this code and reboot, unmounting this disk and running the flashed code.
- Now open a second terminal, this will be our serial console to see the logs. Run a serial port software (we use picocom; make sure you configure it at 115200bps and to add a carriage return). Device name is usually
/dev/ttyACM0
. Wait a bit and plug your network cable:$ picocom /dev/ttyACM0 -i -b 115200 --imap=lfcrlf picocom v2.2 ... 4653 2 main.c:79:main Ethernet: down 520b 2 main.c:79:main Ethernet: down 5dc3 2 main.c:79:main Ethernet: down 6593 1 mongoose.c:6757:onstatechange Link up 659a 3 mongoose.c:6840:tx_dhcp_discover DHCP discover sent 667b 3 mongoose.c:6723:arp_cache_add ARP cache: added 192.168.0.1 @ 90:5c:44:55:19:8b 667d 2 mongoose.c:6749:onstatechange READY, IP: 192.168.0.24 667e 2 mongoose.c:6750:onstatechange GW: 192.168.0.1 6680 2 mongoose.c:6752:onstatechange Lease: 86062 sec 697b 2 main.c:79:main Ethernet: up 7533 2 main.c:79:main Ethernet: up
Try it out
Start a browser on http://IP_ADDRESS
where IP_ADDRESS
is the board's IP address assigned by your DHCP server. You should see ok
. You can also use curl:
$ curl 192.168.0.24
ok
How it works
Mongoose detects it is being compiled for an RP2040 architecture, so it will configure itself to call the proper SDK functions it needs, for example for its time base in milliseconds. Then the networking stack will call the W5500 driver that works over an SPI connection, we'll provide a set of basic functions that wrap the SDK functions so the driver can use them.
MCU and board initialization
We call SDK functions to initialize the MCU, stdio, and those peripherals we are going to use:
Mongoose initialization
Then we initialize Mongoose, this is no different from what we always do in any example.
There are also two timers we use to blink the LED and print the link state
In this tutorial we'll use a Mongoose low level function to check the timers, in the main loop
SPI low-level functions
We provide a basic set of functions to do transactions, that is, a couple of functions to begin and end the transaction (enable and disable chip select) and a third function to transfer a byte (send one, receive one, simultaneously). These are of course wrappers to SDK functions:
MIP initialization
MIP has to be enabled to be compiled in, and so Mongoose will work in association with it. This is done in the CMakeLists.txt
file by defining MG_ENABLE_MIP=1
.
Then this networking stack has to be configured and initialized. This is done by calling mip_init()
and passing it a pointer to a struct mip_if
. Inside this structure:
- have pointers to a
struct mip_driver
and any extra data that it could need. - For DHCP: set
ip
as zero - For a static configuration, specify
ip
,mask
, andgw
in network byte order
In this example, we use DHCP:
Note that, we also need to specify a unique MAC address. For this example we chose a fancy unicast locally administered address.
Some drivers, as you have probably noticed, require extra data. In this case the W5500 driver needs to know which functions to call to perform SPI initialization and transfers, so we included pointers to those functions we described above
Run Mongoose
Then we run Mongoose. This is a bit different from what we always do in any example, as we'll check the timers, blink the LED and print a message. The logic is standard: initialize the event manager (as we already did), start a listener on port 80, and fall into an infinite event loop:
The listener will call this function when it receives an HTTP request:
There we just reply "ok"
Custom application
In order to create your own Mongoose-enabled application you have several ways:
The obvious way, is to add the required functionality to this example.
If, for some reason, you can't use this example as a base, you can do the following:
- Add relevant project files to your project
- Add
mongoose.c
and.h
files to your project - Add Mongoose-specific configuration flags, see
CMakeLists.txt
- Add the required preprocessor symbols:
MG_ENABLE_MIP=1
- Now write code similar to that in
main.c
; for that you can read Mongoose documentation and follow our examples and tutorials