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.
Hello readers, I hope you all are doing great. Welcome to Section 5 of the ESP32 Programming Series. In this section, we are going to interface different Embedded Sensors with the ESP32 Microcontroller Board. ESP32 development board is featured with some inbuilt sensors(i.e. hall effect sensor, capacitive touch sensor) so, in the initial tutorials of this section, we will explore these built-in ESP32 sensors and in the later lectures, we will interface third-party sensors with the ESP32.
In today's lecture, we will discuss the working/operation of the ESP32 built-in Hall Effect Sensor. Hall Effect sensor is used to detect the variation in the magnetic field of its surroundings. So, let's first understand What's Hall Effect:
Where To Buy? | ||||
---|---|---|---|---|
No. | Components | Distributor | Link To Buy | |
1 | ESP32 | Amazon | Buy Now |
The Hall Effect phenomenon was first discovered by Edwin Hall in 1879. When current passes through a conductor, the electrons move in a straight line and thus the voltage difference across the conductor's surface remains zero, as shown in the below figure:
However, when a magnet is placed near the current-carrying conductor in a way that the direction of the magnetic field is perpendicular to the flow of current, the electrons get diverted and don't follow a straight line, which results in generating a small potential difference across the conductor's surface, as shown in the below figure:
This small potential difference generated because of magnetic field presence is called Hall Voltage. This magnetic field influence over the current-carrying conductor is termed the Hall Effect.
A Hall Effect Sensor is a non-contact type embedded sensor, used to detect the presence & intensity of a magnetic field in its surroundings. Different third-party Hall Effect Sensors available in the market are shown in the below figure:
In ESP32, the Hall effect sensor is located inside the ESP-WROOM-32 metallic cover. As the Hall Effect sensor is a non-contact type, it doesn't have to be in contact with the magnet. We just need to place the magnet above this metallic sheet and the ESP32 Hall Effect sensor will detect it.
To understand the working of the Hall sensor with ESP32, let's test the builtin ESP32 example:
Here's the code for this ESP32 Hall Sensor example:
int val = 0;
void setup()
{
Serial.begin (9600);
}
void loop()
{
val = hallRead();
Serial.print ("sensor value = ");
Serial.println (val);//to graph
delay(100);
}
The code is quite simple, where the hallRead() function is called to read the hall sensor value, store it into a variable and then print it on the Serial monitor. Finally added a small delay to get the next value. Let me explain the code line by line for the beginners:
int val = 0;
void setup()
{
Serial.begin (9600);
}
void loop()
{
val = hallRead();
Serial.print ("sensor value = ");
Serial.println (val);//to graph
delay(100);
}
This concludes the tutorial. I hope you found this helpful, test it out and if feel any difficulty, let me know in the comments. In the next tutorial, we will have a look at another built-in sensor of ESP32 i.e. Capacitive Touch Sensor. Thanks for reading.
Hello readers, I hope you are all doing great. In this tutorial, we are going to discuss the OTA web updater on the ESP32.
We already covered the fundamentals of OTA programming in ESP32, in our previous tutorial where we used the Arduino IDE to upload OTA code into the ESP32 module using the network port.
In the OTA web updater, you need to create a web server page for OTA programming.
Fig.1 ESP32 OTA web updater
Where To Buy? | ||||
---|---|---|---|---|
No. | Components | Distributor | Link To Buy | |
1 | ESP32 | Amazon | Buy Now |
Fig. 2
In this tutorial, we will discuss only the OTA web updater method using Arduino IDE and ESP32 dev-Kit V1 module.
If you want to know more about the basics of ESP32 and how to get started with Arduino IDE, then read Introduction to ESP32 Programming Series.
#include <WiFi.h> #include <WiFiClient.h> #include <WebServer.h> #include <ESPmDNS.h> #include <Update.h> const char* host = "esp32"; const char* ssid = "SSID"; const char* password = "password"; WebServer server(80); /* * Login page */ const char* loginIndex = "<form name='loginForm'>" "<table width='20%' bgcolor='A09F9F' align='center'>" "<tr>" "<td colspan=2>" "<center><font size=4><b>ESP32 Login Page</b></font></center>" "<br>" "</td>" "<br>" "<br>" "</tr>" "<td>Username:</td>" "<td><input type='text' size=25 name='userid'><br></td>" "</tr>" "<br>" "<br>" "<tr>" "<td>Password:</td>" "<td><input type='Password' size=25 name='pwd'><br></td>" "<br>" "<br>" "</tr>" "<tr>" "<td><input type='submit' onclick='check(this.form)' value='Login'></td>" "</tr>" "</table>" "</form>" "<script>" "function check(form)" "{" "if(form.userid.value=='admin' && form.pwd.value=='admin')" "{" "window.open('/serverIndex')" "}" "else" "{" " alert('Error Password or Username')/*displays error message*/" "}" "}" "</script>"; /* * Server Index Page */ const char* serverIndex = "<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>" "<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>" "<input type='file' name='update'>" "<input type='submit' value='Update'>" "</form>" "<div id='prg'>progress: 0%</div>" "<script>" "$('form').submit(function(e){" "e.preventDefault();" "var form = $('#upload_form')[0];" "var data = new FormData(form);" " $.ajax({" "url: '/update'," "type: 'POST'," "data: data," "contentType: false," "processData:false," "xhr: function() {" "var xhr = new window.XMLHttpRequest();" "xhr.upload.addEventListener('progress', function(evt) {" "if (evt.lengthComputable) {" "var per = evt.loaded / evt.total;" "$('#prg').html('progress: ' + Math.round(per*100) + '%');" "}" "}, false);" "return xhr;" "}," "success:function(d, s) {" "console.log('success!')" "}," "error: function (a, b, c) {" "}" "});" "});" "</script>"; /* * setup function */ void setup(void) { Serial.begin(115200); // Connect to WiFi network WiFi.begin(ssid, password); Serial.println(""); // Wait for connection while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.print("Connected to "); Serial.println(ssid); Serial.print("IP address: "); Serial.println(WiFi.localIP()); /*use mdns for host name resolution*/ if (!MDNS.begin(host)) { //http://esp32.local Serial.println("Error setting up MDNS responder!"); while (1) { delay(1000); } } Serial.println("mDNS responder started"); server.on("/", HTTP_GET, []() { server.sendHeader("Connection", "close"); server.send(200, "text/html", loginIndex); }); server.on("/serverIndex", HTTP_GET, []() { server.sendHeader("Connection", "close"); server.send(200, "text/html", serverIndex); }); /*handling uploading firmware file */ server.on("/update", HTTP_POST, []() { server.sendHeader("Connection", "close"); server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK"); ESP.restart(); }, []() { HTTPUpload& upload = server.upload(); if (upload.status == UPLOAD_FILE_START) { Serial.printf("Update: %s\n", upload.filename.c_str()); if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { //start with max available size Update.printError(Serial); } } else if (upload.status == UPLOAD_FILE_WRITE) { /* flashing firmware to ESP*/ if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) { Update.printError(Serial); } } else if (upload.status == UPLOAD_FILE_END) { if (Update.end(true)) { //true to set the size to the current progress Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize); } else { Update.printError(Serial); } } }); server.begin(); } void loop(void) { server.handleClient(); delay(1); }
Fig. 4
Fig. 6
Fig. 7
Fig. 9
Fig. 10
Fig. 12
Fig. 13
Fig. 14
Fig. 16
Fig. 17
#include <WiFi.h> #include <WiFiClient.h> #include <WebServer.h> #include <ESPmDNS.h> #include <Update.h> const char* host = "esp32"; const char* ssid = "SSID"; const char* password = "password"; //variabls to blink without delay: const int led = 2; unsigned long previousMillis = 0; // will store last time LED was updated const long interval = 1000; // interval at which to blink (milliseconds) int ledState = LOW; // ledState used to set the LED WebServer server(80); /* * Login page */ const char* loginIndex = "<form name='loginForm'>" "<table width='20%' bgcolor='A09F9F' align='center'>" "<tr>" "<td colspan=2>" "<center><font size=4><b>ESP32 Login Page</b></font></center>" "<br>" "</td>" "<br>" "<br>" "</tr>" "<td>Username:</td>" "<td><input type='text' size=25 name='userid'><br></td>" "</tr>" "<br>" "<br>" "<tr>" "<td>Password:</td>" "<td><input type='Password' size=25 name='pwd'><br></td>" "<br>" "<br>" "</tr>" "<tr>" "<td><input type='submit' onclick='check(this.form)' value='Login'></td>" "</tr>" "</table>" "</form>" "<script>" "function check(form)" "{" "if(form.userid.value=='admin' && form.pwd.value=='admin')" "{" "window.open('/serverIndex')" "}" "else" "{" " alert('Error Password or Username')/*displays error message*/" "}" "}" "</script>"; /* * Server Index Page */ const char* serverIndex = "<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>" "<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>" "<input type='file' name='update'>" "<input type='submit' value='Update'>" "</form>" "<div id='prg'>progress: 0%</div>" "<script>" "$('form').submit(function(e){" "e.preventDefault();" "var form = $('#upload_form')[0];" "var data = new FormData(form);" " $.ajax({" "url: '/update'," "type: 'POST'," "data: data," "contentType: false," "processData:false," "xhr: function() {" "var xhr = new window.XMLHttpRequest();" "xhr.upload.addEventListener('progress', function(evt) {" "if (evt.lengthComputable) {" "var per = evt.loaded / evt.total;" "$('#prg').html('progress: ' + Math.round(per*100) + '%');" "}" "}, false);" "return xhr;" "}," "success:function(d, s) {" "console.log('success!')" "}," "error: function (a, b, c) {" "}" "});" "});" "</script>"; /* * setup function */ void setup(void) { pinMode(led, OUTPUT); Serial.begin(115200); // Connect to WiFi network WiFi.begin(ssid, password); Serial.println(""); // Wait for connection while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.print("Connected to "); Serial.println(ssid); Serial.print("IP address: "); Serial.println(WiFi.localIP()); /*use mdns for host name resolution*/ if (!MDNS.begin(host)) { //http://esp32.local Serial.println("Error setting up MDNS responder!"); while (1) { delay(1000); } } Serial.println("mDNS responder started"); /*return index page which is stored in serverIndex */ server.on("/", HTTP_GET, []() { server.sendHeader("Connection", "close"); server.send(200, "text/html", loginIndex); }); server.on("/serverIndex", HTTP_GET, []() { server.sendHeader("Connection", "close"); server.send(200, "text/html", serverIndex); }); /*handling uploading firmware file */ server.on("/update", HTTP_POST, []() { server.sendHeader("Connection", "close"); server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK"); ESP.restart(); }, []() { HTTPUpload& upload = server.upload(); if (upload.status == UPLOAD_FILE_START) { Serial.printf("Update: %s\n", upload.filename.c_str()); if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { //start with max available size Update.printError(Serial); } } else if (upload.status == UPLOAD_FILE_WRITE) { /* flashing firmware to ESP*/ if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) { Update.printError(Serial); } } else if (upload.status == UPLOAD_FILE_END) { if (Update.end(true)) { //true to set the size to the current progress Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize); } else { Update.printError(Serial); } } }); server.begin(); } void loop(void) { server.handleClient(); delay(1); //loop to blink without delay unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= interval) { // save the last time you blinked the LED previousMillis = currentMillis; // if the LED is off turn it on and vice-versa: ledState = not(ledState); // set the LED with the ledState of the variable: digitalWrite(led, ledState); } }
Fig. 19
Fig. 20
Fig. 21 bin file
Fig. 23 LED blink
This concludes the tutorial. I hope, you found this helpful and I hope to see you soon for the new ESP32 tutorial.
Hello readers, hope you all are doing great. In this tutorial, we are going to discuss a mechanism that allows users to update the ESP32 with a new program wirelessly or over the air (without using a USB cable to upload a new program).
Where To Buy? | ||||
---|---|---|---|---|
No. | Components | Distributor | Link To Buy | |
1 | ESP32 | Amazon | Buy Now |
Fig. 2 OTA programming for IoT
There are two methods of OTA implementation.
In this tutorial, we will discuss only the basic OTA method using Arduino IDE and ESP32 module.
If you want to know more about the basics of ESP32 and how to get started with Arduino IDE, then follow the tutorial Introduction to ESP32 Programming Series.
To implement the Basic OTA method, an example is available is Arduino IDE.
#include <WiFi.h> #include <ESPmDNS.h> #include <WiFiUdp.h> #include <ArduinoOTA.h> const char* ssid = "SSID"; const char* password = "Password"; void setup() { Serial.begin(115200); Serial.println("Booting"); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); while (WiFi.waitForConnectResult() != WL_CONNECTED) { Serial.println("Connection Failed! Rebooting..."); delay(5000); ESP.restart(); } ArduinoOTA.onStart([]() { String type; if (ArduinoOTA.getCommand() == U_FLASH) type = "sketch"; else // U_SPIFFS type = "filesystem"; // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end() Serial.println("Start updating " + type); }) .onEnd([]() { Serial.println("\nEnd"); }) .onProgress([](unsigned int progress, unsigned int total) { Serial.printf("Progress: %u%%\r", (progress / (total / 100))); }) .onError([](ota_error_t error) { Serial.printf("Error[%u]: ", error); if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); else if (error == OTA_END_ERROR) Serial.println("End Failed"); }); ArduinoOTA.begin(); Serial.println("Ready"); Serial.print("IP address: "); Serial.println(WiFi.localIP()); } void loop() { ArduinoOTA.handle(); }
Fig. 11 Serial monitor
#include <WiFi.h> #include <ESPmDNS.h> #include <WiFiUdp.h> #include <ArduinoOTA.h> const char* ssid = "public"; const char* password = "ESP32@123"; //variabls for blinking an LED with Millis const int led = 2; // ESP32 Pin to which onboard LED is connected unsigned long previousMillis = 0; // will store last time LED was updated const long interval = 1000; // interval at which to blink (milliseconds) int ledState = LOW; // ledState used to set the LED void setup() { pinMode(led, OUTPUT); Serial.begin(115200); Serial.println("Booting"); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); while (WiFi.waitForConnectResult() != WL_CONNECTED) { Serial.println("Connection Failed! Rebooting..."); delay(5000); ESP.restart(); } ArduinoOTA .onStart([]() { String type; if (ArduinoOTA.getCommand() == U_FLASH) type = "sketch"; else // U_SPIFFS type = "filesystem"; // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end() Serial.println("Start updating " + type); }) .onEnd([]() { Serial.println("\nEnd"); }) .onProgress([](unsigned int progress, unsigned int total) { Serial.printf("Progress: %u%%\r", (progress / (total / 100))); }) .onError([](ota_error_t error) { Serial.printf("Error[%u]: ", error); if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); else if (error == OTA_END_ERROR) Serial.println("End Failed"); }); ArduinoOTA.begin(); Serial.println("Ready"); Serial.print("IP address: "); Serial.println(WiFi.localIP()); } void loop() { ArduinoOTA.handle(); //loop to blink without delay unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= interval) { // save the last time you blinked the LED previousMillis = currentMillis; // if the LED is off turn it on and vice-versa: ledState = not(ledState); // set the LED with the ledState of the variable: digitalWrite(led, ledState); } }
Note: It is required to upload the OTA programming handler code every time you upload a new code into ESP32 over the air. So that, OTA programming remains enabled for future use.
This concludes the tutorial. I hope you found this helpful. In the next tutorial, we will discuss the OTA web updater in ESP32.
Hello readers, hope you all are doing great. In this tutorial, we will discuss low power modes in ESP32, their purpose and their implementation to increase the battery life by reducing power consumption.
Where To Buy? | ||||
---|---|---|---|---|
No. | Components | Distributor | Link To Buy | |
1 | ESP32 | Amazon | Buy Now |
Fig.1
Along with multiple wireless and processing features, ESP32 also provides us with a power-saving feature by offering sleep modes. When you are powering the ESP32 module from the live supply using an adaptor or a USB cable, there is nothing to worry about power consumption. But when you are using a battery, as a power source to ESP32, you need to manage the power consumption for longer battery life.
When ESP32 is in sleep mode, a small amount of power is required to maintain the state of ESP32 in RAM (random access memory) and retain necessary data. Meanwhile, the power supply won’t be consumed by any unnecessary peripheral or inbuilt modules like Wi-Fi and Bluetooth.
ESP32 offers 5 power modes. Each mode is configurable and offers different power-saving capabilities:
Fig. 2
Fig 3
For a better understanding of low power modes in ESP32, we are going to implement deep sleep mode in esp32 and will also discuss how to wake up the device from deep sleep mode.
To implement deep sleep modes we are going to use another ESP32 feature that is Capacitive Touch Sensing pins. These pins can sense the presence of a body that holds an electric charge.
So we are going to use these touch-sensitive pins for waking up ESP32 from deep sleep mode using the Arduino IDE compiler.
In Arduino IDE examples are given for deep sleep mode with various wake-up methods.
#define Threshold 40 /* Greater the value, more the sensitivity */ RTC_DATA_ATTR int bootCount = 0; touch_pad_t touchPin; /* Method to print the reason by which ESP32 has been awaken from sleep */ void print_wakeup_reason(){ esp_sleep_wakeup_cause_t wakeup_reason; wakeup_reason = esp_sleep_get_wakeup_cause(); switch(wakeup_reason) { case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break; case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break; case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break; case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break; case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break; default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break; } } /* Method to print the touchpad by which ESP32 has been awaken from sleep */ void print_wakeup_touchpad(){ touchPin = esp_sleep_get_touchpad_wakeup_status(); switch(touchPin) { case 0 : Serial.println("Touch detected on GPIO 4"); break; case 1 : Serial.println("Touch detected on GPIO 0"); break; case 2 : Serial.println("Touch detected on GPIO 2"); break; case 3 : Serial.println("Touch detected on GPIO 15"); break; case 4 : Serial.println("Touch detected on GPIO 13"); break; case 5 : Serial.println("Touch detected on GPIO 12"); break; case 6 : Serial.println("Touch detected on GPIO 14"); break; case 7 : Serial.println("Touch detected on GPIO 27"); break; case 8 : Serial.println("Touch detected on GPIO 33"); break; case 9 : Serial.println("Touch detected on GPIO 32"); break; default : Serial.println("Wakeup not by touchpad"); break; } } void callback(){ //placeholder callback function } void setup(){ Serial.begin(115200); delay(1000); //Take some time to open up the Serial Monitor //Increment boot number and print it every reboot ++bootCount; Serial.println("Boot number: " + String(bootCount)); //Print the wakeup reason for ESP32 and touchpad too print_wakeup_reason(); print_wakeup_touchpad(); //Setup interrupt on Touch Pad 3 (GPIO15) touchAttachInterrupt(T3, callback, Threshold); //Configure Touchpad as wakeup source esp_sleep_enable_touchpad_wakeup(); //Go to sleep now Serial.println("Going to sleep now"); esp_deep_sleep_start(); Serial.println("This will never be printed"); } void loop(){ //This will never be reached }
Fig 12
Fig. 13 waking up esp32 using capacitive sensitive GPIO pin
We have attached a screenshot from the serial monitor for reference.Fig. 14
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */ #define TIME_TO_SLEEP 5 /* Time ESP32 will go to sleep (in seconds) */ RTC_DATA_ATTR int bootCount = 0; /* Method to print the reason by which ESP32 has been awaken from sleep */ void print_wakeup_reason(){ esp_sleep_wakeup_cause_t wakeup_reason; wakeup_reason = esp_sleep_get_wakeup_cause(); switch(wakeup_reason) { case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break; case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break; case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break; case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break; case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break; default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break; } } void setup(){ Serial.begin(115200); delay(1000); //Take some time to open up the Serial Monitor //Increment boot number and print it every reboot ++bootCount; Serial.println("Boot number: " + String(bootCount)); //Print the wakeup reason for ESP32 print_wakeup_reason(); /* First we configure the wake up source We set our ESP32 to wake up every 5 seconds */ esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR); Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) + " Seconds"); /* Next we decide what all peripherals to shut down/keep on By default, ESP32 will automatically power down the peripherals not needed by the wakeup source, but if you want to be a poweruser this is for you. Read in detail at the API docs http://esp-idf.readthedocs.io/en/latest/api-reference/system/deep_sleep.html Left the line commented as an example of how to configure peripherals. The line below turns off all RTC peripherals in deep sleep. */ //esp_deep_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF); //Serial.println("Configured all RTC Peripherals to be powered down in sleep"); /* Now that we have setup a wake cause and if needed setup the peripherals state in deep sleep, we can now start going to deep sleep. In the case that no wake up sources were provided but deep sleep was started, it will sleep forever unless hardware reset occurs. */ Serial.println("Going to sleep now"); Serial.flush(); esp_deep_sleep_start(); Serial.println("This will never be printed"); } void loop(){ //This is not going to be called }
Fig 15
Fig. 16
Fig. 17
Fig. 18
Fig. 19
Fig 20
This concludes the tutorial. I hope you found this useful, and I hope to see you soon for the new ESP32 tutorial.
Hello readers, I hope you all are doing great. Welcome to the 3rd Lecture of Section 2 in the ESP32 Programming Series. In this tutorial, we are going to discuss another important feature of ESP32 i.e. PWM(Pulse Width Modulation).
Pulse Width Modulation is a technique to reduce the voltage by pulsating it. In today's lecture, we will first understand the basic concept of PWM, and after that will design two projects to fully grasp it. In the first project, we will control the brightness of an LED, while in the second one, we will control the speed of a DC Motor.
Before going forward, let's first have a look at the PWM working:
Where To Buy? | ||||
---|---|---|---|---|
No. | Components | Distributor | Link To Buy | |
1 | ESP32 | Amazon | Buy Now |
PWM is used to control the power delivered to the load by pulsating the ON-Time of the voltage pulse, without causing any power loss. Let's understand the PWM concept with the help of below image:
Suppose a DC Motor runs at 200RPM over 5V. Now, if we want to reduce its speed to 100 RPM, we need to reduce its input voltage to 2.5V(approx). So, either we can replace the 5V battery with a 2.5V Battery or use a PWM circuit to reduce the voltage level from 5V to 2.5V. In this specific case, the PWM pulse will be ON for 50% of the time and get OFF for the remaining 50% of the time.
The behavior of the PWM signal is determined by the following factors:
As you can see in the below figure, we have taken two signals for a duration of 1 second. The first signal completes 10 Cycles in 1 second, so we can say it has a frequency of 10Hz, while the second one has a frequency of 5Hz as it completes 5 cycles in 1 second. So, I hope now it's clear that the number of cycles per second is the frequency of a signal.
Duty Cycle is the ratio of ON time(when the signal is high) to the total time taken to complete the cycle. The duty cycle is represented in the form of a percentage (%) or ratio. Let's understand the PWM Duty Cycle with the help of below image:
The resolution of a PWM signal defines the number of steps it can have from zero power to full power. The resolution of the PWM signal is configurable for example, the ESP32 module has a 1-16 bit resolution, which means we can configure maximum a of 65536 (2^16) steps from zero to full power.
In the ESP WROOM-32 module, there are 16 PWM channels. All the channels are divided into two groups containing 8 channels in each group. The resolution can be programmed between 1 to 16 bits and frequency also depends upon the programmed resolution of the PWM signal.
Now
For the demonstration of PWM in ESP32 we are going to explain two examples:
We are using Arduino IDE to compile and upload the code into the ESP WROOM-32 board.
// Global variable declaration to set PWM properties
const int ledChannel = 0; // select channel 0
const int resolution = 8; //8-bit resolutin i.e., 0-255
const int frequency = 5000; // set frequency in Hz
int dutyCycle = 0;
void setup()
{
Serial.begin(115200);
ledcSetup(ledChannel, frequency, resolution); // configure LED PWM functionalities
ledcAttachPin(LED_BUILTIN, ledChannel); // attach the channel to the GPIO to be controlled
}
void loop()
{
while(dutyCycle <200)
{
ledcWrite(ledChannel, dutyCycle++); // changing the LED brightness with PWM
Serial.print(" duty Cycle ++ :");
Serial.println(dutyCycle); // display the duty cycle on serial monitor
delay(5);
}
while(dutyCycle>0)
{
ledcWrite(ledChannel, dutyCycle--); // changing the LED brightness with PWM
Serial.print(" duty Cycle -- :");
Serial.println(dutyCycle); // display the duty cycle on serial monitor
delay(5);
}
}
// Global variable declaration to set PWM properties
const int ledChannel = 0; // select channel 0
const int resolution = 8; //8-bit resolutin i.e., 0-255
const int frequency = 5000; // set frequency in Hz
int dutyCycle = 0;
Serial.begin(115200);
ledcSetup(ledChannel, frequency, resolution); // configure LED PWM functionalities
ledcAttachPin(LED_BUILTIN, ledChannel); // attach the channel to the GPIO to be controlled
while(dutyCycle <200)
{
ledcWrite(ledChannel, dutyCycle++); // changing the LED brightness with PWM
Serial.print(" duty Cycle ++ :");
Serial.println(dutyCycle); // display the duty cycle on serial monitor
delay(5);
}
while(dutyCycle>0)
{
ledcWrite(ledChannel, dutyCycle--); // changing the LED brightness with PWM
Serial.print(" duty Cycle -- :");
Serial.println(dutyCycle); // display the duty cycle on serial monitor
delay(5);
}
Fig. 9 Serial plotter PWM output
Fig. 10
In this example, we are going to implement PWM using ESP WROOM-32 to control the speed of a DC motor.
The speed of the DC motor depends upon the input power supply. So, by varying the power input we can also vary (increase or decrease) the speed of DC motor.
Hardware components required:
L298N motor driver: A motor driver is used between the ESP32 board and DC motor to resolve the power compatibility issues.
Both the ESP32 board and DC motor operate at different power ratings due to which you can not connect the two devices directly. So a motor driver is used to receive a low power input from the ESP32 board and drive/run DC motor at slightly high power.
L298N can drive a DC motor that operated between 5 to 35 voltage range and maximum current of 2A.
There are various DC motor drivers available in the market for example L293D, DRV8833, MAX14870 single brushed motor driver etc. You can choose the driver of your choice depending upon the application and power ratings.
Fig. 11
FIG. 12 IC L298N pin-out
IN_1 | IN_2 | Rotation |
HIGH | LOW | DC motor rotates in a clockwise direction |
LOW | HIGH | The motor rotates in an anti-clockwise direction |
LOW | LOW | Motor STOP |
HIGH | HIGH | Motor STOP |
Table 1
//configure GPIO pins to connect motor driver
int enable1Pin = 14;
int M_Pin1 = 26;
int M_Pin2 = 27;
// Setting PWM properties
const int freq = 10000;
const int pwmChannel = 0;
const int resolution = 8;
int dutyCycle = 150;
void setup()
{
Serial.begin(115200);
// sets the pins as outputs:
pinMode(M_Pin1, OUTPUT);
pinMode(M_Pin2, OUTPUT);
pinMode(enable1Pin, OUTPUT);
//Configure LED PWM functionalities
ledcSetup(pwmChannel, freq, resolution);
// attach the channel to the GPIO to be controlled
ledcAttachPin(enable1Pin, pwmChannel);
Serial.print("Testing DC Motor...");
}
void loop()
{
// Move the DC motor in anti-clockwise direction at maximum speed
Serial.println("Moving reverse");
digitalWrite(M_Pin1, LOW);
digitalWrite(M_Pin2, HIGH);
delay(500);
// Move DC motor forward with increasing speed
Serial.println("Moving Forward");
digitalWrite(M_Pin1, HIGH);
digitalWrite(M_Pin2, LOW);
//----while loop----
while (dutyCycle <= 255)
{
ledcWrite(pwmChannel, dutyCycle);
Serial.print("Speed increasing with duty cycle: ");
Serial.println(dutyCycle);
dutyCycle = dutyCycle +5;
delay(100);
}
while (dutyCycle >150)
{
ledcWrite(pwmChannel, dutyCycle);
Serial.print("Speed decreasing with duty cycle: ");
Serial.println(dutyCycle);
dutyCycle = dutyCycle -5;
delay(100);
}
// _____Stop the DC motor
Serial.println("STOP DC motor");
digitalWrite(M_Pin1, LOW);
digitalWrite(M_Pin2, LOW);
delay(500);
}
//configure GPIO pins to connect motor driver
int enable1Pin = 14;
int M_Pin1 = 26;
int M_Pin2 = 27;
// Setting PWM properties
const int freq = 10000;
const int pwmChannel = 0;
const int resolution = 8;
int dutyCycle = 150;
Serial.begin(115200);
// sets the pins as outputs:
pinMode(M_Pin1, OUTPUT);
pinMode(M_Pin2, OUTPUT);
pinMode(enable1Pin, OUTPUT);
//Configure LED PWM functionalities
ledcSetup(pwmChannel, freq, resolution);
// attach the channel to the GPIO to be controlled
ledcAttachPin(enable1Pin, pwmChannel);
Fig. 20
Fig. 21 Increasing speed
Fig. 22 Reducing speed
Fig. 23 STOP DC motor
Fig. 24 PWM output on serial monitor
Fig. 25 PWM output on Serial Plotter
This concludes the tutorial. I hope you found this useful, and I hope to see you soon for the new ESP32 tutorial.Hello readers, hope you all are doing great. In this tutorial, we will discuss another ESP32 protocol that is Web Socket and we will also explain how to create a web server using web socket protocol with ESP32. So, we will have a look at What is a web socket server, How web socket protocol is different from HTTP protocol, What is handshaking in networking, Three-way handshaking, Web socket application, Creating web socket server using ESP32 module etc. Let's get started:
Where To Buy? | ||||
---|---|---|---|---|
No. | Components | Distributor | Link To Buy | |
1 | ESP32 | Amazon | Buy Now |
Fig 1 Web-socket server
A Web Socket is a full-duplex (both the server and the client can send and receive data at the same time) computer communication protocol. Web socket protocol, like HTTP (hypertext transfer protocol), also works in server and client communication format. A web socket uses a process known as handshaking to establish communication between the server and client. This protocol is also known as the stateful protocol. When a client device requests communication with the server, a connection is established between the server and the client, and the connection remains in place until either the server or the client terminates it.
Fig. 2 HTTP protocol
Fig. 3 web socket protocol
In TCP/IP (transmission control protocol/ internet protocol) network, three-way handshaking is used to create a communication channel between server and client.
Three-way handshaking steps are:
Fig. 5 Three-way handshaking
Web socket is used in real-time applications where a client is required to respond quickly to a change or update. The various web socket applications are:
To create a web socket server using ESP32 we are using Arduino IDE as a compiler. Arduino IDE will compile the code and will also upload the compiled code into the ESP32 hardware module.
If you are not familiar with using the Arduino IDE compiler for ESP32 programming then follow our #1 tutorial that is about Introduction to ESP32 programming series.
https://github.com/me-no-dev/AsyncTCP
https://github.com/me-no-dev/ESPAsyncWebServer
Follow our tutorial Introduction to ESP32 programming series to learn about adding a library in Arduino IDE.
Fig. 6 Libraries
Fig. 10 <style> tag
Fig: 11 Styling the button
Fig. 12 <body> tag
Fig. 13 script tag
Fig 14 initWebSocket()
Fig. 15 Update LED status on the web page
Fig. 16 Notify clients
Fig. 17 handles web (client) socket message
Fig. 19 Placeholder
Fig. 20
Fig. 21 Wifi status
Fig. 22
Fig. 23
Fig. 24
Fig 26 Arduino IDE Serial monitor
Fig. 27 Web page displaying LED status HIGH
Fig 28 Web page displaying LED status LOW
Fig. 29 ESP32 LED HIGH
This concludes the tutorial. I hope you find it helpful. In our next tutorial, we will discuss PWM (pulse width modulation) using ESP32.
Where To Buy? | ||||
---|---|---|---|---|
No. | Components | Distributor | Link To Buy | |
1 | ESP32 | Amazon | Buy Now |
Fig 1 Interrupt
Polling is a process that performs continuous monitoring. Basically, the processor continuously monitors the state of a specific device or a peripheral, and when the status of the device satisfies the condition, the device executes the task that was required. Then it moves on to the next device to monitor until each one has been served. The processor performs no other operations and devotes all of its processing time to monitoring, and all other tasks are suspended until the current one is completed.
Fig 2 polling vs Interrupt
So, to overcome the disadvantage of the polling method, we chose the Interrupt method.
ESP32 module has a dual-core processor and each core consists of 32 interrupts. Basically interrupts are of two types:
Fig 3 ESP32 software interrupt
Software interrupts are internal which occur in response to the execution of a software instruction. For example, a timer can be used to generate a software interrupt.
Fig 4 ESP32 software interrupt
Hardware interrupts are the external interrupts that are caused by an external event. For example, an external push button connected to ESP32’s GPIO or a motion sensor will generate an interrupt (event) if a motion is detected.
When an interrupt occurs during normal program execution, an ISR (interrupt service routine) or an interrupt handler is called into action. The normal program execution will be halted, and the interrupt will be executed based on the priority level of the interrupt.
Fig. 5 Interrupt service routing
Every interrupt has a fixed memory location where the address of the ISR is stored.Interrupt Vector Table refers to a memory table or memory table that is used to store the location of an interrupt service routine.
Note: IRAM_ATTR attribute should be defined for interrupt handling. As per the ESP32 datasheet interrupt service routine should run inside the RAM. Because inside the RAM it is fast to execute a code than in flash memory and when an interrupt occurs all the other tasks will be blocked or halted till the time interrupt request is served.
When an interrupt occurs, the microcontroller will go through the following steps:
Fig. 6 ESP32 Interrupt Program flow
We are using Arduino IDE to compile the code and then upload into the ESP32 board.
If you are not familiar with the procedure of getting started with Arduino IDE and hoe to compile a code in Arduino IDE then follow our previous tutorial that is Introduction to ESP32 programming series.
// Set GPIOs for LED and Push button const int led = 2; const int button = 0; // Timer: Auxiliary variables #define timeSeconds 10 unsigned long now = millis(); unsigned long lastTrigger = 0; boolean startTimer = false; // Checks if button input was detected, sets LED HIGH and starts a timer void IRAM_ATTR buttonInput() { Serial.println("input is available !!!"); digitalWrite(led, HIGH); startTimer = true; lastTrigger = millis(); } void setup() { // Serial port for debugging purposes Serial.begin(115200); pinMode(button, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(button), buttonInput, RISING); // Set LED to LOW pinMode(led, OUTPUT); digitalWrite(led, LOW); } void loop() { now = millis(); // Turn off the LED after the number of seconds defined in the timeSeconds variable if(startTimer && (now - lastTrigger > (timeSeconds*500))) { digitalWrite(led, LOW); startTimer = false; } }
As we have already discussed that the interrupt could be software generated (internal interrupt) or due to hardware (external interrupt). This tutorial we are using hardware or external interrupt for demonstration. You can also use software interrupts or both in a single code, as per your requirements.
Fig. 7
In this code, we are using timer to add delay instead of using delay() function.(I will also explain that why it is preferred to use timer in order to create delay instead of using delay() function after the code demonstration).
Inside the loop function which is continuously running, the buttonInput function will be called every time when an interrupt occurs , which we have defined previously inside the setup() function.
Fig 14 Serial monitor
Delay() function is a complete software process and it is mostly used because it is easier to implement delay using only software. On the other hand, when we switch to hardware delay or use a timer to add delay the process is a bit complicated to implement.
But, when we think of a practical perspective we prefer hardware delay over software delay. Because a software delay keeps the processor busy in a continuous loop and the processor need to keep all other tasks on halt.
On the other hand, if we use a timer to add delay the processor can complete some other task while the timer is playing its own part.
This concludes the tutorial. Hope you find it helpful. In our next tutorial, we will discuss the ESP32 Web Socket server.