The objective of this ESP32 Arduino Tutorial is to explain how to develop a simple system where an ESP32 sends HTTP POST requests to a Python Bottle application. The tests were performed on a DFRobot’s ESP32 module, integrated in a ESP32 development board.
Introduction
The objective of this post is to explain how to develop a simple system where an ESP32 sends HTTP POST requests to a Python Bottle application.
Bottle is a light lightweight micro web-framework for Python [1]. Developing a Bottle application is based on the concept of defining routes and functions that are executed when HTTP requests are received on those routes.
It is a pretty minimalist framework that allows us to get started without the need of complicated configurations, needed for more complex web frameworks. This makes it perfect for developing simple proof of concept applications. Nevertheless, there are lots of modules that can be used on top of Bottle and allow to extend its functionality.
The easiest way to install Bottle is by using pip, a Python package installer. To do so, with pip installed on our computer, we just need to send the following command on the command line:
pip install bottle
Note that this application we are going to develop will be relatively simple, but it has all the basics needed to start developing a more complex IoT application where the device sends data to a backend server.
The tests were performed on a DFRobot’s ESP32 module, integrated in a ESP32 development board. This hardware is a very easy to use ESP32 development platform. We will use the ESP32 Arduino support to program the board.
The Python Bottle code
We start our code by importing the functionality needed from the bottle module. The functionalities we are going to import will allow us to run our server, defining that a given route receives POST requests and accessing the body of the request.
from bottle import run, request, post
Then we will define the route where we will receive our HTTP POST requests. To do so, we will use the post decorator. You can read more about Python decorators here. Note that we are going to define an index route, which corresponds to the “/” path.
@post('/')
Alternatively, we could have used the route decorator, specifying the method as POST.
@route('/', method='POST')
Next, we are going to implement the actual function that will run when an HTTP POST request is performed to our route. We will call this function index, since it is serving the index route.
There we will simply access the body of the request and print its content. To do so, we will access to the request object, which is available on our route handling function. On that object, we access the body property and call its read method to get the body content as a string. Then, we will print it, as can be seen bellow.
def index():
data = request.body.read()
print(data)
Finally, to run our bottle server, we simply need to call the run function, specifying as parameters the host and the port. As the host, we should pass the value ‘0.0.0.0‘, so the server will be listening on all the available IPs of the machin.
Note that this value or the specific IP of the machine where we want to be reached needs to be specified in order for the ESP32 to be able to contact the bottle server. You can check here an explanation about the 0.0.0.0 IP and its difference regarding the localhost IP.
Additionally, we will set the debug mode to true in our app by passing an additional parameter to the run function. This way, the outputs printed on the Python console will be more verbose and some information about the incoming requests is available.
run(host='0.0.0.0', port=8090, debug=True)
The final Python code can be seen bellow and is all that we need to start running our server.
from bottle import run, request, post
@post('/')
def index():
data = request.body.read()
print(data)
run(host='0.0.0.0', port=8090, debug=True)
Testing the bottle code
When developing a system with this kind of complexity, it is a good ideia to test each part in separate and only at the end perform whole system tests. This way it becomes much easier to debug.
With this in mind, we will start by debugging the previously created Bottle code. To do so, we will run the server and make a HTTP request from a tool that we know that works well. So, if some problem arises at this step, we know for sure that it is in our backend and we will not complicate our debug by having to look to the ESP32 code.
So, the tool we are going to use is called Postman and you can download it from here. You have desktop versions and you can also use it as a google chrome extension. Postman is a very powerful and easy to use tool that allow us to do HTTP requests without the need to be writing code for a client application.
First of all, run the Bottle program on your Python environment. If you are on IDLE, the IDE that comes with the Python installation, you can run the code from the file editor by pressing F5 or by going to run -> run module. The python interpreter should appear.
Then, after installing Postman, run it. On the interface that appears, select POST from the dropdown to specify the HTTP POST method. On the address bar, put the following address, changing {yourLocalIP} by your machine’s local IP.
http://{yourLocalIp}:8090/
If you are on Windows, you can confirm your local IP by opening the command line and typing the ipconfig command. It is usually an IP with the 192.168.XX.XX format. You can also check this guide from Microsoft.
Then, on the body tab, select the raw radio button. On the rightmost dropdown, select Text. Then, in the editor, write a message that will be sent to the Bottle server on the body of the request. Finally, click send. All those areas are highlighted in figure 1.
Figure 1 – Sending the request to Bottle application from Postman.
In the Python shell where the Bottle application is running, you should get the message sent from Postman, as shown in figure 2.
Figure 2 – Body of the requested printed in the Python shell.
The Arduino ESP32 code
Now we will specify the ESP32 code. It will be very simple and it is very similar to the one we have covered in some previous ESP32 tutorials. Naturally, we will start by importing some libraries, to access both the WiFi connection functions and the HTTP functions. These are the WiFi.h and the HTTPClient.h libraries.
We are also going to store the credentials needed to connect to the WiFi network on two global variables. The credentials needed are the name and the password of the network. You should put the values of the network to which you want to connect to.
#include <WiFi.h>
#include <HTTPClient.h>
const char* ssid = "yourNetworkName";
const char* password = "yourNetworkPassword";
In the setup function, we will handle the connection to the WiFi network. You can check in full detail how to connect to a WiFi network in this previous post.
To sum up, we will call the begin method of the WiFi extern variable defined in the WiFi.h library and pass as input the previously defined credentials needed to connect to the network. Then, we will wait for the connection to be establish in a loop.
We will also print the local IP assigned to the ESP32 upon connecting to the network, so we can later check if it is the same received by the Bottle application. Again, we call a method from the WiFi variable, named localIP. Check bellow the full setup function, with these calls and some additional debugging prints to the serial port.
#include <WiFi.h>
#include <HTTPClient.h>
const char* ssid = "yourNetworkName";
const char* password = "yourNetworkPassword";
void setup() {
Serial.begin(115200);
delay(4000);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) { //Check for the connection
delay(500);
Serial.println("Connecting..");
}
Serial.print("Connected to the WiFi network with IP: ");
Serial.println(WiFi.localIP());
}
We will perform the actual HTTP requests on the Arduino main loop function. You can check in greater detail how to send HTTP POST requests on this previous tutorial. It will be very simple since the HTTPClient.h library exposes an easy to use API.
First, we create an object of class HTTPClient, which we will use to call the methods needed. The first one is the begin method, which receives as input the URL of the server that we want to reach. In our case, it will be the same one used in the previous section to test the Bottle app with Postman.
Next, since we are doing a POST request, we should specify the Content-type HTTP header. This way, we specify what type of data is going to be sent, so the server knows it. To do so, we call the addHeader method of the HTTPClient object, which receives as input the name of the header field and the value for that header field. Since we are going to send a simple “Hello World” message, we specify the Content-type a as text/plain.
HTTPClient http;
http.begin("http://192.168.1.88:8090/"); //Specify destination for HTTP request
http.addHeader("Content-Type", "text/plain"); //Specify content-type header
Then we will call the POST method, passing a input the actual content of the body of our request, which will be received by the Bottle application. We will send a very simple “Hello World” message. This method will perform the actual request, and return the HTTP status code, which we will store in a variable, for error checking.
int httpResponseCode = http.POST("Hello Bottle, from ESP32"); //Send the actual POST request
Since we didn’t specify any return message in our Bottle application, we only need to confirm the status code. It should be 200 on a successful execution of the request. Note that in the ESP32 HTTPClient API, a value lesser than 0 is not a standard HTTP code but rather an internal error while performing the request.
After performing the request, we should call the end method to free the ESP32 resources.
http.end(); //Free resources
The final full Arduino ESP32 code can be seen bellow, for an easy copy and paste. Note that we added the HTTP status code checking and a delay between each HTTP request. We also added a validation before performing the request to confirm that we are still connected to the WiFi network.
#include <WiFi.h>
#include <HTTPClient.h>
const char* ssid = "yourNetworkName";
const char* password = "yourNetworkPassword";
void setup() {
Serial.begin(115200);
delay(4000);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) { //Check for the connection
delay(500);
Serial.println("Connecting..");
}
Serial.print("Connected to the WiFi network with IP: ");
Serial.println(WiFi.localIP());
}
void loop() {
if(WiFi.status()== WL_CONNECTED){ //Check WiFi connection status
HTTPClient http;
http.begin("http://192.168.1.88:8090/"); //Specify destination for HTTP request
http.addHeader("Content-Type", "text/plain"); //Specify content-type header
int httpResponseCode = http.POST("Hello Bottle, from ESP32"); //Send the actual POST request
if(httpResponseCode>0){
Serial.println(httpResponseCode); //Print return code
}else{
Serial.print("Error on sending request: ");
Serial.println(httpResponseCode);
}
http.end(); //Free resources
}else{
Serial.println("Error in WiFi connection");
}
delay(5000); //Send a request every 5 seconds
}
Testing the application
To test the full application, start the Bottle server on your machine and then compile and upload the ESP32 code with the Arduino IDE. Then, open the serial console.
If everything is working well, you should get an output similar to figure 3. Upon connecting to the WiFi network, we start sending the POST requests and receiving as an answer the 200 HTTP response code.
Figure 3 – Output of running the code on the ESP32.
Now if you go to the Python shell, as shown in figure 4, you should get the prints of the body of the request sent by the ESP32. Note that, in my case, the IP shown matches the one printed in the serial console, on figure 3.
Figure 4 – Output of the Bottle application, upon receiving the requests from the ESP32.
Finally, just to exemplify, we can shut down the bottle server and go back to the Arduino IDE serial monitor. In this case, an error message starts getting printed. This happens because the server is no longer available and now the ESP32 cannot connect anymore.
Figure 5 – Output of the ESP32 program when is no longer possible to connect to the Bottle server.