How to build a REST API server with ESP32

Introduction

In this article we will see how to build a REST API server using an ESP32 NodeMCU. We have already seen an example of what REST APIs are and how to use them in the How to make a datalogger with the ESP32 NodeMCU connected to a Django cloud on Raspberry article. Basically, REST APIs are a way for different devices to communicate with each other or to allow us to interact with them.

With this tutorial we will try to create a web server with ESP32 so that it exposes a set of REST APIs. We can use these REST APIs to interact with the ESP32 in order to receive or send data.

We will focus our attention on developing REST APIs using GET and POST methods. In particular, the POST method will send data to the ESP32 via a Json file. We will implement two different APIs:

  • a GET that will return the values ​​of interest set in some variables
  • a POST that will allow us to send values ​​to the ESP32 that will be stored in appropriate variables and will modify the operation of the device

To have a practical example of the potential of this type of communication with the ESP32, we will use a DHT22 sensor to measure ambient temperature and humidity and two red LEDs which we will command in two different ways:

  • a LED can only be ON or OFF
  • the other LED can be adjusted in brightness from a minimum value (LED OFF) to a maximum value (LED ON at maximum brightness) passing through the intermediate values

With the GET API we will be able to read the temperature and humidity values ​​detected by the DHT22 and the variables that control the status of the two LEDs (one boolean which controls the LED which can only be turned ON or OFF and the other integer to control the brightness of the other LED).

The GET type API will look like this:

http://IP_ESP32/getValues

while the POST type API looks like this:

http://IP_ESP32/setStatus

and it will send to our ESP32 a Json file like this:

{
	"led1Status": "34",
	"led2Status":  false
}

where IP_ESP32 is the IP address assigned to the board by the router, led1Status represents the brightness value to adjust for one of the two LEDs while led2Status is used to turn ON/OFF the other LED.

To interact with the ESP32 through the REST API we will use a program called Postman, whose use we will see later.

Also for this tutorial we will create the project using the Platformio IDE.

What components do we need?

The list of components is not particularly long:

Project realization

The wiring diagram

Before realizing the real circuit let’s look at the pinout of the board:

ESP32 pinout
ESP32 pinout

We will use GPIO 14 to connect the DHT22 sensor.

The DHT22 sensor pinout is as follows:

The DHT22 sensor and its pinout
The DHT22 sensor and its pinout

We’ll use GPIOs 16 and 17 to connect the LEDs.

The LED connected to pin 17 is the one that can only be set ON or OFF. The GPIO17 will be set to HIGH to turn the LED ON and LOW to turn it OFF.

The LED connected to pin 16 is the one whose brightness can be adjusted. To obtain this result, it will be driven by the ESP32 via a PWM signal whose duty cycle varies as the value contained in a specific variable varies, a value that goes from 0 (LED OFF) to 255 (LED completely ON) passing through the intermediate values.

The commands to manage the two LEDs will be sent to the ESP32 via the POST type REST API.

At this point you can proceed with the creation of the circuit by following the wiring diagram below. Unfortunately, the ESP32 NodeMCU is too large to fit on the breadboard, which is why it will be connected with flying leads to the rest of the circuit.

The two LEDs are connected to the ESP32 via two 100Ω resistors to limit the current flowing through them and avoid burning them (and burning the two digital outputs).

The LED has two terminals (called anode and cathode) and, like all diodes, it is a component that has its own polarity: it passes the current when it is forward polarized (i.e. the voltage at the anode is greater than that at the cathode) and it blocks current when it is reverse polarized (i.e. the anode voltage is lower than the cathode voltage). The voltage between the anode and cathode, which we will indicate with Vd, varies according to the color of the light emitted. In particular we have that:

  • Vd = 1.8 V for red LEDs
  • Vd = 1.9 V for yellow LEDs
  • Vd = 2 V for green LEDs
  • Vd = 2 V for orange LEDs
  • Vd = 3 V for blu LEDs
  • Vd = 3 V for white LEDs

Below is the assembly diagram created with Fritzing:

The wiring diagram
The wiring diagram

As you can see, the sensor power is taken from the 3.3V output of the NodeMCU (pin 3V3). It is necessary to feed it with 3.3V so that its output is also 3.3V as the digital pins of the NodeMCU do not accept voltages higher than 3.3V.

WARNING: in the ESP32 NodeMCU the maximum voltage tolerated by the digital inputs is equal to 3.3V. Any higher voltage would damage it irreparably!!

How do we identify the anode and cathode of the LED? We do this by looking at its terminals. The longest corresponds to the anode. Also, the LED body has a flattening at one point on the edge indicating that the nearby terminal is the cathode.

So if an LED doesn’t light up it’s possible that it’s wired upside down. In this case, to make it work, simply reverse the wiring.

