How to run a webserver on Raspberry Pi Pico W
At the tail end of last week we launched Raspberry Pi Pico W. Based around our own RP2040 microcontroller, the Pico W brings 802.11n wireless networking to the Pico family.

This means that your Raspberry Pi Pico W can now talk to the network, but also that the network can talk back to it; and you can run a webserver on your Pico W to allow you to control things remotely.
Installing MicroPython on Raspberry Pi Pico W
The fastest way to get MicroPython is to download the pre-built release binary from the Documentation pages.
Then go ahead and push and hold the BOOTSEL button, and plug your Pico W into the USB port of your computer. Release the BOOTSEL button after your Pico W is firmly connected, and it will mount as a mass storage device called RPI-RP2. Drag and drop the MicroPython UF2 file onto the RPI-RP2 volume. Your Pico W will now reboot. You are now running MicroPython, and you can now access the REPL via USB Serial.
Controlling a LED via the web
When you’re writing software for hardware, turning an LED on, off, and then on again, is typically the first program that gets run in a new programming environment. Learning how to blink an LED gets you half way to anywhere. We’re going to do exactly that, via a web browser.

We are in fact going to build a RESTful(ish) web server to control our LED.
I’ve chosen to attach an external LED to GP15 of our Raspberry Pi Pico W, but you could just as easily use the on-board LED for testing things out. Open up Thonny, and upload the following Python script to your Pico W. If you haven’t used MicroPython and Thonny before, full instructions on how to do that can be found in the Raspberry Pi Pico Python SDK book.
import network
import socket
import time
from machine import Pin
led = Pin(15, Pin.OUT)
ssid = 'YOUR NETWORK NAME'
password = 'YOUR NETWORK PASSWORD'
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(ssid, password)
html = """<!DOCTYPE html>
<html>
<head> <title>Pico W</title> </head>
<body> <h1>Pico W</h1>
<p>%s</p>
</body>
</html>
"""
max_wait = 10
while max_wait > 0:
if wlan.status() < 0 or wlan.status() >= 3:
break
max_wait -= 1
print('waiting for connection...')
time.sleep(1)
if wlan.status() != 3:
raise RuntimeError('network connection failed')
else:
print('connected')
status = wlan.ifconfig()
print( 'ip = ' + status[0] )
addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
s = socket.socket()
s.bind(addr)
s.listen(1)
print('listening on', addr)
# Listen for connections
while True:
try:
cl, addr = s.accept()
print('client connected from', addr)
request = cl.recv(1024)
print(request)
request = str(request)
led_on = request.find('/light/on')
led_off = request.find('/light/off')
print( 'led on = ' + str(led_on))
print( 'led off = ' + str(led_off))
if led_on == 6:
print("led on")
led.value(1)
stateis = "LED is ON"
if led_off == 6:
print("led off")
led.value(0)
stateis = "LED is OFF"
response = html % stateis
cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
cl.send(response)
cl.close()
except OSError as e:
cl.close()
print('connection closed')
Make sure to replace the ssid
and password
with the name and password for your own wireless network at home, and then hit the button to run the script on your Pico W.

After your Pico W connects to your wireless network, you should see the the IP address for your board appear on the REPL inside the Thonny shell window.
To turn our LED on, you can open up a web browser and go to http://10.3.15.120/light/on
to turn the LED on, and http://10.3.15.120/light/off
to turn the LED off again.

