Last active
May 3, 2025 23:32
-
-
Save danthedaniel/abf41958a3dbf18c3d0f0e4b017598d2 to your computer and use it in GitHub Desktop.
Pi Pico 2W LED control web server
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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