Built-in project · Raspberry Pi Pico W

Pico W web server sets an RGB LED

MicroPython on a Pico W: a socket server on port 80 serves a page whose links set an RGB LED on GP13 to GP15 and that shows a thermistor’s temperature. The whole build, a Raspberry Pi Pico W and 6 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.

Teaching? Assign it to your class in one click.

Advanced About 30 minutes to build and run. Runs in your browser. Free, and no account needed.

The parts retail for about $6.60. On Mokxi they're free.

Pico W web server sets an RGB LED · Raspberry Pi Pico Wlive0.000 s 0.00x
Click to open it in the editor
The circuit itself, running here on the simulator. Press what can be pressed; click anything else to open it in the editor.

What you'll learn

  • Joining Wi-Fi and serving a web page with sockets
  • How a link's path in an HTTP request can control hardware
  • Mixing red, green and blue with three on or off pins
  • Reading a thermistor divider with an ADC
  • Difficulty: Advanced
  • Time: About 30 minutes
  • Board: Raspberry Pi Pico W
  • Parts: 8

How it works

The Pico W joins a Wi-Fi network called Mokxi-Lab, then opens a socket server on port 80. It prints its address, and you open that address from the Web panel.

The page it serves has four links: red, green, blue and off. Each link is a path like /red. When a request arrives, the sketch sets the RGB LED pins GP13, GP14 and GP15 to match, then sends back a page with the LED state.

The page also shows the temperature. A 10k thermistor and a 10k resistor form a divider on GP28, and the Pico turns that reading into degrees Celsius each time the page is built.

Parts list

8 parts, plus the jumper wires. They retail for about $6.60; on Mokxi they're free.

Kits that can build it

These 2 starter kits have every part in the circuit. Each kit's page lists what else it can build.

Wiring table

Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.

Part and pinConnects to
Resistor, 330 Ω pin 2Raspberry Pi Pico W pin GP13
Resistor, 220 Ω (1) pin 2Raspberry Pi Pico W pin GP14
Resistor, 220 Ω (2) pin 2Raspberry Pi Pico W pin GP15
Resistor, 10k Ω pin 2Raspberry Pi Pico W pin GP28
Thermistor, 10k NTC pin 1Raspberry Pi Pico W pin GP28
Resistor, 10k Ω pin 13.3 V
Thermistor, 10k NTC pin 2Ground
Resistor, 330 Ω pin 1RGB LED pin R
RGB LED pin CGround
Resistor, 220 Ω (1) pin 1RGB LED pin G
Resistor, 220 Ω (2) pin 1RGB LED pin B

The code

The MicroPython code exactly as the editor opens it. Change a line there and press Run: the board runs it on the official MicroPython firmware, with nothing to compile.

main.py · Raspberry Pi Pico W
# Set up a web server: the Pico W serves a page on port 80 with four
# links that set the RGB LED (GP13, GP14, GP15) and the temperature from
# the thermistor divider on GP28. Open the board's address from the Web
# panel to see it.
from machine import ADC, Pin
import math
import network
import socket
import time

SSID = "Mokxi-Lab"
PASSWORD = "breadboard"
rgb = [Pin(n, Pin.OUT, value=0) for n in (13, 14, 15)]
sensor = ADC(28)
COLORS = {"/red": (1, 0, 0), "/green": (0, 1, 0), "/blue": (0, 0, 1), "/off": (0, 0, 0)}

def connect(ssid, password):
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    wlan.connect(ssid, password)
    for _ in range(20):                    # give it ten seconds
        if wlan.isconnected():
            return wlan.ifconfig()[0]
        time.sleep_ms(500)
    raise RuntimeError("could not join " + ssid)

def celsius():
    v = sensor.read_u16() / 65535 * 3.3
    r = 10000 * v / (3.3 - v)
    return 1 / (1 / 298.15 + math.log(r / 10000) / 3950) - 273.15