How do you calculate the resistance to connect to the LED?

Please note: this paragraph deals with the calculation of the limiting resistance in a theoretical way and requires a minimum knowledge of the basics of Electrotechnics. Therefore it is not essential for understanding the rest of the project and can be skipped by the reader not interested in such theoretical aspects.

As we have already said, the resistor between the generic GPIO and the LED serves to limit the current flowing through the LED. But how can we calculate its resistance value? Ohm’s Law comes to our aid which says that the potential difference across a resistor (i.e. the voltage measured at the ends of the resistor) is proportional to the current I flowing through it and the constant of proportionality is precisely the resistance value of the resistor R:

V2 - V1 = RI

Please note: for the sake of precision it must be pointed out that while the resistor is the physical component (the actual object), the resistance is its value. So it is improper (even if it happens frequently) to call the resistor with the term resistance.

We can see Ohm’s Law on a simple circuit consisting of a voltage source (the circle on the left) and a resistor:

Representation of Ohm's Law
Representation of Ohm’s Law

The voltage (or potential difference) V2 – V1 impressed by the voltage source on the resistor is equal to the product of R by I.

Now let’s see a slightly more complex scheme where the usual voltage generator, the resistor and a red LED are present:

Circuit for calculating the current limiting resistor on the LED
Circuit for calculating the current limiting resistor on the LED

In our case the Vg represents the voltage present at the digital output of the ESP32 when it is HIGH and is therefore equal to 3.3V.

Vd is the voltage across the diode (between anode and cathode) when it is forward biased (ie when it is carrying current). Having chosen a red LED, we know from the previous table that Vd = 1.8V.

We need to determine the R-value of the resistor. We still have one unknown: the value of the current I which must flow in the circuit when the pin is in the HIGH state.

Please note: when the digital pin is in the LOW state its voltage (ie Vg) is zero, it follows that also the current I in the circuit is zero.

LEDs generally cannot withstand currents greater than 20mA, so we impose a maximum current of 15mA to be on the safe side.

By Kirchhoff’s voltage law we have that:

Vg - Vr - Vd = 0

From which we derive that:

Vr = Vg - Vd 

Passing to the real values, we have that:

Vr = 3.3V - 1.8V

It follows that:

Vr = 1.5V

But, by Ohm’s Law, we have that:

Vr = RI

from which:

R = Vr / I

Substituting the real values:

R = 1.5V / 0.015A

The result is a value of R equal to 100Ω.

The sketch

Let’s create the PlatformIO project

We have already seen the procedure for creating a PlatformIO project in the article How to create a project for NodeMCU ESP8266 with PlatformIO.

Although it refers to the ESP8266 board, the procedure is similar.
Simply, when choosing the platform, you will have to choose the AZ-Delivery ESP-32 Dev Kit C V4.

Of the libraries indicated, install, following the procedure, the DHT sensor library for ESPx by Bernd Giesecke (which is used to read the data transmitted by the DHT22 temperature and humidity sensor) and leave the other two alone (WiFiManager and UniversalTelegramBot).

We will install the WiFiManager library in another way later.

Install now the library ArduinoJson by Benoit Blanchon:

Install the ArduinoJson library on the PlatformIO project
Install the ArduinoJson library on the PlatformIO project

We still have to install the WiFiManager library that we left pending. We will install it by editing the platformio.ini file and adding the entry https://github.com/tzapu/WiFiManager.git to the lib_deps section. The file will then look like this:

[env:az-delivery-devkit-v4]
platform = espressif32
board = az-delivery-devkit-v4
framework = arduino
monitor_speed = 115200
upload_speed = 921600
lib_deps = 
	bblanchon/ArduinoJson@^6.21.0
	https://github.com/tzapu/WiFiManager.git
	beegee-tokyo/DHT sensor library for ESPx@^1.18

Note the WiFiManager library added directly to this file.

Of course you can download the project from the following link:

Replace the main.cpp file of the project you created with the one present in the zip file.

Now let’s see how the sketch works.

The sketch begins with including the necessary libraries:

#include <Arduino.h>
#include <WebServer.h>
#include <ArduinoJson.h>
#include "DHTesp.h"
#include <WiFiManager.h>

The dht object is then created which is used to read the sensor and the GPIO to which to connect it is defined:

DHTesp dht;
#define  DHT22_PIN 14   

We then define the webserver that listens on port 80:

WebServer server(80);

and the project variables are defined:

float temperature;
float humidity;
int led1Status = 3;
bool led2Status = false;

The pin in which to connect the LED controlled with the PWM and the operating parameters of the PWM signal are then defined:

// set the pin for the PWM LED
const int ledPinPWM = 16;  // 16 corresponds to GPIO16