You should subsitute your IP address, which for most home networks will probably be in the 192.168.1.X
range, for the one shown here.
Going further
This example lets you remotely turn an LED on, and then off again. However, we can also extend this example to add buttons to the web page we’re serving to allow you to control the LED directly from a web interface rather than by using a RESTful server — which after all is more suited to programmatic use rather than working from a web browser. Alternatively we can go further and reimplement our server, so that rather than blocking, it will operate asynchronously.
More information about connecting your Pico W to the web can be found in the online documentation and the “Connecting to the internet with Raspberry Pi Pico W” book.
32 comments
Alan Robertson
Thanks for such a clear guide, can’t wait for mine to arrive to try it out! Also looking forward to seeing the most excessive webhosting creation someone comes up with for the Pico W 😉
Scott Stevens
Definitely opens up a lot of options for the Pico W!
A word of warning though – using GET requests to control LEDs or any physical device may have some unintended side effects https://twitter.com/rombulow/status/990684453734203392
Mark Tomlin
Yeah, same thing happened when Google Chrome added a feature that would try to predict the next page you would go to and load it in the background. Lots of software would use GET requests instead of the proper POST request for things that would make changes. phpMyAdmin for example used to use GET request for things like DROP TABLE. This caused a lot of havoc in production for a lot of people.
Alan Robertson
@Scott Stevens – brilliant thread, thanks 😂
Christoff Smith
This is great! Will this become a new chapter in the (surely now needed) update of the ‘Get Started with MicroPython on Raspberry Pi Pico Paperback’?
Raspberry Pi Staff Alasdair Allan — post author
There is now a companion book to Getting started with Raspberry Pi Pico called Connecting to the internet with Raspberry Pi Pico W.
Christoff Smith
First thing we’ll try (as Pico noobs) is converting the Foundations’ ‘People in Space’ API project to Pico.
My kids loved that project.
Ian
Hoping to also get some examples using Arduino/c++ code at some point soon.
Raspberry Pi Staff Alasdair Allan — post author
See the pico-examples repo for code, and the Connecting to the internet with Raspberry Pi Pico W book for documentation
Dan Kitcher
I am able to try stuff on an arduino nano (328) chip. That being an 8 bit computer, it never is going to be be good for public internet nor coded wireless such as wifi. It is fairly easy to command a digital pin level from another computer across the room. I suggest being clear with everyone that an 8 bit 328 with 16k of ram and a 16 MHz cpu is meant for different tasks to the new raspberry pi W with wireless. How many mW does the raspberry pi W need to run as an i2c slave device (on a local four wire bus) and what code would that need ?
Marco Griep
I use my rasperry as a coding server. Just installed code-server (from docker hub) and all my development tools like nodejs, go, dotnet sdk and hugo and published this on my subdomain. I can code anywhere now, even on my android tablet. Maybe this is worth an blog article?
Best Regards
Marco
rand
I would love to see this and was wondering if you could briefly explain it to me. Thanks
SCOTT WHITNEY
I added these lines to 82 & 83
# adding this code to try to fix eaddrinuse error
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
and I added these lines to 97 & 98
#added this line to help with ‘led’ isn’t defined error
led = machine.Pin(‘LED’, machine.Pin.OUT)
This solved a few problems I was having.
Raspberry Pi Staff Alasdair Allan — post author
That shouldn’t be necessary, especially the definition of “led” which is defined near the start of the code in line 7.
Dom
I’m also getting an EADDRINUSE error. Sometimes the server will run for a day, then just stop and I can’t reconnect it even powering off completely. Console shows that error on trying to reconnect. Your fix didn’t work for me unfortunately!
Anne
The going further tutorial you link to https://projects.raspberrypi.org/en/projects/get-started-pico-w/0 does not allow scrolling and trhe scroll bar is hidden on Chrome on Windows (latest version) to it cannot be used.
Dino McLean
Hi,
Line 7 should be modified to reflect the change of the LED, which is located on the wireless chip living on WL_GPIO0.
This can be accessed by changing line 7 to:
led = machine.Pin(“LED”, Pin.OUT)
Thanks!
Raspberry Pi Staff Alasdair Allan — post author
Nope, the code is correct. If you look at the breadboard diagram above the code block you’ll see it is controlling an external LED attached to GP15. You’re thinking about the on-board LED, which for Pico is attached to GP25 and for Pico W is attached to WL_GPIO0. But we’re not controlling the on-board LED in this example.
solar3000
Just got one today. I have the first pico when it came out. Now, that I also have the pico w, I forgotten how I got the first one to work.
John
Any way to extend this to remote access using ngrok?
urs
a little modification for using the onboard led
led = Pin(“LED”, Pin.OUT)
Raspberry Pi Staff Alasdair Allan — post author
Nope, the code is correct. If you look at the breadboard diagram above the code block you’ll see it is controlling an external LED attached to GP15. You’re thinking about the on-board LED, which for Pico is attached to GP25 and for Pico W is attached to WL_GPIO0. But we’re not controlling the on-board LED in this example.
Eric Cavalcanti
I copied and pasted the code as it is. Made sure I changed SSID and Password as mentioned.
Got the PICO W, led, resistor, etc as showed in the diagram and it will not work.
Diagram is different from the picture, but it seems the same as the pins are the same (just grnd on the other side).
Eric Cavalcanti
BTW, I get no error when I load the URLs, it just doesn’t work.
If I use the onboard, it works. I tested the resistor and LEDs on Arduino and they work.
Edgar
When it says remotely does that mean from anywhere, without being connected to the same wifi? It only works for me when I’m connected to the same wifi, when I turn the wifi off on my phone it doesn’t operate the light.
Cyril
I run the server on my second core and it works fine. However when I try to fetch a url from first core it always fails with this error: [Errno 115] EINPROGRESS. Is it not possible to run a server and make http calls to outside?
Joseph koo
Pico w server using socket example is not workinh
Nathan Smith
I can’t get the very first line to work.
ImportError: no module named ‘network’
This StackOverflow (https://stackoverflow.com/questions/56666590/how-to-fix-the-network-module-not-found-error-in-micropython-on-raspberry-pi) seems to think that the Pico W running CircuitPython doesn’t work with that lib, that only esp32 and ESP8266 has it. But your example seems to prove otherwise. I’ve found other posts with this same issue, and even in REPL when I import network, it can’t find the module. Am I missing something? Thanks!
James Cotter
I ran the code and then it showed an ip address and said connected and then it gave this error?
Traceback (most recent call last):
File “”, line 43, in
OSError: [Errno 98] EADDRINUSE
James Cotter
well I tried it again this time on the Thonny of my RPI4 and it worked fine ?
Thanks for your post
Brian
First try of a Pico W here – usually use ESP32.in C
I’ve seen EADDRINUSE too many times. Looks like something is holding the IP address open. But a dirty cure seems to be unplug the Pico.
mike
The code works fine the first time it’s run, but if you press Stop in Thonny and then run the code again it fails with the EADDRINUSE error other people have mentioned. I’ve fixed this by setting some socket options:
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(addr)
s.listen(1)
I don’t really understand why that prevents the error, but it does and I consider it a much better solution than unplugging the Pico and plugging it back in again every time I make a change to the code.
Comments are closed