A minimal, fully async HTTP/1.0 server for tiny devices (ESP32, Raspberry Pi Pico, etc.) running MicroPython or CircuitPython. Compatible with MicroPython 1.21+.
from uht import HTTPServer
app = HTTPServer()
@app.route("/hello/<name>")
async def greet(req, resp, name):
await resp.send(f"Hello, {name}!")
app.run()The Basic Usage section shows how to quickly get started. The Installation Instructions section shows advanced installation options. The Examples section shows real-world examples. There is also an API reference page.
This shows the 3 steps to get an app running on a board, assuming you have mpremote installed.
Make sure your board is connected via USB and install uht with mip:
mpremote mip install logging
mpremote mip install "https://github.com/nmattia/uht/releases/latest/download/uht.py"Create a file named main.py with the following content:
import network
from uht import HTTPServer
nic = network.WLAN(network.AP_IF)
nic.active(True)
nic.config(ssid="uht-test-server", security=network.WLAN.SEC_OPEN)
(ip, _, _, _) = nic.ifconfig()
print(f"URL: 'http://{ip}'")
app = HTTPServer()
@app.route("/")
async def index(req, resp):
await resp.send(b"Hello, world!")
app.run()Run the server on the device:
mpremote run main.pyThe server is now running. Connect to WiFi network uht-test-server and visit http://192.168.4.1/.
This assumes MicroPython was already flashed to your board.
This uses mpremote. This does not require the board to be connected to the internet.
Install the library and dependencies:
mpremote mip install logging
mpremote mip install "https://github.com/nmattia/uht/releases/latest/download/uht.py"If resource constrained or if you don't plan on tweaking the code install the .mpy module:
mpremote mip install "https://github.com/nmattia/uht/releases/latest/download/uht.mpy"
https://docs.micropython.org/en/latest/reference/mpremote.html
Use MicroPython's built-in package manager mip to install the logging library (dependency of uht) and uht. This requires the board to be connected to the internet.
import mip
mip.install("logging")
mip.install("https://github.com/nmattia/uht/releases/latest/download/uht.py")These examples use mpremote. See instructions on how to install it or adapt to your IDE.
The first two examples show how to set up networking. The other examples assume working networking.
The devices creates a new WiFi network (i.e. Access Point mode) and serves "Hello, World!". The device won't have access to the internet and you will need to connect to it directly.
Create a file main_ap.py with the following:
import network
from uht import HTTPServer
nic = network.WLAN(network.AP_IF)
nic.active(True)
nic.config(ssid="uht-test-server", security=network.WLAN.SEC_OPEN)
(ip, _, _, _) = nic.ifconfig()
print(f"URL: 'http://{ip}'")
app = HTTPServer()
@app.route("/")
async def index(req, resp):
await resp.send(b"Hello, world!")
app.run()Run the server on the device:
mpremote run main_ap.pyThe devices connects to a known WiFi network and serves "Hello, World!".
Create a file main_sta.py with the following:
$ mpremote edit :/config.json
{ "SSID": "my-ssid",
"PK": "wpa-password"
}Write this to main_sta.py:
import io
import time
import json
import network
from uht import HTTPServer
def read_config():
with io.open("config.json", "r") as f:
return json.load(f)
# enable station interface and connect to WiFi access point
nic = network.WLAN(network.WLAN.IF_STA)
nic.active(True)
cfg = read_config()
nic.connect(cfg["SSID"], cfg["PK"])
while not nic.isconnected():
print("waiting for connect")
time.sleep(1)
(ip, _, _, _) = nic.ifconfig()
print(f"URL: 'http://{ip}'")
app = HTTPServer()
@app.route("/")
async def index(req, resp):
await resp.send(b"Hello, world!")
app.run()Run the server on the device:
mpremote run main_sta.pyNote the URL printed out.
Define a dynamic route that greets the user by name:
from uht import HTTPServer
app = HTTPServer()
@app.route("/hello/<name>")
async def greet(req, resp, name):
await resp.send("Hello, {name}!")
app.run()Custom HTTP status codes and additional response headers can be set as follows:
from uht import HTTPServer
app = HTTPServer()
@app.route("/custom")
async def custom_response(req, resp):
resp.set_status_code(202)
resp.set_reason_phrase("Accepted")
resp.add_header("X-Custom", "Value")
await resp.send(b"Custom response")
app.run()Note
The status code and headers must be set before the first call to send() otherwise an exception will be thrown!
Response:
HTTP/1.0 202 Accepted
X-Custom: Value
A catch-all handler can be registered:
@app.catchall()
async def not_found(req, resp):
resp.set_status_code(404)
await resp.send(b"Custom 404 Not Found")Note
The status code and headers must be set before the first call to send() otherwise an exception will be thrown!
Any request that doesn't match a defined route will now return:
HTTP/1.0 404
Custom 404 Not Found
If you need to integrate the server with other async code (e.g., background tasks), use the start() method instead of run(). This approach gives you more control and allows you to schedule other coroutines alongside the HTTP server.
from machine import Pin
from uht import HTTPServer
import asyncio
app = HTTPServer()
led_gpio = 8 # GPIO pin driving an LED
state = { "blink_interval_ms" : 500 } # shared state
@app.route("/fast")
async def index(req, resp):
state['blink_interval_ms'] = 250
@app.route("/slow")
async def index(req, resp):
state['blink_interval_ms'] = 1000
async def blink():
pin = Pin(led_gpio, Pin.OUT)
while True:
pin.toggle()
await asyncio.sleep_ms(state['blink_interval_ms'])
async def main():
server = await app.start()
asyncio.create_task(blink())
print("Server started")
await server.wait_closed() # Finish if the server shuts down for any reason
asyncio.run(main())The uht library started as a fork of tinyweb by Konstantin Belyalov. Over time the library was pretty much completely rewritten.
The full diff (before the first commit on this repo) can be found here.