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.
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.
- 1 × Raspberry Pi Pico W
- 1 × Resistor, 10k ΩIn Starter Kit, Claw Machine Kit, Halloween Prop Kit.
- 1 × Thermistor, 10k NTC
- 1 × RGB LEDIn Starter Projects Kit.
- 1 × Resistor, 330 ΩIn Starter Kit, Claw Machine Kit, Halloween Prop Kit.
- 2 × Resistor, 220 ΩIn Starter Kit, Claw Machine Kit, Halloween Prop Kit.
- 1 × Power, 3.3 V
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 pin | Connects to |
|---|---|
| Resistor, 330 Ω pin 2 | Raspberry Pi Pico W pin GP13 |
| Resistor, 220 Ω (1) pin 2 | Raspberry Pi Pico W pin GP14 |
| Resistor, 220 Ω (2) pin 2 | Raspberry Pi Pico W pin GP15 |
| Resistor, 10k Ω pin 2 | Raspberry Pi Pico W pin GP28 |
| Thermistor, 10k NTC pin 1 | Raspberry Pi Pico W pin GP28 |
| Resistor, 10k Ω pin 1 | 3.3 V |
| Thermistor, 10k NTC pin 2 | Ground |
| Resistor, 330 Ω pin 1 | RGB LED pin R |
| RGB LED pin C | Ground |
| Resistor, 220 Ω (1) pin 1 | RGB LED pin G |
| Resistor, 220 Ω (2) pin 1 | RGB 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.
# 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.15page 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 thatSSIDandPASSWORDmatch 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.