// Compiling: // ESP32 Wroover Module // // Use from 0 to 4. Higher number, more debugging messages and memory usage. //#define _WIFIMGR_LOGLEVEL_ 4 // #define DEBUG 1 #include #include #include "debug.h" #include #include #include #include #include #include #include #include #include #define MDNS_NAME "wlanthermometer" #define AP_SSID_CONFIG_NAME "WLANTHERMOMETER-Config" #define AP_CONFIG_PASSWORD "wlanthermometer" #define AP_DATA_RESET_PIN 25 #define TEMP_SENS_PIN 27 #define LCD_I2C_ADDR 0x3f #define LCD_COL 20 #define LCD_ROW 4 #define KRATE_TEMP 5000 #define KRATE_TIME 500 #define KRATE_RESET_AP_DATA 5000 #define KRATE_CHECK_DATETIME 250000 #define ONBOARD_LED 2 #define NTP_TIMEOUT 30000 #define NTP_SERVER "time.fu-berlin.de" #define WIFI_CONNECT_TIMEOUT 30 #define CFG_PORTAL_TIMEOUT 90 #define DHTTYPE DHT22 #define FIRST_MIN_MAX 300000 // = 5 min #define HTML_RELOAD_PAGE_SECS 30 #define WDT_TIMEOUT 300 #define RESET_AFTER_DAYS 7 LiquidCrystal_I2C lcd(LCD_I2C_ADDR, LCD_COL, LCD_ROW); looper sched; float temp[3] = { NAN, NAN, NAN }; float hum[3] = { NAN, NAN, NAN }; float heat[3] = { NAN, NAN, NAN }; bool led_state; String header; WiFiServer server(80); DHT dht(TEMP_SENS_PIN, DHTTYPE); const uint8_t degree_sign[8] = { B00010, B00101, B00010, B00000, B00000, B00000, B00000, B00000 }; uint8_t add_summertime = 0; bool last_reset_ap_check = false; //bool minmax_enabled = false; char date_string_old[11]; char uptime_string[23]; enum { ACT, MIN, MAX }; void ConfigAPWeb(void); void ConfigAPStarted(WiFiManager* wm); void setup() { bool ap_reset_last_state; esp_task_wdt_init(WDT_TIMEOUT, true); watchdog_reset(); esp_task_wdt_add(NULL); pinMode(AP_DATA_RESET_PIN, INPUT_PULLDOWN); pinMode(ONBOARD_LED, OUTPUT); Serial.begin(115200); Serial.println(F("WLANThermometer (c)2020 H. Wirtz ")); delay(200); ap_reset_last_state = digitalRead(AP_DATA_RESET_PIN); lcd.init(); lcd.backlight(); lcd.clear(); lcd.noCursor(); lcd.setCursor(2, 0); lcd.print(F("WLAN THERMOMETER")); lcd.setCursor(2, 1); lcd.print(F("(c)parasiTstudio")); if (digitalRead(AP_DATA_RESET_PIN) == HIGH && ap_reset_last_state == HIGH) { ap_data_reset(); } WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP WiFiManager wm; lcd.setCursor(0, 2); lcd.print(F("Connecting WiFi... ")); wm.setWebServerCallback(ConfigAPWeb); wm.setAPCallback(ConfigAPStarted); wm.setConnectTimeout(WIFI_CONNECT_TIMEOUT); wm.setConfigPortalTimeout(CFG_PORTAL_TIMEOUT); wm.setBreakAfterConfig(true); wm.setRemoveDuplicateAPs(true); wm.setWiFiAutoReconnect(false); wm.setEnableConfigPortal(false); if (!wm.autoConnect(AP_SSID_CONFIG_NAME, AP_CONFIG_PASSWORD)) { lcd.setCursor(0, 2); lcd.print(F("Failed ")); DEBUG_MSG("Failed to connect\n"); delay(1000); lcd.setCursor(7, 2); lcd.print(F("- restart")); delay(1000); ESP.restart(); } else { DEBUG_MSG("Connected\n"); if (!MDNS.begin(MDNS_NAME)) { DEBUG_MSG("Error setting up MDNS responder!\n"); } else { DEBUG_MSG("mDNS started.\n"); } lcd.setCursor(0, 2); lcd.print(F("Mode WiFi client")); lcd.setCursor(0, 3); lcd.print(WiFi.localIP()); delay(500); } lcd.setCursor(0, 3); lcd.print("Getting time..."); DateTime.setTimeZone("CET-1"); DateTime.setServer(NTP_SERVER); DateTime.begin(NTP_TIMEOUT); /* while (!DateTime.isTimeValid()) { DEBUG_MSG("Failed to get time from server.\n"); DateTime.forceUpdate(); }*/ if (DateTime.isTimeValid()) { if (is_wintertime(DateTime.getTime()) == false) { add_summertime = 1; DEBUG_MSG("Summertime\n"); } else { add_summertime = 0; DEBUG_MSG("Wintertime\n"); } DEBUG_MSG("add_summertime=%d\n", add_summertime); } // create a degree sign lcd.createChar(0, (uint8_t*)degree_sign); dht.begin(); server.begin(); sched.addJob(show_temperature, KRATE_TEMP); sched.addJob(show_time, KRATE_TIME); sched.addJob(check_reset_ap_data, KRATE_RESET_AP_DATA); sched.addJob(check_datetime, KRATE_CHECK_DATETIME); sched.addJob(watchdog_reset, WDT_TIMEOUT / 10); get_sensor_data(); temp[MIN] = temp[ACT]; temp[MAX] = temp[ACT]; hum[MIN] = hum[ACT]; hum[MAX] = hum[ACT]; heat[MIN] = heat[ACT]; heat[MAX] = heat[ACT]; strcpy(date_string_old, "--:--:----"); watchdog_reset(); setup_screen(); show_time(); show_temperature(); DEBUG_MSG("10add_summertime=%d\n", add_summertime); } void loop() { static uint32_t next_time_check; sched.scheduler(); if (!DateTime.isTimeValid() && millis() - 10000 > next_time_check) { next_time_check = millis(); DateTime.setTimeZone("CET-1"); DateTime.forceUpdate(); if (is_wintertime(DateTime.getTime()) == false) { add_summertime = 1; DEBUG_MSG("Summertime\n"); } else { add_summertime = 0; DEBUG_MSG("Wintertime\n"); } DEBUG_MSG("add_summertime=%d\n", add_summertime); } WiFiClient client = server.available(); // Listen for incoming clients if (client) { // If a new client connects, DEBUG_MSG("New Client.\n"); // 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 if (client.available()) { // if there's bytes to read from the client, 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) { // checking if header is valid // YWRtaW46c3RhYWdhcg== (user:pass) admin:staagar if (header.indexOf("YWRtaW46c3RhYWdhcg==") >= 0) { client.println("HTTP/1.1 200 OK"); client.println("Content-type:text/html"); client.println("Connection: close"); client.println(); client.println(""); client.println(""); client.println(""); client.println("Temperatur"); client.print(""); client.println(""); client.println(""); client.println(""); client.println(""); client.println("
"); client.println("

