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.

Turning an LED on and off, over the network, with Raspberry Pi Pico W
Turning an LED on and off, over the network, with Raspberry Pi Pico W

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.

Downloading and installing MicroPython on Raspberry Pi Pico W.

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.

Connecting you Raspberry Pi Pico W to a LED.
Connecting your Raspberry Pi Pico W to a LED.

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)


wlan = network.WLAN(network.STA_IF)
wlan.connect(ssid, password)

html = """<!DOCTYPE html>
    <head> <title>Pico W</title> </head>
    <body> <h1>Pico W</h1>

max_wait = 10
while max_wait > 0:
    if wlan.status() < 0 or wlan.status() >= 3:
    max_wait -= 1
    print('waiting for connection...')

if wlan.status() != 3:
    raise RuntimeError('network connection failed')
    status = wlan.ifconfig()
    print( 'ip = ' + status[0] )

addr = socket.getaddrinfo('', 80)[0][-1]

s = socket.socket()

print('listening on', addr)

# Listen for connections
while True:
        cl, addr = s.accept()
        print('client connected from', addr)
        request = cl.recv(1024)
        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")
            stateis = "LED is ON"
        if led_off == 6:
            print("led off")
            stateis = "LED is OFF"
        response = html % stateis
        cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
    except OSError as e:
        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.

Running our Python script on Pico W from the Thonny editor

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 to turn the LED on, and to turn the LED off again.

Turning our LED on from a web browser

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.


Alan Robertson avatar

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 avatar

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

Mark Tomlin avatar

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 avatar

@Scott Stevens – brilliant thread, thanks 😂

Christoff Smith avatar

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’?

Alasdair Allan avatar
Christoff Smith avatar

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 avatar

Hoping to also get some examples using Arduino/c++ code at some point soon.

Alasdair Allan avatar

See the pico-examples repo for code, and the Connecting to the internet with Raspberry Pi Pico W book for documentation

Dan Kitcher avatar

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 avatar

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

rand avatar

I would love to see this and was wondering if you could briefly explain it to me. Thanks


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.

Alasdair Allan avatar

That shouldn’t be necessary, especially the definition of “led” which is defined near the start of the code in line 7.

Dom avatar

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 avatar

The going further tutorial you link to does not allow scrolling and trhe scroll bar is hidden on Chrome on Windows (latest version) to it cannot be used.

Dino McLean avatar

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)

Alasdair Allan avatar

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 avatar

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 avatar

Any way to extend this to remote access using ngrok?

urs avatar

a little modification for using the onboard led
led = Pin(“LED”, Pin.OUT)

Alasdair Allan avatar

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 avatar

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 avatar

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 avatar

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 avatar

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 avatar

Pico w server using socket example is not workinh

Nathan Smith avatar

I can’t get the very first line to work.
ImportError: no module named ‘network’
This StackOverflow ( 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 avatar

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 avatar

well I tried it again this time on the Thonny of my RPI4 and it worked fine ?
Thanks for your post

Brian avatar

First try of a Pico W here – usually use 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 avatar

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)

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