// set the PWM properties
const int freq = 5000;
const int ledChannel = 0;
const int resolution = 8;

Then is defined the pin for the LED which can only be ON or OFF:

const int ledPinBool = 17;  // 17 corresponds to GPIO17

The variables:

unsigned long measureDelay = 3000;                //    NOT LESS THAN 2000!!!!!   
unsigned long lastTimeRan;

time the readings by the DHT22 (in this case the reading takes place every 3 seconds). It is advisable not to go below this value as the DHT22 takes about 2 seconds to take a reading.

Next the Json document is created:

StaticJsonDocument<1024> jsonDocument;
char buffer[1024];

The handlePost function manages the POST type API and extracts the led1Status and led2Status values ​​from the Json received from the POST and saves them in the respective variables:

void handlePost() {
  if (server.hasArg("plain") == false) {
    //handle error here
  }
  String body = server.arg("plain");
  deserializeJson(jsonDocument, body);
  
  // Get RGB components
  led1Status = jsonDocument["led1Status"];
  led2Status = jsonDocument["led2Status"];

  // Respond to the client
  server.send(200, "application/json", "{}");
}

The createJson, addJsonObject and getValues ​​functions create the Json document which is sent by the ESP32 in response to the GET API by inserting the values ​​present in the temperature, humidity, led1Status and led2Status variables:

void createJson(char *name, float value, char *unit) {  
  jsonDocument.clear();
  jsonDocument["name"] = name;
  jsonDocument["value"] = value;
  jsonDocument["unit"] = unit;
  serializeJson(jsonDocument, buffer);  
}
 
void addJsonObject(char *name, float value, char *unit) {
  JsonObject obj = jsonDocument.createNestedObject();
  obj["name"] = name;
  obj["value"] = value;
  obj["unit"] = unit; 
}

void getValues() {
  Serial.println("Get all values");
  jsonDocument.clear(); // Clear json buffer
  addJsonObject("temperature", temperature, "°C");
  addJsonObject("humidity", humidity, "%");
  addJsonObject("led1Status", led1Status, "%");
  addJsonObject("led2Status", led2Status, "boolean");

  serializeJson(jsonDocument, buffer);
  server.send(200, "application/json", buffer);
}

The setupApi function routes the getValues ​​and setStatus APIs by associating them with the getValues ​​and handlePost functions:

void setupApi() {
  server.on("/getValues", getValues);
  server.on("/setStatus", HTTP_POST, handlePost);
 
  // start server
  server.begin();
}

At this point there is the setup function.

The serial port is activated:

Serial.begin(115200);
  // This delay gives the chance to wait for a Serial Monitor     without blocking if none is found
delay(1500); 

Next is the part that manages the WiFi connection:

  WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP
  // it is a good practice to make sure your code sets wifi mode how you want it.



  //WiFiManager, Local intialization. Once its business is done, there is no need to keep it around
  WiFiManager wm;

  // reset settings - wipe stored credentials for testing
  // these are stored by the esp library
  // wm.resetSettings();

  // Automatically connect using saved credentials,
  // if connection fails, it starts an access point with the specified name ( "AutoConnectAP"),
  // if empty will auto generate SSID, if password is blank it will be anonymous AP (wm.autoConnect())
  // then goes into a blocking loop awaiting configuration and will return success result

  bool res;
  // res = wm.autoConnect(); // auto generated AP name from chipid
  // res = wm.autoConnect("AutoConnectAP"); // anonymous ap
  res = wm.autoConnect("AutoConnectAP","password"); // password protected ap

  if(!res) {
      Serial.println("Failed to connect");
      ESP.restart();
  } 
  else {
      //if you get here you have connected to the WiFi    
      Serial.println("Connected...yeey :)");
  }

and the connection of the DHT22 to the ESP32 via the GPIO14 is defined:

dht.setup(DHT22_PIN, DHTesp::DHT22); // Connect DHT sensor to GPIO 14 

The setupApi function is called, which routes the APIs:

setupApi();

Finally the channels for the two LEDs are configured:

// configure LED PWM functionalitites
ledcSetup(ledChannel, freq, resolution);
  
// attach the channel to the GPIO to be controlled
ledcAttachPin(ledPinPWM, ledChannel);

pinMode(ledPinBool, OUTPUT);

Immediately after that, there is the loop function.

Initially the server is activated:

server.handleClient();

Then it starts an if block which contains some functions inside it.

The condition inside the if determines the time interval with which the measurements are made and the data sent:

