Skip to content

Instantly share code, notes, and snippets.

@danthedaniel
Last active May 3, 2025 23:32
Show Gist options
  • Save danthedaniel/abf41958a3dbf18c3d0f0e4b017598d2 to your computer and use it in GitHub Desktop.
Save danthedaniel/abf41958a3dbf18c3d0f0e4b017598d2 to your computer and use it in GitHub Desktop.
Pi Pico 2W LED control web server
import network
import socket
import json
from machine import Pin
LED = Pin("GP21", Pin.OUT)
FAVICON = (
r"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAB60lEQVQ4T4WTz2sTURDHv7MvP"
r"5ootDRIBcGSarvtwR5MQVov7aE38VoVPIliTz2IN1NCYm89FQotngv9A7zoxUMh0BoQLG2TKu"
r"QWBUHFuJtmu+89Z7OENputGVj2vZnvfPY7A0sICX2Uz8Cgl4C+y2WDn10otUoTuWJQTsGELhe"
r"ecG4DhEhHjSChsURmdv18vgOgv6zchlS7Xc1nHRKgGTJf7bVTnYByfgtEj8LGOpfbZhcPwwHH"
r"hWO2OQp1CsjfQPSKrzv9AYgB3kaUL/ormcuj4YBK/ogtjkMroFHhpn7WsUkPljD56O1Tlxkwc"
r"QHg9SYLnrWKmse1GeJF0msW/pmwSWPZ5+GA/cINxPGZx0i2BM53Xxe72tZbXLtF49lqKKD14U"
r"rhBb9WQwGkl2hsea3d7BsKhD7IxRARNU6nAg6+oSav01zO/S/Ad5F/z+x5D6A10IzdhIXUXsp"
r"8PE1EvOGz6HJQKpWif12nGBHGVCIWgYs+2I6GkvJwZKg/k06nT3oCanVZrNv2lOM4EEIgEY/j"
r"8qXkYd+1wcxcb0AtaeHnvhDGiLeik2YTjabD/5LauT97Z7bnCJ69tx8+Pqg37BXDIIYAriurf"
r"yxrcXHh3rvg0rt20BZ8qlYHVEM/VVAUd6NvJieHfwWbvfs/pr6yEcRnPEoAAAAASUVORK5CYI"
r"I="
)
SCRIPT = r"""
const button = document.getElementById('toggle_led');
button.addEventListener('click', function() {
fetch('/toggle_led.json', {
method: 'POST',
}).then(response => response.json()).then(data => {
console.log(data);
});
});
"""
STYLE = r"""
body {
background-color: #f0f0f0;
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.container {
background-color: white;
padding: 2rem;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
text-align: center;
}
h1 {
color: #333;
margin-bottom: 1.5rem;
}
button {
background-color: #4CAF50;
color: white;
border: none;
padding: 12px 24px;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.2s;
}
button:hover {
background-color: #45a049;
}
button:active {
transform: translateY(1px);
}
"""
INDEX_HTML = """
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:image/png;base64,{FAVICON}">
<style>{STYLE}</style>
</head>
<body>
<div class="container">
<h1>Press the Button!</h1>
<button id="toggle_led">Toggle LED</button>
</div>
<script>{SCRIPT}</script>
</body>
</html>
""".format(FAVICON=FAVICON, SCRIPT=SCRIPT, STYLE=STYLE)
def ap_mode():
ap = network.WLAN(network.AP_IF)
ap.config(essid="PicoWiFi", password="raspberrypi")
ap.active(True)
while ap.active() == False:
pass
LED.on()
print("AP Mode Is Active, You can Now Connect")
print("IP Address To Connect to: " + ap.ifconfig()[0])
STATUS_CODES = {
200: "OK",
400: "Bad Request",
307: "Temporary Redirect",
404: "Not Found",
405: "Method Not Allowed",
500: "Internal Server Error",
}
class WebServer:
def __init__(self, host="", port=80):
self.host = host
self.port = port
self.routes = {}
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def add_route(self, method, path, handler):
"""Register a handler for a specific path and HTTP method"""
if path not in self.routes:
self.routes[path] = {}
self.routes[path][method.upper()] = handler
def parse_request(self, request):
"""Parse the HTTP request to extract method and path"""
request_line = request.decode().split("\n")[0]
method, path, _ = request_line.split(" ")
return method.upper(), path
def bad_request(self, conn):
"""Send a 400 Bad Request response"""
self.send_response(conn, "400 Bad Request", 400, {"Content-Type": "text/plain"})
def not_found(self, conn):
"""Send a 404 Not Found response"""
self.send_response(conn, "404 Not Found", 404, {"Content-Type": "text/plain"})
def method_not_allowed(self, conn):
"""Send a 405 Method Not Allowed response"""
self.send_response(conn, "405 Method Not Allowed", 405, {"Content-Type": "text/plain"})
def internal_server_error(self, conn):
"""Send a 500 Internal Server Error response"""
self.send_response(conn, "500 Internal Server Error", 500, {"Content-Type": "text/plain"})
def send_response(self, conn, content, status_code=200, headers=None):
"""Send HTTP response with headers"""
headers = {
"Connection": "close",
"Content-Length": len(content),
}
if headers:
headers.update(headers)
status_message = STATUS_CODES.get(status_code, "Unknown")
response = (
"HTTP/1.1 {status_code} {status_message}\r\n".format(status_code=status_code, status_message=status_message)
+ "\r\n".join(f'{header}: {value}' for header, value in headers.items())
+ "\r\n\r\n"
+ content
)
conn.send(response.encode())
def run(self):
"""Start the web server"""
self.socket.bind((self.host, self.port))
self.socket.listen(5)
print("Server running on port {port}".format(port=self.port))
while True:
conn, addr = self.socket.accept()
print("Got connection from {addr}".format(addr=addr))
request = conn.recv(1024)
method, path = self.parse_request(request)
# Look up handler for this route
route = self.routes.get(path)
if not route:
self.not_found(conn)
continue
handler = route.get(method)
if not handler:
self.method_not_allowed(conn)
continue
handler(self, conn)
conn.close()
def index(server, conn):
server.send_response(conn, INDEX_HTML, headers={"Content-Type": "text/html"})
def toggle_led(server, conn):
LED.toggle()
response = {
"status": "success",
"led_state": LED.value() == 1,
}
server.send_response(conn, json.dumps(response), headers={"Content-Type": "application/json"})
def led_status(server, conn):
response = {
"status": "success",
"led_state": LED.value() == 1,
}
server.send_response(conn, json.dumps(response), headers={"Content-Type": "application/json"})
ap_mode()
web_server = WebServer()
web_server.add_route("GET", "/", index)
web_server.add_route("GET", "/index.html", index)
web_server.add_route("POST", "/toggle_led.json", toggle_led)
web_server.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment