The Pico W is a fantastic little device, but the examples I have found for serving web pages from it were all too low-level for my taste.
I'm big fan of Bottle and Flask as light-weight web frameworks. And was very excited to find the microdot project. This looks very much like Flask and Bottle, using the decorator syntax (@) to mark functions that are to respond to particular web requests. This leads to code as simple as this for a minimal web server.
from microdot import Microdot app = Microdot() @app.route('/') def index(request): return 'Hello, world!' app.run()
Now thats what I call the right level of abstraction. Not a reference to socket in sight!
The great news is that microdot works with the Raspberry Pi Pico. Although it takes care of all the web serving for you, it will not initiate your WiFi connection. Connecting to WiFi has a few subtleties such as showing connection progress and handling failure to connect and re-connection. It's definitely a wheel that doesn't need reinventing every time you want to serve a web page. So, I have put together a tiny module to wrap that process up as well. Its available here: mm_wlan.
Once installed, your entire web server code for your Pico will just become this:
from microdot import Microdot import mm_wlan ssid = 'my network name' password = 'my password' app = Microdot() mm_wlan.connect_to_network(ssid, password) @app.route('/') def index(request): return 'Hello, from Pico' app.run(port=80)
Step by Step
2. Start a new file in Thonny and paste in the code immediately above. Change ssid and password to match your WiFi network and run it. When you do so, you will see the IP address that the server is running on in the Shell area at the bottom of the Thonny window.
Now open a web browser on that IP address:
Hurray! Your Pico W is being a web server!Adding Another Page
Let's now add a second page to the web server called memory that reports how much free memory the Pico W has. The previous page returned plain text rather than HTML. The default response type of microdot is text, so even if we had put HTML tags into the response, it is displayed literally and not be interpreted by the browser as HTML.
For this new page we will need to import the gc (Garbage Collector) package:
import gc
and then add a handler like this:
@app.route('memory')
def index(request):
response = '<h1>Free Memory={} bytes</hi>'.format(gc.mem_free())
return response, {'Content-Type': 'text/html'}
Notice that now, as well as returning the response text, we also return the content type.
Run the program and, when you point your browser to the page memory, this is what you should see:
Try refreshing the page a few times and you should see the free memory change.
Here's the whole program:
from microdot import Microdotimport mm_wlanimport gc
sid = 'my network name'password = 'my passord'app = Microdot()mm_wlan.connect_to_network(ssid, password)
@app.route('/')def index(request):return 'Hello, from Pico'
@app.route('memory')
def index(request):response = '<h1>Free Memory={} bytes</hi>'.format(gc.mem_free())return response, {'Content-Type': 'text/html'}
app.run(port=80)
Templates
As your web pages get a bit more complex, then some kind of templating will help you construct the response text. Here's an example of a multi-line example taken from an example project in the MonkMakes Plant Monitor.
from microdot import Microdot
import mm_wlan
from pmon import PlantMonitor
ssid = 'network name'
password = 'password'
html = """
<!DOCTYPE html>
<meta http-equiv="refresh" content="1" >
<html>
<head> <title>My Plant</title> </head>
<body>
<h1>Pico W Plant Monitor</h1>
<h2>Water: {water}</h2>
<h2>Temp (C): {temp}</h2>
<h2>Humidity: {humidity}</h2>
</body>
</html>
"""
pm = PlantMonitor()
app = Microdot()
mm_wlan.connect_to_network(ssid, password)
@app.route('/')
def index(request):
w = pm.get_wetness()
t = pm.get_temp()
h = pm.get_humidity()
response = html.format(water=w, temp=t, humidity=h)
return response, {'Content-Type': 'text/html'}
app.run(port=80)
The neat trick here is to use the meta tag to automatically refresh the wen page every second. Perhaps a better way to do this would be to have a microdot page that serves the sensor values of JSON that Javascript then handles on the browser, with the home page including the Javascript to do that. But, that's a lot more work.
Summary
The microdot framework will take a lot of the effort out of your Pico W projects where web serving is required. It's a really impressive bit of framework code.
The microdot framework is pretty complete, supporting different request types, request parameters and pretty much anything you could expect from such a compact web server framework.
1 comment:
I suggest putting the wifi setup in a separate boot.py script that gets run automatically before main.py. This is standard on all other networked MicroPython boards, but it seems to have got lost from Pico lore - see docs on the main MicroPython.org site
Post a Comment