ESP32 MicroPython: HTTP Webserver with Picoweb

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.

The tests were performed using a DFRobot’s ESP-WROOM-32 device integrated in a ESP32 FireBeetle board. The pictures shown through the tutorial are from the tests on the ESP32.

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")
  station.connect(ssid, password)

  while station.isconnected() == False:

  print("Connection successful")

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

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

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.

ESP32 MicroPython WiFi connect get IP

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 will need to install the uasyncio and pkg_resources libraries. Fortunately, we can install them using upip, a MicroPython package installer.

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

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

You can check the expected result at figure 2, which shows the newly created library folder.

ESP32 MciroPython install with upip.png

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.

Installing Picoweb

To install Picoweb, we will basically copy the source files from the GitHub repository to our MicroPython directory. The 2 files that matter are inside the picoweb folder on the repository.

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

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 and 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 file, with the relevant menu buttons highlighted (save file and upload file). Don’t forget to maintain the original file names in the popup.

ESP32 MicroPython uPyCraft File Upload example.png

Figure 3 – Example of uploading the file.

The 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('', 'picoweb/')
os.rename('', 'picoweb/')

You should now be able to import Picoweb, without any error being thrown.

import picoweb

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.

import picoweb

Then, we will create an instance of class WebApp, that we will use latter. We can just use the __name__ macro to pass the module’s name as input.

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.

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 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, 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__)

def index(req, resp):
    yield from picoweb.start_response(resp)
    yield from resp.awrite("Hello world from picoweb running on the ESP32"), host = "")

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.

ESP32 MicroPython picoweb hello world.png

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.

ESP32 MicroPython picoweb framework test hello world.png

Figure 5 – Output of the Picoweb program.

Related Posts

This entry was posted in ESP32 and tagged , , , , , , , , , , . Bookmark the permalink.

5 Responses to ESP32 MicroPython: HTTP Webserver with Picoweb

  1. Pingback: ESP32 MicroPython: Changing the HTTP response content-type of Picoweb route | techtutorialsx

  2. Pingback: ESP32 MicroPython: Serving HTML from the file system in Picoweb | techtutorialsx

  3. Mateusz says:


    Grate and simple manual. Thanks!

    One question/issue…

    I would like to do some kind of simple thermostat based on esp32. I would like to use “json” to comuicate (not mqtt) with user.

    So I need “two thread” program/script.

    One thread to serve html page to show temperature and switch status or set temperature (write to file) to control thermostat and second thread to chceck themperature every e.g. 5 minutes and compare it to temperature in file (some kind of cron job).

    Colud you be so kind and help me… Give some example how to do two threded app (with picoweb to serv html and get some “post” date from form). I do not want to use some kind of hub. The solution should be simple and standalone as possible.

    Best regards,

    Liked by 1 person

    • antepher says:

      Hi! Thanks for the feedback 🙂

      First of all and to clarify your question, JSON and MQTT are two different things than cannot be compared. MQTT is a communication protocol (like HTTP is) and JSON is a data format (like XML is).

      So JSON is not an alternative to MQTT, but rather a way of formatting information.

      Other important thing to note is that Picoweb doesn’t use MQTT at all, but rather HTTP.

      The HTML part should be pretty simple with pico web. You will need to set up a route to serve your HTML file (pretty much what is covered in this tutorial) and then other route to handle the switch/set temperature actions you mentioned.

      Going back to the JSON talk, you can use it as a data format here to send your actions to the picoweb server.

      As for the other thread to check the temperature I cannot currently help you with that since I haven’t yet explored the threading support on MicroPython.

      Many of the things that work on Python work on MicroPython the same way, so if there is already a threading module my guess is that the interface will be very similar to the Python one, to which there are plenty of tutorials around the web.

      If not, you can ask around the MicroPython forums or GitHub page for more information on if this support exists and how it works.

      Let us know if you discover more about the thread support.

      Hope this helps getting you started.

      Best regards,
      Nuno Santos


  4. Mateusz says:

    Thanks for reply !

    I know that MQTT and JSON is something different… it was some kind of shortcut in my post 😉 so I’m starting “extended googling” for uasync lib.

    Best regards,


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s