You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
361 lines
9.6 KiB
361 lines
9.6 KiB
// Use from 0 to 4. Higher number, more debugging messages and memory usage.
|
|
#define _WIFIMGR_LOGLEVEL_ 1
|
|
#define DEBUG 1
|
|
|
|
#include <WiFi.h>
|
|
#include <WiFiManager.h>
|
|
#include "debug.h"
|
|
#include <Arduino.h>
|
|
#include <ESPmDNS.h>
|
|
#include <LiquidCrystal_I2C.h>
|
|
#include <looper.h>
|
|
#include <RunningMedian.h>
|
|
#include <ESPDateTime.h>
|
|
#include <Time.h>
|
|
|
|
#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 34
|
|
#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 MEDIAN_SAMPLES 60
|
|
#define ONBOARD_LED 2
|
|
#define NTP_TIMEOUT 15000
|
|
#define WIFI_CONNECT_TIMEOUT 30
|
|
#define CFG_PORTAL_TIMEOUT 90
|
|
|
|
LiquidCrystal_I2C lcd(LCD_I2C_ADDR, LCD_COL, LCD_ROW);
|
|
looper sched;
|
|
RunningMedian samples = RunningMedian(MEDIAN_SAMPLES);
|
|
float temp_min;
|
|
float temp_max;
|
|
float temp;
|
|
bool led_state;
|
|
String header;
|
|
WiFiServer server(80);
|
|
const uint8_t degree_sign[8] = { B00010, B00101, B00010, B00000, B00000, B00000, B00000, B00000 };
|
|
uint8_t add_summertime = 0;
|
|
bool last_config_check = false;
|
|
|
|
void setup()
|
|
{
|
|
pinMode(AP_DATA_RESET_PIN, INPUT_PULLDOWN);
|
|
pinMode(ONBOARD_LED, OUTPUT);
|
|
|
|
Serial.begin(115200);
|
|
Serial.println(F("WLANThermometer (c)2020 H. Wirtz <wirtz@parasitstudio.de>"));
|
|
|
|
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"));
|
|
|
|
DEBUG_MSG("Mode Client\n");
|
|
WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP
|
|
|
|
WiFiManager wm;
|
|
|
|
lcd.setCursor(0, 2);
|
|
lcd.print(F("Connecting WiFi... "));
|
|
lcd.setCursor(0, 3);
|
|
lcd.print(F("CFG-AP: 192.168.4.1"));
|
|
|
|
wm.setConnectTimeout(WIFI_CONNECT_TIMEOUT);
|
|
wm.setConfigPortalTimeout(CFG_PORTAL_TIMEOUT);
|
|
wm.setBreakAfterConfig(true);
|
|
|
|
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(8, 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.clear();
|
|
lcd.setCursor(0, 0);
|
|
lcd.print(F("Mode WiFi client"));
|
|
lcd.setCursor(0, 1);
|
|
lcd.print(WiFi.localIP());
|
|
|
|
delay(500);
|
|
}
|
|
|
|
lcd.setCursor(0, 3);
|
|
lcd.print("Getting time...");
|
|
DateTime.setTimeZone(1);
|
|
DateTime.begin(NTP_TIMEOUT);
|
|
while (!DateTime.isTimeValid())
|
|
{
|
|
Serial.println("Failed to get time from server.");
|
|
DateTime.forceUpdate();
|
|
}
|
|
if (is_wintertime(DateTime.getTime()) == false)
|
|
add_summertime = 1;
|
|
else
|
|
add_summertime = 0;
|
|
|
|
// create a degree sign
|
|
lcd.createChar(0, (uint8_t*)degree_sign);
|
|
|
|
server.begin();
|
|
|
|
sched.addJob(show_temperature, KRATE_TEMP);
|
|
sched.addJob(show_time, KRATE_TIME);
|
|
sched.addJob(start_config_portal, KRATE_RESET_AP_DATA);
|
|
|
|
temp_min = get_temp_sensor_data();
|
|
temp_max = temp_min;
|
|
temp = temp_min;
|
|
|
|
lcd.clear();
|
|
show_time();
|
|
show_temperature();
|
|
}
|
|
|
|
void loop()
|
|
{
|
|
sched.scheduler();
|
|
|
|
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
|
|
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("<!DOCTYPE html>\n");
|
|
client.println("<html>\n");
|
|
client.println("<meta http-equiv=\"refresh\" content=\"5\">\n");
|
|
client.println("<body>\n");
|
|
client.println("<center>\n");
|
|
client.println("<h1 style=\"color:blue;\">Temperatur Kirche</h1>\n");
|
|
client.print("<h2 style=\"color:green;\">Aktuell: ");
|
|
client.print(temp);
|
|
client.println(" °C</h2>\n");
|
|
client.print("<h2 style=\"color:green;\">Minimum: ");
|
|
client.print(temp_min);
|
|
client.println(" °C</h2>\n");
|
|
client.print("<h2 style=\"color:green;\">Maximum: ");
|
|
client.print(temp_max);
|
|
client.println(" °C</h2>\n");
|
|
client.println("</center>\n");
|
|
client.println("</body>\n");
|
|
client.println("</html>");
|
|
|
|
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("<html>Authentication failed</html>");
|
|
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 start_config_portal(void)
|
|
{
|
|
if (digitalRead(AP_DATA_RESET_PIN) == HIGH && last_config_check == true)
|
|
{
|
|
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();
|
|
}
|
|
else if (digitalRead(AP_DATA_RESET_PIN) == HIGH)
|
|
{
|
|
if (digitalRead(AP_DATA_RESET_PIN) == HIGH)
|
|
DEBUG_MSG("Reset AP data pressed\n");
|
|
last_config_check = true;
|
|
}
|
|
else
|
|
last_config_check = false;
|
|
}
|
|
|
|
void show_time(void)
|
|
{
|
|
DateTimeParts p = DateTime.getParts();
|
|
char dt[21];
|
|
|
|
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;
|
|
else
|
|
add_summertime = 0;
|
|
}
|
|
|
|
sprintf(dt, "%02d.%02d.%4d %02d:%02d:%02d", p.getMonthDay(), p.getMonth() + 1, p.getYear(), p.getHours() + add_summertime, p.getMinutes(), p.getSeconds());
|
|
Serial.print(dt);
|
|
if (add_summertime > 0)
|
|
Serial.println(" Summertime");
|
|
else
|
|
Serial.println(" Wintertime");
|
|
|
|
|
|
lcd.setCursor(0, 0);
|
|
lcd.print(dt);
|
|
|
|
digitalWrite(ONBOARD_LED, led_state);
|
|
led_state = !led_state;
|
|
}
|
|
|
|
void show_temperature(void)
|
|
{
|
|
float new_temp = get_temp_sensor_data();
|
|
|
|
DEBUG_MSG("Temperature: %02.2f\n", new_temp);
|
|
|
|
if (new_temp > temp)
|
|
{
|
|
if (new_temp - temp > 1.0)
|
|
temp = temp + 1.0;
|
|
else
|
|
temp = new_temp;
|
|
}
|
|
else if (new_temp < temp)
|
|
{
|
|
if (new_temp - temp < -1.0)
|
|
temp = temp - 1.0;
|
|
else
|
|
temp = new_temp;
|
|
}
|
|
|
|
samples.add(temp);
|
|
|
|
if (samples.getLowest() < temp_min)
|
|
temp_min = samples.getLowest();
|
|
if (samples.getHighest() > temp_max)
|
|
temp_max = samples.getHighest();
|
|
|
|
lcd.setCursor(0, 1);
|
|
lcd.print("Temperatur: ");
|
|
lcd.print(temp);
|
|
lcd.write(0);
|
|
lcd.print("C");
|
|
lcd.setCursor(0, 2);
|
|
lcd.print("Minimum: ");
|
|
lcd.print(temp_min);
|
|
lcd.write(0);
|
|
lcd.print("C");
|
|
lcd.setCursor(0, 3);
|
|
lcd.print("Maximum: ");
|
|
lcd.print(temp_max);
|
|
lcd.write(0);
|
|
lcd.print("C");
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
float get_temp_sensor_data(void)
|
|
{
|
|
float t = constrain((analogRead(TEMP_SENS_PIN) / 2048.0) * 330.0, 0.0, 50.0);
|
|
|
|
return (t);
|
|
}
|
|
|