ESP32 IMAP Protocol for Retrieving Commands or System Upgrade Data from a Specified Email

ESP32 IMAP Protocol for Retrieving Commands or System Upgrade Data from a Specified Email

In this article, we will implement the functionality of the ESP32 based on the IMAP protocol to retrieve operational commands or system upgrade data from a specified email. This functionality is mainly used to send non-real-time commands to the ESP32 without a server, or to send program upgrade packages and other non-time-sensitive data to the ESP32. This is a perfect solution for sending sporadic small data without a server, where real-time data requirements are not high.

1. The ESP32 Chip Used in This Article

ESP32 IMAP Protocol for Retrieving Commands or System Upgrade Data from a Specified Email

ESP32 IMAP Protocol for Retrieving Commands or System Upgrade Data from a Specified Email

2. The Development Platform Chosen in This Article

The development platform chosen for the program code involved in this article is Arduino.

3. Enable IMAP Service Functionality for the Email

The IMAP service functionality for the email is not enabled by default and needs to be enabled manually. For instructions on how to enable the IMAP service, please refer to the article on ESP32 reporting data to email based on the SMTP protocol.

4. Introduction to Common IMAP Commands

Below are some commonly used commands in IMAP. It is worth noting that different email service providers may have slight variations in their support for IMAP commands. If you encounter issues during debugging, it is advisable to test each command individually before integrating them into the program.

Common IMAP commands: where AXX is the command number (user-defined, generally incrementing natural numbers).

The principle of interacting with the email server using IMAP commands is as follows: the client sends numbered commands to the server, and the server returns results to the client in the order of the command numbers. It is particularly important to note that if UID is used in the command, it must be used consistently. UID refers to a fixed identifier for each email in the mailbox, which does not change during email processing. It is recommended to use UID when applicable.

A001 CAPABILITY  Check the list of IMAP features supported by the server
A002 STATUS INBOX (MESSAGES UNSEEN) Check the mailbox status (e.g., total emails, unread count)
A003 SELECT INBOX  Select the mailbox (e.g., enter the inbox)
A005 UID SEARCH UNSEEN Search for all unread emails (ALL, UNSEEN)
A006 UID SEARCH FROM "[email protected]" Search for emails from "[email protected]"
A010 UID FETCH {EmailID} BODY[HEADER] Get the email header
A011 UID FETCH {EmailID} BODY[TEXT] Get the email body
A012 UID FETCH {EmailID} BODY.PEEK[] Get the email content without marking it as read
A013 UID FETCH {EmailID} BODY[HEADER.FIELDS(Subject)] Get the subject from the email header
A015 UID STORE {EmailID} +FLAGS (\Seen) Mark as read
A016 UID STORE {EmailID} +FLAGS (\Deleted) Mark as deleted (requires subsequent EXPUNGE)
A018 UID EXPUNGE Permanently delete emails marked as \Deleted
A017 UID COPY {EmailID} {TargetMailbox}

5. A Complete Program for an ESP32 to Retrieve Commands from a Specified Email and Execute Them

Below is a program for the ESP32 that retrieves commands from a specified email to control an LED light. It is particularly important to note that during the experiment, to observe the effect, the interval for the ESP32 to check emails can be set shorter. In actual use, it is recommended to set the interval to 20 minutes, 30 minutes, or 60 minutes, etc.

using namespace std;  // Add at the top of the file
#include <WiFi.h>
#include <Base64.h>
#include <WiFiClientSecure.h>
#include <vector>  // Must include header files
#define STATUS_LED 35 // Low level to light up
// Configuration information
#define wifi_ssid "***" // Your WIFI name
#define wifi_pwd "***" // Your WIFI password
#define EMAIL_USER "***@qq.com" // Your email account
#define EMAIL_PASS "***" // Authorization code
#define FROM_USER "***@qq.com" // Used to filter emails from a specific account
// IMAP server configuration
#define IMAP_SERVER "imap.qq.com"
#define IMAP_PORT 993 // Default uses 143 (plain text) or 993 (SSL/TLS encrypted), test both if unsuccessful
WiFiClientSecure imapClient;
void connectWiFi();
void initNetworkClients();
vector<int> parseMailIDs(const String& response);
void checkEmails();
void markAsRead(int mailID);
void sendIMAPCommand(String cmd);
String readIMAPResponse();
unsigned long previousMillis = 0;
const long interval = 60000; // Unit is milliseconds, 1 minute = 60000; 30 minutes = 30*60*1000 = 1800000
String temp;
void setup() {  Serial.begin(115200);  pinMode(STATUS_LED, OUTPUT);    connectWiFi();  initNetworkClients();    checkEmails();}
void loop() {  if(millis()-previousMillis>interval)  {      previousMillis=millis();      //checkEmails();  }  if (Serial.available()) // Mainly used for debugging commands from the serial port  {        temp=Serial.readString();        // The string sent to the ESP32 from the serial port automatically adds a newline, so add \n here
        Serial.println("\nExecuting command: "+temp+"\n");        if(temp=="TEST\n")        {          checkEmails();        }    }}