Temperatur

"); client.print("

Aktuell: "); if (!isnan(temp[ACT])) client.print(temp[ACT]); else client.print("--.--"); client.println(" °C

"); if (millis() > FIRST_MIN_MAX) { client.print("

Minimum: "); if (!isnan(temp[MIN])) client.print(temp[MIN]); else client.print("--.--"); client.print(" °C"); client.print("

\n"); client.print("

Maximum: "); if (!isnan(temp[MAX])) client.print(temp[MAX]); else client.print("--.--"); client.print(" °C"); client.println("

"); } client.println("
"); client.println("

Gefühlte Temperatur

"); client.print("

Aktuell: "); if (!isnan(heat[ACT])) client.print(heat[ACT]); else client.print("--.--"); client.println(" °C

"); if (millis() > FIRST_MIN_MAX) { client.print("

Minimum: "); if (!isnan(heat[MIN])) client.print(heat[MIN]); else client.print("--.--"); client.print(" °C"); client.println("

"); client.print("

Maximum: "); if (!isnan(heat[MAX])) client.print(heat[MAX]); else client.print("--.--"); client.print(" °C"); client.println("

"); } client.println("
"); client.println("

Luftfeuchtigkeit

"); client.print("

Aktuell: "); if (!isnan(hum[ACT])) client.print(hum[ACT]); else client.print("--.--"); client.println(" %

"); if (millis() > FIRST_MIN_MAX) { client.print("

Minimum: "); if (!isnan(hum[MIN])) client.print(hum[MIN]); else client.print("--.--"); client.print(" %"); client.println("

