pico intermediate 30 min

Pico W: serve a web page from the chip over Wi-Fi

Connect the Pico W to Wi-Fi and serve an HTML page from the chip. The smallest useful web server on a $6 microcontroller.

Code available for: MicroPythonArduino C
Published Aug 1, 2026

The Pico W has Wi-Fi. Combined with MicroPython’s socket library, you can serve a web page from the chip. No Arduino IDE, no ESP-IDF setup, just a small Python script and a USB cable.

This tutorial gets you from a fresh Pico W to “I have a web page running on my microcontroller” in about 30 minutes.

What you need

  • Raspberry Pi Pico W (the W version is required for Wi-Fi)
  • MicroPython firmware installed (covered in the previous tutorial)
  • A known Wi-Fi network

Step 1: connect to Wi-Fi

Save this as main.py:

import network
import time

ssid = "your-wifi-ssid"
password = "your-wifi-password"

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

print("Connecting to Wi-Fi...")
while not wlan.isconnected():
    print(".", end="")
    time.sleep(1)

print()
print("Connected:", wlan.ifconfig())

The ifconfig() shows the IP address, subnet, gateway, and DNS. Save the IP address; you will need it.

Step 2: serve a web page

MicroPython (Pico)

Replace main.py with:

import network
import socket
import time
from machine import Pin

ssid = "your-wifi-ssid"
password = "your-wifi-password"

led = Pin("LED", Pin.OUT)

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

while not wlan.isconnected():
    print("Connecting...")
    time.sleep(1)

ip = wlan.ifconfig()[0]
print(f"Web server running on http://{ip}")

addr = socket.getaddrinfo(ip, 80)[0][-1]
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(addr)
s.listen(5)

while True:
    try:
        cl, addr = s.accept()
        print("Client connected from", addr)
        request = cl.recv(1024).decode("utf-8")
        print("Request:", request.split("\r\n")[0])

        if "/led/on" in request:
            led.value(1)
            response = "LED is now ON"
        elif "/led/off" in request:
            led.value(0)
            response = "LED is now OFF"
        else:
            response = """
                <html>
                <head><title>Pico W</title></head>
                <body>
                <h1>Pico W Web Server</h1>
                <p><a href="/led/on">Turn LED on</a></p>
                <p><a href="/led/off">Turn LED off</a></p>
                </body>
                </html>
            """

        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:
        print("Error:", e)
        cl.close()

Save and reboot the Pico. Watch the REPL for the IP address. Open that in a browser. Click the links to toggle the onboard LED.

Arduino (Pico)

The Pico W Arduino core supports the WiFi and WebServer libraries the same way the ESP32 does (Earle Philhower’s arduino-pico core ships with both).

#include <WiFi.h>
#include <WebServer.h>

const char* ssid     = "your-wifi-ssid";
const char* password = "your-wifi-password";

WebServer server(80);

void handleRoot() {
  server.send(200, "text/html",
    "<h1>Pico W Web Server</h1>"
    "<p><a href=\"/led/on\">Turn LED on</a></p>"
    "<p><a href=\"/led/off\">Turn LED off</a></p>");
}

void handleLedOn() {
  digitalWrite(LED_BUILTIN, HIGH);
  server.send(200, "text/plain", "LED is now ON");
}

void handleLedOff() {
  digitalWrite(LED_BUILTIN, LOW);
  server.send(200, "text/plain", "LED is now OFF");
}

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(115200);

  WiFi.begin(ssid, password);
  Serial.print("Connecting");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }
  Serial.println();
  Serial.print("IP: ");
  Serial.println(WiFi.localIP());

  server.on("/", handleRoot);
  server.on("/led/on", handleLedOn);
  server.on("/led/off", handleLedOff);
  server.begin();
}

void loop() {
  server.handleClient();
}

Same wiring (no wiring changes needed for the onboard LED). The WiFi and WebServer libraries are the same API as the ESP32, so most tutorials that use the ESP32 Wi-Fi libraries port over with no changes.

What you learned

  • The network module handles Wi-Fi on the Pico W.
  • The socket module is the standard Python socket library.
  • A basic HTTP server is just accept() -> recv() -> send() -> close().
  • You can use the request path (/led/on) to trigger different actions.

Common pitfalls

  • The HTML response does not include the right headers. The browser expects Content-Type: text/html for HTML. Without it, the browser shows the raw HTML or downloads it.
  • The request is too long. recv(1024) reads up to 1024 bytes. For larger requests, loop until you have it all (most browser requests fit in 1024 bytes).
  • Multiple connections at once. The single-threaded server above handles one connection at a time. For a real site, use socket.settimeout() and a pool, or move to asyncio (covered in the book Pico Wi-Fi Projects).

Reading a sensor over Wi-Fi

Combine with the DHT22 or DS18B20 tutorial:

import dht
import machine

sensor = dht.DHT22(machine.Pin(4))

# in the request handler:
sensor.measure()
temp = sensor.temperature()
hum = sensor.humidity()

response = f"<h1>{temp:.1f} C, {hum:.1f} %</h1>"

Now the page shows the current temperature and humidity.

The asyncio version

For projects with multiple things happening at once (e.g. reading a sensor every 5 seconds and serving a web page at the same time), MicroPython supports asyncio:

import asyncio
import network
from machine import Pin

async def blink():
    led = Pin("LED", Pin.OUT)
    while True:
        led.toggle()
        await asyncio.sleep(1)

async def main():
    asyncio.create_task(blink())
    # ... other tasks ...

asyncio.run(main())

The asyncio version is in the book Pico Wi-Fi Projects.

When the Wi-Fi keeps dropping

The Pico W’s Wi-Fi is not as stable as the ESP32’s. If your connection is flaky:

  • Add a reconnect loop:
def ensure_wifi():
    if not wlan.isconnected():
        print("Reconnecting...")
        wlan.disconnect()
        time.sleep(1)
        wlan.connect(ssid, password)
        while not wlan.isconnected():
            time.sleep(1)
        print("Reconnected")
  • Call ensure_wifi() periodically from your main loop.
  • Use a fixed Wi-Fi channel (set in the router). Auto-channel selection can confuse the Pico W.

When to use Pico W vs. ESP32

  • Pico W: $6, MicroPython or C, very low power, fewer peripherals. Great for “sensor reading + simple web interface” projects.
  • ESP32: $4-8, Arduino or MicroPython, more RAM, more peripherals, BLE. Better for complex projects, MQTT-heavy stuff, anything with Bluetooth.

The rule of thumb: if you are doing Wi-Fi + a sensor or two, the Pico W is often the better pick (cheaper, easier to deploy, MicroPython). If you need BLE, MQTT with persistent sessions, or anything with multiple concurrent connections, use the ESP32.

What to build next

  • A weather station with multiple sensors.
  • A MQTT client (publish readings to a broker).
  • A simple web-based UI with sliders and buttons.

The MQTT client is in the book Pico Wi-Fi Projects. The weather station is one of the next tutorials on this site.