Sådan bruger du NTP med Aduino Leonardo Placeret på 2020-10-25 Arduino Leonardo - NTP Request #include // You can find the TimeLib and the ds3231 library on my github page https://github.com/Sani7/NTP-config_reader-DS3231 #include struct ts t; #include #define ssid "SSID" // Put you're WiFi SSID here #define pswd "PSWD" // Put you're WiFi PSWD here // NTP stuff // Get Time from NTP server (Network Time Protocol, RFC 5905) // NTP uses UDP/IP packets for data transfer because of the fast // connection setup and response times. // The official port number for the NTP (that ntpd and ntpdate listen and talk to) is 123. // The unit of time is in seconds, and the epoch is 1 January 1900. // its gives you UTC time, which is the time at Greenwich Meridian (GMT) // The NTP timestamp is a 64 bit binary value, built from an unsigned 32-bit seconds value // and a 32-bit fractional part. In this notation the value 3.5 would be represented by the 64-bit string: // 0000|0000|0000|0000|0000|0000|0000|0011 . 1000|0000|0000|0000|0000|0000|0000|0000 // If you take all the bits as a 64 bit unsigned integer, // store it in a floating point variable with at least 64 bits of mantissa (usually double) // and do a floating point divide by 2^32, you'll get the right answer. // On a standard arduino unfortunately we don't have 64 bits doubles. // but most RTC have just second level info, so no need to get the second half (or first byte possibly) // Only the first four bytes of an outgoing NTP packet need to be set for what we want to achieve // appropriately, the rest can be whatever. //The header fields of the NTP message are as follows: // //LI Leap Indicator (2 bits) //This field indicates whether the last minute of the current day is to have a leap second applied. The field values follow: //0: No leap second adjustment //1: Last minute of the day has 61 seconds //2: Last minute of the day has 59 seconds //3: Clock is unsynchronized // //VN NTP Version Number (3 bits) (current version is 4). // //Mode NTP packet mode (3 bits) //The values of the Mode field follow: //0: Reserved //1: Symmetric active //2: Symmetric passive //3: Client //4: Server //5: Broadcast //6: NTP control message //7: Reserved for private use // //Stratum level of the time source (8 bits) //The values of the Stratum field follow: //0: Unspecified or invalid //1: Primary server //2–15: Secondary server //16: Unsynchronized //17–255: Reserved // //Poll Poll interval (8-bit signed integer) //The log2 value of the maximum interval between successive NTP messages, in seconds. // //Precision Clock precision (8-bit signed integer) //The precision of the system clock, in log2 seconds. static const unsigned long ntpFirstFourBytes = 0xEC0600E3; // NTP request header, first 32 bits const uint8_t NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message // after a successful querry we will get a 48 byte answer from the NTP server. // To understand the structure of an NTP querry and answer // see http://www.cisco.com/c/en/us/about/press/internet-protocol-journal/back-issues/table-contents-58/154-ntp.html // so if we want to read the "Transmit Timestamp" then we need to // Read the integer part which are bytes 40,41,42,43 // if we want to round to the nearest second if we want some accuracy // Then fractionary part are byte 44,45,46,47 // if it is greater than 500ms byte 44 will be > 128 // and thus by only checking byte 44 of the answer we can round to the next second; // 90% of the NTP servers have network delays below 100ms // We can also account for an assumed averaged network delay of 50ms, and thus instead of // comparing with 128 you can compare with (0.5s - 0.05s) * 256 = 115; #define SECONDROUNDINGTHRESHOLD 115 // the epoch for NTP starts in year 1900 while the epoch in UNIX starts in 1970 // Unix time starts on Jan 1 1970. 7O years difference in seconds, that's 2208988800 seconds #define SEVENTYYEARS 2208988800UL // to transform a number of seconds into a current time you need to do some maths #define NUMBEROFSECONDSPERDAY 86400UL #define NUMBEROFSECONDSPERHOUR 3600UL #define NUMBEROFSECONDSPERMINUTE 60UL // you might not leave in UTC time zone, set up your delta time in hours #define UTC_DELTA_HOURS 2 /*Set offset time to adjust for your timezone, for example: * GMT +1 * GMT +2 * GMT +8 * GMT -1 * GMT 0 */ #define UTC_DELTA_MINUTES 0 const long UTC_DELTA = ((UTC_DELTA_HOURS * NUMBEROFSECONDSPERHOUR) + (UTC_DELTA_MINUTES*NUMBEROFSECONDSPERMINUTE)); // MANAGE the ESP8266. Using an ESP - 01 // we control the reset pin through the arduino #define hardRestPIN 22 #define SHORT_PAUSE 1000UL #define LONG_PAUSE 5000UL const char * OK_STR = "OK\r\n"; #define ESPSERIALBAUD 115200 // Set to whatever is the default for your ESP. /* * ===================================================================================== * -------------------------------------- * emptyESP_RX waits for duration ms * and get rid of anything arriving * on the ESP Serial port during that delay * -------------------------------------- */ void emptyESP_RX(unsigned long duration) { unsigned long currentTime; currentTime = millis(); while (millis() - currentTime <= duration) { if (Serial1.available() > 0) Serial1.read(); } } /* * -------------------------------------- * waitForString wait max for duration ms * while checking if endMarker string is received * on the ESP Serial port * returns a boolean stating if the marker * was found * -------------------------------------- */ boolean waitForString(const char * endMarker, unsigned long duration) { int localBufferSize = strlen(endMarker); // we won't need an \0 at the end char localBuffer[localBufferSize]; int index = 0; boolean endMarkerFound = false; unsigned long currentTime; memset(localBuffer, '\0', localBufferSize); // clear buffer currentTime = millis(); while (millis() - currentTime <= duration) { if (Serial1.available() > 0) { if (index == localBufferSize) index = 0; localBuffer[index] = (uint8_t) Serial1.read(); endMarkerFound = true; for (int i = 0; i < localBufferSize; i++) { if (localBuffer[(index + 1 + i) % localBufferSize] != endMarker[i]) { endMarkerFound = false; break; } } index++; } if (endMarkerFound) break; } return endMarkerFound; } /* -------------------------------------- * espATCommand executes an AT commmand * checks if endMarker string is received * on the ESP Serial port for max duration ms * returns a boolean stating if the marker * was found * -------------------------------------- */ boolean espATCommand(const char * command, const char * endMarker, unsigned long duration) { Serial1.println(command); return waitForString(endMarker, duration); } /* -------------------------------------- * epochUnixNTP returns the UNIX time * number of seconds sice Jan 1 1970 * adjusted for timezoneDeltaFromUTC * -------------------------------------- */ unsigned long epochUnixNTP() { unsigned long secsSince1900 = 0UL; unsigned long epochUnix; // AT+CIPSTART="UDP","be.pool.ntp.org",123 espATCommand("AT+CIPSTART=\"UDP\",\"be.pool.ntp.org\",123", OK_STR, LONG_PAUSE); espATCommand("AT+CIPSEND=48", OK_STR, SHORT_PAUSE); //send 48 (NTP_PACKET_SIZE) bytes emptyESP_RX(1000UL); // empty the buffer (we get a > character) Serial1.write((char*) &ntpFirstFourBytes, NTP_PACKET_SIZE); // the first 4 bytes matters, then we don't care, whatever is in the memory will do // skip AT command answer ("Recv 48 bytes\r\n\r\nSEND OK\r\n\r\n+IPD,48:") waitForString(":", SHORT_PAUSE); // read the NTP packet, extract the TRANSMIT TIMESTAMP in Seconds from bytes 40,41,42,43 for (int i = 0; i < NTP_PACKET_SIZE; i++) { while (!Serial1.available()); int c = Serial1.read(); if ((i >= 40) && (i < 44)) secsSince1900 = (secsSince1900 << 8) + (unsigned long) ((uint8_t) (c & (int) 0x00FF)); // Read the integer part of sending time else if (i == 44) secsSince1900 += (((uint8_t) c) > SECONDROUNDINGTHRESHOLD ? 1 : 0); } // subtract seventy years: epochUnix = secsSince1900 - SEVENTYYEARS; espATCommand("AT+CIPCLOSE", OK_STR, SHORT_PAUSE); // close connection return epochUnix + UTC_DELTA; } // ===================================================================================== void setup() { Serial.begin(115200); while (!Serial); Serial1.begin(ESPSERIALBAUD); while (!Serial1); //reset the device (pull the RST pin to ground) digitalWrite(hardRestPIN, LOW); delay(100); digitalWrite(hardRestPIN, HIGH); // connect to my WiFi network espATCommand("AT+RESTORE", "ready", LONG_PAUSE); // reset emptyESP_RX(SHORT_PAUSE); espATCommand("AT", OK_STR, SHORT_PAUSE); //is all OK? espATCommand("AT+CWMODE=1", OK_STR, SHORT_PAUSE); //Set the wireless mode espATCommand("AT+CWQAP", OK_STR, SHORT_PAUSE); //disconnect - it shouldn't be but just to make sure espATCommand("AT+CWJAP=\""ssid"\",\""pswd"\"", "OK", LONG_PAUSE); // connect to wifi espATCommand("AT+CIPMUX=0", OK_STR, SHORT_PAUSE); //set the single connection mode Wire.begin(); // Initialize the control register of the DS3231 module DS3231_init(0x00); // Get epochTime and convert it to hours, minutes, seconds, day, month, year unsigned long epochTime = epochUnixNTP(); t.hour=hour(epochTime); t.min=minute(epochTime); t.sec=second(epochTime); t.mday=day(epochTime); t.mon=month(epochTime); t.year=year(epochTime); // Write date and time to the DS3231 module DS3231_set(t); } void loop() { // Get date and time from the DS3231 module and print it in the serial monitor DS3231_get(&t); char currentTime[8]; sprintf(currentTime, "%d:%d:%d", t.hour, t.min, t.sec); Serial.print("Current time: "); Serial.println(currentTime); char currentDate[9]; sprintf(currentDate, "%d/%d/%d", t.mday, t.mon, t.year); Serial.print("Current date: "); Serial.println(currentDate); delay(1000); } Forrige side 6. ESP8266 Nuværende side 7. Fuldt program 1. Introduktion 2. Skematisk 3. Hvordan virker NTP? 4. DS3231 modul 5. LCD-skærmen 6. ESP8266 7. Fuldt program Sendt af Sander Speetjens Kommentarer I2C LCD interface modul REAL DS3231 ESP-01 USB adapter Arduino Leonardo - klon 20x4 Characters lcd-modul blå ESP8266 ESP-01S WIFI-modul