17.08.2024, 14:38
(17.08.2024, 08:00)CigarNerd schrieb: Schönes Hygrometer das du da gebastelt hast.
Verwendest du die Heizung des SHT4x?
Ich kann das nur empfehlen, da der Sensor sonst wegdriftet: https://zigarren-community.de/thread-177...l#pid73822
Die Heitzungsfunktionen sind im Code implementiert, ich habe sie aber noch nicht eingesetzt, da ich mich gerade erst mit der Frage auseinandersetze, welchen Effekt die Heitzung genau hat.
Du hattest dich mit dem Thema schon auseinander gesetzt, oder?
Für interessierte hier der Code in Version 0.96:
Code:
/*********
Various code snipped frankensteined together with a lot of try & error by Crix1990.
Modded for Humidor use by Crix1990.
*********/
// Load Wi-Fi library
#include <ESP8266WiFi.h>
#include <Wire.h>
#include "Adafruit_SHT4x.h"
#include <Arduino.h>
#include <U8g2lib.h>
#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif
#include <ESP_Mail_Client.h>
#include <time.h> // time() ctime()
time_t now; // this is the epoch
tm myTimeInfo; // the structure tm holds time information in a more convient way
Adafruit_SHT4x sht4 = Adafruit_SHT4x();
#define SDA_PIN 14 //GPIO14 / 5
#define SCL_PIN 12 //GPIO12 / 6
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, SCL_PIN, SDA_PIN, U8X8_PIN_NONE);
int intervalOledRun = 10000;
unsigned long previousOledRun = 0;
String HumidityPrint = "Humidity:";
String TemperaturePrint = "Temperature:";
//Humidor Setup
//Name you Humidor:
String HumidorName = "Tisch Humidor";
//Define target humidity
double TargetHumidity = 69;
//Define your humidity tolerance (1 = 1% tolerance, 2 = 2% tolerance, etc.)
double TargetTolerance = 5;
//Define your Mail delay in Miliseconds
int MailDelay = 300000;
// Replace with your network credentials
const char* ssid = "WIFINAME";
const char* password = "WIFIPW";
// Configure NTP services
const char* ntpServer = "10.10.70.1";
const long gmtOffset_sec = 1;
const int daylightOffset_sec = 7200;
//Configure mail credentials
#define RECIPIENT_EMAIL "REC@MAIL.de"
#define AUTHOR_EMAIL "SEND@MAIL.de"
#define AUTHOR_PASSWORD "MAILPW"
#define SMTP_HOST "smtp.xxx.de"
#define SMTP_PORT 465
// Set web server port number to 80
WiFiServer server(80);
// Variable to store the HTTP request
String header;
// Declare the global used SMTPSession object for SMTP transport
SMTPSession smtp;
// Declare global message class
SMTP_Message message;
unsigned long previousMillis = 0;
unsigned long AlarmStateMillis = 0;
unsigned long currentMillis = 0;
const long interval = 60000; // 30 seconds
//Alert states
int AlarmState = 0;
int AlarmStateOld = 0;
// Declare the Session_Config
// GLOBAL for user defined session credentials */
Session_Config config;
void setup() {
Serial.begin(115200);
bool status;
PrintFileNameDateTime();
// default settings
Serial.println("Adafruit SHT4x test");
if (! sht4.begin()) {
Serial.println("Couldn't find SHT4x");
while (1) delay(1);
}
Serial.println("Found SHT4x sensor");
Serial.print("Serial number 0x");
Serial.println(sht4.readSerial(), HEX);
// You can have 3 different precisions, higher precision takes longer
sht4.setPrecision(SHT4X_HIGH_PRECISION);
switch (sht4.getPrecision()) {
case SHT4X_HIGH_PRECISION:
Serial.println("High precision");
break;
case SHT4X_MED_PRECISION:
Serial.println("Med precision");
break;
case SHT4X_LOW_PRECISION:
Serial.println("Low precision");
break;
}
// You can have 6 different heater settings
// higher heat and longer times uses more power
// and reads will take longer too!
sht4.setHeater(SHT4X_NO_HEATER);
switch (sht4.getHeater()) {
case SHT4X_NO_HEATER:
Serial.println("No heater");
break;
case SHT4X_HIGH_HEATER_1S:
Serial.println("High heat for 1 second");
break;
case SHT4X_HIGH_HEATER_100MS:
Serial.println("High heat for 0.1 second");
break;
case SHT4X_MED_HEATER_1S:
Serial.println("Medium heat for 1 second");
break;
case SHT4X_MED_HEATER_100MS:
Serial.println("Medium heat for 0.1 second");
break;
case SHT4X_LOW_HEATER_1S:
Serial.println("Low heat for 1 second");
break;
case SHT4X_LOW_HEATER_100MS:
Serial.println("Low heat for 0.1 second");
break;
}
// Connect to Wi-Fi network with SSID and password
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// Print local IP address and start web server
Serial.println("");
Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
server.begin();
u8g2.begin();
u8g2.enableUTF8Print();
synchroniseWith_NTP_Time();
MailClient.networkReconnect(true);
setupEmailConfig();
sendEmail();
AlarmState = 1;
}
void loop(){
currentMillis = millis();
sensors_event_t humidity, temp;
sht4.getEvent(&humidity, &temp);// populate temp and humidity objects with fresh data
if(currentMillis - previousOledRun >= intervalOledRun) {
//Display Configuration
HumidityPrint = "Hum: " + String(humidity.relative_humidity) + " %";
TemperaturePrint = "Temp: " + String(temp.temperature) + " °C";
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_t0_16_tf);
if (humidity.relative_humidity < TargetHumidity - TargetTolerance) {
u8g2.drawButtonUTF8(0, 13, U8G2_BTN_INV, 0, 2, 2, HumidorName.c_str());
u8g2.drawButtonUTF8(0, 36, U8G2_BTN_INV, 0, 2, 5, TemperaturePrint.c_str());
u8g2.drawButtonUTF8(0, 60, U8G2_BTN_INV, 0, 2, 5, HumidityPrint.c_str());
}
else if (humidity.relative_humidity > TargetHumidity + TargetTolerance) {
u8g2.drawButtonUTF8(0, 13, U8G2_BTN_INV, 0, 2, 2, HumidorName.c_str());
u8g2.drawButtonUTF8(0, 36, U8G2_BTN_INV, 0, 2, 5, TemperaturePrint.c_str());
u8g2.drawButtonUTF8(0, 60, U8G2_BTN_INV, 0, 2, 5, HumidityPrint.c_str());
}
else {
u8g2.drawUTF8(0,13,HumidorName.c_str());
u8g2.drawUTF8(0,36,TemperaturePrint.c_str());
u8g2.drawUTF8(0,60,HumidityPrint.c_str());
}
u8g2.sendBuffer();
}
currentMillis = millis();
if(currentMillis - previousOledRun >= intervalOledRun) {
}
delay(1000);
//Mail Config
currentMillis = millis();
//Too Low
if (humidity.relative_humidity < TargetHumidity - TargetTolerance) {
if (AlarmState == 2){
}
else if (AlarmState == 20) {
if (currentMillis >= AlarmStateMillis + MailDelay || currentMillis < AlarmStateMillis) {
AlarmState = 2;
sendEmail();
}
}
else if (AlarmState == 10 && AlarmStateOld == 2) {
AlarmState = 2;
}
else {
AlarmState = 20;
AlarmStateMillis = currentMillis;
}
}
//Too High
else if (humidity.relative_humidity > TargetHumidity + TargetTolerance) {
if (AlarmState == 3){
}
else if (AlarmState == 30) {
if (currentMillis >= AlarmStateMillis + MailDelay || currentMillis < AlarmStateMillis) {
AlarmState = 3;
sendEmail();
}
}
else if (AlarmState == 10 && AlarmStateOld == 3) {
AlarmState = 3;
}
else {
AlarmState = 30;
AlarmStateMillis = currentMillis;
}
}
//Normal
else {
if (AlarmState == 20 || AlarmState == 30) {
AlarmState = 1;
AlarmStateMillis = currentMillis;
}
else if (AlarmState == 2 || AlarmState == 3) {
AlarmStateOld = AlarmState;
AlarmState = 10;
AlarmStateMillis = currentMillis;
}
else if (AlarmState == 10) {
if (currentMillis >= AlarmStateMillis + 300000 || currentMillis < AlarmStateMillis) {
AlarmState = 1;
sendEmail();
}
}
}
WiFiClient client = server.available(); // Listen for incoming clients
if (client) { // If a new client connects,
Serial.println("New Client."); // print a message out in the serial port
String currentLine = ""; // make a String to hold incoming data from the client
while (client.connected()) { // loop while the client's connected
//Serial.println("1");
if (client.available()) { // if there's bytes to read from the client,
Serial.println("2");
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
header += c;
if (c == '\n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Connection: close");
client.println();
// Display the HTML web page
client.println("<!DOCTYPE html><html>");
// client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
client.println("<head><meta http-equiv=refresh content=1>");
client.println("<link rel=\"icon\" href=\"data:,\">");
// CSS to style the table
client.println("<style>body { text-align: center; font-family: \"Trebuchet MS\", Arial;}");
client.println("table { border-collapse: collapse; width:35%; margin-left:auto; margin-right:auto; }");
client.println("th { padding: 12px; background-color: #0043af; color: white; }");
client.println("tr { border: 1px solid #ddd; padding: 12px; }");
client.println("tr:hover { background-color: #0088FF; }");
client.println("td { border: none; padding: 12px; }");
if (humidity.relative_humidity < TargetHumidity - TargetTolerance) {
client.println(".sensor { color:black; font-weight: bold; background-color: #CC3333; padding: 1px; }");
}
else if (humidity.relative_humidity > TargetHumidity + TargetTolerance) {
client.println(".sensor { color:black; font-weight: bold; background-color: #CC3333; padding: 1px; }");
}
else {
client.println(".sensor { color:black; font-weight: bold; background-color: #FFFFFF; padding: 1px; }");
}
// Web Page Heading
client.println("</style></head><body><h1>");
client.println(HumidorName);
client.println("</h1>");
client.println("<table><tr><th>MEASUREMENT</th><th>VALUE</th></tr>");
client.println("<tr><td>Temp. Celsius</td><td><span class=\"sensor\">");
client.println(temp.temperature);
client.println(" *C</span></td></tr>");
client.println("<tr><td>Temp. Fahrenheit</td><td><span class=\"sensor\">");
client.println(1.8 * temp.temperature + 32);
client.println(" *F</span></td></tr>");
client.println("<tr><td>Humidity</td><td><span class=\"sensor\">");
client.println(humidity.relative_humidity);
client.println(" %</span></td></tr>");
client.println("<tr><td>Target Humidity</td><td><span class=\"sensor\">");
client.println(TargetHumidity);
client.println(" %</span></td></tr>");
client.println("<tr><td>Humidity Tolerance</td><td><span class=\"sensor\">");
client.println(TargetTolerance);
client.println(" %</span></td></tr>");
client.println("</body></html>");
// The HTTP response ends with another blank line
client.println();
// Break out of the while loop
Serial.println("3");
break;
} else { // if you got a newline, then clear currentLine
currentLine = "";
}
} else if (c != '\r') { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
}
}
// Clear the header variable
header = "";
// Close the connection
client.stop();
Serial.println("Client disconnected.");
Serial.println("");
}
}
void synchroniseWith_NTP_Time() {
Serial.print("configTime uses ntpServer ");
Serial.println(ntpServer);
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
Serial.print("synchronising time");
while (myTimeInfo.tm_year + 1900 < 2000 ) {
time(&now); // read the current time
localtime_r(&now, &myTimeInfo);
delay(100);
Serial.print(".");
}
Serial.print("\n time synchronsized \n");
showTime();
}
String getTimeString() {
unsigned long currentTime = millis(); // Get the current time in milliseconds
int seconds = (currentTime / 1000) % 60;
int minutes = (currentTime / 60000) % 60;
int hours = (currentTime / 3600000) % 24;
// Create a formatted time string
String timeStr = String(hours) + ":" + (minutes < 10 ? "0" : "") + String(minutes) + ":" + (seconds < 10 ? "0" : "") + String(seconds);
return timeStr;
}
void showTime() {
time(&now); // read the current time
localtime_r(&now, &myTimeInfo); // update the structure tm with the current time
Serial.print("year:");
Serial.print(myTimeInfo.tm_year + 1900); // years since 1900
Serial.print("\tmonth:");
Serial.print(myTimeInfo.tm_mon + 1); // January = 0 (!)
Serial.print("\tday:");
Serial.print(myTimeInfo.tm_mday); // day of month
Serial.print("\thour:");
Serial.print(myTimeInfo.tm_hour); // hours since midnight 0-23
Serial.print("\tmin:");
Serial.print(myTimeInfo.tm_min); // minutes after the hour 0-59
Serial.print("\tsec:");
Serial.print(myTimeInfo.tm_sec); // seconds after the minute 0-61*
Serial.print("\twday");
Serial.print(myTimeInfo.tm_wday); // days since Sunday 0-6
if (myTimeInfo.tm_isdst == 1) // Daylight Saving Time flag
Serial.print("\tDST");
else
Serial.print("\tstandard");
Serial.println();
}
void PrintFileNameDateTime() {
Serial.println( F("Code running comes from file ") );
Serial.println( F(__FILE__) );
Serial.print( F(" compiled ") );
Serial.print( F(__DATE__) );
Serial.print( F(" ") );
Serial.println( F(__TIME__) );
}
void setupEmailConfig() {
// 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(1);
// Set the 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 = "";
message.addRecipient(F("Me-myself-and-I"), RECIPIENT_EMAIL);
}
void sendEmail() {
sensors_event_t humidity, temp;
sht4.getEvent(&humidity, &temp);// populate temp and humidity objects with fresh data
// Set the message headers
String StyleBody = "";
String MessageSubject = "Humidor Control System of " + HumidorName + " just started.";
if (AlarmState == 0) {
MessageSubject = "Humidor Control System of " + HumidorName + " just started.";
StyleBody = "<style>body { text-align: center; font-family: \"Trebuchet MS\", Arial;}table { border-collapse: collapse; width:35%; margin-left:auto; margin-right:auto; }th { padding: 12px; background-color: #0043af; color: white; }tr { border: 1px solid #ddd; padding: 12px; }tr:hover { background-color: #0088FF; }td { border: none; padding: 12px; }.sensor { color:black; font-weight: bold; background-color: #FFFFFF; padding: 1px; }";
}
else if (AlarmState == 1) {
MessageSubject = HumidorName + " went back to normal conditions.";
StyleBody = "<style>body { text-align: center; font-family: \"Trebuchet MS\", Arial;}table { border-collapse: collapse; width:35%; margin-left:auto; margin-right:auto; }th { padding: 12px; background-color: #0043af; color: white; }tr { border: 1px solid #ddd; padding: 12px; }tr:hover { background-color: #0088FF; }td { border: none; padding: 12px; }.sensor { color:black; font-weight: bold; background-color: #FFFFFF; padding: 1px; }";
}
else if (AlarmState == 2) {
MessageSubject = "WARNING: " + HumidorName + " has too low humidity!";
StyleBody = "<style>body { text-align: center; font-family: \"Trebuchet MS\", Arial;}table { border-collapse: collapse; width:35%; margin-left:auto; margin-right:auto; }th { padding: 12px; background-color: #0043af; color: white; }tr { border: 1px solid #ddd; padding: 12px; }tr:hover { background-color: #0088FF; }td { border: none; padding: 12px; }.sensor { color:black; font-weight: bold; background-color: #CC3333; padding: 1px; }";
}
else if (AlarmState == 3) {
MessageSubject = "WARNING: " + HumidorName + " has too high humidity!";
StyleBody = "<style>body { text-align: center; font-family: \"Trebuchet MS\", Arial;}table { border-collapse: collapse; width:35%; margin-left:auto; margin-right:auto; }th { padding: 12px; background-color: #0043af; color: white; }tr { border: 1px solid #ddd; padding: 12px; }tr:hover { background-color: #0088FF; }td { border: none; padding: 12px; }.sensor { color:black; font-weight: bold; background-color: #CC3333; padding: 1px; }";
}
else {
MessageSubject = "Unknown Mail Reason " + String(AlarmState);
StyleBody = "<style>body { text-align: center; font-family: \"Trebuchet MS\", Arial;}table { border-collapse: collapse; width:35%; margin-left:auto; margin-right:auto; }th { padding: 12px; background-color: #0043af; color: white; }tr { border: 1px solid #ddd; padding: 12px; }tr:hover { background-color: #0088FF; }td { border: none; padding: 12px; }.sensor { color:black; font-weight: bold; background-color: #FFFFFF; padding: 1px; }";
}
message.sender.name = HumidorName;
message.sender.email = AUTHOR_EMAIL;
//Send HTML message
message.subject = MessageSubject;
String Mail1 = "</style></head><body><h1>" + HumidorName + "</h1><table><tr><th>MEASUREMENT</th><th>VALUE</th></tr><tr><td>Temp. Celsius</td><td><span class=\"sensor\">" + temp.temperature + " *C</span></td></tr><tr><td>Temp. Fahrenheit</td><td><span class=\"sensor\">" + 1.8 * temp.temperature + 32 + " *F</span></td></tr><tr><td>Humidity</td><td><span class=\"sensor\">" + humidity.relative_humidity;
String Mail2 = " %</span></td></tr><tr><td>Target Humidity</td><td><span class=\"sensor\">" + String(TargetHumidity) + " %</span></td></tr><tr><td>Humidity Tolerance</td><td><span class=\"sensor\">" + String(TargetTolerance) + " %</span></td></tr></body></html>";
String htmlMsg = StyleBody + Mail1 + Mail2;
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;
message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_high;
message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay;
// Connect to the server
if (!smtp.connect(&config)) {
ESP_MAIL_PRINTF("Connection error, Status Code: %d, Error Code: %d, Reason: %s", smtp.statusCode(), smtp.errorCode(), smtp.errorReason().c_str());
return;
}
Serial.println("smtp.connect(&config) done");
Serial.print("sending email to #");
Serial.print(RECIPIENT_EMAIL);
Serial.println("#");
/* Start sending Email and close the session */
if (!MailClient.sendMail(&smtp, &message)) {
Serial.println("error MailClient.sendMail(&smtp, &message)");
ESP_MAIL_PRINTF("Error, Status Code: %d, Error Code: %d, Reason: %s", smtp.statusCode(), smtp.errorCode(), smtp.errorReason().c_str());
}
Serial.print("sending email to #");
Serial.print(RECIPIENT_EMAIL);
Serial.println("# done");
Serial.println("MailClient.sendMail(&smtp, &message) done");
}
Einen kleinen Fehler habe ich noch im Code:
Schaut man sich das Webinterface an und beendet den Browser, statt das Fenster zu schließen, denkt der ESP, dass die Websession noch offen ist, da der Browser sie nicht geschlossen hat, und der Code hängt für ca. eine Minute, bis der ESP die Session auf timeout setzt.
Grüße,
Felix