"); client.print("

Maximum: "); if (!isnan(hum[MAX])) client.print(hum[MAX]); else client.print("--.--"); client.print(" %"); client.println("

"); } if (DateTime.isTimeValid()) { DateTimeParts p = DateTime.getParts(); char dt[21]; if (is_wintertime(DateTime.getTime()) == false) add_summertime = 1; else add_summertime = 0; client.println("
"); client.print("

"); sprintf(dt, "%02d.%02d.%4d %02d:%02d:%02d", p.getMonthDay(), p.getMonth() + 1, p.getYear(), p.getHours() + add_summertime, p.getMinutes(), p.getSeconds()); client.print(dt); if (add_summertime > 0) client.print(" Sommerzeit"); else client.print(" Winterzeit"); client.println("

"); } client.print("
"); client.print(uptime_string); client.println("
"); client.println("
"); client.println(""); client.println(""); break; } // Wrong user or password, so HTTP request fails... else { client.println("HTTP/1.1 401 Unauthorized"); client.println("WWW-Authenticate: Basic realm=\"Secure\""); client.println("Content-Type: text/html"); client.println(); client.println("Authentification failed"); 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(); DEBUG_MSG("Client disconnected.\n\n"); } } void check_reset_ap_data(void) { if (digitalRead(AP_DATA_RESET_PIN) == HIGH && last_reset_ap_check == true) { ap_data_reset(); } else if (digitalRead(AP_DATA_RESET_PIN) == HIGH) { if (digitalRead(AP_DATA_RESET_PIN) == HIGH) DEBUG_MSG("Reset AP data pressed\n"); last_reset_ap_check = true; } else last_reset_ap_check = false; } void show_time(void) { char date_string[11]; char time_string[9]; uptime::calculateUptime(); if (DateTime.isTimeValid()) { DateTimeParts p = DateTime.getParts(); if (p.getHours() == 0 && p.getMinutes() == 0 && p.getSeconds() == 0) DateTime.forceUpdate(); if ((p.getHours() == 2 || p.getHours() == 3) && p.getMinutes() == 0 && p.getSeconds() == 0) { if (is_wintertime(DateTime.getTime()) == false) { add_summertime = 1; DEBUG_MSG("Summertime\n"); } else { add_summertime = 0; DEBUG_MSG("Wintertime\n"); } DEBUG_MSG("add_summertime=%d\n", add_summertime); } sprintf(date_string, "%02d.%02d.%4d", p.getMonthDay(), p.getMonth() + 1, p.getYear()); sprintf(time_string, "%02d:%02d:%02d", p.getHours() + add_summertime, p.getMinutes(), p.getSeconds()); sprintf(uptime_string, "Uptime: %4dd %02d:%02d:%02d", uptime::getDays(), uptime::getHours(), uptime::getMinutes(), uptime::getSeconds()); DEBUG_MSG("%s %s", date_string, time_string); if (add_summertime > 0) DEBUG_MSG(" Summertime\n"); else DEBUG_MSG(" Wintertime\n"); DEBUG_MSG("%s\n", uptime_string); } else { strcpy(date_string, "--:--:----"); strcpy(time_string, "--:--:--"); } if (strcmp(date_string, date_string_old)) { lcd.setCursor(0, 0); lcd.print(date_string); strcpy(date_string_old, date_string); } lcd.setCursor(12, 0); lcd.print(time_string); if (uptime::getDays() >= RESET_AFTER_DAYS) { lcd.clear(); lcd.print("Automatic restart"); DEBUG_MSG("Automatic restart\n"); delay(2000); ESP.restart(); } digitalWrite(ONBOARD_LED, led_state); led_state = !led_state; } void show_temperature(void) { get_sensor_data(); DEBUG_MSG("Temperature: %02.2f\n", temp[ACT]); lcd.setCursor(14, 1); if (isnan(temp[ACT])) lcd.print("--.-"); else lcd.print(round_float(temp[ACT], 1), 1); if (millis() > FIRST_MIN_MAX) { lcd.setCursor(5, 2); if (isnan(temp[MIN])) lcd.print("--.-"); else lcd.print(round_float(temp[MIN], 1), 1); lcd.setCursor(16, 2); if (isnan(temp[MAX])) lcd.print("--.-"); else lcd.print(round_float(temp[MAX], 1), 1); } lcd.setCursor(5, 3); if (isnan(hum[ACT])) lcd.print("--.-"); else lcd.print(round_float(hum[ACT], 1), 1); lcd.setCursor(16, 3); if (isnan(heat[ACT])) lcd.print("--.-"); else lcd.print(round_float(heat[ACT], 1), 1); } void setup_screen(void) { lcd.clear(); lcd.setCursor(0, 1); lcd.print("Temperatur:"); lcd.setCursor(18, 1); lcd.write(0); lcd.print("C"); lcd.setCursor(0, 2); lcd.print("Min: --.- Max: --.-"); lcd.setCursor(0, 3); lcd.print("LF: % GT:"); } bool is_wintertime(time_t t) { // Sommerzeit/Winterzeit http://manfred.wilzeck.de/Datum_berechnen.html#Jahreszahl // Tag = 31 – ( 4 + Jahr*5 / 4) MOD 7 ' Datum Tag für Beginn Sommerzeit (Jahr 4-stellig) // Tag = 31 – ( 1 + Jahr*5 / 4) MOD 7 ' Datum Tag für Ende Sommerzeit (Jahr 4-stellig) tmElements_t tm; byte day_of_switch[2]; time_t switch_secs[2]; byte i; day_of_switch[0] = 31 - (4 + year(t) * 5 / 4) % 7; // winter->summer day_of_switch[1] = 31 - (1 + year(t) * 5 / 4) % 7; // summer->winter for (i = 0; i < 2; i++) { tm.Second = 0; if (i == 0) tm.Hour = 2; else tm.Hour = 3; tm.Minute = 0; tm.Day = day_of_switch[i]; if (i == 0) tm.Month = 3; else tm.Month = 10; tm.Year = year(t) - 1970; switch_secs[i] = makeTime(tm); } if (t >= switch_secs[0] && t < switch_secs[1]) return (false); else return (true); } void get_sensor_data(void) { temp[ACT] = dht.readTemperature(); hum[ACT] = dht.readHumidity(); if (!isnan(temp[ACT]) && !isnan(hum[ACT])) heat[ACT] = dht.computeHeatIndex(temp[ACT], hum[ACT], false); else heat[ACT] = NAN; if (!isnan(temp[ACT])) { if (isnan(temp[MIN])) temp[MIN] = temp[ACT]; if (isnan(temp[MAX])) temp[MAX] = temp[ACT]; if (temp[ACT] < temp[MIN]) temp[MIN] = temp[ACT]; else if (temp[ACT] > temp[MAX]) temp[MAX] = temp[ACT]; } if (!isnan(hum[ACT])) { if (isnan(hum[MIN])) hum[MIN] = hum[ACT]; if (isnan(hum[MAX])) hum[MAX] = hum[ACT]; if (hum[ACT] < hum[MIN]) hum[MIN] = hum[ACT]; else if (hum[ACT] > hum[MAX]) hum[MAX] = hum[ACT]; } if (!isnan(heat[ACT])) { if (isnan(heat[MIN])) heat[MIN] = heat[ACT]; if (isnan(heat[MAX])) heat[MAX] = heat[ACT]; if (heat[ACT] < heat[MIN]) heat[MIN] = heat[ACT]; else if (heat[ACT] > heat[MAX]) heat[MAX] = heat[ACT]; } //} } void ConfigAPWeb(void) { lcd.setCursor(0, 2); lcd.print(F("Mode Config-AP ")); lcd.setCursor(0, 3); lcd.print(F("192.168.4.1")); } void ConfigAPStarted(WiFiManager * wm) { DEBUG_MSG("Config-AP started\n"); } float round_float(float num, uint8_t dec_place) { return ((round(num * powf(10, dec_place))) / powf(10, dec_place)); } void ap_data_reset(void) { DEBUG_MSG("Reset AP data\n"); WiFiManager wm; lcd.clear(); lcd.setCursor(0, 0); lcd.print("Resetting AP Data"); delay(2000); wm.resetSettings(); lcd.setCursor(0, 1); lcd.print("Done."); delay(1000); ESP.restart(); } void check_datetime(void) { if (!DateTime.isTimeValid()) { lcd.setCursor(0, 0); lcd.print(" Time update "); DateTime.forceUpdate(); show_time(); } } void watchdog_reset(void) { esp_task_wdt_reset(); }