/* This sketch is based on the TimeNTP_ESP8266WiFi.ino Modified example showing time sync to NTP time source and daylight saving for European Union Requires ESP Core 2.4.0 Modifications - Definied timeout for DNS Lookup of NTP timeout (requires ESP Core 2.4.0) - gets rid of the 10 seconds blocking of NTP lookup on failure - Program upodates threashold for NTP timeout dynamically to minimal but optimistic timeouts - system clock runs in UTC - not in local time zone (!) - daylight saving model according regulations in Europe. - Print date time function Memory Usage: Original Sketch with ESP Core 2.3.0: Flash 229781 Bytes (21%) - RAM 32260 Bytes (39%) - Just as note Original Sketch with ESP Core 2.4.0: Flash 255579 Bytes (24%) - RAM 33540 Bytes (40%) - Reference This sktech minimized: Flash 255759 Bytes (24%) - RAM 33392 Bytes (40%) - doing the same like the Reference This sketch with daylight saving: Flash 255979 Bytes (24%) - Ram 33412 Bytes (40%) - final version */ #include #include #include #include // fuer Flashen ueber WLAN ( OTA = Over The Air) // Netzwerk-Parameter festlegen //_________________________________________________________________________________________ // Set your own !!!! network variables in this section !!! const char* OTA_host = "Voltmeter-Uhr"; //### Name for OTA-Flashing (OTA = Over the Air) const char* ssid = "xxxxxxxx"; //### your WLAN SSID //### the name of your WiFi-network / <= 31 Signs const char* pwd = "xxxxxxxx"; //### your WLAN Password //### the password of your WiFi-network / >= 8 or <= 63 signs or NULL IPAddress ip(xxx.xxx.xxx.xxx); //### local ip-address of NodeMCU IPAddress gateway(xxx.xxx.xxx.xxx); //### router IPAddress subnet(255, 255, 255, 0); //### subnet-mask IPAddress dns(xxx.xxx.xxx.xxx); //### dns-server = mostly the same adress as router //_________________________________________________________________________________________ // NTP Servers: static const char ntpServerName[] = "de.pool.ntp.org"; // use the nearest NTP pool to your location //static const char ntpServerName[] = "194.25.134.196"; // ntp1 t-online.de //static const char ntpServerName[] = "time.nist.gov"; //static const char ntpServerName[] = "time-a.timefreq.bldrdoc.gov"; //static const char ntpServerName[] = "time-b.timefreq.bldrdoc.gov"; //static const char ntpServerName[] = "time-c.timefreq.bldrdoc.gov"; const int timeZone = 1; // Central European Time // if you change to any timeZone out of EU, adopt daylightSaving function! //const int timeZone = -5; // Eastern Standard Time (USA) //const int timeZone = -4; // Eastern Daylight Time (USA) //const int timeZone = -8; // Pacific Standard Time (USA) //const int timeZone = -7; // Pacific Daylight Time (USA) const uint32_t syncIntervalMax = 14400; // 14400 = 4 Std | could be the same value like the syncInterval in time.cpp | 300 const uint32_t syncIntervalAfterFail = 60; // if sync with NTP fails, retry earlier | 60 const uint32_t ntpWaitMax = 1000; // maximum time in ms to wait for an answer of NTP Server, most used value 1500 I prefere below one second: 900 uint32_t ntpWaitActual = ntpWaitMax; // optimized/reduced wait time, start with maximum. WiFiUDP Udp; unsigned int localPort = 8888; // local port to listen for UDP packets time_t getNtpTime(); int sekInstr = 14; //D5 int minInstr = 12; //D6 int stdInstr = 13; //D7 int plexi = 16; //D0 //_________________________________________________________________________________________ bool connectWIFI(){ WiFi.config(ip, gateway, subnet, dns); delay(200); WiFi.mode(WIFI_STA); WiFi.begin(ssid, pwd); Serial.print("Connecting to "); Serial.println(ssid); while (WiFi.status() != WL_CONNECTED) { //while (WiFi.waitForConnectResult() != WL_CONNECTED) { //Serial.println(millis()); // Debug only if(millis() >= 10000) ESP.restart(); // Falls WLAN nicht connected Serial.print("."); analogWrite(sekInstr,705); //40 analogWrite(minInstr,600); //40 analogWrite(stdInstr, 560); //8 delay(600); Serial.print("."); analogWrite(sekInstr,340); //20 analogWrite(minInstr,285); //20 analogWrite(stdInstr, 272); //4 delay(600); } Serial.print("WiFi connected ---> "); Serial.print("IP: "); Serial.println(WiFi.localIP()); return true; } // end of connectWIFI //_____________________________________________________________________________ void startOTAhandling() { ArduinoOTA.onStart([]() { Serial.println("Start updating"); }); 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"); }); String Hostname = OTA_host; // OTA Name ArduinoOTA.setHostname(Hostname.c_str()); ArduinoOTA.begin(); } //_____________________________________________________________________________ void setup(){ pinMode(sekInstr, OUTPUT); pinMode(minInstr, OUTPUT); pinMode(stdInstr, OUTPUT); pinMode(plexi, OUTPUT); digitalWrite(plexi, HIGH); //LEDs OFF Serial.begin(115200); while (!Serial) ; // Needed for Leonardo only delay(250); Serial.println(""); Serial.println(""); Serial.println(F("Voltmeter-Uhr\n")); connectWIFI(); Serial.println(F("Starting UDP")); Udp.begin(localPort); Serial.print(F("Local port: ")); Serial.println(Udp.localPort()); Serial.println(F("waiting for sync")); setSyncProvider(getNtpTime); setSyncInterval(syncIntervalMax); startOTAhandling(); ArduinoOTA.setHostname(OTA_host); // fuer Flashen ueber WLAN ArduinoOTA.begin(); // fuer Flashen ueber WLAN } time_t prevDisplay = 0; // when the digital clock was displayed //______________________________________________________________________ void loop(){ ArduinoOTA.handle(); //for OTA-Flashing // Reboot if WiFi connection was lost if (WiFi.status() != WL_CONNECTED) { ESP.restart(); } if (timeStatus() != timeNotSet) { if (now() != prevDisplay) { //update the display only if time has changed prevDisplay = now(); digitalClockDisplay(); // use digitalClockDisplay_minor() if you need the same output like the reference sketch } } // do something different } // end of loop //______________________________________________________________________ // print local date time to serial void digitalClockDisplay(){ char readable[20]; // declare a char buffer large enough for your output uint32_t local_t = nowLocal(); //sprintf(readable, "%02d-%02d-%04d %02d:%02d:%02d", day(local_t), month(local_t), year(local_t), hour(local_t), minute(local_t), second(local_t)); // prefered version sprintf(readable, "%d-%d-%d %d:%d:%d", day(local_t), month(local_t), year(local_t), hour(local_t), minute(local_t), second(local_t)); // prefered version Serial.println(readable); if (year(local_t) == 1970) { ESP.restart(); } // Es kommt manchmal vor, dass 1970 ausgegeben wird int msek = second(local_t); int mmin = minute(local_t); int mstd = hour(local_t); int map_sek; int map_min; int map_std; // Sekunden-Anzeige auf Meter // Ausgleich der Unlinearitaet des Sekunden-Meters // via: map(value, fromLow, fromHigh, toLow, toHigh) if (msek <=5) { map_sek = map(msek, 0,5,0,75); analogWrite(sekInstr,map_sek); } else if (msek >=6 & msek <=10) { map_sek = map(msek, 6,10,90,160); analogWrite(sekInstr,map_sek); } else if (msek >=11 & msek <=20){ map_sek = map(msek, 11,20,175,340); analogWrite(sekInstr,map_sek); } else if (msek >=21 & msek <=30){ map_sek = map(msek, 21,30,360,520); analogWrite(sekInstr,map_sek); } else if (msek >=31 & msek <=40){ map_sek = map(msek, 31,40,540,705); analogWrite(sekInstr,map_sek); } else if (msek >=41 & msek <=50){ map_sek = map(msek, 41,50,720,875); analogWrite(sekInstr,map_sek); } else if (msek >=51 & msek <=55){ map_sek = map(msek, 51,55,890,960); analogWrite(sekInstr,map_sek); } else if (msek >=56) { map_sek = map(msek, 56,59,975,1020); analogWrite(sekInstr,map_sek); } // Minuten-Anzeige auf Meter // Ausgleich der Unlinearitaet des Minuten-Meters // via: map(value, fromLow, fromHigh, toLow, toHigh) if (mmin <=5) { map_min = map(mmin, 0,5,0,62); analogWrite(minInstr,map_min); } else if (mmin >=6 & mmin <=10){ map_min = map(mmin, 6,10,75,137); analogWrite(minInstr,map_min); } else if (mmin >=11 & mmin <=20){ map_min = map(mmin, 11,20,150,285); analogWrite(minInstr,map_min); } else if (mmin >=21 & mmin <=30){ map_min = map(mmin, 21,30,300,445); analogWrite(minInstr,map_min); } else if (mmin >=31 & mmin <=40){ map_min = map(mmin, 31,40,455,595); analogWrite(minInstr,map_min); } else if (mmin >=41 & mmin <=50){ map_min = map(mmin, 41,50,615,748); analogWrite(minInstr,map_min); } else if (mmin >=51 & mmin <=55){ map_min = map(mmin, 51,55,765,825); analogWrite(minInstr,map_min); } else if (mmin >=56 & mmin <=59){ map_min = map(mmin, 56,59,840,883); analogWrite(minInstr,map_min); } if ( mstd >=0 && mstd <=7 ) {digitalWrite(plexi, HIGH); } // Zwischen 00:00:00 und 07:59:59 Plexiglas-Beleuchtung ausschalten else if( (mstd >=20 && mstd <=23) ){ digitalWrite(plexi, LOW); } // Zwischen 20:00:00 und 23:59:59 Plexiglas-Beleuchtung einschalten else if ( msek ==0 ) {digitalWrite(plexi, LOW); } // Jede 0te-Sekunde Plexiglas-Beleuchtung einschalten else {digitalWrite(plexi, HIGH);} // Plexiglas-Beleuchtung ausschalten // Stunden-Anzeige auf Meter switch (mstd) { case 0: analogWrite(stdInstr, 0); break; case 1: analogWrite(stdInstr, 63); break; case 2: analogWrite(stdInstr, 130); break; case 3: analogWrite(stdInstr, 200); break; case 4: analogWrite(stdInstr, 272); break; case 5: analogWrite(stdInstr, 343); break; case 6: analogWrite(stdInstr, 413); break; case 7: analogWrite(stdInstr, 490); break; case 8: analogWrite(stdInstr, 560); break; case 9: analogWrite(stdInstr, 630); break; case 10: analogWrite(stdInstr, 702); break; case 11: analogWrite(stdInstr, 775); break; case 12: analogWrite(stdInstr, 845); break; case 13: analogWrite(stdInstr, 63); break; case 14: analogWrite(stdInstr, 130); break; case 15: analogWrite(stdInstr, 200); break; case 16: analogWrite(stdInstr, 272); break; case 17: analogWrite(stdInstr, 343); break; case 18: analogWrite(stdInstr, 413); break; case 19: analogWrite(stdInstr, 490); break; case 20: analogWrite(stdInstr, 560); break; case 21: analogWrite(stdInstr, 630); break; case 22: analogWrite(stdInstr, 702); break; case 23: analogWrite(stdInstr, 775); break; } // Ende Stunden ausgeben } // end of print local date time to serial /*-------- NTP code ----------*/ const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message byte packetBuffer[NTP_PACKET_SIZE]; // buffer to hold incoming & outgoing packets //______________________________________________________________________ time_t getNtpTime(){ IPAddress ntpServerIP; // NTP server's ip address while (Udp.parsePacket() > 0) ; // discard any previously received packets Serial.println(F("Transmit NTP Request")); // get a random server from the pool // Serial.println(millis()); // Debug only if (WiFi.hostByName(ntpServerName, ntpServerIP, ntpWaitActual) == 1) // third parameter introduced with ESP Core 2.4.0 { // Serial.println(millis()); // Debug only Serial.print(ntpServerName); Serial.print(F(": ")); Serial.println(ntpServerIP); sendNTPpacket(ntpServerIP); // Serial.print(F("ntpWaitActual=")); Serial.println(ntpWaitActual); // Debug only uint32_t beginWait = millis(); uint32_t endWait = 0; while (millis() - beginWait < ntpWaitActual) { int size = Udp.parsePacket(); if (size >= NTP_PACKET_SIZE) { endWait = millis(); // Serial.println(beginWait); // Debug only // Serial.println(endWait); // Debug only if ((endWait - beginWait) + 200 < ntpWaitMax) ntpWaitActual = (endWait - beginWait) + 200; // reduce wait time if possible. Serial.println(F("Receive NTP Response")); Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer unsigned long secsSince1900; // convert four bytes starting at location 40 to a long integer secsSince1900 = (unsigned long)packetBuffer[40] << 24; secsSince1900 |= (unsigned long)packetBuffer[41] << 16; secsSince1900 |= (unsigned long)packetBuffer[42] << 8; secsSince1900 |= (unsigned long)packetBuffer[43]; setSyncInterval(syncIntervalMax); //return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR; // plus timeZone ist böse return secsSince1900 - 2208988800UL ; // Important: Systemtime = UTC!!! } } Serial.println(F("No NTP response :-(")); delay(1000); ESP.restart(); } else{ // Serial.println(millis()); // Debug only // Serial.println(F("No DNS lookup :-(")); } if (ntpWaitActual + 200 < ntpWaitMax) ntpWaitActual += 200; // expand wait time if necessary - I'm pretty aware of that this value will hardly reach ntpWaitMax with this simple condition. setSyncInterval(syncIntervalAfterFail); // in both cases: no NTP response or no DNS lookup return 0; // return 0 if unable to get the time } //______________________________________________________________________ // send an NTP request to the time server at the given address void sendNTPpacket(IPAddress &address){ // set all bytes in the buffer to 0 memset(packetBuffer, 0, NTP_PACKET_SIZE); // Initialize values needed to form NTP request // (see URL above for details on the packets) packetBuffer[0] = 0b11100011; // LI, Version, Mode packetBuffer[1] = 0; // Stratum, or type of clock packetBuffer[2] = 6; // Polling Interval packetBuffer[3] = 0xEC; // Peer Clock Precision // 8 bytes of zero for Root Delay & Root Dispersion packetBuffer[12] = 49; packetBuffer[13] = 0x4E; packetBuffer[14] = 49; packetBuffer[15] = 52; // all NTP fields have been given values, now // you can send a packet requesting a timestamp: Udp.beginPacket(address, 123); //NTP requests are to port 123 Udp.write(packetBuffer, NTP_PACKET_SIZE); Udp.endPacket(); } // _____________________________________________________ // returns the current date/time as UNIX timestamp, incl. timezone, including daylightsaving uint32_t nowLocal(){ uint32_t local_t = now(); if (isDayLightSaving(local_t)) local_t += 3600 + timeZone * SECS_PER_HOUR; else local_t += timeZone * SECS_PER_HOUR; return local_t; } // _____________________________________________________ // calculates the daylight saving time for middle Europe. Input: Unixtime in UTC (!) boolean isDayLightSaving(uint32_t local_t) { if (month(local_t) < 3 || month(local_t) > 10) return false; // no DSL in Jan, Feb, Nov, Dez if (month(local_t) > 3 && month(local_t) < 10) return true; // DSL in Apr, May, Jun, Jul, Aug, Sep // if (month == 3 && (hour + 24 * day) >= (1 + tzHours + 24 * (31 - (5 * year / 4 + 4) % 7)) || month == 10 && (hour + 24 * day) < (1 + tzHours + 24 * (31 - (5 * year / 4 + 1) % 7))); if (month(local_t) == 3 && (hour(local_t) + 24 * day(local_t)) >= (1 + 24 * (31 - (5 * year(local_t) / 4 + 4) % 7)) || month(local_t) == 10 && (hour(local_t) + 24 * day(local_t)) < (1 + 24 * (31 - (5 * year(local_t) / 4 + 1) % 7))) return true; else return false; } // _____________________________________________________