def page(state):
    links = " ".join('<a href="%s">%s</a>' % (p, p[1:]) for p in COLORS)
    return ("<html><body><h1>Pico W</h1><p>LED: %s</p><p>%s</p>"
            "<p>Temperature: %.1f C</p></body></html>") % (state, links, celsius())

ip = connect(SSID, PASSWORD)
server = socket.socket()
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(("0.0.0.0", 80))
server.listen(1)
print("Serving on http://%s/" % ip)
state = "off"

while True:
    client, _ = server.accept()
    request = client.recv(1024).decode()
    path = request.split(" ")[1] if " " in request else "/"
    path = path.rstrip("?")
    if path in COLORS:
        for led, v in zip(rgb, COLORS[path]):
            led.value(v)
        state = path[1:]
        print("LED", state)
    client.send("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n")
    client.send(page(state))
    client.close()

Code walkthrough

The key functions of the sketch, and what each one does.

connect main.py

Turns on the Wi-Fi radio, asks it to join with SSID and PASSWORD, and checks every half second for up to ten seconds. If it connects, it returns the board's IP address. If not, it raises an error saying it could not join.

def connect(ssid, password):
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    wlan.connect(ssid, password)
    for _ in range(20):                    # give it ten seconds
        if wlan.isconnected():
            return wlan.ifconfig()[0]
        time.sleep_ms(500)
    raise RuntimeError("could not join " + ssid)

celsius main.py

Reads GP28 as a voltage, works out the thermistor's resistance from the 10k divider, and uses the beta equation with 3950, 25 C and 10k to get degrees Celsius.

def celsius():
    v = sensor.read_u16() / 65535 * 3.3
    r = 10000 * v / (3.3 - v)
    return 1 / (1 / 298.15 + math.log(r / 10000) / 3950) - 273.15

page main.py

Builds the HTML as a string. It makes one link for each path in COLORS, shows the current LED state, and adds the temperature from celsius to one decimal place.

def page(state):
    links = " ".join('<a href="%s">%s</a>' % (p, p[1:]) for p in COLORS)
    return ("<html><body><h1>Pico W</h1><p>LED: %s</p><p>%s</p>"
            "<p>Temperature: %.1f C</p></body></html>") % (state, links, celsius())

Troubleshooting

The usual mistakes with this circuit, and the fix for each.

  • The script stops with could not join. This project
    Check that SSID and PASSWORD match the network. The script waits only ten seconds.
  • The wrong color lights up. This project
    Red goes through the 330 ohm resistor on GP13, green through 220 ohms on GP14 and blue through 220 ohms on GP15. Check the LED's common pin is on ground.
  • The temperature looks wrong. This project
    The 10k resistor goes from 3.3 V to GP28 and the thermistor from GP28 to ground.
  • The LED is dim, burns out, or the pin reads wrong because the resistor value is off. Resistor
    Read the color bands again. A 220 ohm to 330 ohm resistor suits a typical LED on 5 V. 10k is a common pull-up or pull-down value. Check with a multimeter if unsure.
  • A pull-up or pull-down resistor is missing, so the input floats and reads random values. Resistor
    Tie the input to 5 V or ground through about 10k. Or use INPUT_PULLUP and wire the switch to ground.
  • The temperature reads far off or jumps around. Thermistor
    Use a fixed resistor equal to the thermistor's 10k at 25 C in the divider. Convert with the Steinhart-Hart or Beta equation (Beta is about 3950 for common parts), not a straight line.
  • The reading goes the wrong way, or self-heating shifts it. Thermistor
    An NTC lowers its resistance as it warms, so the divider voltage moves with the wiring side. Check which side the thermistor is on. Keep current small, with 3.3 V or 5 V through 10k, so it does not self-heat.
  • The colors are wrong or one color never lights. RGB LED
    Check whether your LED is common cathode (shared leg to ground) or common anode (shared leg to 5 V). Common anode works backwards: LOW turns a color on.

Change it and keep it

Open it in the editor, change the circuit or the code, and keep your version in a free account.