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.


Introduction

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.

 

Update: The library can easily be installed with using upip. To do so, we simply need to import upip and give the following command (after connecting to a WiFi network):

import upip
upip.install('picoweb')

This command will install the Picoweb module and all the dependencies. Thus, you can skip the “Resolving the dependencies” and Installing Picoweb” sections. I will leave them just for reference.

Thank you very much to Siva Kumar for pointing this out. You can check an awesome article about Picoweb from him here.


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.

ESP32 MicroPython WiFi connect get IP

Figure 1 – IP addresses printed upon successful connection to WiFi.


Resolving the dependencies

Deprecated: Check a simpler way to install Picoweb and its dependencies at the end of the introductory section.

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
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.

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

Deprecated: Check a simpler way to install Picoweb and its dependencies at the end of the introductory section.

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
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.

ESP32 MicroPython uPyCraft File Upload example.png

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.

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.

@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.

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

56 thoughts on “ESP32 MicroPython: HTTP Webserver with Picoweb”

  1. Hello! and thanks for this great tutorial!
    I installed picoweb following the updated method, using upip. It seemed to have been installed correctly. However, when running my appplication, I am getting this error:

    File “webServer.py”, line 11, in start
    File “/lib/picoweb/__init__.py”, line 283, in run
    ImportError: no module named ‘logging’

    Do you know what this could mean?

    Regards,

    Javier

    1. Hi!

      You’re welcome, thanks for the feedback 🙂

      Unfortunately I’ve not been using picoweb for a while and at the time I tested I never ran into that issue.

      But it seems to be a missing dependency on a logging module that may have been added later.

      I don’t know if this logging module is some standard one that comes with MicroPython installation or a module that needs to be installed later (I’ve not been using MicroPython for a while and things have evolved a lot since then).

      My suggestion to try to solve your problem is opening a ticket on the picoweb module GitHub page:
      https://github.com/pfalcon/picoweb

      There someone who has already encountered the same issue may be able to help you with a solution.

      Another alternative is trying to manually install that module via upip, although I’m not sure it is available:
      upip.install(‘logging’)

      From the website below, you can also get some luck with:
      upip.install(‘micropython-logging’)

      https://libraries.io/pypi/micropython-logging

      Let us know if you have been able to solve it. If I come across some solution, I will share here.

      Best regards,
      Nuno Santos

      1. Hi Nuno!

        Thanks for your help! I was successful at overcoming that issue with the command:
        upip.install(‘micropython-logging’)….no more complains against the missing ‘logging’ module.

        But, instead, I am getting another error:

        Traceback (most recent call last):
        File “main.py”, line 6, in
        File “webServer.py”, line 12, in start
        File “/lib/picoweb/__init__.py”, line 294, in run
        File “/lib/uasyncio/core.py”, line 224, in get_event_loop
        File “/lib/uasyncio/__init__.py”, line 21, in __init__
        File “/lib/uasyncio/core.py”, line 30, in __init__
        AttributeError: ‘module’ object has no attribute ‘deque’

        I will continue to look at the internet for a solution!!

        BTW I used upycraft to burn the firmware, maybe the fw version is too old?
        Regards,
        Javier

        1. Hi!

          I’m glad you have passed that first error 🙂

          Now it seems to be something related with uasyncio, maybe some update on those libs that you don’t have yet on your MicroPython, which are causing the missing attribute?

          That may be the case indeed! You can try to update uPyCraft to the latest version and install MicroPython again.

          I’m not sure if uPyCraft dynamically fetches a MicroPython build before burning or if it has a static MicroPython version, so it may be a good shot trying with the latest uPyCraft release.

          You can always try a manual burning of MicroPython in your ESP32, so you are sure to use the latest version available.

          I’ve written a tutorial a couple of time ago, I think the procedure still works:
          https://techtutorialsx.com/2017/05/20/esp32-micropython-support/

          Let me know if any of these have helped.

          Best regards,
          Nuno Santos

  2. Hello! and thanks for this great tutorial!
    I installed picoweb following the updated method, using upip. It seemed to have been installed correctly. However, when running my appplication, I am getting this error:
    File “webServer.py”, line 11, in start
    File “/lib/picoweb/__init__.py”, line 283, in run
    ImportError: no module named ‘logging’
    Do you know what this could mean?
    Regards,
    Javier

    1. Hi!
      You’re welcome, thanks for the feedback 🙂
      Unfortunately I’ve not been using picoweb for a while and at the time I tested I never ran into that issue.
      But it seems to be a missing dependency on a logging module that may have been added later.
      I don’t know if this logging module is some standard one that comes with MicroPython installation or a module that needs to be installed later (I’ve not been using MicroPython for a while and things have evolved a lot since then).
      My suggestion to try to solve your problem is opening a ticket on the picoweb module GitHub page:
      https://github.com/pfalcon/picoweb
      There someone who has already encountered the same issue may be able to help you with a solution.
      Another alternative is trying to manually install that module via upip, although I’m not sure it is available:
      upip.install(‘logging’)
      From the website below, you can also get some luck with:
      upip.install(‘micropython-logging’)
      https://libraries.io/pypi/micropython-logging
      Let us know if you have been able to solve it. If I come across some solution, I will share here.
      Best regards,
      Nuno Santos

      1. Hi Nuno!
        Thanks for your help! I was successful at overcoming that issue with the command:
        upip.install(‘micropython-logging’)….no more complains against the missing ‘logging’ module.
        But, instead, I am getting another error:
        Traceback (most recent call last):
        File “main.py”, line 6, in
        File “webServer.py”, line 12, in start
        File “/lib/picoweb/__init__.py”, line 294, in run
        File “/lib/uasyncio/core.py”, line 224, in get_event_loop
        File “/lib/uasyncio/__init__.py”, line 21, in __init__
        File “/lib/uasyncio/core.py”, line 30, in __init__
        AttributeError: ‘module’ object has no attribute ‘deque’
        I will continue to look at the internet for a solution!!
        BTW I used upycraft to burn the firmware, maybe the fw version is too old?
        Regards,
        Javier

        1. Hi!
          I’m glad you have passed that first error 🙂
          Now it seems to be something related with uasyncio, maybe some update on those libs that you don’t have yet on your MicroPython, which are causing the missing attribute?
          That may be the case indeed! You can try to update uPyCraft to the latest version and install MicroPython again.
          I’m not sure if uPyCraft dynamically fetches a MicroPython build before burning or if it has a static MicroPython version, so it may be a good shot trying with the latest uPyCraft release.
          You can always try a manual burning of MicroPython in your ESP32, so you are sure to use the latest version available.
          I’ve written a tutorial a couple of time ago, I think the procedure still works:
          https://techtutorialsx.com/2017/05/20/esp32-micropython-support/
          Let me know if any of these have helped.
          Best regards,
          Nuno Santos

    1. Hi! Unfortunately never had the chance to test it, but my guess is that it should work.

      I think picoweb works on a layer of abstraction that doesn’t need to know if the ESP is working as station or soft AP.

      But let us know f you have the chance to confirm it.

      Best regards,
      Nuno Santos

    1. Hi! Unfortunately never had the chance to test it, but my guess is that it should work.
      I think picoweb works on a layer of abstraction that doesn’t need to know if the ESP is working as station or soft AP.
      But let us know f you have the chance to confirm it.
      Best regards,
      Nuno Santos

  3. hi,
    im trying to install picoweb but upip keep giving me error..

    >>> upip.install(‘picoweb’)
    Installing to: /lib/
    Error installing ‘picoweb’: Package not found, packages may be partially installed
    >>>
    >>>

    it did give me the error once saying SSL cert is not valid.,,

    any help will be great..

    Regards
    Aziz

    1. Hi again,

      sorry I found out the problem apparently the version of micropython and upip that uPyCraft auto installs is old. so downloaded and flashed the latest one and picoweb install worked like a charm..
      thanx for the great tutorials !!
      keep it up!

      Regards
      Aziz

  4. hi,
    im trying to install picoweb but upip keep giving me error..
    >>> upip.install(‘picoweb’)
    Installing to: /lib/
    Error installing ‘picoweb’: Package not found, packages may be partially installed
    >>>
    >>>
    it did give me the error once saying SSL cert is not valid.,,
    any help will be great..
    Regards
    Aziz

    1. Hi again,
      sorry I found out the problem apparently the version of micropython and upip that uPyCraft auto installs is old. so downloaded and flashed the latest one and picoweb install worked like a charm..
      thanx for the great tutorials !!
      keep it up!
      Regards
      Aziz

  5. For logging bug do that in __init__.py of picoweb:
    -> def run(self, host=”127.0.0.1″, port=8081, debug=-1, lazy_init=False, log=None):
    Put debug=-1 in the headers

    For uasyncio in __init__.py, do that:
    in line 60, that self.poller.unregister(sock, False) change for:
    ->
    try:
    self.poller.unregister(sock, False)
    except:
    print(‘Echec unregister’)

    et after, it works well.

  6. For logging bug do that in __init__.py of picoweb:
    -> def run(self, host=”127.0.0.1″, port=8081, debug=-1, lazy_init=False, log=None):
    Put debug=-1 in the headers
    For uasyncio in __init__.py, do that:
    in line 60, that self.poller.unregister(sock, False) change for:
    ->
    try:
    self.poller.unregister(sock, False)
    except:
    print(‘Echec unregister’)
    et after, it works well.

  7. Hello all,

    If you want, I propose a new powerful embedded Web Server for MicroPython (and CPython) that supports route handlers, modules like WebSockets or PyhtmlTemplate and a lot of simultaneous requests.

    Mostly used on ESP32, Pycom WiPy, STM32 on Pyboard, … Robust and efficient (thousands requests on CPython!)

    GitHub – Open Source – MIT.

    –> https://github.com/jczic/MicroWebSrv2 <–

Leave a Reply

Discover more from techtutorialsx

Subscribe now to keep reading and get access to the full archive.

Continue reading