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)

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.

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 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.

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.

30 comments
Jump to the comment form

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 😉

Reply to Alan Robertson

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 https://twitter.com/rombulow/status/990684453734203392

Reply to Scott Stevens

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.

Reply to Mark Tomlin

Avatar

@Scott Stevens – brilliant thread, thanks 😂

Reply to Alan Robertson

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

Reply to Christoff Smith

Alasdair Allan
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.

Reply to Christoff Smith

Avatar

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

Reply to Ian

Alasdair Allan

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

Reply to Alasdair Allan

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 ?

Reply to Dan Kitcher

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
Marco

Reply to Marco Griep

Avatar

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

Reply to rand

Avatar

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.

Reply to SCOTT WHITNEY

Alasdair Allan

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

Reply to Alasdair Allan

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!

Reply to Dom

Avatar

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.

Reply to Anne

Avatar

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!

Reply to Dino McLean

Alasdair Allan

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.

Reply to Alasdair Allan

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.

Reply to solar3000

Avatar

Any way to extend this to remote access using ngrok?

Reply to John

Avatar

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

Reply to urs

Alasdair Allan

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.

Reply to Alasdair Allan

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).

Reply to 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.

Reply to Eric Cavalcanti

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.

Reply to Edgar

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?

Reply to Cyril

Avatar

Pico w server using socket example is not workinh

Reply to Joseph koo

Avatar

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!

Reply to Nathan Smith

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

Reply to James Cotter

Avatar

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

Reply to James Cotter

Leave a Comment