I built a monitor for an old freezer in the garage out of an old NodeMCU I had and a DS18B20 temp sensor. It worked nice but I wanted to be able to request the temperature so I added a super simple web server to it. It just responds with a text string when the root page is requested.
It works, but only for a little while. After a little while the webserver and OTA both stop responding. The temperature probe still works and the heartbeat still blinks so the board isn't locking up. It also still sends an email if I warm up the probe, so it does have the network.
But going to the IP address with a browser or attempting an OTA upload fails for no response. If I reset the board then everything will work as normal for a little while.
Can anyone see where I messed up? I'm not terribly experienced using the network from these parts so I might have done something dumb.
/*
FreezerMonitor.ino
Copyright (C) 2023 David C.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Arduino.h"
#include <ESP8266WiFi.h>
#include <ArduinoOTA.h>
#include <ESP8266WebServer.h>
#include <ESP_Mail_Client.h>
#include "SecureDefine.h"
#include <OneWire.h>
#include <DallasTemperature.h>
/*****************************
******** WiFi Vars ***********
*****************************/
const char* ssid = WIFI_SSID;
const char* password = WIFI_PASSWORD;
const uint8_t HEART_PIN = D4;
uint32_t heartDelay = 250;
const uint8_t ONE_WIRE_BUS = D5;
uint32_t sensorReadInterval = 30000;
IPAddress ipa(192, 168, 1, 72);
IPAddress gate(192, 168, 1, 1);
IPAddress sub(255, 255, 255, 0);
IPAddress dns1(8, 8, 8, 8);
IPAddress dns2(8, 8, 4, 4);
/*****************************
******* Sensor Vars **********
*****************************/
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensor(&oneWire);
uint8_t sensorAddress[8] = { 0x28, 0xFF, 0xCE, 0x3A, 0x7E, 0x60, 0x1F, 0x06 };
float tempC;
float tempLimit = 0.0;
bool excursion = false;
/*****************************
******** Email Vars **********
*****************************/
SMTPSession smtp;
// Minimum time between mails.
unsigned long mailDelay = 3600000;
unsigned long lastMailTime = -mailDelay;
/* Callback function to get the Email sending status */
void smtpCallback(SMTP_Status status);
/* Declare the Session_Config for user defined session credentials */
Session_Config config;
/*****************************
******* Server Vars **********
*****************************/
ESP8266WebServer server(80);
void handleRoot();
void handleNotFound();
/*****************************
******** Functions ***********
*****************************/
void setup() {
pinMode(HEART_PIN, OUTPUT);
Serial.begin(115200);
Serial.println("Booting");
WiFi.mode(WIFI_STA);
WiFi.config(ipa, gate, sub, dns1, dns2);
WiFi.begin(ssid, password);
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println("Connection Failed! Rebooting...");
delay(5000);
ESP.restart();
}
WiFi.setAutoReconnect(true);
WiFi.persistent(true);
setupOTA();
sensor.begin();
setupMail();
Serial.println("Ready");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
server.on("/", handleRoot);
server.onNotFound(handleNotFound);
server.begin();
Serial.println("Server Started");
}
void loop() {
heartbeat();
handleSensor();
server.handleClient();
while(WiFi.status() != WL_CONNECTED){
heartDelay = 100;
heartbeat();
}
if (tempC >= tempLimit) {
if (!excursion) {
// If this is a new thing
Serial.println("Excursion");
excursion = true;
heartDelay = 500;
if (millis() - lastMailTime >= mailDelay) {
Serial.println("Sending Mail");
sendMessage();
lastMailTime = millis();
}
}
} else {
heartDelay = 2000;
excursion = false;
}
ArduinoOTA.handle();
}
/********************
Webserver Callbacks
********************/
void handleRoot() {
char response[40];
char tempbuf[10];
dtostrf(tempC, 3, 1, tempbuf);
sprintf(response, "Freezer Temp is %s C", tempbuf);
server.send(200, "text/plain", response);
}
void handleNotFound() {
server.send(404, "text/plain", "404: Page Not Found!");
}
/*
Gets temperture from sensor to global tempC variable
*/
void handleSensor() {
// Set previous millis to past so sensor will read immediately at startup
// to prevent a spurrious report at startup.
static uint32_t pm = 0 - sensorReadInterval;
uint32_t cm = millis();
if (cm - pm >= sensorReadInterval) {
sensor.requestTemperatures();
Serial.print("Temp Is : ");
tempC = sensor.getTempC(sensorAddress);
Serial.println(tempC);
pm = cm;
}
}
/*
Blinks onboard LED to show status
0.25Hz = Normal
1Hz = Alarm
5Hz = Lost Connection
*/
void heartbeat() {
static uint32_t pm = millis();
uint32_t cm = millis();
static boolean state = true;
if (cm - pm >= heartDelay) {
pm = cm;
state = !state;
digitalWrite(HEART_PIN, state ? HIGH : LOW);
}
}
/*
Sends email message on freezer alarm
*/
void sendMessage() {
/* Declare the message class */
SMTP_Message message;
/* Set the message headers */
message.sender.name = F("ESP Mail");
message.sender.email = AUTHOR_EMAIL;
message.subject = F("Freezer Alarm!");
message.addRecipient(F("Someone"), RECIPIENT_EMAIL);
message.addRecipient("S", EMAIL_S);
message.addRecipient("D", EMAIL_D);
char textMsg[40];
char tempbuf[10];
dtostrf(tempC, 3, 1, tempbuf);
sprintf(textMsg, "Freezer is over temperature %s C", tempbuf);
message.text.content = textMsg;
message.text.charSet = F("us-ascii");
message.text.transfer_encoding = Content_Transfer_Encoding::enc_7bit;
message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_low;
message.addHeader(F("Message-ID: <abcde.fghij@gmail.com>"));
/* Connect to the server */
if (!smtp.connect(&config)) {
MailClient.printf("Connection error, Status Code: %d, Error Code: %d, Reason: %s\n", smtp.statusCode(), smtp.errorCode(), smtp.errorReason().c_str());
return;
}
if (!smtp.isLoggedIn()) {
Serial.println("Not yet logged in.");
} else {
if (smtp.isAuthenticated())
Serial.println("Successfully logged in.");
else
Serial.println("Connected with no Auth.");
}
/* Start sending Email and close the session */
if (!MailClient.sendMail(&smtp, &message))
MailClient.printf("Error, Status Code: %d, Error Code: %d, Reason: %s\n", smtp.statusCode(), smtp.errorCode(), smtp.errorReason().c_str());
}
/*
Call from setup to start email
*/
void setupMail() {
/* Set the network reconnection option */
MailClient.networkReconnect(true);
/** Enable the debug via Serial port
* 0 for no debugging
* 1 for basic level debugging
*
* Debug port can be changed via ESP_MAIL_DEFAULT_DEBUG_PORT in ESP_Mail_FS.h
*/
smtp.debug(0);
/* Set the callback function to get the sending results */
smtp.callback(smtpCallback);
/* Set the session config */
config.server.host_name = SMTP_HOST;
config.server.port = SMTP_PORT;
config.login.email = AUTHOR_EMAIL;
config.login.password = AUTHOR_PASSWORD;
config.login.user_domain = F("127.0.0.1");
config.time.ntp_server = F("time.nist.gov");
config.time.gmt_offset = 3;
config.time.day_light_offset = 0;
}
/*
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("----------------");
MailClient.printf("Message sent success: %d\n", status.completedCount());
MailClient.printf("Message sent failed: %d\n", status.failedCount());
Serial.println("----------------\n");
for (size_t i = 0; i < smtp.sendingResult.size(); i++) {
/* Get the result item */
SMTP_Result result = smtp.sendingResult.getItem(i);
MailClient.printf("Message No: %d\n", i + 1);
MailClient.printf("Status: %s\n", result.completed ? "success" : "failed");
MailClient.printf("Date/Time: %s\n", MailClient.Time.getDateTimeString(result.timestamp, "%B %d, %Y %H:%M:%S").c_str());
MailClient.printf("Recipient: %s\n", result.recipients.c_str());
MailClient.printf("Subject: %s\n", result.subject.c_str());
}
Serial.println("----------------\n");
// You need to clear sending result as the memory usage will grow up.
smtp.sendingResult.clear();
}
}
/*
OTA setup
*/
void setupOTA() {
ArduinoOTA.setPort(8266);
ArduinoOTA.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH) {
type = "sketch";
} else { // U_FS
type = "filesystem";
}
// NOTE: if updating FS this would be the place to unmount FS using FS.end()
Serial.println("Start updating " + type);
});
ArduinoOTA.onEnd([]() {
Serial.println("\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.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();
}