Pico W — Web servo controller is a runnable microcontroller simulation example on SensBench — open the wired circuit, inspect the code, and compile real firmware in your browser, no install needed. Last updated Aug 14, 2026.

FREE SIMULATOR EXAMPLE · INTERMEDIATE

Pico W — Web servo controller

This example serves a web page from the Pico W with a slider that drives a servo on GP15 — move the slider, the servo follows. WiFi join, HTTP server, PWM servo control, and a browser UI in one MicroPython script.

Board Raspberry Pi Pico W (RP2040 + WiFi)
Difficulty intermediate
Category robotics
Libraries None — just the Arduino core
Runs in Sensbench browser simulator — free, no account
Raspberry Pi Pico W controlling a servo on GP15 from a web page slider in the Sensbench simulator

The exact circuit you get when you open this example — already wired, ready to run.

The MicroPython script

# main.py
# Pico W Web Servo Controller — MicroPython

import network
import socket
from machine import Pin, PWM
import time

ssid = "SENSBENCH-GUEST"
password = ""

print("Connecting to WiFi...")
sta = network.WLAN(network.STA_IF)
sta.active(True)
sta.connect(ssid, password)
while not sta.isconnected():
    time.sleep(0.5)
print("Connected!", sta.ifconfig()[0])

servo = PWM(Pin(15), freq=50)

def write_servo(angle):
    angle = max(0, min(180, angle))
    pulse_us = 500 + (2500 - 500) * (angle / 180)
    duty = int((pulse_us / 20000) * 65535)
    servo.duty_u16(duty)

def webpage(pos):
    return ("<html><body><h1>Servo {p}&deg;</h1>"
            "<input type=range min=0 max=180 value={p} "
            "oninput=\"fetch('?value='+this.value)\"></body></html>").format(p=pos)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('', 80))
s.listen(5)

current_pos = 90
write_servo(current_pos)
print("Web server started. Open browser: http://%s/" % sta.ifconfig()[0])

while True:
    conn, addr = s.accept()
    request = conn.recv(1024).decode('utf-8')
    if "GET /?value=" in request:
        try:
            v = int(request.split("/?value=")[1].split(" ")[0])
            if 0 <= v <= 180:
                current_pos = v; write_servo(v)
        except (ValueError, IndexError):
            pass
    conn.send('HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n')
    conn.sendall(webpage(current_pos).encode('utf-8'))
    conn.close()

How it works

The script joins WiFi and serves one page: a slider whose position maps to a servo angle. The server converts the requested angle to a pulse width — roughly 1000000 + angle * 5555 nanoseconds for 0-180 degrees — and writes it to PWM on GP15. The servo sweeps as you drag the slider.

The servo angle math is the same on every platform: a 50 Hz PWM frame where the duty encodes the position. MicroPython’s duty_ns() makes the nanosecond-level widths readable, which matters because the 0-180 range lives inside just one millisecond of pulse-width difference.

Before you build it for real

The servo’s signal line is fine on GP15, but the motor power must come from a separate 5 V supply — a real servo draws hundreds of milliamps and stalls over an amp, far past what the Pico W’s 3V3 rail can give; powering it from the board browns out the WiFi chip mid-sweep. The pulse-width mapping is approximate: real servos vary slightly, so the endpoints need per-unit calibration (measure at 0 and 180, adjust the constants). And the HTML form has no authentication — anyone on the network can move the servo, which matters the moment this becomes a camera pan-tilt.

Get the parts

Related examples

FAQ

How does the web page control the servo angle?

The server returns an HTML page with a slider; moving it posts the angle back, and the server sets the PWM duty accordingly. Servo position is encoded in pulse width — 1 ms for 0 degrees, 2 ms for 180 — and MicroPython’s PWM.duty_ns() lets you set that width directly.

Why GP15 for the servo?

Any GPIO works on the Pico — every pin is PWM-capable. GP15 is convenient because it sits away from the I2C and UART pins this project might later use. The real constraint is not the pin but the power: the servo motor must be fed from a separate supply.