diff --git a/Wetterstation.HMI b/Wetterstation.HMI index dfc54bf2550c1b2488f437b699c14cc2118bf681..aff3ccafa7bf6ea3f3ed8e6e62786ca1750dcbc0 100644 Binary files a/Wetterstation.HMI and b/Wetterstation.HMI differ diff --git a/wetterstation/NTPClient.cpp b/wetterstation/NTPClient.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7e9a332b83008b9f3c83a90779632aaf04486bee --- /dev/null +++ b/wetterstation/NTPClient.cpp @@ -0,0 +1,200 @@ +/** + * The MIT License (MIT) + * Copyright (c) 2015 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "NTPClient.h" + +NTPClient::NTPClient(UDP& udp) { + this->_udp = &udp; +} + +NTPClient::NTPClient(UDP& udp, long timeOffset) { + this->_udp = &udp; + this->_timeOffset = timeOffset; +} + +NTPClient::NTPClient(UDP& udp, const char* poolServerName) { + this->_udp = &udp; + this->_poolServerName = poolServerName; +} + +NTPClient::NTPClient(UDP& udp, const char* poolServerName, long timeOffset) { + this->_udp = &udp; + this->_timeOffset = timeOffset; + this->_poolServerName = poolServerName; +} + +NTPClient::NTPClient(UDP& udp, const char* poolServerName, long timeOffset, unsigned long updateInterval) { + this->_udp = &udp; + this->_timeOffset = timeOffset; + this->_poolServerName = poolServerName; + this->_updateInterval = updateInterval; +} + +void NTPClient::begin() { + this->begin(NTP_DEFAULT_LOCAL_PORT); +} + +void NTPClient::begin(int port) { + this->_port = port; + + this->_udp->begin(this->_port); + + this->_udpSetup = true; +} + +bool NTPClient::forceUpdate() { + #ifdef DEBUG_NTPClient + Serial.println("Update from NTP Server"); + #endif + + this->sendNTPPacket(); + + // Wait till data is there or timeout... + byte timeout = 0; + int cb = 0; + do { + delay ( 10 ); + cb = this->_udp->parsePacket(); + if (timeout > 100) return false; // timeout after 1000 ms + timeout++; + } while (cb == 0); + + this->_lastUpdate = millis() - (10 * (timeout + 1)); // Account for delay in reading the time + + this->_udp->read(this->_packetBuffer, NTP_PACKET_SIZE); + + unsigned long highWord = word(this->_packetBuffer[40], this->_packetBuffer[41]); + unsigned long lowWord = word(this->_packetBuffer[42], this->_packetBuffer[43]); + // combine the four bytes (two words) into a long integer + // this is NTP time (seconds since Jan 1 1900): + unsigned long secsSince1900 = highWord << 16 | lowWord; + + this->_currentEpoc = secsSince1900 - SEVENZYYEARS; + + return true; +} + +bool NTPClient::update() { + if ((millis() - this->_lastUpdate >= this->_updateInterval) // Update after _updateInterval + || this->_lastUpdate == 0) { // Update if there was no update yet. + if (!this->_udpSetup) this->begin(); // setup the UDP client if needed + return this->forceUpdate(); + } + return true; +} + +unsigned long NTPClient::getEpochTime() const { + return this->_timeOffset + // User offset + this->_currentEpoc + // Epoc returned by the NTP server + ((millis() - this->_lastUpdate) / 1000); // Time since last update +} + +int NTPClient::getDay() const { + return (((this->getEpochTime() / 86400L) + 4 ) % 7); //0 is Sunday +} +int NTPClient::getHours() const { + return ((this->getEpochTime() % 86400L) / 3600); +} +int NTPClient::getMinutes() const { + return ((this->getEpochTime() % 3600) / 60); +} +int NTPClient::getSeconds() const { + return (this->getEpochTime() % 60); +} + +String NTPClient::getFormattedTime() const { + unsigned long rawTime = this->getEpochTime(); + unsigned long hours = (rawTime % 86400L) / 3600; + String hoursStr = hours < 10 ? "0" + String(hours) : String(hours); + + unsigned long minutes = (rawTime % 3600) / 60; + String minuteStr = minutes < 10 ? "0" + String(minutes) : String(minutes); + + unsigned long seconds = rawTime % 60; + String secondStr = seconds < 10 ? "0" + String(seconds) : String(seconds); + + return hoursStr + ":" + minuteStr + ":" + secondStr; +} + +void NTPClient::end() { + this->_udp->stop(); + + this->_udpSetup = false; +} + +void NTPClient::setTimeOffset(int timeOffset) { + this->_timeOffset = timeOffset; +} + +void NTPClient::setUpdateInterval(unsigned long updateInterval) { + this->_updateInterval = updateInterval; +} + +void NTPClient::setPoolServerName(const char* poolServerName) { + this->_poolServerName = poolServerName; +} + +void NTPClient::sendNTPPacket() { + // set all bytes in the buffer to 0 + memset(this->_packetBuffer, 0, NTP_PACKET_SIZE); + // Initialize values needed to form NTP request + // (see URL above for details on the packets) + this->_packetBuffer[0] = 0b11100011; // LI, Version, Mode + this->_packetBuffer[1] = 0; // Stratum, or type of clock + this->_packetBuffer[2] = 6; // Polling Interval + this->_packetBuffer[3] = 0xEC; // Peer Clock Precision + // 8 bytes of zero for Root Delay & Root Dispersion + this->_packetBuffer[12] = 49; + this->_packetBuffer[13] = 0x4E; + this->_packetBuffer[14] = 49; + this->_packetBuffer[15] = 52; + + // all NTP fields have been given values, now + // you can send a packet requesting a timestamp: + this->_udp->beginPacket(this->_poolServerName, 123); //NTP requests are to port 123 + this->_udp->write(this->_packetBuffer, NTP_PACKET_SIZE); + this->_udp->endPacket(); +} + +String NTPClient::getFormattedDate(unsigned long secs) { + unsigned long rawTime = (secs ? secs : this->getEpochTime()) / 86400L; // in days + unsigned long days = 0, year = 1970; + uint8_t month; + static const uint8_t monthDays[]={31,28,31,30,31,30,31,31,30,31,30,31}; + + while((days += (LEAP_YEAR(year) ? 366 : 365)) <= rawTime) + year++; + rawTime -= days - (LEAP_YEAR(year) ? 366 : 365); // now it is days in this year, starting at 0 + days=0; + for (month=0; month<12; month++) { + uint8_t monthLength; + if (month==1) { // february + monthLength = LEAP_YEAR(year) ? 29 : 28; + } else { + monthLength = monthDays[month]; + } + if (rawTime < monthLength) break; + rawTime -= monthLength; + } + String monthStr = ++month < 10 ? "0" + String(month) : String(month); // jan is month 1 + String dayStr = ++rawTime < 10 ? "0" + String(rawTime) : String(rawTime); // day of month + return dayStr + "." + monthStr + "." + String(year); +} diff --git a/wetterstation/NTPClient.h b/wetterstation/NTPClient.h new file mode 100644 index 0000000000000000000000000000000000000000..dc468d20d28150bfc00d03eefab68c873ad1a6d2 --- /dev/null +++ b/wetterstation/NTPClient.h @@ -0,0 +1,104 @@ +#pragma once + +#include "Arduino.h" + +#include <Udp.h> + +#define SEVENZYYEARS 2208988800UL +#define NTP_PACKET_SIZE 48 +#define NTP_DEFAULT_LOCAL_PORT 1337 +#define LEAP_YEAR(Y) ( (Y>0) && !(Y%4) && ( (Y%100) || !(Y%400) ) ) + +class NTPClient { + private: + UDP* _udp; + bool _udpSetup = false; + + const char* _poolServerName = "pool.ntp.org"; // Default time server + int _port = NTP_DEFAULT_LOCAL_PORT; + long _timeOffset = 0; + + unsigned long _updateInterval = 60000; // In ms + + unsigned long _currentEpoc = 0; // In s + unsigned long _lastUpdate = 0; // In ms + + byte _packetBuffer[NTP_PACKET_SIZE]; + + void sendNTPPacket(); + + public: + NTPClient(UDP& udp); + NTPClient(UDP& udp, long timeOffset); + NTPClient(UDP& udp, const char* poolServerName); + NTPClient(UDP& udp, const char* poolServerName, long timeOffset); + NTPClient(UDP& udp, const char* poolServerName, long timeOffset, unsigned long updateInterval); + + /** + * Set time server name + * + * @param poolServerName + */ + void setPoolServerName(const char* poolServerName); + + /** + * Starts the underlying UDP client with the default local port + */ + void begin(); + + /** + * Starts the underlying UDP client with the specified local port + */ + void begin(int port); + + /** + * This should be called in the main loop of your application. By default an update from the NTP Server is only + * made every 60 seconds. This can be configured in the NTPClient constructor. + * + * @return true on success, false on failure + */ + bool update(); + + /** + * This will force the update from the NTP Server. + * + * @return true on success, false on failure + */ + bool forceUpdate(); + + int getDay() const; + int getHours() const; + int getMinutes() const; + int getSeconds() const; + + /** + * Changes the time offset. Useful for changing timezones dynamically + */ + void setTimeOffset(int timeOffset); + + /** + * Set the update interval to another frequency. E.g. useful when the + * timeOffset should not be set in the constructor + */ + void setUpdateInterval(unsigned long updateInterval); + + /** + * @return time formatted like `hh:mm:ss` + */ + String getFormattedTime() const; + + /** + * @return date formatted like `dd.mm.yyyy` + */ + String getFormattedDate(unsigned long secs = 0); + + /** + * @return time in seconds since Jan. 1, 1970 + */ + unsigned long getEpochTime() const; + + /** + * Stops the underlying UDP client + */ + void end(); +}; diff --git a/wetterstation/settings.h b/wetterstation/settings.h index 638b3300cfe4700d52db6964b7644bac8776dbaa..c54f4fe64102238a715bf9f6c265260e1ff6d354 100644 --- a/wetterstation/settings.h +++ b/wetterstation/settings.h @@ -1,24 +1,24 @@ // // settings for WiFi connection // -char * ssid = "YOUR_SSID"; // WiFi SSID -char * password = "YOUR_WLAN_PASSWORD"; // WiFi password +char * ssid = "Sensors"; // WiFi SSID +char * password = "mySensorNetz"; // WiFi password // // MQTT Server // String frontendId = String(ESP.getChipId()); // Set chipId as frontendId -const char* mqttHost = "MQTT_HOST"; // Adresse des MQTT Servers -const char* mqttUser = "MQTT_USER"; // MQTT Username -const char* mqttPass = "MQTT_PASS"; // MQTT Password +const char* mqttHost = "mqtt.itstall.de"; // Adresse des MQTT Servers +const char* mqttUser = "wetterstation"; // MQTT Username +const char* mqttPass = "wetterstation"; // MQTT Password int mqttPort = 1883; // 1883 - Ist der Standard TCP Port // // Settings for Backend // -String backendPlz = "YOUT_PLZ"; -String backendLngCode = "YOUT_LANDUAGE_CODE"; -String backendCity = "YOUR_CITY"; +String backendPlz = "97453"; +String backendLngCode = "de"; +String backendCity = "Schonungen"; // // settings diff --git a/wetterstation/wetterstation.ino b/wetterstation/wetterstation.ino index 8afab68e993969f89588522d20ad7ec29f40536b..705b50fb15e86bc8f0f118a03238e6fd4293e435 100644 --- a/wetterstation/wetterstation.ino +++ b/wetterstation/wetterstation.ino @@ -26,10 +26,10 @@ #include <Wire.h> #include <SPI.h> // I2C library #include <ArduinoJson.h> // https://github.com/bblanchon/ArduinoJson -#include <NTPClient.h> #include <ESP8266WiFi.h> // WiFi library #include <WiFiUdp.h> // Udp library #include <TimeLib.h> // Time library +#include "NTPClient.h" #include "PubSubClient.h" // http://knolleary.net/arduino-client-for-mqtt/ #include "settings.h" @@ -119,7 +119,8 @@ void loop() { // Update date and time on display sendToLCD(1, "time", String(timeClient.getFormattedTime())); - sendToLCD(1, "date", dayAsString(timeClient.getDay())); + sendToLCD(1, "dateDay", dayAsString(timeClient.getDay())); + sendToLCD(1, "date", String(timeClient.getFormattedDate())); reconnectToWifi(); @@ -201,19 +202,21 @@ void reconnectToWifi() { if(WiFi.status() != WL_CONNECTED) { int wifiBlink = 0; - // WiFi.enableSTA(true); + // WiFi.enableSTA(true); WiFi.mode(WIFI_STA); WiFi.disconnect(); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { if (wifiBlink == 0) { - printNextionCommand("wifi_connect.txt=\"connect...\""); - sendToLCD(1, "txtIp", "\"No IP yet...\""); + sendToLCD(1, "txt_ip", "\"No IP yet...\""); + sendToLCD(3, "btn_wifi", "31"); wifiBlink = 1; } else { - printNextionCommand("wifi_connect.txt=" + doubleQuote); + //printNextionCommand("wifi_connect.txt=" + doubleQuote); + sendToLCD(1, "txt_ip", doubleQuote); + sendToLCD(3, "btn_wifi", "30"); wifiBlink = 0; } delay(500); @@ -223,9 +226,9 @@ void reconnectToWifi() { Serial.print("Wifi IP Address: "); Serial.println(WiFi.localIP().toString()); #endif - sendToLCD(1, "txtIp", WiFi.localIP().toString()); - - printNextionCommand("wifi_connect.txt=" + doubleQuote); + sendToLCD(1, "txt_ip", WiFi.localIP().toString()); + sendToLCD(3, "btn_wifi", "30"); + //printNextionCommand("wifi_connect.txt=" + doubleQuote); } void getTimeFromServer() { #ifdef DEBUG @@ -249,8 +252,10 @@ void reconnectMqtt() { serializeJson(doc, settings); while (!mqttClient.connected()) { + sendToLCD(3, "btn_mqtt", "28"); if (mqttClient.connect(frontendId.c_str(), mqttUser, mqttPass)) { Serial.println("MQTT Connected"); + sendToLCD(3, "btn_mqtt", "29"); mqttClient.subscribe(("/wetterstation/frontend/" + frontendId + "/weather").c_str()); mqttClient.publish("/wetterstation/backend/settings", settings.c_str()); } @@ -263,6 +268,9 @@ void reconnectMqtt() { delay(5000); } } + if(mqttClient.connected()) { + sendToLCD(3, "btn_mqtt", "29"); + } } void getWeatherDataMqtt(char* topic, byte* payload, unsigned int length) { #ifdef DEBUG @@ -274,8 +282,7 @@ void getWeatherDataMqtt(char* topic, byte* payload, unsigned int length) { deserializeJson(root, payload, length); String tmp0 = backendCity; - //String tmp1 = root["main"]; // Icon main - //String tmp2 = root["description"]; // Icon description + float tmp1 = root["temp"]; // Icon main float tmp3 = root["tempMin"]; float tmp4 = root["tempMax"]; float tmp5 = root["humidity"]; @@ -285,7 +292,6 @@ void getWeatherDataMqtt(char* topic, byte* payload, unsigned int length) { float tmp9 = root["windSpeed"]; int tmp10 = root["windDeg"]; float tmp11 = root["pressure"]; - //String tmp12 = root["list"]["dt_text"]; command = command + tmp12; #if !defined (DEBUG_NO_PAGE_FADE) displayFadeOut(displayDimValue, dimPageDelay); @@ -302,12 +308,14 @@ void getWeatherDataMqtt(char* topic, byte* payload, unsigned int length) { //sendToLCD(1, "description", tmp2); sendToLCD(1, "humidity", String(tmp5, 0)); sendToLCD(1, "rain", String(tmp7, 1)); + sendToLCD(1, "snow", String(tmp8, 1)); sendToLCD(1, "wind_dir", getShortWindDirection(tmp10)); sendToLCD(1, "wind_speed", String(tmp9, 1)); sendToLCD(1, "pressure", String(tmp11, 0)); sendToLCD(1, "clouds", String(tmp6, 0)); sendToLCD(1, "temp_min", String(tmp3, 1)); sendToLCD(1, "temp_max", String(tmp4, 1)); + sendToLCD(1, "temp", String(tmp1, 1)); } String getWindDirection (int degrees) { #ifdef DEBUG