Servo Motors are among the most important actuators in robotics, with applications ranging from RC planes to automated door locks.
Where To Buy? | ||||
---|---|---|---|---|
No. | Components | Distributor | Link To Buy | |
1 | ESP32 | Amazon | Buy Now |
There are several specific types of electric motor applications in which the motor must rotate only at a specific angle. For such applications, we need a special type of motor with such a special arrangement that causes the motor to rotate at a specific angle for a given electric signal (input). The servo motor comes into effect for this purpose.
A servo motor is a linear or rotary actuator that enables accurate control of linear or angular position, acceleration, and velocity. It is made up of a motor and a sensor for position feedback. It also necessitates a fairly sophisticated controller, which is frequently a dedicated module designed specifically for use with servo motors.
The primary reason to use a servo motor is that it offers angular precision, which means that it will only rotate as far as we want it to before stopping and waiting for the next signal to take action. The servo motor, unlike a standard electric motor, begins to turn as soon as we apply input power to it and continues to rotate until we turn off the power. We cannot restrict the rotational progress of an electric motor, but we can control its speed and turn it on and off. Small servo motors are included in numerous beginner Arduino launcher kits since they are simple to use in small electronic projects and applications.
When compared to an Arduino, interfacing a servo motor to the ESP32 is extremely difficult because it does not support analogWrite(). However, it employs a variety of frequencies and timers, allowing all digital pins to be used as PWM pins and to send signals much faster than any Arduino.
Servo motor consists of three wires;
A servo motor is controlled by sending a PWM or pulse width modulated signal. A servo motor can only turn for a total of 180-degree movement (90 degrees in either direction).
The PWM signal sent to the motor specifies the position of the shaft, and the rotor will turn to the desired position depending on the duration of the pulse sent through the control wire.
We are using Arduino IDE to compile and upload code into the ESP32 module. To know more about Arduino IDE and how to use it, follow our previous tutorial i.e., on the ESP32 programming series.
https://github.com/RoboticsBrno/ServoESP32
#include <Servo.h>
static const int servoPin = 4;
Servo servo1;
void setup()
{
Serial.begin( 115200 );
servo1.attach( servoPin);
}
void loop() {
for(int posDegrees = 0;
posDegrees <= 180;
posDegrees++)
{
servo1.write( posDegrees );
Serial.println( posDegrees );
delay( 20);
}
for(int posDegrees = 180;
posDegrees >= 0;
posDegrees--)
{
servo1.write( posDegrees);
Serial.println( posDegrees);
delay(20);
}
}
Fig.
Fig. Serial monitor output
Fig. ESP32 and servo motor
Let’s create a web server to control the position of the servo motor. We will create a web page containing a slider to control the angle/position of the servo motor’s shaft.
We have also created a tutorial on creating a simple web server with ESP32.
#include <WiFi.h>
#include <Servo.h>
Servo servo1; // create servo object to control a servo
// twelve servo objects can be created on most boards
// GPIO the servo is attached to
static const int servoPin = 13;
// Replace with your network credentials
const char* ssid = "SSID";
const char* password = "PASSWORD";
// Set web server port number to 80
WiFiServer server(80);
// Variable to store the HTTP request
String header;
String valueString = String(5);
int angle_x = 0;
int angle_y = 0;
// Current time
unsigned long currentTime = millis();
// Previous time
unsigned long previousTime = 0;
// Define timeout time in milliseconds (example: 2000ms = 2s)
const long timeoutTime = 2000;
void setup() {
Serial.begin(115200);
servo1.attach(servoPin); // attaches the servo on the servoPin to the servo object
// Connect to Wi-Fi network with SSID and password
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// Print local IP address and start web server
Serial.println("");
Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
server.begin();
}
void loop(){
WiFiClient client = server.available(); // Listen for incoming clients
if (client) { // If a new client connects,
currentTime = millis();
previousTime = currentTime;
Serial.println("New Client."); // print a message out in the serial port
String currentLine = ""; // make a String to hold incoming data from the client
while (client.connected() && currentTime - previousTime <= timeoutTime) { // loop while the client's connected
currentTime = millis();
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
header += c;
if (c == '\n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Connection: close");
client.println();
// Display the HTML web page
client.println("<!DOCTYPE html><html>");
client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
client.println("<link rel=\"icon\" href=\"data:,\">");
// CSS to style the slider
// Feel free to change the background-color and font-size attributes to fit your preferences
client.println("<style>body { text-align: justify; font-family: \"Trebuchet MS\", Arial; margin-left:auto; margin-right:auto;}");
client.println(".slider { width: 600px; }</style>");
client.println("<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>");
// Styling Web Page
client.println("</head><body><h1>Control Servo via ESP32</h1>");
client.println("<p>Angle: <span id=\"servoPos\"></span></p>");
client.println("<input type=\"range\" min=\"0\" max=\"180\" class=\"slider\" id=\"servoSlider\" onchange=\"servo(this.value)\" value=\""+valueString+"\"/>");
client.println("<script>var slider = document.getElementById(\"servoSlider\");");
client.println("var servoP = document.getElementById(\"servoPos\"); servoP.innerHTML = slider.value;");
client.println("slider.oninput = function() { slider.value = this.value; servoP.innerHTML = this.value; }");
client.println("$.ajaxSetup({timeout:2000}); function servo(pos) { ");
client.println("$.get(\"/?value=\" + pos + \"&\"); {Connection: close};}</script>");
client.println("</body></html>");
//GET /?value=180& HTTP/1.1
if(header.indexOf("GET /?value=")>=0) {
angle_x = header.indexOf('=');
angle_y = header.indexOf('&');
valueString = header.substring(angle_x+1, angle_y);
//Rotate the servo
servo1.write(valueString.toInt());
Serial.println(valueString);
}
// The HTTP response ends with another blank line
client.println();
// Break out of the while loop
break;
} else { // if you got a newline, then clear currentLine
currentLine = "";
}
} else if ( c != '\r' ) { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
}
}
// Clear the header variable
header = "";
// Close the connection
client.stop();
Serial.println("Client disconnected.");
Serial.println(" ");
}
}
Fig.
Fig.
Fig.
Fig.
Fig
Fig.
Fig.
Fig.
Fig. Close the connection
Fig. Serial Monitor
Fig. Web Page
This concludes the tutorial. I hope you found this of some help and also to see you soon with a new tutorial on ESP32.
We are familiar with multiple features of the ESP32 module. Like Wi-Fi capabilities, classic Bluetooth, Bluetooth low energy (BLE), inbuilt sensors etc.
Hello readers, I hope you all are doing great. We have already mentioned in our previous tutorials that, ESP32 is also featured with a Dual-Core Processor. Which provides better performance and efficiency.
In this tutorial, we will learn how to use ESP32’s dual cores. ESP32 has two (32-bit each) Tensilica Xtensa LX6 microprocessors namely, core0 and core1 which makes it a powerful dual-core microcontroller and hence stands apart from its predecessors.
When we compile and upload a code using Arduino IDE, we do not have to worry about which core executes the code. It just runs.
Fig. 1 ESP32 dual-core processor
Where To Buy? | ||||
---|---|---|---|---|
No. | Components | Distributor | Link To Buy | |
1 | ESP32 | Amazon | Buy Now |
A FreeRTOS (real-time operating system) firmware is already available in the ESP32 module. FreeRTOS is helpful in improving the system performance and managing the resources of the module. FreeRTOS allows users to handle several tasks like measuring sensor readings, making a network request, controlling motor speed etc. all of them run simultaneously and independently.
FreeRTOS offers multiple APIs for different applications. Those APIs can be used to create tasks and make them run on different cores. You need to create tasks to assign a particular part of code to a specific core. You can also prioritize that on which core the code will run. Priority value starts with level_0.
Whenever we run a code on Arduino IDE, it runs on core_1 by default.
There is a function you can use to check on which core the code is running on.
xPortGetCoreID()
Run the following code in Arduino IDE:
void setup(){
Serial.begin(115200);
Serial.print( " setup() is running on: Core_" );
Serial.println( xPortGetCoreID() );
delay(1000);
}
void loop()
{
Serial.print( " loop() is running on: Core_" );
Serial.println( xPortGetCoreID() );
delay(1000);
}
Fig. 2 Serial Monitor
From the results on the serial monitor, you can see that both setup() function and loop() function are running on core_1.
Fig. 3
This function takes seven arguments:
Fig. 4
For example, we have created a function named task_code(). Inside the task_code() function a for(;;) loop is used which will create an infinite loop. All the instructions to be given for a particular core to perform a particular task like led blinking, sensor readings etc. will be written inside this for(;;) loop.
Fig. 5
You can use the function vTaskDelete() during the code execution to delete a task. This function takes the task handle (task) as an argument.
In this code we will use two LEDs to be processed by different core.
TaskHandle_t task1;
TaskHandle_t task2;
// Assign GPIOs pins to LEDs
const int led1 = LED_BUILTIN;
const int led2 = 25;
void setup() {
Serial.begin(115200 );
pinMode( led1, OUTPUT );
pinMode( led2, OUTPUT );
//create a task that will be executed in the Task1code() function, with priority 1 and executed on core 0
xTaskCreatePinnedToCore(task_1code, // Task function.
"Task1", // name of task.
10000, // Stack size of task
NULL, // parameter of the task
1, // priority of the task
&task1, // Task handle to keep track of created task
1); // pin task to core 1
delay(1000);
//create a task that will be executed in the Task2code() function, with priority 1 and executed on core 1
xTaskCreatePinnedToCore(task_2code, //Task function.
"task2", //name of task.
10000, //Stack size of task
NULL, //parameter of the task
1, //priority of the task
&task2, //Task handle to keep track of created task
0); //pin task to core 0
delay(1000);
}
//task_1code: blinks an LED every 1000 ms
void task_1code( void * pvParameters ){
Serial.print( "task1 running on: core " );
Serial.println( xPortGetCoreID() );
for(;;)
{
digitalWrite( led1, HIGH);
delay(1000);
digitalWrite(led1, LOW);
delay(1000);
}
}
//task_2code: blinks an LED every 500 ms
void task_2code( void * pvParameters )
{
Serial.print( "task2 running on: core " );
Serial.println(xPortGetCoreID() );
for(;;){
digitalWrite(led2, HIGH );
delay(500);
digitalWrite(led2, LOW );
delay(500);
}
}
void loop(){
Serial.print( " loop() is running on: Core " );
Serial.println( xPortGetCoreID() );
delay(1000);
}
Fig. 6
Fig. 7
Fig. 8 setup() function
Fig. 9
Fig. 10
Fig. 11
Fig. 12
Fig. 13 loop function
Components required:
Fig. 14 connecting LED with ESP32
Fig. 15 Results on the serial monitor
This concludes the tutorial. We hope you found this helpful and also hope to see you soon with a new tutorial on ESP32.
Hello readers, I hope you all are doing great. In our previous tutorial, we learnt how to make HTTP POST from ESP32 to the IFTTT server.
In this tutorial, we will learn about another application of the ESP32 module in the field of IoT (Internet of Things). We can publish multiple sensor readings from ESP32 to Google sheets via the IFTTT web service.
IFTTT is used as a third-party web service to integrate Google sheets with ESP32.
Fig. 1
Where To Buy? | ||||
---|---|---|---|---|
No. | Components | Distributor | Link To Buy | |
1 | ESP32 | Amazon | Buy Now |
We are going to create an applet (on the IFTTT server) that is responsible to integrate the Webhooks and Google Sheets services.
While operating with the IFTTT server there are some services/utilities that we are going to deal with like Applets and Webhooks. Before getting started with the project, let’s first introduce you to those terms:
An Applet is a small application or a utility program, which is used for one or a few simple functions. It connects two or more devices or apps together. An applet provides integration between two devices or services to enable some functionality that those devices or services cannot do alone or on their own. An applet consists of actions and triggers.
Fig. 2
Fig. 3: Creating an Applet
Fig. 4: “If This”
Fig. 5: Search and Select Webhooks
Fig. 6: Receive a Web Request
Fig. 7: Create Trigger
Fig. 8: Then That
Fig. 9: Google Sheets
Fig. 10: Select an Action
Fig. 11: Connect to Google Sheets Service
Fig. 12: Allow IFTTT Service to Access Files of your Google Drive
Fig. 13: Complete Action Fields
Fig. 14: Applet Successfully Created
Before interfacing the IFTTT service (applet) with ESP32, let us test the applet whether it is created successfully or not.
Fig. 15
Fig. 16: Test your Applet
Fig. 17: IFTTT Folder in Google Drive
No external components are required as we are using the ESP32’s inbuilt sensors to take the readings.
Let’s have an overview of the project before writing the Arduino code:
#include <WiFi.h>
// Replace with your SSID and Password
const char* ssid = "SSID";
const char* password = "Password";
// Replace with your unique IFTTT URL resource
const char* serverName = "https://maker.ifttt.com/trigger/replace_this_with_eventName/with/key/replace_this_with_your_unique_key ";
// Maker Webhooks IFTTT
const char* server="maker.ifttt.com";
//----Timer for sleep
uint64_t uS_TO_S_FACTOR = 1000000; // Conversion factor for micro seconds to seconds
uint64_t TIME_TO_SLEEP = 900; //sleep for 15 minutes
void setup()
{
Serial.begin(115200);
delay(100);
Serial.print("Connecting to: ");
Serial.print(ssid);
WiFi.begin(ssid, password);
int timeout = 10 * 4; // 10 seconds
while( WiFi.status() != WL_CONNECTED && ( timeout-- > 0) )
{
delay(200);
Serial.print(".");
}
Serial.println(" ");
if(WiFi.status() != WL_CONNECTED )
{
Serial.println(" Failed to connect, going back to sleep ");
}
Serial.print("WiFi connected in: ");
Serial.print(millis());
Serial.print(", IP address: ");
Serial.println(WiFi.localIP());
makeIFTTTRequest();
// enable timer deep sleep
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
Serial.println("Going to sleep now");
esp_deep_sleep_start(); // start deep sleep for 900 seconds (15 minutes)
}
void loop()
{
// sleeping so wont get here
}
void makeIFTTTRequest()
{
Serial.print("Connecting to ");
Serial.print(server);
WiFiClient client;
int retries = 5;
while(!!!client.connect(server, 80) && (retries-- > 0))
{
Serial.print(".");
}
Serial.println();
if(!!!client.connected())
{
Serial.println(" Failed to connect... ");
}
Serial.print(" Request server: ");
Serial.println( serverName );
// Hall sensor values
String jsonObject = String("{\"value1\":\"") +
hallRead() +
"\",\"value2\":\"" + hallRead()
+ "\",\"value3\":\"" +
hallRead() + "\"}";
client.println(String("POST ") + serverName + " HTTP/1.1");
client.println(String("Host: ") + server);
client.println("Connection: close\r\nContent-Type: application/json");
client.print("Content-Length: ");
client.println(jsonObject.length());
client.println();
client.println(jsonObject);
int timeout = 5 * 10; // 5 seconds
while(!!!client.available() && (timeout-- > 0)){
delay(100);
}
if(!!!client.available()) {
Serial.println("No response...");
}
while(client.available()){
Serial.write(client.read());
}
Serial.println("\nclosing connection");
client.stop();
}
Fig. 18: Library Files
Fig. 19: Network Credentials
Fig. 20
Fig. 21
Note: The ESP32 sleep time should not be very short. A very short sleep time can result in the exceeded limit of requests imposed by the IFTTT service.
Fig. 22: Timer
Fig. 23: Serial Monitor
Fig. 24: Wi-Fi
Fig. 25
Fig. 26
Fig. 27
Fig. 28
Fig. 29
Fig. 30
Fig. 31: Hall Sensor Readings on Google Sheets
Fig. 32: Serial Monitor
This concludes the tutorial. I hope you found this of some help and also to see you soon with the new tutorial on ESP32.
ESP32 is a powerful chip for Internet of Things applications. This tutorial is also based on another ESP32 application in the field of IoT.
Hello readers, I hope you all are doing great. In the previous tutorial, we learned how to send sensor readings from ESP32 to the cloud (ThingSpeak webserver).
In this tutorial, we will learn to send HTTP POST requests from the ESP32 board to ThingSpeak and IFTTT APIs.
Where To Buy? | ||||
---|---|---|---|---|
No. | Components | Distributor | Link To Buy | |
1 | ESP32 | Amazon | Buy Now |
Fig. 1 Hypertext Transfer Protocol
HTTP stands for hypertext transfer control and it is a protocol for transferring data between a web client and a web server. Hyper text transfer protocol was invented alongside HTML (Hypertext markup language) to create the first interactive, text-based web browser: the original www or World Wide Web.
Server and client communication process over HTTP:
Fig. 2 HTTP POST Request
Hypertext transfer protocol uses particular request methods to execute various tasks. Two mostly used HTTP request methods are: HTTP GET request and HTTP POST request.
HTTP GET request is generated to request data from a specific resource and the HTTP POST request method is used to send data from the client device to the server to create or update resources.
In this tutorial, we will demonstrate only the HTTP POST method with ThingSpeak and IFTTT web services.
Features of the HTTP POST request:
IFTT stands for If This Then That. It is a free web service for making different services like email, weather services, Twitter etc to connect.
IFTTT means if a service is triggered, other IFTTT services will take action.
Fig. 3 IFTTT
IFTTT acts as a bridge between ESP32 and other web services. Some of the tasks the ESP32 board can perform with the IFTTT API service are:
IFTTT comprises Applets and Applets further contains two IFTTT services namely trigger and action.
You can use the applets created by a company or can also create your own applet. To use the IFTTT applet with ESP32, we need to create an applet by ourselves. Such applet will contain Webhooks service to interact directly with ESP32 and other services that you want to use like email, Twitter service etc.
There are cases while using ESP32 with the IFTTT: either ESP32 will trigger the IFTTT to do some task or the IFTTT triggers ESP32 to do some task.
Steps to trigger IFTTT via ESP32
Enter the following link in the web browser: https://ifttt.com
Fig. 4 Creating an Applet
Fig. 5 ” If This”
Fig. 6 Search and Select Webhooks
Fig. 7 Receive a Web Request
Fig. 8 Create Trigger
Fig. 9 Then that
Fig. 10 Selecting a Service
Fig. 11
Fig. 12 To Trigger an Event
Fig. 13 Event Successfully Triggered
#include <WiFi.h>
#include <HTTPClient.h>
//---------Netwrok Credentials
const char* ssid = "SSID";
const char* password = "Password";
const char* serverName = "http://maker.ifttt.com/trigger/ESP32_test/with/key/Enter you API key";
unsigned long lastTime = 0;
unsigned long timerDelay = 15000;
void setup()
{
Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.println("Connecting");
while(WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to WiFi network with IP Address: ");
Serial.println(WiFi.localIP());
// Random seed is a number used to initialize a pseudorandom number generator
randomSeed(hallRead());
}
Void Loop()
//Send an HTTP POST request after every 15 seconds
if ((millis() - lastTime) > timerDelay)
{
//Check WiFi connection status
if(WiFi.status()== WL_CONNECTED)
{
WiFiClient client;
HTTPClient http;
// Your Domain name with URL path or IP address with path
http.begin(client, serverName);
// Specify content-type header
http.addHeader("Content-Type", "application/x-www-form-urlencoded");
// Data to send with HTTP POST
String httpRequestData = "value1=" + String(random(25)) + "&value2=" + String(random(25))+ "&value3=" + String(random(25));
// Send HTTP POST request
int httpResponseCode = http.POST(httpRequestData);
/*
// If you need an HTTP request with a content type: application/json, use the following:
http.addHeader("Content-Type", "application/json");
// JSON data to send with HTTP POST
String httpRequestData = "{\"value1\":\"" + String(random(40)) + "\",\"value2\":\"" + String(random(40)) + "\",\"value3\":\"" + String(random(40)) + "\"}";
// Send HTTP POST request
int httpResponseCode = http.POST(httpRequestData);
*/
Serial.print("HTTP Response code: ");
Serial.println(httpResponseCode);
Serial.println("successfully conected to host");
// Free resources
http.end();
}
else
{
Serial.println("WiFi Disconnected");
}
lastTime = millis();
}
}
Fig. Libraries
Fig. Network Credentials
Fig.
Fig.
Fig.
Fig.
Fig
Fig.
Fig.
Fig.
Fig. 14 Serial Monitor
Fig. 15 View Activity
Fig. 16 Received data.
Fig. 17 Email Received from IFTTT Server
We have already posted an article on sending sensor readings from ESP32 to ThingSpeak. In this article, we will learn how to send HTTP POST requests from ESP32 to send JSON data to the ThigSpeak server.
ThingSpeak is a web service operated by MathWorks where we can send sensor readings/data to the cloud. We can also visualize and act on the data (calculate the data) posted by the devices to ThingSpeak. The data can be stored in either private or public channels.
Steps to be followed to access ThingSpeak API:
Fig. 18 Getting Started for Free
Fig. 19 Create New Account
Fig. 20 MathWorks Sign in
Fig. 21 New Channel
Fig. 22 Create a New Channel
//-----------Libraries
#include <WiFi.h>
#include <HTTPClient.h>
//-----------Network Credentials
const char* ssid = "replace with your network SSID";
const char* password = "replace with netwrok password";
// Domain Name with full URL Path for HTTP POST Request
const char* serverName = "http://api.thingspeak.com/update";
// Service API Key
String apiKey = "Write API Key";
unsigned long lastTime = 0;
unsigned long timerDelay = 5000; //to add delay of 5sec
void setup()
{
Serial.begin(115200);
WiFi.begin(ssid, password); //initialize ESP32 wifi module
Serial.println("Connecting");
while(WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to WiFi network with IP Address: ");
Serial.println(WiFi.localIP());
Serial.println("Timer set to 10 seconds (timerDelay variable), it will take 10 seconds before publishing the first reading.");
// Random seed is a number used to initialize a pseudorandom number generator
randomSeed(analogRead(25));
}
void loop()
{
//Send an HTTP POST request after every 5 seconds
if ((millis() - lastTime) > timerDelay)
{
//Check the WiFi connection status
if(WiFi.status()== WL_CONNECTED)
{
WiFiClient client;
HTTPClient http;
http.begin( client, serverName );
http.addHeader("Content-Type", "application/json");
String httpRequestData = "{\"api_key\":\"" + apiKey +
"\",\"field1\":\"" +
String(random(30)) + "\"}";
int httpResponseCode = http.POST(httpRequestData);
Serial.print("HTTP Response code: ");
Serial.println(httpResponseCode);
// Free resources
http.end();
}
else {
Serial.println("WiFi Disconnected");
}
lastTime = millis();
}
}
Fig.
Fig.
Fig.
Fig. : data (JSON) Chart on ThingSpeak
This concludes the tutorial. I hope you found this of some help and also to see you soon with the new tutorial on ESP32.
The Internet of Things ( or IoT) is a network of interconnected computing devices such as digital machines, automobiles with built-in sensors, or humans with unique identifiers and the ability to communicate data over a network without human intervention.
Hello readers, I hope you all are doing great. In this tutorial, we will learn how to send sensor readings from ESP32 to the ThingSpeak cloud. Here we will use the ESP32’s internal sensor like hall-effect sensor and temperature sensor to observe the data and then will share that data cloud.
Where To Buy? | ||||
---|---|---|---|---|
No. | Components | Distributor | Link To Buy | |
1 | ESP32 | Amazon | Buy Now |
It is an open data platform for IoT (Internet of Things). ThingSpeak is a web service operated by MathWorks where we can send sensor readings/data to the cloud. We can also visualize and act on the data (calculate the data) posted by the devices to ThingSpeak. The data can be stored in either private or public channels.
ThingSpeak is frequently used for internet of things prototyping and proof of concept systems that require analytics.
Downloading and installing the required Library file:
https://github.com/mathworks/thingspeak-arduino
Fig. 2: Adding ThingSpeak Library
To check whether the library is successfully added or not:
Fig. 3
This library comes with multiple example codes. You can use any of the example codes as per your requirements ad also modify the example code.
Fig. 6: Getting Started For Free
Fig. 7: Create New Account
Fig. 8: MathWorks Sign in
Fig. 9: New Channel
Fig. 13: Field Chart Edit
We have already published a tutorial on the ESP32 hall sensor and internal temperature sensor.
// ------style guard ----
#ifdef __cplusplus
extern "C"
{
#endif
uint8_t temprature_sens_read();
#ifdef __cplusplus
}
#endif
uint8_t temprature_sens_read();
// ------header files----
#include <WiFi.h>
#include "ThingSpeak.h"
// -----netwrok credentials
const char* ssid = "SSID"; // your network SSID (name)
const char* password = "PASSWORD"; // your network password
WiFiClient client;
// -----ThingSpeak channel details
unsigned long myChannelNumber = 1;
const char * myWriteAPIKey = "API Key";
// ----- Timer variables
unsigned long lastTime = 0;
unsigned long timerDelay = 1000;
void setup()
{
Serial.begin(115200); // Initialize serial
WiFi.mode(WIFI_STA);
if(WiFi.status() != WL_CONNECTED)
{
Serial.print("Attempting to connect");
while(WiFi.status() != WL_CONNECTED )
{
WiFi.begin(ssid, password);
delay(1000);
}
Serial.println("\nConnected. ");
}
ThingSpeak.begin(client); // Initialize ThingSpeak
}
void loop()
{
if ((millis() - lastTime) > timerDelay )
{
int hall_value = 0;
float temperature = 0;
hall_value = hallRead();
// Get a new temperature reading
temperature = ((temprature_sens_read()-32)/1.8 );
Serial.print("Temperature (ºC): " );
Serial.print(temperature);
Serial.println("ºC" );
Serial.print("Hall value:" );
Serial.println(hall_value);
ThingSpeak.setField(1, temperature );
ThingSpeak.setField(2, hall_value );
// Write to ThingSpeak. There are up to 8 fields in a channel, allowing you to store up to 8 different
// pieces of information in a channel. Here, we write to field 1.
int x = ThingSpeak.writeFields(myChannelNumber,
myWriteAPIKey );
if(x == 200)
{
Serial.println("Channel update successful." );
}
else
{
Serial.println("Problem updating channel. HTTP error code " + String(x) );
}
lastTime = millis();
}
}
Fig. 18
Fig. 19
Fig. 20
Fig. 21
Fig. 22
Fig. 23
Fig. 24
Fig. 25
(F-32) *(5/9) = degree Celsius
Fig. 26
Fig. 27
Fig. 28
Fig. 29
Fig. 30
Fig. 31: ThingSpeak Channel Stats
Fig. 32: Results on the Serial Monitor
This concludes the tutorial. I hope you found this of some help and also to see you soon with new tutorial on ESP32.
Hello readers, I hope you all are doing great. In this tutorial, we will learn about the ESP-NOW protocol and how to communicate data between two ESP modules through ESP-NOW protocol and that is too without Wi-Fi connectivity.
Where To Buy? | ||||
---|---|---|---|---|
No. | Components | Distributor | Link To Buy | |
1 | ESP32 | Amazon | Buy Now |
Fig. 1: ESP-NOW Protocol
ESP–NOW is a connectionless communication protocol that is used for sharing small data packets between two ESP boards. This protocol is developed by Espressif.
Although, ESP-NOW protocol can communicate only small data packets ( maximum 250 bytes), but it is a high-speed protocol for wireless communication.
ESP devices can communicate over ESP-NOW protocol in different network topologies which makes it a very versatile protocol. The communication can be a point to point or point to multipoint (broadcast).
Fig. 2: Point to Point Communication
In peer-to-peer communication, only two ESP (either ESP32 or ESP8266) devices can connect with each other for data exchange. Each ESP device can act as a master device, a slave device or both master and slave at the same time.
In broadcast one ESP device (known as a broadcaster) act as a master device and broadcast the data to ESP devices acting as slave devices. Data is shared with all the slave devices simultaneously.
This communication method is used when users want to control multiple slave devices at a time.
Fig. 4: Many to One Communication
In many to one communication scenarios, there will be a central node or gateway which collects all the data from its nearby connected ESP devices.
This scenario can be applied when you need to collect sensor data from various sensor nodes to a single collector or central device, which is connected to all the nearby sensors.
MAC address or Media Access Control address is a six-byte hexadecimal address, that is used to track or connect with devices in a network. It provides the user with a secure way to identify senders and receivers in a network and avoid unwanted network access.
Fig. 5: MAC Address
Each ESP device has a unique MAC address.
So, before sharing the data between two or more ESP devices the MAC address of the receiver device should be known to the sender device.
Both, the ESP32 and ESP8266 modules support the ESP-NOW protocol.
In this tutorial, we will connect the ESP32 and ESP8266 using the ESP-NOW protocol.
Fig. 6: ESP-NOW Example Code in Arduino IDE
#include <esp_now.h>
#include <WiFi.h>
// REPLACE WITH YOUR RECEIVER MAC Address
uint8_t broadcastAddress[] = {0xEE, 0xFA, 0xBC, 0xC5, 0xA4, 0xBF};
typedef struct struct_message {
char a[32];
int b;
float c;
bool d;
} struct_message;
// Create a struct_message called myData
struct_message myData;
// callback when data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
Serial.print("\r\nLast Packet Send Status:\t");
Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}
void setup() {
// Init Serial Monitor
Serial.begin(115200);
// Set device as a Wi-Fi Station
WiFi.mode(WIFI_STA);
// Init ESP-NOW
if (esp_now_init() != ESP_OK) {
Serial.println("Error initializing ESP-NOW");
return;
}
// get the status of Trasnmitted packet
esp_now_register_send_cb(OnDataSent);
// Register peer
esp_now_peer_info_t peerInfo;
memcpy(peerInfo.peer_addr, broadcastAddress, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
// Add peer
if (esp_now_add_peer(&peerInfo) != ESP_OK){
Serial.println("Failed to add peer");
return;
}
}
void loop()
{ strcpy(myData.a, "THIS IS A CHAR"); // Send message via ESP-NOW esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData)); if (result == ESP_OK) { Serial.println("Sent with success"); } else { Serial.println("Error sending the data"); } delay(2000); }
Fig. 12: Serial monitor and Wi-Fi Initialization
Fig. 13
Fig. 14
#include <WifiEspNow.h>
#if defined(ARDUINO_ARCH_ESP8266)
#include <ESP8266WiFi.h>
#elif defined(ARDUINO_ARCH_ESP32)
#include <WiFi.h>
#endif
// The recipient MAC address. It must be modified for each device.
static uint8_t PEER[]{0x02, 0x00, 0x00, 0x45, 0x53, 0x50};
void printReceivedMessage(const uint8_t mac[WIFIESPNOW_ALEN],
const uint8_t* buf, size_t count, void* arg)
{
Serial.printf("Message from %02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3],
mac[4], mac[5]);
for (int i = 0; i < static_cast<int>(count); ++i) {
Serial.print(static_cast<char>(buf[i]));
}
Serial.println();
}
void setup()
{
Serial.begin(115200);
Serial.println();
WiFi.persistent(false);
WiFi.mode(WIFI_AP);
WiFi.disconnect();
WiFi.softAP("ESPNOW", nullptr, 3);
WiFi.softAPdisconnect(false);
Serial.print("MAC address of this node is ");
Serial.println(WiFi.softAPmacAddress());
uint8_t mac[6];
WiFi.softAPmacAddress(mac);
Serial.println();
Serial.println("You can paste the following into the program for the other device:");
Serial.printf("static uint8_t PEER[]{0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X};\n", mac[0],
mac[1], mac[2], mac[3], mac[4], mac[5]);
Serial.println();
bool ok = WifiEspNow.begin();
if (!ok) {
Serial.println("WifiEspNow.begin() failed");
ESP.restart();
}
WifiEspNow.onReceive(printReceivedMessage, nullptr);
ok = WifiEspNow.addPeer(PEER);
if (!ok) {
Serial.println("WifiEspNow.addPeer() failed");
ESP.restart();
}
}
loop()
{
char msg[60];
int len = snprintf(msg, sizeof(msg), "hello ESP-NOW from %s at %lu",
WiFi.softAPmacAddress().c_str(), millis());
WifiEspNow.send(PEER, reinterpret_cast<const uint8_t*>(msg), len);
delay(1000);
}
This concludes the tutorial. I hope, you found this helpful and I hope to see you soon for the new ESP32 tutorial.
Hello friends, I hope you all are doing great. Welcome to the 3rd lecture of Section 5(ESP32 Sensors) in the ESP32 Programming Series. We have already discussed the two built-in ESP32 sensors i.e. Hall Effect Sensor and Capacitive Touch Sensor. Today, we are going to discuss the 3rd and final built-in ESP32 sensor i.e. Internal Temperature Sensor.
ESP32 Internal Temperature Sensor is used to calculate the temperature of the ESP32 core. So, we can't use it to measure the ambient temperature (the temperature of the atmosphere), for that, we need to use embedded temperature sensors i.e. DS18B20, DHT11, BMP280 etc. We will first discuss the basics of this Internal Temperature Sensor and then will design a code to monitor the change in temperature by changing the frequency of the ESP32 CPU.
Note:
Important specs of the ESP32 Temperature Sensor are given in the below table:
ESP32 Temperature Sensor Features |
||||
---|---|---|---|---|
Parameter | Value | |||
Converter Types | ADC (Analog-to-Digital Converter), DAC (Digital-to-Analog Converter) | |||
Accurate Temperature Sensing Range | -40 °C to 125 °C | |||
Suitability | Good | |||
Most Accurate Range | -10 °C to 80 °C | |||
Temperature Fluctuation Measurement | High resolution | |||
Potential Performance & Accuracy Issues | Voltage fluctuations, Noise, Environmental factors, Nearby heat sources |
Where To Buy? | ||||
---|---|---|---|---|
No. | Components | Distributor | Link To Buy | |
1 | ESP32 | Amazon | Buy Now |
ESP32’s on-chip temperature sensor cannot be used for monitoring external temperature. It can only be used to monitor the temperature of the core. This temperature sensor is available on some selective ESP32 boards and obsolete on most ESP32 variants. It has a high-temperature sensing range of -40 to 125 °C.
ESP32 boards are normally used in real-time IoT Projects i.e. home automation, back security etc. Such projects need to run 24/7 to get live updates and may heat up the motherboard. Thus, to get a stable performance in continuous operations, these internal temperature sensors are introduced to monitor the ESP32 Core.
Most of the modern ESP32 variants are equipped with the Internal Temperature Sensor. I have created a list of the ESP32 boards by taking the ESP-IDF documentation that caters to the built-in temperature sensor. Here's the list:
As you can see, most of the newer versions have a temperature sensor, but some older versions may also have one.
ESP32 temperature sensor consists of 2 converters:
Sigma-delta ADCs are widely favored for their exceptional accuracy and remarkable resolution, enabling them to deliver precise measurements. This ADC takes the analog signal from the temperature sensor, converts it to a digital signal and feeds it to the microcontroller for processing.
The DAC is responsible for the accuracy of the temperature measurements. It is embedded within the ESP32 and converts the digital values(converted by the Sigma-Delta ADC) again into analog values to offset any temperature-induced variation. As a result, it ensures accurate readings from the sensor.
The accuracy of this temperature sensor changes according to the range group of the temperature values. Among different groups, the range of -10 ~ 80 is the most accurate. The reading errors along with their measuring ranges are shown in the below table:
Measuring Errors in ESP32 Temperature Sensor | ||||
---|---|---|---|---|
No. | Offset | Predefined Range (°C) | Error (°C) | Operating Range (°C) |
1 | -2 |
50 ~ 125 | < 3 | Not recommended(Error) |
2 |
-1 |
20 ~ 100 | < 2 | Ideal range for best accuracy |
3 |
0 | -10 ~ 80 | < 1 | Acceptable range with moderate accuracy |
4 |
1 |
-30 ~ 50 | < 2 | Usable range with increased error at extremes |
5 |
2 |
-40 ~ 20 | < 3 | Not recommended (error) |
Different factors, including voltage fluctuations, noise, environmental factors, and nearby heat sources, can affect the performance and accuracy of this sensor.
Formula to convert observed temperature i.e. Fahrenheit to Celsius:
(F-32) *(5/9) = degree Celsius
Here are the main applications of the ESP32 built-in temperature sensor:
A designer can easily monitor the internal chip’s temperature through the temperature sensor, identify bottleneck conditions, and conduct performance evaluations under extreme circumstances. So, the sensor helps in optimizing the chip's temperature and, thus, the performance.
Electronic components/modules are sensitive, and if they are designed to work for prolonged operation, temperature management is one of the most crucial points to be considered. The built-in temperature sensor is a useful way to measure the operating temperature of the components connected to its peripherals and the whole system. These values are then utilized to set the threshold value so the system can trigger the safety mechanism when a certain heat level is passed. This can be done using the ESP32 code and surely prevent overheating to maintain the performance.
The built-in temperature sensor helps to maintain the energy monitoring that, in turn, allows the energy monitoring of the project. The careful observation of the built-in temperature sensor output helps the user understand the relationship between temperature change and energy consumption. Using this approach, designers can target an optimized energy consumption.
#ifdef __cplusplus
extern "C" {
#endif
uint8_t temprature_sens_read();
#ifdef __cplusplus
}
#endif
uint8_t temprature_sens_read();
void setup()
{
Serial.begin(115200);
}
void loop()
{
Serial.print("Temperature: ");
Serial.print(temprature_sens_read() );
Serial.print(" F");
Serial.print("______");
// Convert raw temperature in F to Celsius degrees
Serial.print((temprature_sens_read() - 32) / 1.8);
Serial.println(" C");
delay(1000);
}
#ifdef __cplusplus
extern "C" {
#endif
uint8_t temprature_sens_read();
#ifdef __cplusplus
}
#endif
uint8_t temprature_sens_read();
void setup()
{
Serial.begin(115200);
}
void loop()
{
Serial.print("Temperature: ");
Serial.print(temprature_sens_read() );
Serial.print("______");
}
// Convert raw temperature in F to Celsius degrees
Serial.print((temprature_sens_read() - 32) / 1.8);
Serial.println(" C");
delay(1000);
This concludes the tutorial. I hope you found this of some help and also to see you soon with the new tutorial on ESP32.
Hello readers, I hope you all are doing great. In our previous tutorial, we learned SMTP server and how to implement an SMTP server for sending emails with ESP32. In the previous tutorial, we also demonstrated some examples like sharing raw text, HTML text, images and text files.
So, at the transmitter end, we are using the SMTP server.
But, what about the receiver end?
At the receiver end, we use another protocol called IMAP (or Internet Message Access Protocol) and POP3 (Post office Protocol V3) for receiving the emails.
Where To Buy? | ||||
---|---|---|---|---|
No. | Components | Distributor | Link To Buy | |
1 | ESP32 | Amazon | Buy Now |
IMAP is an application layer (TCP/IP) protocol that is used at the receiver end to receive emails from SMTP server or mail server. IMAP follows the client/server model.
POP3 stands for Post Office Protocol version 3.
POP3 is another protocol to receive emails. This protocol is used to access the TCP/IP mailbox. The protocol is quite popular due to its offline mail access model.
The offline access model enables the user to access the mails from the mail server on the local machine, and then delete them from the mail server.
Though in some applications POP3 protocol is still used, but in most of the email receivers, it is preferred to use IMAP protocol over POP3 protocol.
Similarly, other email service providers like Outlook and Hotmail, have different setting parameters.
To send emails with ESP32 we need to install this ESP Mail Client library. This library, make ESP32 able to send emails over SMTP server.
Step to install ESP Mail Client Library:
Your Arduino IDE is ready to send email using ESP32.
It is recommended to create a new email account for sending emails using ESP32 or ESP8266 modules.
If you are using your main (personal) email account (for sending emails) with ESP and by mistake, something goes wrong in the ESP code or programming part, your email service provider can ban or disable your main (personal) email account.
In this tutorial, we are using a Gmail account.
Follow the link to create a new Gmail account: https://accounts.google.com
To get access to this new Gmail account, you need to enable Allow less secure apps and this will make you able to send emails. The link is attached below:
https://myaccount.google.com/lesssecureapps?pli=1
Fig.
Fig IMAP and SMTP Example Code
Note: You can not use the exact code. Hence, you need to make some changes like replacing SSID and password with your network credentials, email address of sender and receiver, setting IMAP and SMTP parameters for respective email service providers etc, needs to be done before uploading the code. We will also describe these things during code description.
In this code, we will implement both IMAP and SMTP protocols to receive and transmit emails.
Although, we are using SMTP in this tutorial, but we have already discussed and demonstrated the implementation on SMTP protocol in our previous tutorial. So, in this tutorial we will not explain the SMTP part.
Follow our previous tutorial for detailed study of SMTP implementation in ESP32.
#include <Arduino.h>
#include <WiFi.h>
#include <ESP_Mail_Client.h>
//To use only IMAP functions, you can exclude the SMTP from compilation, see ESP_Mail_FS.h.
#define WIFI_SSID "public"
#define WIFI_PASSWORD "ESP32@123"
//-----------setting IMAP parameters------
/* The imap host name e.g. imap.gmail.com for GMail or outlook.office365.com for Outlook */
#define IMAP_HOST "imap.gmail.com"
#define IMAP_PORT 993
#define AUTHOR_EMAIL "techeesp697@gmail.com"
#define AUTHOR_PASSWORD "Tech@ESP123"
#define RECIPIENT_EMAIL "maneesha607ece@gmail.com"
//------------setting SMTP credentials----------
#define SMTP_HOST "smtp.gmail.com"
#define SMTP_PORT 465
//------IMAP Rx emails and their status
/* Callback function to get the Email reading status */
void imapCallback(IMAP_Status status);
void printAllMailboxesInfo(IMAPSession &imap);
void printSelectedMailboxInfo(SelectedFolderInfo sFolder);
void printMessages(std::vector<IMAP_MSG_Item> &msgItems, bool headerOnly);
/* Print all attachments info from the message */
void printAttacements(std::vector<IMAP_Attach_Item> &atts);
/* The IMAP Session object used for Email reading */
IMAPSession imap;
//-------SMTP sending mails and their status----
/* The SMTP Session object used for Email sending */
SMTPSession smtp;
/* Callback function to get the Email sending status */
void smtpCallback(SMTP_Status status);
void setup()
{
Serial.begin(115200);
#if defined(ARDUINO_ARCH_SAMD)
while (!Serial)
;
Serial.println();
Serial.println("**** Custom built WiFiNINA firmware need to be installed.****\nTo install firmware, read the instruction here, https://github.com/mobizt/ESP-Mail-Client#install-custom-built-wifinina-firmware");
#endif
Serial.println();
Serial.print("Connecting to AP");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED)
{
Serial.print(".");
delay(200);
}
Serial.println("");
Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
Serial.println();
/** Enable the debug via Serial port
* none debug or 0
* basic debug or 1
*
* Debug port can be changed via ESP_MAIL_DEFAULT_DEBUG_PORT in ESP_Mail_FS.h
*/
imap.debug(1);
/* Set the callback function to get the reading results */
imap.callback(imapCallback);
ESP_Mail_Session session;
session.server.host_name = IMAP_HOST;
session.server.port = IMAP_PORT;
session.login.email = AUTHOR_EMAIL;
session.login.password = AUTHOR_PASSWORD;
/* Setup the configuration for searching or fetching operation and its result */
IMAP_Config config;
/* Set seen flag */
//config.fetch.set_seen = true;
/* Search criteria */
config.search.criteria.clear();
/* Also search the unseen message */
config.search.unseen_msg = true;
/* Set the storage to save the downloaded files and attachments */
config.storage.saved_path = "/email_data";
config.storage.type = esp_mail_file_storage_type_flash;
config.download.header = true;
config.download.text = true;
config.download.html = true;
config.download.attachment = true;
config.download.inlineImg = true;
config.enable.html = true;
config.enable.text = true;
/* Set to enable the sort the result by message UID in the ascending order */
config.enable.recent_sort = true;
/* Set to report the download progress via the default serial port */
config.enable.download_status = true;
config.limit.search = 5;
config.limit.msg_size = 512;
config.limit.attachment_size = 1024 * 1024 * 5;
/* Connect to server with the session and config */
if (!imap.connect(&session, &config))
return;
/* {Optional} */
printAllMailboxesInfo(imap);
/* Open or select the mailbox folder to read or search the message */
if (!imap.selectFolder("INBOX"))
return;
/* {Optional} */
printSelectedMailboxInfo(imap.selectedFolder());
String uid = String(imap.getUID(imap.selectedFolder().msgCount()));
config.fetch.uid = uid;
/* Read or search the Email and close the session */
//When message was fetched or read, the /Seen flag will not set or message remained in unseen or unread status,
//as this is the purpose of library (not UI application), user can set the message status as read by set \Seen flag
//to message, see the Set_Flags.ino example.
MailClient.readMail(&imap);
/* Clear all stored data in IMAPSession object */
imap.empty();
}
void loop()
{
}
/* Callback function to get the Email reading status */
void imapCallback(IMAP_Status status)
{
/* Print the current status */
Serial.println(status.info());
/* Show the result when reading finished */
if (status.success())
{
/* Print the result */
/* Get the message list from the message list data */
IMAP_MSG_List msgList = imap.data();
printMessages(msgList.msgItems, imap.headerOnly());
/* Clear all stored data in IMAPSession object */
imap.empty();
SMTP_MSG();
}
}
void printAllMailboxesInfo(IMAPSession &imap)
{
/* Declare the folder collection class to get the list of mailbox folders */
FoldersCollection folders;
/* Get the mailbox folders */
if (imap.getFolders(folders))
{
for (size_t i = 0; i < folders.size(); i++)
{
/* Iterate each folder info using the folder info item data */
FolderInfo folderInfo = folders.info(i);
ESP_MAIL_PRINTF("%s%s%s", i == 0 ? "\nAvailable folders: " : ", ", folderInfo.name, i == folders.size() - 1 ? "\n" : "");
}
}
}
void printSelectedMailboxInfo(SelectedFolderInfo sFolder)
{
/* Show the mailbox info */
ESP_MAIL_PRINTF("\nInfo of the selected folder\nTotal Messages: %d\n", sFolder.msgCount());
ESP_MAIL_PRINTF("Predicted next UID: %d\n", sFolder.nextUID());
for (size_t i = 0; i < sFolder.flagCount(); i++)
ESP_MAIL_PRINTF("%s%s%s", i == 0 ? "Flags: " : ", ", sFolder.flag(i).c_str(), i == sFolder.flagCount() - 1 ? "\n" : "");
}
void printAttacements(std::vector<IMAP_Attach_Item> &atts)
{
ESP_MAIL_PRINTF("Attachment: %d file(s)\n****************************\n", atts.size());
for (size_t j = 0; j < atts.size(); j++)
{
IMAP_Attach_Item att = atts[j];
/** att.type can be
* esp_mail_att_type_none or 0
* esp_mail_att_type_attachment or 1
* esp_mail_att_type_inline or 2
*/
ESP_MAIL_PRINTF("%d. Filename: %s, Name: %s, Size: %d, MIME: %s, Type: %s, Creation Date: %s\n", j + 1, att.filename, att.name, att.size, att.mime, att.type == esp_mail_att_type_attachment ? "attachment" : "inline", att.creationDate);
}
Serial.println();
}
void printMessages(std::vector<IMAP_MSG_Item> &msgItems, bool headerOnly)
{
for (size_t i = 0; i < msgItems.size(); i++)
{
/* Iterate to get each message data through the message item data */
IMAP_MSG_Item msg = msgItems[i];
Serial.println("****************************");
ESP_MAIL_PRINTF("Number: %d\n", msg.msgNo);
ESP_MAIL_PRINTF("UID: %d\n", msg.UID);
ESP_MAIL_PRINTF("Messsage-ID: %s\n", msg.ID);
ESP_MAIL_PRINTF("Flags: %s\n", msg.flags);
//The attachment may not detect in search because the multipart/mixed
//was not found in Content-Type header field.
ESP_MAIL_PRINTF("Attachment: %s\n", msg.hasAttachment ? "yes" : "no");
if (strlen(msg.acceptLang))
ESP_MAIL_PRINTF("Accept Language: %s\n", msg.acceptLang);
if (strlen(msg.contentLang))
ESP_MAIL_PRINTF("Content Language: %s\n", msg.contentLang);
if (strlen(msg.from))
ESP_MAIL_PRINTF("From: %s\n", msg.from);
if (strlen(msg.sender))
ESP_MAIL_PRINTF("Sender: %s\n", msg.sender);
if (strlen(msg.to))
ESP_MAIL_PRINTF("To: %s\n", msg.to);
if (strlen(msg.cc))
ESP_MAIL_PRINTF("CC: %s\n", msg.cc);
if (strlen(msg.date))
{
ESP_MAIL_PRINTF("Date: %s\n", msg.date);
ESP_MAIL_PRINTF("Timestamp: %d\n", (int)MailClient.Time.getTimestamp(msg.date));
}
if (strlen(msg.subject))
ESP_MAIL_PRINTF("Subject: %s\n", msg.subject);
if (strlen(msg.reply_to))
ESP_MAIL_PRINTF("Reply-To: %s\n", msg.reply_to);
if (strlen(msg.return_path))
ESP_MAIL_PRINTF("Return-Path: %s\n", msg.return_path);
if (strlen(msg.in_reply_to))
ESP_MAIL_PRINTF("In-Reply-To: %s\n", msg.in_reply_to);
if (strlen(msg.references))
ESP_MAIL_PRINTF("References: %s\n", msg.references);
if (strlen(msg.comments))
ESP_MAIL_PRINTF("Comments: %s\n", msg.comments);
if (strlen(msg.keywords))
ESP_MAIL_PRINTF("Keywords: %s\n", msg.keywords);
/* If the result contains the message info (Fetch mode) */
if (!headerOnly)
{
if (strlen(msg.text.content))
ESP_MAIL_PRINTF("Text Message: %s\n", msg.text.content);
if (strlen(msg.text.charSet))
ESP_MAIL_PRINTF("Text Message Charset: %s\n", msg.text.charSet);
if (strlen(msg.text.transfer_encoding))
ESP_MAIL_PRINTF("Text Message Transfer Encoding: %s\n", msg.text.transfer_encoding);
if (strlen(msg.html.content))
ESP_MAIL_PRINTF("HTML Message: %s\n", msg.html.content);
if (strlen(msg.html.charSet))
ESP_MAIL_PRINTF("HTML Message Charset: %s\n", msg.html.charSet);
if (strlen(msg.html.transfer_encoding))
ESP_MAIL_PRINTF("HTML Message Transfer Encoding: %s\n\n", msg.html.transfer_encoding);
if (msg.rfc822.size() > 0)
{
ESP_MAIL_PRINTF("RFC822 Messages: %d message(s)\n****************************\n", msg.rfc822.size());
printMessages(msg.rfc822, headerOnly);
}
}
Serial.println();
}
}
void SMTP_MSG()
{
smtp.debug(1);
smtp.callback(smtpCallback);
ESP_Mail_Session session;
session.server.host_name = SMTP_HOST;
session.server.port = SMTP_PORT;
session.login.email = AUTHOR_EMAIL;
session.login.password = AUTHOR_PASSWORD;
session.login.user_domain = "";
/* Declare the message class */
SMTP_Message message;
message.sender.name = "The_Engineering_Projects";
message.sender.email = AUTHOR_EMAIL;
message.subject = "Auto_Response";
message.addRecipient("Maneesha", RECIPIENT_EMAIL);
//Send raw text message
String textMsg = "Thanks for contacting us. One of our client will contact you soon. www.theengineeringprojects";
message.text.content = textMsg.c_str();
message.text.charSet = "us-ascii";
message.text.transfer_encoding = Content_Transfer_Encoding::enc_7bit;
message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_low;
message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay;
/* Connect to server with the session config */
if (!smtp.connect(&session))
return;
/* Start sending Email and close the session */
if (!MailClient.sendMail(&smtp, &message))
Serial.println("Error sending Email, " + smtp.errorReason());
}
//-----------SMTP Status function-----
// Callback function to get the Email sending status
void smtpCallback(SMTP_Status status)
{
/* Print the current status */
Serial.println(status.info());
/* Print the sending result */
if (status.success())
{
Serial.println("----------------");
ESP_MAIL_PRINTF("Message sent success: %d\n", status.completedCount());
ESP_MAIL_PRINTF("Message sent failled: %d\n", status.failedCount());
Serial.println("----------------\n");
struct tm dt;
for (size_t i = 0; i < smtp.sendingResult.size(); i++)
{
/* Get the result item */
SMTP_Result result = smtp.sendingResult.getItem(i);
time_t ts = (time_t)result.timestamp;
localtime_r(&ts, &dt);
ESP_MAIL_PRINTF("Message No: %d\n", i + 1);
ESP_MAIL_PRINTF("Status: %s\n", result.completed ? "success" : "failed");
ESP_MAIL_PRINTF("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec);
ESP_MAIL_PRINTF("Recipient: %s\n", result.recipients);
ESP_MAIL_PRINTF("Subject: %s\n", result.subject);
}
Serial.println("----------------\n");
}
}
Fig. SMTP server address and port number
Fig.
Fig.
Fig.
Fig.
Note:- We have already discussed the SMTP server in our previous tutorial that is Sending Emails with ESP32 using SMTP server. So, follow our previous tutorial for a detailed study on SMTP protocol and send an email using ESP32.
This concludes the tutorial. I hope you found this helpful and also hope to see you soon with a new tutorial on ESP32.
Hello readers, I hope you all are doing great. In this tutorial, we will learn how to send an email using ESP32 module. We will also learn to send text files, images or some sensor readings using the SMTP server using the ESP32 module.
In IoT (Internet of things), there are various applications where we need to send emails carrying information like sending some sensor readings, altering emails, images, text files and much more.
Where To Buy? | ||||
---|---|---|---|---|
No. | Components | Distributor | Link To Buy | |
1 | ESP32 | Amazon | Buy Now |
SMTP or simple mail transfer protocol is an internet standard for sending and receiving electronic mail (or email) where an SMTP server receives emails from the email client.
SMTP is also used for setting communication between servers.
Various email providers like Gmail, Hotmail, Yahoo, etc. have unique SMTP addresses and port numbers.
SMTP protocol which is also known as a push protocol is used to send emails and IMAP that is Internet Message Access Protocol (or post office protocol or POP) is used to receive emails at the receiver end.
SMTP protocol operates at the application layer of TCP/IP protocol.
When a client wants to send emails, a TCP connection will be open for the SMTP server and emails will be sent across the connection.
SMTP commands:
Gmail is the email service provided by Google and Gmail SMTP server is free to access and anyone can access this service, who has a Gmail account.
Components required to send and receive emails using ESP32 over SMTP server are:
To send emails with ESP32 we need to install this ESP Mail Client library. This library, make ESP32 able to send emails over an SMTP server.
Step to install ESP Mail Client Library:
Your Arduino IDE is ready to send email using ESP32.
It is recommended to create a new email account for sending emails using ESP32 or ESP8266 modules.
If you are using your main (personal) email account (for sending emails) with ESP and by mistake, something goes wrong in the ESP code or programming part, your email service provider can ban or disable your main (personal) email account.
In this tutorial, we are using a Gmail account.
Follow the link to create a new Gmail account: https://accounts.google.com
To get access to this new Gmail account, you need to enable Allow less secure apps and this will make you able to send emails. The link is: https://myaccount.google.com/lesssecureapps?pli=1
Fig SMTP example code
#include <WiFi.h>
#include <ESP_Mail_Client.h>
#define WIFI_SSID "SSID"
#define WIFI_PASSWORD "PASSWORD"
#define SMTP_HOST "smtp.gmail.com"
#define SMTP_PORT 465
/* The sign in credentials */
#define AUTHOR_EMAIL "email address"
#define AUTHOR_PASSWORD "email password"
/* Recipient's email*/
#define RECIPIENT_EMAIL "email address_Rx"
/* The SMTP Session object used for Email sending */
SMTPSession smtp;
/* Callback function to get the Email sending status */
void smtpCallback(SMTP_Status status);
void setup(){ Serial.begin(115200);
Serial.println();
Serial.print("Connecting to AP");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED){
Serial.print(".");
delay(200);
}
Serial.println("");
Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
Serial.println();
/** Enable the debug via Serial port
none debug or 0 basic debug or 1*/
smtp.debug(1);
/* Set the callback function to get the sending results */
smtp.callback(smtpCallback);
/* Declare the session config data */
ESP_Mail_Session session;
/* Set the session config */
session.server.host_name = SMTP_HOST; session.server.port = SMTP_PORT; session.login.email = AUTHOR_EMAIL; session.login.password = AUTHOR_PASSWORD;
session.login.user_domain = "";
/* Declare the message class */
SMTP_Message message; message.sender.name = "ESP32"; message.sender.email = AUTHOR_EMAIL; message.subject = "ESP32 Test Email";
message.addRecipient("Sara", RECIPIENT_EMAIL);
/*Send HTML message*/
String htmlMsg = "<div style=\"color:#2f4468;\"><h1>Hello CLient!</h1><p>- Sent from ESP board</p></div>"; message.html.content = htmlMsg.c_str(); message.html.content = htmlMsg.c_str(); message.text.charSet = "us-ascii";
message.html.transfer_encoding = Content_Transfer_Encoding::enc_7bit;
//Send raw text message
/* String textMsg = "Hello Client! - you have a message from ESP32 board"; message.text.content = textMsg.c_str(); message.text.charSet = "us-ascii";
message.text.transfer_encoding = Content_Transfer_Encoding::enc_7bit;*/
message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_low;
message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay;
/* Set the custom message header */
//message.addHeader("Message-ID: <abcde.fghij@gmail.com>");
/* Connect to server with the session config */ if (!smtp.connect(&session))
return;
/* Start sending Email and close the session */ if (!MailClient.sendMail(&smtp, &message))
Serial.println("Error sending Email, " + smtp.errorReason());
}
void loop(){
}
/* Callback function to get the Email sending status */
void smtpCallback(SMTP_Status status){
/* Print the current status */
Serial.println(status.info());
/* Print the sending result */ if (status.success()){
Serial.println("----------------");
ESP_MAIL_PRINTF("Message sent success: %d\n", status.completedCount());
ESP_MAIL_PRINTF("Message sent failled: %d\n", status.failedCount());
Serial.println("----------------\n"); struct tm dt;
for (size_t i = 0; i < smtp.sendingResult.size(); i++){
/* Get the result item */
SMTP_Result result = smtp.sendingResult.getItem(i);
time_t ts = (time_t)result.timestamp; localtime_r(&ts, &dt);
ESP_MAIL_PRINTF("Message No: %d\n", i + 1);
ESP_MAIL_PRINTF("Status: %s\n", result.completed ? "success" : "failed"); ESP_MAIL_PRINTF("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900,
dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec); ESP_MAIL_PRINTF("Recipient: %s\n", result.recipients); ESP_MAIL_PRINTF("Subject: %s\n", result.subject);
}
Serial.println("----------------\n");
}
}
Write the message content (raw text) in the textMsg variable which you want to share over email.
To send HTML text write the respective content in the htmlMsg variable.
Otherwise, you won’t be able to send emails and an error will be printed on the serial monitor.
Arduino IDE code, for sending images and text files using ESP32 and SMTP server:
In this example code, we will demonstrate how to share text files and images through emails using ESP32 over the SMTP server.
But, before sharing attachments (text files or images) you need to save those files on the ESP32 filesystem (SPIFFS).
SPIFFS stands for Serial Peripheral Interface Flash File System, which is built into the ESP32 module. This is a lightweight filesystem designed for microcontrollers with flash chips connected via SPI bus, such as the ESP32 flash memory. In this flash memory, we can write, delete, read, and close files.
Fig.
Check if the plugin is successfully uploaded or not:
Fig.
Finally, uploading files using SPIFFS or filesystem upload:
Fig.
Fig.
Fig.
Fig.
A message “SPIFFS Image Uploaded” will be displayed at the bottom of Arduino IDE, once the SPIFFS image is uploaded successfully.
Fig.
Arduino IDE Code
Code is already available in ESP Mail Client Library. As shown below:
Fig.
// To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1
// The file systems for flash and sd memory can be changed in ESP_Mail_FS.h.
#include <Arduino.h>
#include <WiFi.h>
#include <ESP_Mail_Client.h>
#define WIFI_SSID "SSID"
#define WIFI_PASSWORD "PASSWORD"
// server address for Gmail account
#define SMTP_HOST "smtp.gmail.com"
/** The smtp port e.g.
25 or esp_mail_smtp_port_25 465 or esp_mail_smtp_port_465 587 or esp_mail_smtp_port_587*/
#define SMTP_PORT 465
/* The log in credentials */
#define AUTHOR_EMAIL "Sender's email address"
#define AUTHOR_PASSWORD "password"
/* Recipient's email*/
#define RECIPIENT_EMAIL "receiver's email address"
/* The SMTP Session object used for Email sending */
SMTPSession smtp;
/* Callback function to get the Email sending status */
void smtpCallback(SMTP_Status status);
void setup(){
Serial.begin(115200);
Serial.println();
Serial.print("Connecting to AP");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED){
Serial.print(".");
delay(200);
}
Serial.println("");
Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
Serial.println();
if (!SPIFFS.begin(true)) {
Serial.println("An error has occurred while mounting SPIFFS");
}
else{
Serial.println("SPIFFS mounted successfully");
}
/** Enable the debug via Serial port
- none debug or 0
- basic debug or 1
*/
smtp.debug(1);
/* Set the callback function to get the sending results */
smtp.callback(smtpCallback);
/* Declare the session config data */
ESP_Mail_Session session;
/* Set the session config */ session.server.host_name = SMTP_HOST; session.server.port = SMTP_PORT; session.login.email = AUTHOR_EMAIL; session.login.password = AUTHOR_PASSWORD;
session.login.user_domain = "mydomain.net";
/* Declare the message class */
SMTP_Message message;
/* Enable the chunked data transfer with pipelining for large message if server supported
*/
message.enable.chunking = true;
/* Set the message headers */ message.sender.name = "ESP Mail";
message.sender.email = AUTHOR_EMAIL;
message.subject = "Test sending Email with attachments and inline images from SD card and Flash";
message.addRecipient("Sara", RECIPIENT_EMAIL);
/** Two alternative content versions are sending in this example e.g. plain text and html */
String htmlMsg = "This message contains attachments: image and text file."; message.html.content = htmlMsg.c_str(); message.html.charSet = "utf-8";
message.html.transfer_encoding = Content_Transfer_Encoding::enc_qp;
message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_normal; message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay;
/* The attachment data item */
SMTP_Attachment att;
/** Set the attachment info e.g.
- file name, MIME type, file path, file storage type,
- transfer encoding and content encoding
*/
att.descr.filename = "image.png"; att.descr.mime = "image/png"; //binary data
att.file.path = "/image.png";
att.file.storage_type = esp_mail_file_storage_type_flash;
att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64;
/* Add attachment to the message */
message.addAttachment(att);
message.resetAttachItem(att);
att.descr.filename = "text_file.txt"; att.descr.mime = "text/plain"; att.file.path = "/text_file.txt";
att.file.storage_type = esp_mail_file_storage_type_flash;
att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64;
/* Add attachment to the message */
message.addAttachment(att);
/* Connect to server with the session config */ if (!smtp.connect(&session))
return;
/* Start sending the Email and close the session */ if (!MailClient.sendMail(&smtp, &message, true))
Serial.println("Error sending Email, " + smtp.errorReason());
}
void loop()
{
}
/* Callback function to get the Email sending status */
void smtpCallback(SMTP_Status status){
/* Print the current status */
Serial.println(status.info());
/* Print the sending result */ if (status.success()){
Serial.println("----------------");
ESP_MAIL_PRINTF("Message sent success: %d\n", status.completedCount());
ESP_MAIL_PRINTF("Message sent failled: %d\n", status.failedCount());
Serial.println("----------------\n"); struct tm dt;
for (size_t i = 0; i < smtp.sendingResult.size(); i++){
/* Get the result item */
SMTP_Result result = smtp.sendingResult.getItem(i); time_t ts = (time_t)result.timestamp;
localtime_r(&ts, &dt);
ESP_MAIL_PRINTF("Message No: %d\n", i + 1);
ESP_MAIL_PRINTF("Status: %s\n", result.completed ? "success" : "failed"); ESP_MAIL_PRINTF("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900,
dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec);
ESP_MAIL_PRINTF("Recipient: %s\n", result.recipients);
ESP_MAIL_PRINTF("Subject: %s\n", result.subject);
}
Serial.println("----------------\n");
}
}
Most part of the code is similar to the previous one (that is sending raw text and HTML text), including libraries, network credentials, enabling Wi-Fi and the serial monitor, setting the email parameters of the respective email service provided. So we not explaining the complete code but, we will explain the programming part which is different than the previous one.
Fig.
Fig.
Fig.
Fig.
Fig.
Fig.
Fig.
Fig.
This concludes the tutorial; I hope you found this helpful and also hope to see you again with a new tutorial on ESP32.
Hello readers, I hope you are all doing great. Welcome to the 2nd lecture of Section 5(ESP32 Sensors) in the ESP32 Programming Series. In the previous tutorial, we discussed the built-in ESP32 Hall Effect Sensor. In this tutorial, we will discuss another inbuilt sensor of the ESP32 i.e. Capacitive Touch Sensor.
ESP32 Board has 10 built-in capacitive touch pins, which generate an electrical signal when someone touches these pins. These ESP32 touch pins are normally used to wake up the board from deep sleep mode. These touch pins are also used to replace the normal mechanical buttons with touch pads, improving the presentation of the IoT projects.
Here's the video demonstration of the ESP32 Capacitive Touch Sensor:
Before going forward, let's first understand how this touch sensor works:
Where To Buy? | ||||
---|---|---|---|---|
No. | Components | Distributor | Link To Buy | |
1 | ESP32 | Amazon | Buy Now |
Capacitance is determined by the geometry of the conductors and the dielectric materials used. Changing any of these factors will result in changing the capacitance.
C = Ad
As we know, the human body also carries a small electric charge. So, when a body approaches the metallic plates(of a capacitor), the mutual capacitance between the two metal plates decreases. This change in capacitance is used to detect the touch in these capacitive sensors.
So, if someone touches any of these pins, ESP32 can easily detect it. The pin mapping of touch-sensitive pins in DOIT ESP32 DevKit V1 with GPIO pins is shown below:
ESP32 Capacitive Touch Pins | ||||
---|---|---|---|---|
No. | Parameter Name | Parameter Value | ||
1 |
Touch0 | GPIO4 | ||
2 |
Touch1 | GPIO0(not available in DOIT ESP32 Dev-kit V1 30-pin module but available in the 36-pin module) | ||
3 |
Touch2 | GPIO2 | ||
4 |
Touch3 | GPIO15 | ||
5 |
Touch4 | GPIO13 | ||
6 |
Touch5 | GPIO12 | ||
7 |
Touch6 | GPIO14 | ||
8 |
Touch7 | GPIO27 | ||
9 |
Touch8 | GPIO33 | ||
10 |
Touch9 | GPIO32 |
We are using the Arduino IDE development environment for programming ESP32. If you are new to Arduino IDE, read out How to Install ESP32 in Arduino IDE. Let's use the builtin Touch Sensor example in Arduino IDE:
In Arduino IDE there are two example codes available for the ESP32 touch sensor. We will discuss and implement both example codes in this tutorial. So, let's first open the TouchRead Code:
Here's the code for the TouchRead Example:
// ESP32 Touch Test
void setup()
{
Serial.begin(115200);
delay(1000); // give me time to bring up serial monitor
Serial.println("ESP32 Touch Test");
}
void loop()
{
Serial.println(touchRead(T0)); // get value using T0
delay(1000);
}
Inside the setup() function, the serial monitor is initialized at a baud rate of 115200 to display the sensor readings. Finally, we printed the message(ESP32 Touch Test) on the Serial Monitor:
void setup()
{
Serial.begin(115200);
delay(1000); // give me time to bring up serial monitor
Serial.println("ESP32 Touch Test");
}
void loop()
{
Serial.println(touchRead(T0)); // get value using T0
delay(1000);
}
These capacitive touch sensor pins are mainly used to generate an external interrupt for waking up ESP32 from low power modes(deep sleep mode). Moreover, can also be used to control external peripherals like LED blinking or tuning on a DC motor, when a capacitive touch-interrupt is observed. So, let's have a look at How to Generate external interrupt by touching the ESP32 capacitive touch pins:
Here's the ESP32 Touch Interrupt Code:
const int CAPACITIVE_TOUCH_INPUT_PIN = T0; // GPIO pin 4
const int LED_OUTPUT_PIN = LED_BUILTIN;
const int TOUCH_THRESHOLD = 40; // turn on light if touchRead value < this threshold
volatile boolean _touchDetected = false;
void setup()
{
Serial.begin(115200);
pinMode(LED_OUTPUT_PIN, OUTPUT);
pinMode(LED_OUTPUT_PIN, LOW);
touchAttachInterrupt(CAPACITIVE_TOUCH_INPUT_PIN, touchDetected, TOUCH_THRESHOLD);
}
void touchDetected()
{
_touchDetected = true;
}
void loop()
{
if(_touchDetected)
{
Serial.println("Touch detected.");
_touchDetected = false;
Serial.println("blink the LED");
digitalWrite(LED_OUTPUT_PIN, HIGH);
delay(1000);
digitalWrite(LED_OUTPUT_PIN, LOW);
delay(1000);
}
}
Let's understand the code by parts:
const int CAPACITIVE_TOUCH_INPUT_PIN = T0; // GPIO pin 4
const int LED_OUTPUT_PIN = LED_BUILTIN;
const int TOUCH_THRESHOLD = 40; // turn on light if touchRead value < this threshold
volatile boolean _touchDetected = false;
void setup()
{
Serial.begin(115200);
pinMode(LED_OUTPUT_PIN, OUTPUT);
pinMode(LED_OUTPUT_PIN, LOW);
touchAttachInterrupt(CAPACITIVE_TOUCH_INPUT_PIN, touchDetected, TOUCH_THRESHOLD);
}
void touchDetected()
{
_touchDetected = true;
}
void loop()
{
if(_touchDetected)
{
Serial.println("Touch detected.");
_touchDetected = false;
Serial.println("blink the LED");
digitalWrite(LED_OUTPUT_PIN, HIGH);
delay(1000);
digitalWrite(LED_OUTPUT_PIN, LOW);
delay(1000);
}
}
This concludes the tutorial; I hope you found this helpful and also hope to see you again with a new tutorial on ESP32.