// WiFi connection
void connectWiFi() {  WiFi.disconnect(); // First close any existing WiFi connections  WiFi.mode(WIFI_STA); // Set working mode, there are three modes: WIFI_AP (hotspot mode), WIFI_STA (station mode), WIFI_AP_STA (hotspot + station mode)  WiFi.begin(wifi_ssid,wifi_pwd);  while (WiFi.status() != WL_CONNECTED) {    delay(500);    Serial.print(".");    digitalWrite(STATUS_LED,!digitalRead(STATUS_LED));  }  // Print connection information  Serial.println("\nWiFi connected");  Serial.print("IP address: ");  Serial.println(WiFi.localIP());  digitalWrite(STATUS_LED, HIGH);}
// Initialize secure connection
void initNetworkClients() {  smtpClient.setInsecure();  // In production, certificates should be configured  imapClient.setInsecure();}
// IMAP receiving module================================================
void checkEmails() {  if (!imapClient.connect(IMAP_SERVER, IMAP_PORT))   {    Serial.println("imap server link fail!!!");  }     String response;   sendIMAPCommand("A01 LOGIN " + String(EMAIL_USER) + " " + EMAIL_PASS);  delay(1000);  response = readIMAPResponse();  Serial.print(response);     sendIMAPCommand("A02 STATUS INBOX (MESSAGES)"); // A02 STATUS INBOX (MESSAGES)  delay(1000);  response = readIMAPResponse();  Serial.print(response);    sendIMAPCommand("A03 SELECT INBOX");  delay(1000);  response = readIMAPResponse();  Serial.print(response);    sendIMAPCommand("A04 UID SEARCH  FROM "+String(FROM_USER)+" UNSEEN"); // ALL UNSEEN     //sendIMAPCommand("A04 UID SEARCH UNSEEN");//ALL UNSEEN  delay(1000);   response = readIMAPResponse(); // Return format * SEARCH 157 158  Serial.print(response);  vector<int> mailIDs=parseMailIDs(response);  for (int id : mailIDs) {    sendIMAPCommand("A05 UID FETCH "+String(id)+" BODY[HEADER.FIELDS(Subject)]");    delay(1000);     response = readIMAPResponse();    Serial.println(response);    if(response.indexOf("equipmentA-STATUSLED-ON")>5)    {      digitalWrite(STATUS_LED, LOW);      markAsRead(id); // Mark as read    }    if(response.indexOf("equipmentA-STATUSLED-OFF")>5)    {      digitalWrite(STATUS_LED, HIGH);      markAsRead(id); // Mark as read    }  }    sendIMAPCommand("A09 LOGOUT");  response = readIMAPResponse();  Serial.print(response);  imapClient.stop(); // This is essential, otherwise the second call to the function will not respond}
void markAsRead(int mailID) {  sendIMAPCommand("A06 UID STORE " + String(mailID) + " FLAGS (\Seen)");  //temp=readIMAPResponse();  //Serial.println("[IMAP]< " + temp);}
void sendIMAPCommand(String cmd) {  imapClient.println(cmd);  Serial.println("[IMAP] > " + cmd);}
String readIMAPResponse() {  String response;  while (imapClient.connected()) {    String line = imapClient.readStringUntil('\n');    response += line + "\n";    // After each command execution, the server returns a line like: A02 OK [READ-WRITE] SELECT complete    // Therefore, when reading a line starting with A0, it indicates that the data has been read completely, and we can break out of the loop. If this line is not added, the program will hang here.    // However, with this line, after executing each command, we must read the returned data, otherwise the results of the previous command will still be read after executing the next command.    // Also, be aware of synchronization issues; sometimes if commands are executed too quickly, the previous command may not have finished reading, which will affect the feedback information of the next command.    if (line.startsWith("A0")) break;  }  return response;}
std::vector<int> parseMailIDs(const String& response) {  std::vector<int> ids;  int startPos = response.indexOf("* SEARCH") + 8; // Skip the protocol prefix  if (startPos < 8) return ids; // No valid data found
  String idString = response.substring(startPos);  idString.trim(); // Remove leading and trailing spaces
  // Split the string  char* token = strtok(const_cast<char*>(idString.c_str()), " ");  while (token != nullptr) {    if (isdigit(token[0])) {      ids.push_back(atoi(token));    }    token = strtok(nullptr, " ");  }    return ids;}

6. Experimental Phenomenon

After uploading the program to the ESP32, we send an email to ourselves with the subject “equipmentA-STATUSLED-ON” and any random characters in the body. Here is a small tip: to achieve the characteristic of sporadic small data, try to keep the data volume small, so the email content can just be a few characters. In actual use, the content of the email is not read; only the subject is used. You can design your own control protocol, such as the format above: [Device Name – Status Light – Control Command]. After sending such an email to yourself, when the ESP32 retrieves this command from the mailbox, it can control the LED light to turn on or off.

ESP32 IMAP Protocol for Retrieving Commands or System Upgrade Data from a Specified Email

ESP32 IMAP Protocol for Retrieving Commands or System Upgrade Data from a Specified Email

Leave a Comment