if (millis() > lastTimeRan + measureDelay)  {

So the described events happen every measureDelay ms thanks to this condition.

Continuing with the if, we see that the measurements of the physical quantities of our interest are made and the lastTimeRan variable is updated with the current value in ms:

humidity = dht.getHumidity();
temperature = dht.getTemperature();

lastTimeRan = millis();

Immediately afterwards, the states of the two LEDs are set consistently with the values ​​contained in the led1Status and led2Status variables, set via the POST API:

ledcWrite(ledChannel, led1Status);

if(led2Status) {
  digitalWrite(ledPinBool, HIGH);
} else {
  digitalWrite(ledPinBool, LOW);
} 

At this point you just have to upload the sketch to the board and connect it to the WiFi network.

How to connect the board to the Internet

After uploading the sketch to the board, open the Serial Monitor to see the messages coming from the device.

First the board goes into Access Point mode and will give us an IP address which we will use shortly. This operation is used to connect the board to the Internet without having to enter the WiFi network parameters (SSID and password) in the code.

The board provides us with its IP address
The board provides us with its IP address

In this case the IP address is 192.168.4.1.

At this point the ESP32 is in Access Point mode (with SSID AutoConnectAP) and we need to connect our computer to the AutoConnectAP network. If we go to the networks menu of our computer, we should also see the AutoConnectAP network in the list of wireless networks.

List of available WiFi networks
List of available WiFi networks

Connect the computer to the AutoConnectAP network. Then go to your browser and enter the IP previously provided by the ESP32 (which in this example is 192.168.4.1)

You will see a screen like this:

The browser screen for choosing the network
The browser screen for choosing the network

Click the ConfigureWiFi button. It will show you the available networks:

List of available networks
List of available networks

Choose the SSID of your network:

Choose your network
Choose your network

Enter your network password and click the save button:

Enter the password
Enter the password

The response of the board
The response of the board

The ESP32 module keeps the access parameters memorized even if you turn it off, it will remember them on restart and it will reconnect automatically without having to repeat this procedure. Only if you reset it by uncommenting this line

// wm.resetSettings();

will lose the connection parameters.

Please note: the device can memorize only one network. If you later connect it to another network, it will forget the previous network settings.

Let’s test the project on REST APIs

Once the ESP32 has been connected to the WiFi network, it will provide us with its IP address via the PlatformIO Serial Monitor, as shown in the following figure:

We get the IP of the board
We get the IP of the board

In this case the IP assigned by the router to the board is 192.168.1.9. We will need this IP to compose the REST APIs.

To interact with the board we need a special software called Postman. After installing the program, we are ready to use it.

Here’s what its home screen looks like:

Postman home screen
Postman home screen

In the main window there is a bar where you will need to enter the API.

To the left of this bar there is a drop-down menu that allows you to choose the type of API (for example GET, POST, PUT…).

Now choose the GET type and enter the GET API which will have the format:

http://IP_ESP32/getValues

For example, in this case, being the assigned IP 192.168.1.9, the URL of the API will be:

http://192.168.1.9/getValues

Obviously you will have to put the IP address assigned to your ESP32.

Press the Send button on the right.

The API will return a Json file reporting the values ​​detected by the DHT22 and the status of the two LEDs, as shown in the following image:

Result of the GET REST API
Result of the GET REST API

Now let’s try the POST API to send data to the ESP32.

Select the POST type from the drop-down menu.

Enter the URL in the bar:

http://IP_ESP32/setStatus

which in this particular case becomes:

http://192.168.1.9/setStatus

Obviously you will have to put the IP address assigned to your ESP32.

Before pressing the Send button, you will need to select the Body item below the URL bar. Then select the raw item (under Body) and then, on the drop-down menu on the right, select the JSON item instead of Text, as shown in the picture:

Selecting parameters for the POST type API
Selecting parameters for the POST type API

In the window below enter the Json:

{
	"led1Status": "20",
	"led2Status":  false
}

This is what the Postman window should look like:

The Postman window with the POST API
The Postman window with the POST API

By changing the value of the led1Status variable (from 0 to 255) you can vary the brightness of the corresponding LED.

By changing the led2Status value (false or true) you can turn the other LED OFF or ON.

As for the GET, to send the command it is necessary to press the Send key.

Final remarks

You could see how to interact with the ESP32 via the REST API. To do this you used the Postman program. But you could also interact in other ways, such as with a Python script that queries the board automatically via REST API. Therefore having a program that interacts, giving commands or receiving data, in a completely autonomous and independent way.

But these commands can also be given by another device such as another ESP32, thus realizing a real communication between devices (without manual intervention on our part) as already done in article How to make a datalogger with the ESP32 NodeMCU connected to a Django cloud on Raspberry where the communication takes place between an ESP32 and a Raspberry always using a REST API.

Newsletter

If you want to be informed about the release of new articles, subscribe to the newsletter. Before subscribing to the newsletter read the page Privacy Policy (UE)

If you want to unsubscribe from the newsletter, click on the link that you will find in the newsletter email.

Enter your name
Enter your email
Scroll to Top