poniedziałek, 23 marca 2020

[8] ESP32 - Arduino - NTP

W tym poście chciałbym opisać sposób implementacji klienta NTP.

Znalezione obrazy dla zapytania arduino esp32
[Źródło: http://paulobrien.co.nz/2017/03/16/esp32-programming-with-arduino-on-windows/]

Na samym początku należy zdefiniować dane do połączenia po WIFI oraz nazwę serwera NTP:

  1. #include <WiFi.h>
  2. #include "time.h"
  3. //----------------------------------------------
  4. #define TIME_VALUE 3600
  5. #define GMT_OFFSET 0
  6. #define DAY_OFFSET 3600
  7. //----------------------------------------------
  8. const char* ssid     = "SSID";
  9. const char* password = "PASSWORD";
  10. const char* ntpServer_0 = "0.pl.pool.ntp.org";
  11. const char* ntpServer_1 = "1.pl.pool.ntp.org";
  12. const char* ntpServer_2 = "2.pl.pool.ntp.org";
  13. uint16_t readTimeCounter = 0;

W funkcji setup następuje inicjalizacja UART'u, podłączenie do sieci wifi, ustawienie parametrów do pobrania danych z serwera NTP.

  1. void setup(){
  2.   //-------------------------------------------
  3.   Serial.begin(115200);
  4.   //-------------------------------------------
  5.   Serial.print("Connect to: ");
  6.   Serial.println(ssid);
  7.   Serial.println(password);
  8.   WiFi.begin(ssid, password);
  9.   //-------------------------------------------
  10.   while (WiFi.status() != WL_CONNECTED) {
  11.     delay(1000);
  12.     Serial.print(".");
  13.   }
  14.   //-------------------------------------------
  15.   Serial.println("WiFi connected!!");
  16.   //-------------------------------------------
  17.   configTime(GMT_OFFSET, DAY_OFFSET, ntpServer_0,
  18.   ntpServer_1, ntpServer_2);
  19.   displayTime();
  20.   //-------------------------------------------
  21.   //Disconnect wifi to save power
  22.   WiFi.disconnect(true);
  23.   WiFi.mode(WIFI_OFF);
  24. }

Funkcja odpowiedzialna za połączenie z serwerem NTP została zdefiniowana w pliku time.h.

  1. void configTime(long gmtOffset_sec, int daylightOffset_sec, const char* server1, const char* server2, const char* server3)
  2. {
  3.     tcpip_adapter_init();  // Should not hurt anything if already inited
  4.     if(sntp_enabled()){
  5.         sntp_stop();
  6.     }
  7.     sntp_setoperatingmode(SNTP_OPMODE_POLL);
  8.     sntp_setservername(0, (char*)server1);
  9.     sntp_setservername(1, (char*)server2);
  10.     sntp_setservername(2, (char*)server3);
  11.     sntp_init();
  12.     setTimeZone(-gmtOffset_sec, daylightOffset_sec);
  13. }

Do funkcji można podać maksymalnie trzy rodzaje serwerów. Wyłączanie wifi jest wykonywane w celu oszczędzenia energii.

W pętli głównej wyświetlam czas umieszczony na serwerach NTP. Dodatkowo raz na godzinę wykonuje ponowne połączenie z serwerem NTP.

  1. void loop(){
  2.   delay(1000);
  3.   displayTime();
  4.   if(readTimeCounter >= TIME_VALUE) {
  5.     readTimeCounter = 0;
  6.     connectToWifiAndGetTime();
  7.   }
  8.   readTimeCounter++;
  9. }

Funkcja wykonywająca ponowne odczytanie danych:

  1. void connectToWifiAndGetTime()
  2. {
  3.   uint8_t connectionCounter = 0;
  4.   Serial.print("Reconnect to ");
  5.   Serial.println(ssid);
  6.   WiFi.begin(ssid, password);
  7.   while (WiFi.status() != WL_CONNECTED) {
  8.     delay(1000);
  9.     Serial.print(".");
  10.     connectionCounter++;
  11.    
  12.     if(connectionCounter >= 6)
  13.     {
  14.       Serial.println("Connection error...");
  15.       connectionCounter = 0xFF;
  16.       break;
  17.     }
  18.   }
  19.   if(connectionCounter == 0xFF)
  20.   {
  21.     /* Data calculated with previos time settings */
  22.     return;
  23.   }
  24.   Serial.println("WiFi connected!!");
  25.   configTime(GMT_OFFSET, DAY_OFFSET, ntpServer_0, ntpServer_1, ntpServer_2);
  26.   displayTime();
  27.   WiFi.disconnect(true);
  28.   WiFi.mode(WIFI_OFF);
  29. }

Wyświetlenie czasu:

  1. void displayTime(){
  2.   struct tm timeinfo;
  3.   if(!getLocalTime(&timeinfo)){
  4.     Serial.println("Get local time error!");
  5.     return;
  6.   }
  7.   Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
  8.   Serial.print(" DOW: ");
  9.   Serial.print(&timeinfo, "%A");
  10.   Serial.print(" Month: ");
  11.   Serial.print(&timeinfo, "%B");
  12.   Serial.print(" DOM: ");
  13.   Serial.print(&timeinfo, "%d");
  14.   Serial.print(" Year: ");
  15.   Serial.println(&timeinfo, "%Y");
  16.   Serial.print(" Hour: ");
  17.   Serial.print(&timeinfo, "%H");
  18.   Serial.print(" Hour (12 hour format): ");
  19.   Serial.print(&timeinfo, "%I");
  20.   Serial.print(" Minute: ");
  21.   Serial.print(&timeinfo, "%M");
  22.   Serial.print(" Second: ");
  23.   Serial.println(&timeinfo, "%S");
  24. }

Funkcja odpowiedzialna za odczytanie danych z czasem została zdefiniowana w bibliotekach ESP:

  1. bool getLocalTime(struct tm * info, uint32_t ms)
  2. {
  3.     uint32_t start = millis();
  4.     time_t now;
  5.     while((millis()-start) <= ms) {
  6.         time(&now);
  7.         localtime_r(&now, info);
  8.         if(info->tm_year > (2016 - 1900)){
  9.             return true;
  10.         }
  11.         delay(10);
  12.     }
  13.     return false;
  14. }

Cały projekt wygląda następująco:

  1. #include <WiFi.h>
  2. #include "time.h"
  3. //----------------------------------------------
  4. #define TIME_VALUE 3600
  5. #define GMT_OFFSET 0
  6. #define DAY_OFFSET 3600
  7. //----------------------------------------------
  8. const char* ssid     = "SSID";
  9. const char* password = "PASSWORD";
  10. const char* ntpServer_0 = "0.pl.pool.ntp.org";
  11. const char* ntpServer_1 = "1.pl.pool.ntp.org";
  12. const char* ntpServer_2 = "2.pl.pool.ntp.org";
  13. uint16_t readTimeCounter = 0;
  14. void setup(){
  15.   //-------------------------------------------
  16.   Serial.begin(115200);
  17.   //-------------------------------------------
  18.   Serial.print("Connect to: ");
  19.   Serial.println(ssid);
  20.   Serial.println(password);
  21.   WiFi.begin(ssid, password);
  22.   //-------------------------------------------
  23.   while (WiFi.status() != WL_CONNECTED) {
  24.     delay(1000);
  25.     Serial.print(".");
  26.   }
  27.   //-------------------------------------------
  28.   Serial.println("WiFi connected!!");
  29.   //-------------------------------------------
  30.   configTime(GMT_OFFSET, DAY_OFFSET, ntpServer_0,
  31.   ntpServer_1, ntpServer_2);
  32.   displayTime();
  33.   //-------------------------------------------
  34.   //Disconnect wifi to save power
  35.   WiFi.disconnect(true);
  36.   WiFi.mode(WIFI_OFF);
  37. }
  38. void loop(){
  39.   delay(1000);
  40.   displayTime();
  41.   if(readTimeCounter >= TIME_VALUE) {
  42.     readTimeCounter = 0;
  43.     connectToWifiAndGetTime();
  44.   }
  45.   readTimeCounter++;
  46. }
  47. void connectToWifiAndGetTime()
  48. {
  49.   uint8_t connectionCounter = 0;
  50.   Serial.print("Reconnect to ");
  51.   Serial.println(ssid);
  52.   WiFi.begin(ssid, password);
  53.   while (WiFi.status() != WL_CONNECTED) {
  54.     delay(1000);
  55.     Serial.print(".");
  56.     connectionCounter++;
  57.    
  58.     if(connectionCounter >= 6)
  59.     {
  60.       Serial.println("Connection error...");
  61.       connectionCounter = 0xFF;
  62.       break;
  63.     }
  64.   }
  65.   if(connectionCounter == 0xFF)
  66.   {
  67.     /* Data calculated with previos time settings */
  68.     return;
  69.   }
  70.   Serial.println("WiFi connected!!");
  71.   configTime(GMT_OFFSET, DAY_OFFSET, ntpServer_0, ntpServer_1, ntpServer_2);
  72.   displayTime();
  73.   WiFi.disconnect(true);
  74.   WiFi.mode(WIFI_OFF);
  75. }
  76. void displayTime(){
  77.   struct tm timeinfo;
  78.   if(!getLocalTime(&timeinfo)){
  79.     Serial.println("Get local time error!");
  80.     return;
  81.   }
  82.   Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
  83.   Serial.print(" DOW: ");
  84.   Serial.print(&timeinfo, "%A");
  85.   Serial.print(" Month: ");
  86.   Serial.print(&timeinfo, "%B");
  87.   Serial.print(" DOM: ");
  88.   Serial.print(&timeinfo, "%d");
  89.   Serial.print(" Year: ");
  90.   Serial.println(&timeinfo, "%Y");
  91.   Serial.print(" Hour: ");
  92.   Serial.print(&timeinfo, "%H");
  93.   Serial.print(" Hour (12 hour format): ");
  94.   Serial.print(&timeinfo, "%I");
  95.   Serial.print(" Minute: ");
  96.   Serial.print(&timeinfo, "%M");
  97.   Serial.print(" Second: ");
  98.   Serial.println(&timeinfo, "%S");
  99. }