yettiz
Published © CC0

Easily check the accuracy of the RTC from the ESP32

Chech the accuracy of the ESP32 RTC and show on display and save dates on Webserver

BeginnerFull instructions provided6 hours133
Easily check the accuracy of the RTC from the ESP32

Things used in this project

Hardware components

esp32 30pin
×1
SH1106 1,3 inch OLED
×1
Resistor 10k ohm
Resistor 10k ohm
×1
Pushbutton switch 12mm
SparkFun Pushbutton switch 12mm
×1
Jumper wires (generic)
Jumper wires (generic)
×1
Breadboard (generic)
Breadboard (generic)
×1
Raspberry Pi 3 Model B
Raspberry Pi 3 Model B
or anything with webserver apache and php (Not absolutely necessary )
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Schematics

Easily Timecomparison for ESP32 Breadboard

Easily Timecomparison for ESP32 PHP

<?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";
?>

Chart timemessure

Code

Easily Timecomparison for ESP32

Arduino
with display and push-button
/* 
 *  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();
}

Credits

yettiz

yettiz

2 projects • 0 followers

Comments