Introduction
In this tutorial we will see how to create a REST API server
using an ESP32 NodeMCU. 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 DHT11/DHT22 sensor to measure ambient temperature and humidity
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:
|
1 |
while the POST type API looks like this:
|
1 |
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:
- a breadboard to
connect the ESP32 NodeMCU to the other components
- some DuPont wires
(male – male, male – female, female – female)
- a DHT22 sensor
- a
4.7kΩ resistor
- two
100Ω resistors
- 2
red LEDs
- and,
of course, an ESP32 NodeMCU !
Project realization
The wiring diagram
Before realizing the real circuit let’s look at the pinout
of the board:
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
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
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
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
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
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:
|
1 |
[env:az-delivery-devkit-v4] |
|
2 |
platform = espressif32 |
|
3 |
board = az-delivery-devkit-v4 |
|
4 |
framework = arduino |
|
5 |
monitor_speed = 115200 |
|
6 |
upload_speed = 921600 |
|
7 |
lib_deps = |
|
8 |
bblanchon/ArduinoJson@^6.21.0 |
|
9 |
https://github.com/tzapu/WiFiManager.git |
|
10 |
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:
|
1 |
#include <Arduino.h> |
|
2 |
#include <WebServer.h> |
|
3 |
#include <ArduinoJson.h> |
|
4 |
#include "DHTesp.h" |
|
5 |
#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:
|
1 |
DHTesp dht; |
|
2 |
#define DHT22_PIN 14 |
We then define the webserver that listens on port 80:
|
1 |
WebServer server(80); |
and the project variables are defined:
|
1 |
float temperature; |
|
2 |
float humidity; |
|
3 |
int led1Status = 3; |
|
4 |
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:
|
1 |
// set the pin for the PWM LED |
|
2 |
const int ledPinPWM =
16; // 16 corresponds to GPIO16 |
|
3 |
|
|
4 |
// set the PWM properties |
|
5 |
const int freq = 5000; |
|
6 |
const int ledChannel = 0; |
|
7 |
const int resolution = 8; |
Then is defined the pin for the LED which can only be ON or
OFF:
|
1 |
const int ledPinBool =
17; // 17 corresponds to GPIO17 |
The variables:
|
1 |
unsigned long measureDelay =
3000; //
NOT LESS THAN 2000!!!!! |
|
2 |
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:
|
1 |
StaticJsonDocument<1024> jsonDocument; |
|
2 |
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:
|
1 |
void handlePost() { |
|
2 |
if (server.hasArg("plain")
== false) { |
|
3 |
//handle error here |
|
4 |
} |
|
5 |
String body = server.arg("plain"); |
|
6 |
deserializeJson(jsonDocument, body); |
|
7 |
|
|
8 |
// Get RGB components |
|
9 |
led1Status =
jsonDocument["led1Status"]; |
|
10 |
led2Status =
jsonDocument["led2Status"]; |
|
11 |
|
|
12 |
// Respond to the client |
|
13 |
server.send(200, "application/json", "{}"); |
|
14 |
} |
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:
|
1 |
void createJson(char *name, float value, char *unit)
{ |
|
2 |
jsonDocument.clear(); |
|
3 |
jsonDocument["name"] = name; |
|
4 |
jsonDocument["value"] = value; |
|
5 |
jsonDocument["unit"] = unit; |
|
6 |
serializeJson(jsonDocument, buffer); |
|
7 |
} |
|
8 |
|
9 |
void addJsonObject(char *name, float value, char *unit)
{ |
|
10 |
JsonObject obj =
jsonDocument.createNestedObject(); |
|
11 |
obj["name"] = name; |
|
12 |
obj["value"] = value; |
|
13 |
obj["unit"] = unit; |
|
14 |
} |
|
15 |
|
|
16 |
void getValues() { |
|
17 |
Serial.println("Get all values"); |
|
18 |
jsonDocument.clear(); // Clear json
buffer |
|
19 |
addJsonObject("temperature",
temperature, "°C"); |
|
20 |
addJsonObject("humidity",
humidity, "%"); |
|
21 |
addJsonObject("led1Status",
led1Status, "%"); |
|
22 |
addJsonObject("led2Status",
led2Status, "boolean"); |
|
23 |
|
|
24 |
serializeJson(jsonDocument, buffer); |
|
25 |
server.send(200, "application/json",
buffer); |
|
26 |
} |
The setupApi function routes
the getValues and setStatus APIs
by associating them with the getValues and handlePost functions:
|
1 |
void setupApi() { |
|
2 |
server.on("/getValues", getValues); |
|
3 |
server.on("/setStatus", HTTP_POST,
handlePost); |
|
4 |
|
5 |
// start server |
|
6 |
server.begin(); |
|
7 |
} |
At this point there is the setup function.
The serial port is activated:
|
1 |
Serial.begin(115200); |
|
2 |
// This delay gives the chance to wait for a
Serial Monitor without blocking if none is found |
|
3 |
delay(1500); |
Next is the part that manages the WiFi connection:
|
1 |
WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults
to STA+AP |
|
2 |
// it is a good practice to make sure your code sets wifi
mode how you want it. |
|
3 |
|
|
4 |
|
|
5 |
|
|
6 |
//WiFiManager, Local intialization. Once its business is
done, there is no need to keep it around |
|
7 |
WiFiManager wm; |
|
8 |
|
|
9 |
// reset settings - wipe stored credentials for testing |
|
10 |
// these are stored by the esp library |
|
11 |
// wm.resetSettings(); |
|
12 |
|
|
13 |
// Automatically connect using saved credentials, |
|
14 |
// if connection fails, it starts an access point with the
specified name ( "AutoConnectAP"), |
|
15 |
// if empty will auto generate SSID, if password is blank
it will be anonymous AP (wm.autoConnect()) |
|
16 |
// then goes into a blocking loop awaiting configuration
and will return success result |
|
17 |
|
|
18 |
bool res; |
|
19 |
// res = wm.autoConnect(); // auto generated AP name from
chipid |
|
20 |
// res = wm.autoConnect("AutoConnectAP"); //
anonymous ap |
|
21 |
res =
wm.autoConnect("AutoConnectAP","password"); //
password protected ap |
|
22 |
|
|
23 |
if(!res) { |
|
24 |
Serial.println("Failed to
connect"); |
|
25 |
ESP.restart(); |
|
26 |
} |
|
27 |
else { |
|
28 |
//if you get here you have
connected to the WiFi |
|
29 |
Serial.println("Connected...yeey
:)"); |
|
30 |
} |
and the connection of the DHT22 to the ESP32 via the GPIO14
is defined:
|
1 |
dht.setup(DHT22_PIN, DHTesp::DHT22); // Connect DHT
sensor to GPIO 14 |
The setupApi function is called,
which routes the APIs:
|
1 |
setupApi(); |
Finally the channels for the two LEDs are configured:
|
1 |
// configure LED PWM functionalitites |
|
2 |
ledcSetup(ledChannel, freq, resolution); |
|
3 |
|
|
4 |
// attach the channel to the GPIO to be controlled |
|
5 |
ledcAttachPin(ledPinPWM, ledChannel); |
|
6 |
|
|
7 |
pinMode(ledPinBool, OUTPUT); |
Immediately after that, there is the loop function.
Initially the server is activated:
|
1 |
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:
|
1 |
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:
|
1 |
humidity = dht.getHumidity(); |
|
2 |
temperature = dht.getTemperature(); |
|
3 |
|
|
4 |
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:
|
1 |
ledcWrite(ledChannel, led1Status); |
|
2 |
|
|
3 |
if(led2Status) { |
|
4 |
digitalWrite(ledPinBool, HIGH); |
|
5 |
} else { |
|
6 |
digitalWrite(ledPinBool, LOW); |
|
7 |
} |
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
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
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
Click the ConfigureWiFi button. It
will show you the available networks:

List of available networks
Choose the SSID of your network:

Choose your network
Enter your network password and click the save button:

Enter the password

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
|
1 |
// 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
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
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:
|
1 |
For example, in this case, being the assigned IP
192.168.1.9, the URL of the API will be:
|
1 |
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
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:
|
1 |
which in this particular case becomes:
|
1 |
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

In the window below enter the Json:
|
1 |
{ |
|
2 |
"led1Status":
"20", |
|
3 |
"led2Status":
false |
|
4 |
} |
This is what the Postman window should look like:
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.