The objective of this post is to explain how to install Picoweb, a HTTP Micro web framework for MicroPython. The tests were performed using a DFRobot’s ESP-WROOM-32 device integrated in a ESP32 FireBeetle board.
The objective of this post is to explain how to install Picoweb, a Micro web framework for MicroPython. This framework allows us to create an HTTP server on the ESP32, without needing to worry about the low level details.
Note that at the time of writing Picoweb was not officially supported on the ESP32, and thus this post presents a workaround to install it and make it work.
The tests were performed using uPyCraft, a MicroPython IDE. You can check how to use uPyCraft in this previous post. In particular, we will be uploading a lot of scripts to the ESP32 file system, which is very simple with uPyCraft. You can check here how to do it.
If you prefer, you can check a video version of this tutorial on my YouTube channel.
Connecting to WiFi
In order for us to be able to complete this tutorial, the ESP32 needs to be previously connected to WiFi, to both install some libraries needed and then to run the HTTP Web server.
Connecting to a WiFi network on MicroPython was covered in this previous post, so I will not be covering in detail the procedure needed. Also, on this other tutorial, there’s an explanation on how to create a script for automatically connect to WiFi on the ESP32, using MicroPython.
Since we are using uPyCraft, we can use the code bellow to write a script and run it on the ESP32, in order to automatically connect to the WiFi. Note that you need to change the two first variables to hold your WiFi network credentials.
import network def connect(): ssid = "yourNetworkName" password = "yourNetworkPassword" station = network.WLAN(network.STA_IF) if station.isconnected() == True: print("Already connected") return station.active(True) station.connect(ssid, password) while station.isconnected() == False: pass print("Connection successful") print(station.ifconfig())
After uploading this file, you can automatically connect to the WiFi just by importing the module and calling its connect function.
Note that the name of the module to import corresponds to the name you have given to the file. In my case, I’ve named it wifiConnect. On the version of uPyCraft used in this tutorial (v0.22), we don’t need to put the .py at the end of the file, since the IDE appends it automatically. This however may change in future versions, and the .py extension is needed for us to be able to import the module.
import wifiConnect wifiConnect.connect()
If you are having troubles finding the uploaded file, just import the os module and run the listdir method. This should show you the uploaded file on the file system.
import os os.listdir()
Upon a successful connection, a list of IP addresses should be printed on the MicroPython command line, as shown in figure 1. You should save them since we will need them later.
Figure 1 – IP addresses printed upon successful connection to WiFi.
Resolving the dependencies
In order to be able to use Picoweb on the ESP32, we will need to resolve some of its dependencies, namely some modules that don’t come by default with the MicroPython installation.
So, we start by importing upip and then installing the mentioned modules. Please note that this will not work if the ESP32 is not connected to the WiFi, which we have done in the previous section. The installation is done by calling the install method of the upip module, passing as input a string describing the module we want to install.
import upip upip.install('micropython-uasyncio') upip.install('micropython-pkg_resources')
Please note that the installation may take a while. When everything finishes, a new folder called lib should be available at our file system. You can confirm that with the following commands:
import os os.listdir()
You can check the expected result at figure 2, which shows the newly created library folder.
Figure 2 – New library folder for MicroPython Modules.
You can further explore the contents of this library by using the chdir method of the os module to navigate inside it, but for now we will confirm a successful installation by trying to import both modules.
import uasyncio import pkg_resources
If no error is thrown when executing the 2 previous commands, the the installation has concluded without problems.
As a final note for this section, it’s important to know that Picoweb as a soft dependency to a library called utemplate. This is a soft dependency since as long as we don’t use the template functionalities, the library is not imported, and thus no error occurs.
Although during the tests I’ve performed I’ve been able to manually install it (with the same method we will see below for Picoweb), I’ve not been able to use the template functionalities due to running out of memory on the ESP32. Thus, for this tutorial, we will not worry about utemplate.
So, to maintain the folder structure, we will first create on our ESP32 MicroPython file system a folder called picoweb, as shown below.
import os os.mkdir("picoweb") os.listdir()
Upon the execution of the last line of code, the new directory should appear on the file system list.
Now, we will upload both the __init__.py and utils.py files of the repository’s picoweb folder. As before, you can include the .py extension or not when saving the file, since in the current version of uPyCraft it will put it for you if it is not present.
You can check on figure 3 the upload of the utils.py file, with the relevant menu buttons highlighted (save file and upload file). Don’t forget to maintain the original file names in the popup.
Figure 3 – Example of uploading the utils.py file.
The __init__.py file will take a while to upload, due to its size. Don’t worry if after the upload of this file a memory error is thrown on the console. You can ignore it.
After the upload, you can repeat the os.listdir() command to confirm that the files are on the file system.
Finally, we will move our files to the picoweb folder, with the commands bellow. I’m assuming the os module was already imported from the previous commands. The final command is run to confirm the files are no longer in the root directory and have been copied.
os.rename('__init__.py', 'picoweb/__init__.py') os.rename('utils.py', 'picoweb/utils.py') os.listdir()
You should now be able to import Picoweb, without any error being thrown.
Hello World code
Our hello world program will be a simplified version of one of the examples available in the GitHub repository. Note that although we are analyzing the code step by step, this should be created on a script file for later uploading.
Naturally, we will start by importing the Picoweb module we have just installed.
app = picoweb.WebApp(__name__)
Now we can simply start defining the endpoints for the HTTP requests, in a Flask or Bottle frameworks style. So, to add a new route, we just use the route decorator of our app object, passing as input the URL that will trigger the execution of the function we will define next. In this case we will define the index route, which corresponds to the “/” URL.
Then, we define the function (we will call it index) that will handle this route. This function receives as input two arguments. The first one corresponds to an object of class HTTPRequest and the second one will be a StreamWriter for a socket that we will use to send our response. Note that Picoweb automatically handles the request to pass these two arguments to the function.
@app.route("/") def index(req, resp): (...)
Inside the function, we will start by calling the start_response method of the picoweb module, which starts writing the initial part of the HTTP response, which includes the content-type and the status code.
This function receives as input the previously mentioned stream writer object, which we received in the index function.
Note that by default the status code is 200 and the content-type is text/html, but we can pass these values as parameters. For now, we won’t pass any additional value and thus the defaults will be used.
Finally, we will use the awrite method of our stream writer to send the rest of the content. We will send a simple hello world message.
One important aspect to consider is that we use the yield from keywords before calling any of each functions. This is related to more advanced Python features that are outside of the scope of this post. You can read more about yield here. You can also read more about asynchronous Python and the yield from keywords here.
yield from picoweb.start_response(resp) yield from resp.awrite("Hello world from picoweb running on the ESP32")
To start our server, we end the code by calling the run method on our app object. This method receives as input some important parameters, such as the host and the port where the server will be listening.
The port defaults to 8081, if we don’t pass this argument. The host defaults to 127.0.0.1 if we don’t specify it. In our case, we will not specify the port, and thus use the default 8081.
Nonetheless, we will use the IP address we obtained before when connecting to the WiFi network, on a previous section. We will pass this IP as a string, in the host parameter.
Note that multiple IPs have been printed. Although this may depend on the router, the IP that we should use is the one that begins in 192 and doesn’t end in 254. Thus, in my case, it is 192.168.1.87, but yours will most likely be different. It’s important to consider that this is a local IP address, and thus you should not be able to access the server from outside your network without port forwarding the router.
Additionally, we will set the debug parameter of the run method to True, so we get some additional information printed to the console.
The final complete code, which already includes this call, can be seen bellow.
import picoweb app = picoweb.WebApp(__name__) @app.route("/") def index(req, resp): yield from picoweb.start_response(resp) yield from resp.awrite("Hello world from picoweb running on the ESP32") app.run(debug=True, host = "192.168.1.87")
Testing the code
To test the code, you simply need to upload the script for the ESP32. In my case, since I’m using uPyCraft, this is very easy.
Upon uploading the code, it will be automatically executed in uPyCraft. If you are using other method to upload the code, you may need to manually run the script by importing it as a module.
Upon execution, a message indicating the server is running gets printed on MicroPython serial console, as can be seen on figure 4.
Figure 4 – Running the Picoweb hello world example.
To test that the ESP32 is answering the requests, you can simply copy the http address printed on the console (it should have your ESP32 address). On uPyCraft, you need to select the portion of the text you want to copy, right click it and select copy. Be careful because a ctrl+c command will stop the execution of the server instead of copying the content.
Then, as shown in figure 5, just open a web browser and paste the http address there. You should get the hello world message we defined early. Note that this will only work if the computer is connected to the same network of the ESP32.
If you want to reach the ESP32 server from outside its WiFi network, then you need to port forward the router. This is an advanced procedure that depends on the model of the router, and thus it’s outside the scope of this post.
Figure 5 – Output of the Picoweb program.
- ESP32 MicroPython: Serving HTML from the file system in Picoweb
- ESP32 MicroPython: Changing the HTTP response content-type of Picoweb route