Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
|
While looking for the accuracy of the ESP32 RTC, the idea came up to measure it and continue working with the data.
The ESP32 should get the time from a time server via WiFi and compare it with the RTC of the ESP. The times and the time difference should be shown on the display. Between the measurements, the ESP should go into deep sleep mode. In addition, the data should be saved on a web server.
A Raspberry Pi with Apache and PHP serves as the web server.
The structureThe SH1106 display is connected via I2C, with GPIO22 on the ESP with SCK on the Display (yellow line) and GPIO21 with SDA (green line). Ground with ground (black line) and 3.3V with VDD (red wire). The 10k Ohm resitor comes from 3.3v and go to GPIO15 (white line) and the push button. Ground go to push button (black line).
AttentionIn this Sketch you have to set your Wlan-Name and Wlan-Password, your Router or Timeserver IP, your URL of the Webside, the Messurement Cycle is 5 min (must between 2 and 59 min).
The Timezone is GMT+1 with Daylightsavingtime, you may have to change it.
Easily Timecomparison for ESP32 PHP
echo "Executing: insert_time.php";
$con= mysqli_connect("localhost", "PHP_User",
"PHP_Password", "Database_db"); // (Host, User, Password, Databasename, "port")
if ($con->connect_errno) {
die("Connection Error: " . $con->connect_error);
}
$p = $_GET["p"];
$e = $_GET["e"];
$t = $_GET["t"];
$z = $_GET["z"];
echo "<br>p = ".$p." e =".$e. " t =".$t. " z =".$z. " v =".$v;
$sql ="INSERT INTO Timemessure (ptbtime, esptime, timedif, zpm) VALUES ('$p', '$e', '$t', '$z')";
mysqli_query($con, $sql);
mysqli_close($con);
echo "<br>done";
?>
/*
* Easily check the accuracy of the RTC from the ESP32 by yeTTiz (twitter.com/yeTTizTV)
*
* In this projekt you can check the RTC of the ESP32 make a Time comparison with Timeserver
* The time is saved on a web server with PHP and mysql and can be shown on the sh1106 display using a button.
* A timer server is queried via WiFi and compared with the RTC time of the ESP.
* Every 24 hours the RTC time of the ESP is set to the ntp time.
* The measurement is carried out every x minutes,
* then the new deep sleep time is calculated and the ESP is switched to deep sleep mode.
*
*
*
* Contains code from Michael Margolis Udp NTP Client created 4 Sep 2010
*
* This code is in the public domain.
*/
#include <WiFi.h>
#include <Arduino.h>
#include <Wire.h>
//#include <Adafruit_Sensor.h>
#include <U8g2lib.h>
#include <HTTPClient.h>
#include <time.h>
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
// Only GPIO15
// 00000000000000001000000000000000
#define BUTTON_PIN_BITMASK2 0x8000 // GPIO 15 -- GPIO for Wakeup-Button --
static const int GPIOPin15 = 15; // to set GPIO to HIGH
const char* ssid = "ESPWLAN"; // Your WLAN Name
const char* password = "ESP32Passwort123"; // Your WLAN Password
const char timeServer[] = "192.168.22.1"; // Your Router or your timeserver
const char* URL = "http://192.168.22.55/insert_time.php?"; // Your Webside to store Data
const uint16_t messcycle = 5; // Measuring cycle in minutes (minimum 2 min und maximum 59min)
int TIME_TO_SLEEP = messcycle * 60; // messcycle in seconds
const int $DST = 1; // 0 for Wintertime, 1 for Summertime
unsigned int localPort = 8888; // local port to listen for UDP packets
const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
unsigned int WiFiTryCounter = 0;
String $datum = "";
String $zeit = "";
String $esptime = "";
String $ntptime = "";
String $ntphour = "";
String $ntpmin = "";
String $ntpsec = "";
String $timedif = "";
String $strdifpm = "";
float $difpm = 0.0;
time_t $ntpunixtime = time(0);
time_t $espunixtime = time(0);
int year;
byte month, day, hour, minute, second;
RTC_DATA_ATTR int beginntime = 99; // to store the minute of the first measurement, it is retained after DeepSleep
RTC_DATA_ATTR int bootcount = 0;
WiFiUDP Udp; // A UDP instance to let us send and receive packets over UDP
WiFiServer server(80);
IPAddress apIP(192, 168, 22, 44); // IP-Adresse of ESP32
void print_wakeup_reason();
void setup() {
Serial.begin(115200);
Serial.println("Waking up...");
pinMode(GPIOPin15, INPUT_PULLUP);
u8g2.begin(); // init Display
Serial.println("conectWIFI()");
conectWIFI(); // connectt Wlan
if (bootcount == 0) {
Serial.println("Get time from timeserver");
getPTB(); // Get time from timeserver and store in locale RTC in ESP32
}
setenv("TZ", "CET-1CEST,M3.5.0,M10.5.0/3", 1); // Set local time with daylight saving time
//++bootcount of timerboot;
if (bootcount > (1440 / messcycle)){ // After ~24 hours, the time should be synchronized by the time server
bootcount = 0; // at the next start
}
getNTP(); // Get time from timeserver to calculate
Serial.println("Readtime()");
readtime(); // Get time from ESP and calculate time difference
if (($timedif.toInt()) > 0){
$difpm = float($timedif.toFloat()) / (bootcount + 1); // Calculate the average time difference per measurement cycle
$strdifpm = String($difpm, 3);
}
if (beginntime == 99){
doAction(); // Send data to webserver
doAction2();
beginntime = minute; // Store minutes of the first messurment
Serial.print("Minute = ");
Serial.println(minute);
}
Serial.println("Print Wakeup Reason");
print_wakeup_reason(); // Evaluate the reason for waking up
Serial.println("calcnext");
calcnext(); // calculate the next DeepSleepTime
Serial.println("Ausgabe Serial ..");
showonserial(); // Show Data on Serial-Monitor
esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK2, ESP_EXT1_WAKEUP_ALL_LOW); // set wakeup GPIO
Serial.println("startDeepSleep()");
startDeepSleep();
}
void loop(void) {
}
//================= Funktion connect Wlan =============================
void conectWIFI(){
delay(500); // Connect to WiFi network
Serial.println();
Serial.printf("Connecting to ", ssid);
WiFi.begin(ssid, password);
while ((WiFi.status() != WL_CONNECTED) && (WiFiTryCounter < 10)) { //10 try to connect
delay(500);
WiFiTryCounter++;
Serial.print("Connecting to WiFi..");
}
if ((WiFi.status() == WL_CONNECTED)) {
Serial.println("");
Serial.println("WiFi connected"); // Print the IP address
Serial.println(WiFi.localIP());
}else
{
Serial.println("");
Serial.println("WiFi not connected, restart in 10 Sekunden");
delay(10000);
ESP.restart(); // ESP restart
}
}
//================= Funktion get time from timeserver and store in ESP ====================
void getPTB(){
configTime(0, 0, "192.168.22.1", "ptbtime1.ptb.de", "ptbtime2.ptb.de"); // get Time of locale Router or ptb.de and store in RTC of ESP
Serial.println("Waiting for time ");
while (!time(nullptr)) {
Serial.print(".");
delay(100);
}
delay(100); // wait for PTB time
}
//================= Funktion get time from timeserver to calculate ====================
void getNTP(){
sendNTPpacket(timeServer); // send an NTP packet to a time server
// wait to see if a reply is available
delay(1000);
if (Udp.parsePacket()) { // We've received a packet, read the data from it
Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
// the timestamp starts at byte 40 of the received packet and is four bytes,
// or two words, long. First, extract the two words:
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
unsigned long lowWord = word(packetBuffer[42], 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;
Serial.print("Seconds since Jan 1 1900 = ");
Serial.println(secsSince1900);
// now convert NTP time into everyday time:
Serial.print("Unix time = ");
const unsigned long seventyYears = 2208988800UL; // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
unsigned long epoch = secsSince1900 - seventyYears; // subtract seventy years:
$ntpunixtime = epoch;
Serial.println(epoch); // print Unix time:
// print the hour, minute and second:
Serial.print("The UTC time is "); // UTC is the time at Greenwich Meridian (GMT)
$ntphour = ((epoch %86400L) / 3600) + $DST; // the "+1" is for Daylight Saving Time
Serial.print($ntphour); // print the hour (86400 equals secs per day)
Serial.print(':');
if (((epoch % 3600) / 60) < 10) { // In the first 10 minutes of each hour, we'll want a leading '0'
$ntpmin = "0"; // Serial.print('0');
}
$ntpmin = (String($ntpmin) +((epoch % 3600) / 60)); // print the minute (3600 equals secs per minute)
Serial.print($ntpmin);
Serial.print(':');
if ((epoch % 60) < 10) { // In the first 10 seconds of each minute, we'll want a leading '0'
$ntpsec = "0"; // Serial.print('0');
}
$ntpsec = (String($ntpsec) + (epoch % 60)); //Serial.println(epoch % 60); // print the second
Serial.println($ntpsec);
$ntptime = (String($ntphour) + ":" + $ntpmin + ":" + $ntpsec);
}
}
// === Funktion send an NTP request to the time server at the given address
void sendNTPpacket(const char * 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();
}
//================= Funktion read ESP time ==============================
void readtime() {
time_t now = time(nullptr);
struct tm*timeinfo;
time(&now);
//gettimedif(time(&now));
$timedif = String(time(&now)- $ntpunixtime);
Serial.print("Timedifferenz = ");
Serial.println($timedif);
Serial.print ("time = ");
Serial.println(time(&now));
timeinfo = localtime(&now);
year = 1900 + timeinfo->tm_year;
month = 1 + timeinfo->tm_mon;
day = timeinfo->tm_mday;
hour = timeinfo->tm_hour;
minute = timeinfo->tm_min;
second = timeinfo->tm_sec;
// Serial.printf("%02u-%02u-%4u\n", day, month, year);
Serial.printf("%02u:%02u:%02u \n", hour, minute, second);
$esptime = String(hour) + ":" + minute + ":" + second;
if (year < 2000) { // wrong year and restart
ESP.restart();
}
}
//=========== Funktion Evaluate the reason for waking up ===========================
void print_wakeup_reason(){
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
switch(wakeup_reason)
{
case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO");
//wakeup_reason_ext0();
break;
case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL");
wakeup_reason_ext1(); // Auswerten welcher GPIO Wake_up ausgelöst hat
break;
case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer");
wakeup_reason_timer();
break;
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad");break;
case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program");
default : Serial.println("Wakeup was not caused by deep sleep");break;
}
}
//=========== Funktion Output which GPIO was triggered =========================
void wakeup_reason_ext1(){
Serial.printf("================== GPIO 15 ====================\n");
doAction2();
}
//=========== Funktion Output triggered by timer ================================
void wakeup_reason_timer(){
Serial.printf("================== TIMER ======================\n");
doAction();
}
//=========== Funktion Calculate nexte deepsleeptime ==================================
void calcnext() {
uint16_t actmin = 0;
uint16_t actsec = 0;
if (minute == beginntime){
Serial.printf("Minutes == beginntime, TIME_TO_SLEEP will set to mescycle in seconds \n");
TIME_TO_SLEEP = messcycle * 60; // conversion in seconds
Serial.printf("Messcycle %u Sleeptime %u \n", messcycle, TIME_TO_SLEEP);
} else
{
if (minute > beginntime) {
actmin = (minute - beginntime);
Serial.printf("Minutes > beginntime, past time actmin = %u \n");
} else
{
actmin = (minute + 60 - beginntime);
Serial.printf("Minutes < beginntime, past time actmin = %u \n");
}
actmin = (actmin % messcycle);
Serial.printf ("actmin modulo messcycle = %u \n", actmin );
actmin = (messcycle - actmin);
Serial.printf ("Mintutes to the next messurement = %u \n", actmin);
if (actmin !=0){
if (second != 0) {
Serial.printf("current second = %u , therefore 1 minute is deducted \n", second);
actmin = actmin -1;
}
}
actsec = actmin * 60; // convert minutes in seconds
actsec = actsec + 60 - second; // to get seconds to the next minute
if (actsec > 30) {
Serial.printf("more then 30 seconds \n"); // set Deep_Sleeptime to new Time
TIME_TO_SLEEP = actsec;
} else
{
doAction(); // Save values because less than 30 seconds to the next start
Serial.printf("les then 30 seconds \n"); // set Deep_Sleeptime to messcycle in seconds
//TIME_TO_SLEEP = messcycle * 60;
Serial.printf("akt.Min:Sek %u : %u Messcycle %u Sleeptime %u \n", actmin, actsec, beginntime, TIME_TO_SLEEP);
}
}
}
//=========== Funktion deepspleep =====================
void startDeepSleep(){
delay(500);
u8g2.clearBuffer();
u8g2.sendBuffer();
TIME_TO_SLEEP = TIME_TO_SLEEP + TIME_TO_SLEEP * 5 / 100;
Serial.printf("go to sleep for %u s runtime was %lu ms\n", TIME_TO_SLEEP, millis());
Serial.flush();
WiFi.disconnect(true);
delay(1);
WiFi.mode(WIFI_OFF);
delay(1);
//Serial.println("Going to sleep...");
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * 1000000);
esp_deep_sleep_start();
}
//=========== Funktion show on serial ===========
void showonserial() {
Serial.println("Display on Serial");
Serial.print("PTB-Time: ");
Serial.println($ntptime);
Serial.print("ESP-Time: ");
Serial.println($esptime);
}
//=========== Funktion init Display ==============
void u8g2_prepare(void) {
u8g2.setFont(u8g2_font_6x10_tf);
u8g2.setFontRefHeightExtendedText();
u8g2.setDrawColor(1);
u8g2.setFontPosTop();
u8g2.setFontDirection(0);
}
//=========== Funktion Ausgabe auf Display ==========
void showondisplay() {
Serial.println("Display on external");
u8g2.clearBuffer();
u8g2_prepare();
u8g2.drawStr(0, 0, "NTP-Time: ");
u8g2.drawStr(70, 0, $ntptime.c_str());
u8g2.drawStr(0, 12, "ESP-Time.: ");
u8g2.drawStr(70, 12, $esptime.c_str());
u8g2.drawStr(0, 24, "Differenz: ");
u8g2.drawStr(70, 24, $timedif.c_str());
u8g2.drawStr(100,24, " Sec.");
u8g2.drawStr(0, 36, "Date: "); // display Date
$datum = (String(day) + "." + month + "." + year);
u8g2.drawStr(70, 36, $datum.c_str());
u8g2.drawStr(0, 48, "o dif/mc: "); // Average time difference per measurement cycle
u8g2.drawStr(70, 48, $strdifpm.c_str());
u8g2.sendBuffer();
delay(8500);
}
//=========== Funktion send data to webserver =============
void doAction(){
Serial.println("Doing action...");
++bootcount;
if ((WiFi.status() == WL_CONNECTED)) { //Check the current connection status
HTTPClient http;
String $httpstr = (String(URL) + "p=" + $ntptime.c_str() + "&e=" + $esptime.c_str() + "&t=" + $timedif.c_str()+ "&z=" + $strdifpm.c_str());
Serial.println($httpstr);
http.begin($httpstr); //Specify the URL
int httpCode = http.GET(); //Make the request
if (httpCode > 0) { //Check for the returning code
String payload = http.getString();
Serial.println(httpCode);
Serial.println(payload);
}
else {
Serial.println("Error on HTTP request");
}
http.end(); //Free the resources
}
Serial.println("finish send to Webserver");
}
//================== show only on display ==================
void doAction2(){
Serial.println("Action 2: show on Display");
showondisplay();
}
Comments