-
-
Save m1cr0lab/365858ec1ac4976c2efedf2a11f7777b to your computer and use it in GitHub Desktop.
/********* | |
Rui Santos | |
Complete project details at https://RandomNerdTutorials.com/esp32-email-alert-temperature-threshold/ | |
Permission is hereby granted, free of charge, to any person obtaining a copy | |
of this software and associated documentation files. | |
The above copyright notice and this permission notice shall be included in all | |
copies or substantial portions of the Software. | |
*********/ | |
#include <WiFi.h> | |
#include <AsyncTCP.h> | |
#include <ESPAsyncWebServer.h> | |
#include <OneWire.h> | |
#include <DallasTemperature.h> | |
#include "ESP32_MailClient.h" | |
// REPLACE WITH YOUR NETWORK CREDENTIALS | |
const char* ssid = "REPLACE_WITH_YOUR_SSID"; | |
const char* password = "REPLACE_WITH_YOUR_PASSWORD"; | |
// To send Email using Gmail use port 465 (SSL) and SMTP Server smtp.gmail.com | |
// YOU MUST ENABLE less secure app option https://myaccount.google.com/lesssecureapps?pli=1 | |
#define emailSenderAccount "example_sender_account@gmail.com" | |
#define emailSenderPassword "email_sender_password" | |
#define smtpServer "smtp.gmail.com" | |
#define smtpServerPort 465 | |
#define emailSubject "[ALERT] ESP32 Temperature" | |
// Default Recipient Email Address | |
String inputMessage = "your_email_recipient@gmail.com"; | |
String enableEmailChecked = "checked"; | |
String inputMessage2 = "true"; | |
// Default Temperature Thresholds | |
String inputMessage3 = "25.0"; | |
String inputMessage4 = "6.0"; | |
String lastTemperature; | |
// HTML web page to handle 3 input fields (email_input, enable_email_input, threshold_input) | |
const char index_html[] PROGMEM = R"rawliteral( | |
<!DOCTYPE HTML><html><head> | |
<title>Email Notification with Temperature</title> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
</head><body> | |
<h2>DS18B20 Temperature</h2> | |
<h3>%TEMPERATURE% °C</h3> | |
<h2>ESP Email Notification</h2> | |
<form action="/get"> | |
Email Address <input type="email" name="email_input" value="%EMAIL_INPUT%" required><br> | |
Enable Email Notification <input type="checkbox" name="enable_email_input" value="true" %ENABLE_EMAIL%><br> | |
Upper Temperature Threshold <input type="number" step="0.1" name="upper_threshold_input" value="%UPPER_THRESHOLD%" required><br> | |
Lower Temperature Threshold <input type="number" step="0.1" name="lower_threshold_input" value="%LOWER_THRESHOLD%" required><br> | |
<input type="submit" value="Submit"> | |
</form> | |
</body></html>)rawliteral"; | |
void notFound(AsyncWebServerRequest *request) { | |
request->send(404, "text/plain", "Not found"); | |
} | |
AsyncWebServer server(80); | |
// Replaces placeholder with DS18B20 values | |
String processor(const String& var){ | |
//Serial.println(var); | |
if(var == "TEMPERATURE"){ | |
return lastTemperature; | |
} | |
else if(var == "EMAIL_INPUT"){ | |
return inputMessage; | |
} | |
else if(var == "ENABLE_EMAIL"){ | |
return enableEmailChecked; | |
} | |
else if(var == "UPPER_THRESHOLD"){ | |
return inputMessage3; | |
} | |
else if(var == "LOWER_THRESHOLD"){ | |
return inputMessage4; | |
} | |
return String(); | |
} | |
// Flag variable to keep track if email notification was sent or not | |
bool emailSent = false; | |
const char* PARAM_INPUT_1 = "email_input"; | |
const char* PARAM_INPUT_2 = "enable_email_input"; | |
const char* PARAM_INPUT_3 = "upper_threshold_input"; | |
const char* PARAM_INPUT_4 = "lower_threshold_input"; | |
// Interval between sensor readings. Learn more about timers: https://RandomNerdTutorials.com/esp32-pir-motion-sensor-interrupts-timers/ | |
unsigned long previousMillis = 0; | |
const long interval = 5000; | |
// GPIO where the DS18B20 is connected to | |
const int oneWireBus = 4; | |
// Setup a oneWire instance to communicate with any OneWire devices | |
OneWire oneWire(oneWireBus); | |
// Pass our oneWire reference to Dallas Temperature sensor | |
DallasTemperature sensors(&oneWire); | |
// The Email Sending data object contains config and data to send | |
SMTPData smtpData; | |
void setup() { | |
Serial.begin(115200); | |
WiFi.mode(WIFI_STA); | |
WiFi.begin(ssid, password); | |
if (WiFi.waitForConnectResult() != WL_CONNECTED) { | |
Serial.println("WiFi Failed!"); | |
return; | |
} | |
Serial.println(); | |
Serial.print("ESP IP Address: http://"); | |
Serial.println(WiFi.localIP()); | |
// Start the DS18B20 sensor | |
sensors.begin(); | |
// Send web page to client | |
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ | |
request->send_P(200, "text/html", index_html, processor); | |
}); | |
// Receive an HTTP GET request at <ESP_IP>/get?email_input=<inputMessage>&enable_email_input=<inputMessage2>&threshold_input=<inputMessage3> | |
server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) { | |
// GET email_input value on <ESP_IP>/get?email_input=<inputMessage> | |
if (request->hasParam(PARAM_INPUT_1)) { | |
inputMessage = request->getParam(PARAM_INPUT_1)->value(); | |
// GET enable_email_input value on <ESP_IP>/get?enable_email_input=<inputMessage2> | |
if (request->hasParam(PARAM_INPUT_2)) { | |
inputMessage2 = request->getParam(PARAM_INPUT_2)->value(); | |
enableEmailChecked = "checked"; | |
} | |
else { | |
inputMessage2 = "false"; | |
enableEmailChecked = ""; | |
} | |
// GET threshold_input value on <ESP_IP>/get?upper_threshold_input=<inputMessage3> | |
if (request->hasParam(PARAM_INPUT_3)) { | |
inputMessage3 = request->getParam(PARAM_INPUT_3)->value(); | |
} | |
// GET threshold_input value on <ESP_IP>/get?lower_threshold_input=<inputMessage3> | |
if (request->hasParam(PARAM_INPUT_4)) { | |
inputMessage4 = request->getParam(PARAM_INPUT_4)->value(); | |
} | |
} | |
else { | |
inputMessage = "No message sent"; | |
} | |
Serial.println(inputMessage); | |
Serial.println(inputMessage2); | |
Serial.println(inputMessage3); | |
Serial.println(inputMessage4); | |
request->send(200, "text/html", "HTTP GET request sent to your ESP.<br><a href=\"/\">Return to Home Page</a>"); | |
}); | |
server.onNotFound(notFound); | |
server.begin(); | |
} | |
void loop() { | |
unsigned long currentMillis = millis(); | |
if (currentMillis - previousMillis >= interval) { | |
previousMillis = currentMillis; | |
sensors.requestTemperatures(); | |
// Temperature in Celsius degrees | |
float temperature = sensors.getTempCByIndex(0); | |
Serial.print(temperature); | |
Serial.println(" *C"); | |
// Temperature in Fahrenheit degrees | |
/*float temperature = sensors.getTempFByIndex(0); | |
SerialMon.print(temperature); | |
SerialMon.println(" *F");*/ | |
lastTemperature = String(temperature); | |
// Check if temperature is above threshold and if it needs to send the Email alert | |
if(temperature > inputMessage3.toFloat() && inputMessage2 == "true" && !emailSent){ | |
String emailMessage = String("Temperature above threshold. Current temperature: ") + | |
String(temperature) + String("C"); | |
if(sendEmailNotification(emailMessage)) { | |
Serial.println(emailMessage); | |
emailSent = true; | |
} | |
else { | |
Serial.println("Email failed to send"); | |
} | |
} | |
// Check if temperature is below threshold and if it needs to send the Email alert | |
else if((temperature < inputMessage4.toFloat()) && inputMessage2 == "true" && emailSent) { | |
String emailMessage = String("Temperature below threshold. Current temperature: ") + | |
String(temperature) + String(" C"); | |
if(sendEmailNotification(emailMessage)) { | |
Serial.println(emailMessage); | |
emailSent = false; | |
} | |
else { | |
Serial.println("Email failed to send"); | |
} | |
} | |
} | |
} | |
bool sendEmailNotification(String emailMessage){ | |
// Set the SMTP Server Email host, port, account and password | |
smtpData.setLogin(smtpServer, smtpServerPort, emailSenderAccount, emailSenderPassword); | |
// For library version 1.2.0 and later which STARTTLS protocol was supported,the STARTTLS will be | |
// enabled automatically when port 587 was used, or enable it manually using setSTARTTLS function. | |
//smtpData.setSTARTTLS(true); | |
// Set the sender name and Email | |
smtpData.setSender("ESP32", emailSenderAccount); | |
// Set Email priority or importance High, Normal, Low or 1 to 5 (1 is highest) | |
smtpData.setPriority("High"); | |
// Set the subject | |
smtpData.setSubject(emailSubject); | |
// Set the message with HTML format | |
smtpData.setMessage(emailMessage, true); | |
// Add recipients | |
smtpData.addRecipient(inputMessage); | |
smtpData.setSendCallback(sendCallback); | |
// Start sending Email, can be set callback function to track the status | |
if (!MailClient.sendMail(smtpData)) { | |
Serial.println("Error sending Email, " + MailClient.smtpErrorReason()); | |
return false; | |
} | |
// Clear all data from Email object to free memory | |
smtpData.empty(); | |
return true; | |
} | |
// Callback function to get the Email sending status | |
void sendCallback(SendStatus msg) { | |
// Print the current status | |
Serial.println(msg.info()); | |
// Do something when complete | |
if (msg.success()) { | |
Serial.println("----------------"); | |
} | |
} |
Hello,
Try to declare your sendEmailNotification()
function before the loop()
function. Compilation should go better.
Hello,
first of all, thank you for replying to my question. its been hard and i have been trying for so many hours to make the code work.
i have transferred the sendemailnotification() function before the loop() together with the sendcallback() however now im getting another set of error that i cant figure out again.
i tried putting back the sendcallback() after the loop() but same error occurs.
please see error below:
Hi,
Generally speaking, in C and C++, you must declare a function before calling or designating it. So, since the sendCallback
function is used by the sendEmailNotification
function, you must define sendCallback
before sendEmailNotification
.
When I was still using the Arduino IDE, it was not necessary when all the code was in the .ino
file. But maybe this has changed with the new versions. And if you use a development environment like PlatformIO, it's absolutely necessary.
If you still want to define your function after it is used, you can still do so as long as you add a forward declaration of the function's signature before it is used.
Hoping this might help you.
I took the general framework of Rui's code so as not to disturb Random Nerd Tutorials readers and inserted just enough to add a second temperature threshold... but I personally wouldn't have written it that way ;-)
Hi,
Thank you so much for explaining this. I am new to arduino. c and c++ i have little experience with c# and I was really confused by how the declaration works/calling the functions. I was able to make it work now! I am planning on using VS Code with arduino plugin next time it might be easier :)
I'm glad you made it.
Once you have installed Visual Studio Code, instead of using the Arduino plugin, just try PlatformIO IDE, it's perfectly integrated with Visual Studio Code, and it's much better! ;-)
thank you! will try that instead!
In the sketch above how do you make it send a email of the temperature everyday at a specific time? Thanks
Hi jbeard125,
You will have to deal with time management on ESP32. To do this, I encourage you to consult the following tutorial:
ESP32 NTP Client-Server: Get Date and Time (Arduino IDE)
This was not the purpose of this gist, which was meant to be a simple extension of Rui's code to manage two temperature thresholds. So I refer you to the articles published on Random Nerd Tutorials, where you will find the recipes you are interested in.
Ok thank you for the information. I'm trying to take two of Rui's sketchs, the one you have above on two temperature thresholds and another one on controlling gpio's on a web server. When I combine the two I keep getting errors. How would I take the sketch you modified and insert a web server to control gpio pins?
For remote control of the ESP32, I suggest you read my tutorial on the subject:
ESP32 Remote Control with WebSocket
You should find the methodology to approach this type of problem and adapt it to your project.
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include "ESP32_MailClient.h"
// REPLACE WITH YOUR NETWORK CREDENTIALS
const char* ssid = "Home Wifi";
const char* password = "123456";
// To send Email using Gmail use port 465 (SSL) and SMTP Server smtp.gmail.com
// YOU MUST ENABLE less secure app option https://myaccount.google.com/lesssecureapps?pli=1
#define emailSenderAccount "building"
#define emailSenderPassword "123456"
#define smtpServer "smtp.gmail.com"
#define smtpServerPort 465
#define emailSubject "[ALERT] Building Loop Temperature"
// Default Recipient Email Address
String inputMessage = "lp14255@gmail.com";
String enableEmailChecked = "checked";
String inputMessage2 = "true";
// Default Temperature Thresholds
String inputMessage3 = "75";
String inputMessage4 = "72";
String lastTemperature;
// HTML web page to handle 3 input fields (email_input, enable_email_input, threshold_input)
const char index_html[] PROGMEM = R"rawliteral(
DS18B20 Temperature
%TEMPERATURE% °C
ESP Email Notification
Email AddressEnable Email Notification
Upper Temperature Threshold
Lower Temperature Threshold
Building Controls
%BUTTONPLACEHOLDER% <script>function toggleCheckbox(element) { var xhr = new XMLHttpRequest(); if(element.checked){ xhr.open("GET", "/update?output="+element.id+"&state=1", true); } else { xhr.open("GET", "/update?output="+element.id+"&state=0", true); } xhr.send(); } )rawliteral";void notFound(AsyncWebServerRequest *request) {
request->send(404, "text/plain", "Not found");
}
AsyncWebServer server(80);
// Replaces placeholder with DS18B20 values
String processor(const String& var){
//Serial.println(var);
if(var == "TEMPERATURE"){
return lastTemperature;
}
else if(var == "EMAIL_INPUT"){
return inputMessage;
}
else if(var == "ENABLE_EMAIL"){
return enableEmailChecked;
}
else if(var == "UPPER_THRESHOLD"){
return inputMessage3;
}
else if(var == "LOWER_THRESHOLD"){
return inputMessage4;
}
// Replaces placeholder with button section in your web page
if(var == "BUTTONPLACEHOLDER"){
String buttons = "";
buttons += "
Output - GPIO 2
<label class="switch"><input type="checkbox" onchange="toggleCheckbox(this)" id="2" " + outputState(2) + "><span class="slider">";buttons += "
Output - GPIO 4
<label class="switch"><input type="checkbox" onchange="toggleCheckbox(this)" id="4" " + outputState(4) + "><span class="slider">";buttons += "
Output - GPIO 33
<label class="switch"><input type="checkbox" onchange="toggleCheckbox(this)" id="33" " + outputState(33) + "><span class="slider">";return buttons;
}
return String();
}
String outputState(int output){
if(digitalRead(output)){
return "checked";
}
else {
return "";
}
}
// Flag variable to keep track if email notification was sent or not
bool emailSent = false;
const char* PARAM_INPUT_1 = "email_input";
const char* PARAM_INPUT_2 = "enable_email_input";
const char* PARAM_INPUT_3 = "upper_threshold_input";
const char* PARAM_INPUT_4 = "lower_threshold_input";
const char* PARAM_INPUT_5 = "output";
const char* PARAM_INPUT_6 = "state";
// Interval between sensor readings. Learn more about timers: https://RandomNerdTutorials.com/esp32-pir-motion-sensor-interrupts-timers/
unsigned long previousMillis = 0;
const long interval = 5000;
// GPIO where the DS18B20 is connected to
const int oneWireBus = 4;
// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(oneWireBus);
// Pass our oneWire reference to Dallas Temperature sensor
DallasTemperature sensors(&oneWire);
// The Email Sending data object contains config and data to send
SMTPData smtpData;
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println("WiFi Failed!");
return;
}
Serial.println();
Serial.print("ESP IP Address: http://");
Serial.println(WiFi.localIP());
pinMode(2, OUTPUT);
digitalWrite(2, LOW);
pinMode(4, OUTPUT);
digitalWrite(4, LOW);
pinMode(33, OUTPUT);
digitalWrite(33, LOW);
// Start the DS18B20 sensor
sensors.begin();
// Send web page to client
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/html", index_html, processor);
});
// Receive an HTTP GET request at <ESP_IP>/get?email_input=&enable_email_input=&threshold_input=
server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) {
// GET email_input value on <ESP_IP>/get?email_input=
if (request->hasParam(PARAM_INPUT_1)) {
inputMessage = request->getParam(PARAM_INPUT_1)->value();
// GET enable_email_input value on <ESP_IP>/get?enable_email_input=
if (request->hasParam(PARAM_INPUT_2)) {
inputMessage2 = request->getParam(PARAM_INPUT_2)->value();
enableEmailChecked = "checked";
}
else {
inputMessage2 = "false";
enableEmailChecked = "";
}
// GET threshold_input value on <ESP_IP>/get?upper_threshold_input=
if (request->hasParam(PARAM_INPUT_3)) {
inputMessage3 = request->getParam(PARAM_INPUT_3)->value();
}
// GET threshold_input value on <ESP_IP>/get?lower_threshold_input=
if (request->hasParam(PARAM_INPUT_4)) {
inputMessage4 = request->getParam(PARAM_INPUT_4)->value();
}
}
else {
inputMessage = "No message sent";
}
// Send a GET request to <ESP_IP>/update?output=&state=
server.on("/update", HTTP_GET, [] (AsyncWebServerRequest *request) {
String inputMessage5;
String inputMessage6;
// GET input5 value on <ESP_IP>/update?output=&state=
if (request->hasParam(PARAM_INPUT_5) && request->hasParam(PARAM_INPUT_6)) {
inputMessage5 = request->getParam(PARAM_INPUT_5)->value();
inputMessage6 = request->getParam(PARAM_INPUT_6)->value();
digitalWrite(inputMessage5.toInt(), inputMessage6.toInt());
}
else {
inputMessage5 = "No message sent";
inputMessage6 = "No message sent";
}
Serial.println(inputMessage);
Serial.println(inputMessage2);
Serial.println(inputMessage3);
Serial.println(inputMessage4);
Serial.print("GPIO: ");
Serial.print(inputMessage5);
Serial.print(" - Set to: ");
Serial.println(inputMessage6);
request->send(200, "text/html", "HTTP GET request sent to your ESP.<br><a href=\"/\">Return to Home Page</a>");
});
server.onNotFound(notFound);
server.begin();
}
);
}
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
sensors.requestTemperatures();
// Temperature in Fahrenheit degrees
float temperature = sensors.getTempCByIndex(0);
Serial.print(temperature);
Serial.println(" *F");
// Temperature in Fahrenheit degrees
/*float temperature = sensors.getTempFByIndex(0);
SerialMon.print(temperature);
SerialMon.println(" *F");*/
lastTemperature = String(temperature);
// Check if temperature is above threshold and if it needs to send the Email alert
if(temperature > inputMessage3.toFloat() && inputMessage2 == "true" && !emailSent){
String emailMessage = String("Temperature above threshold. Current temperature: ") +
String(temperature) + String("F");
if(sendEmailNotification(emailMessage)) {
Serial.println(emailMessage);
emailSent = true;
}
else {
Serial.println("Email failed to send");
}
}
// Check if temperature is below threshold and if it needs to send the Email alert
else if((temperature < inputMessage4.toFloat()) && inputMessage2 == "true" && emailSent) {
String emailMessage = String("Temperature below threshold. Current temperature: ") +
String(temperature) + String(" F");
if(sendEmailNotification(emailMessage)) {
Serial.println(emailMessage);
emailSent = false;
}
else {
Serial.println("Email failed to send");
}
}
}
}
bool sendEmailNotification(String emailMessage){
// Set the SMTP Server Email host, port, account and password
smtpData.setLogin(smtpServer, smtpServerPort, emailSenderAccount, emailSenderPassword);
// For library version 1.2.0 and later which STARTTLS protocol was supported,the STARTTLS will be
// enabled automatically when port 587 was used, or enable it manually using setSTARTTLS function.
//smtpData.setSTARTTLS(true);
// Set the sender name and Email
smtpData.setSender("ESP32", emailSenderAccount);
// Set Email priority or importance High, Normal, Low or 1 to 5 (1 is highest)
smtpData.setPriority("High");
// Set the subject
smtpData.setSubject(emailSubject);
// Set the message with HTML format
smtpData.setMessage(emailMessage, true);
// Add recipients
smtpData.addRecipient(inputMessage);
smtpData.setSendCallback(sendCallback);
// Start sending Email, can be set callback function to track the status
if (!MailClient.sendMail(smtpData)) {
Serial.println("Error sending Email, " + MailClient.smtpErrorReason());
return false;
}
// Clear all data from Email object to free memory
smtpData.empty();
return true;
}
// Callback function to get the Email sending status
void sendCallback(SendStatus msg) {
// Print the current status
Serial.println(msg.info());
// Do something when complete
if (msg.success()) {
Serial.println("----------------");
server.begin();
}
}
m1cr0lab
i was able to take rui's two sketchs and compile them correctly , but when i type in the ip address it say it cant find it. can you look over my sketch and see whats wrong. thank you.
Hi Stephane,
i was wondering if you can help me combine this with the code i got from RUI i want to be able to send sms and email. i tried combining the code but to no avail. please see pastebin link: of my current code: https://pastebin.com/ZimY4FEU
i keep on getting this error: hope you can help me out