Gestion d'un serveur de temps NTP sur ESP32 en WiFi

Bonjour à toutes et tous,

comme la question revient souvent et qu'il n'y a pas encore un code d'exemple complet dans les tutos qui gère :

  • la connexion à un réseau WiFi,
  • la configuration d'un serveur NTP avec la timezone réglée pour la France, passage automatique en heure d'hiver ou d'été ,
  • la gestion de la déconnexion et reconnexion au réseau WiFi,

je me suis dit que j'allais poster un petit code qui pourrait servir de référence.

Il est testable sur Wokwi ici:

Voici le code, il faudra bien sûr mettre à jour cette partie au début du code pour fournir vos propres informations (cela suppose que l'on utilise une box WiFi comme point d'accès qui fournira une adresse par DHCP à votre ESP32 et qu'ensuite votre ESP32 puisse accéder à Internet pour rejoindre le service NTP sur fr.pool.ntp.org.

const char *ssid = "******";
const char *wifiPassword = "********";
/*
  # ============================================
  # code is placed under the MIT license
  #  Copyright (c) 2024 J-M-L
  #  For the Arduino Forum : https://forum.arduino.cc/u/j-m-l
  #
  #  Permission is hereby granted, free of charge, to any person obtaining a copy
  #  of this software and associated documentation files (the "Software"), to deal
  #  in the Software without restriction, including without limitation the rights
  #  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  #  copies of the Software, and to permit persons to whom the Software is
  #  furnished to do so, subject to the following conditions:
  #
  #  The above copyright notice and this permission notice shall be included in
  #  all copies or substantial portions of the Software.
  #
  #  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  #  IMPLIED, INCLUDING BUT NOT

  LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  #  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  #  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  #  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  #  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  #  THE SOFTWARE.
  #  ===============================================
*/

#include <WiFi.h>
#include <esp_sntp.h> // https://github.com/espressif/esp-idf/blob/v5.2/components/lwip/include/apps/esp_sntp.h (doc https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/system_time.html)

const char *ssid = "******";
const char *wifiPassword = "********";
const char* ntpServer = "fr.pool.ntp.org"; // French one, use "pool.ntp.org" to be generic

bool synchroNtpEffectuee = false;
bool wifiOK = false;
time_t dernierSynchroNTP;

void ntpSyncCallback(struct timeval *tv) {
  Serial.println("NTP synchronisé");
  dernierSynchroNTP = time(NULL);
  Serial.print("Synchro NTP effectuée le ");
  struct tm *pTime = localtime(&dernierSynchroNTP);
  Serial.println(pTime, "%d/%m/%Y %H:%M:%S"); // https://github.com/espressif/arduino-esp32/blob/ccacb7e3d1dd0e58b309c83e5ebc2302ce97c7b2/cores/esp32/Print.h#L98
  synchroNtpEffectuee = true;
}

void WiFiStationConnection(WiFiEvent_t event, WiFiEventInfo_t info) {
  Serial.println("...Connexion réseau établie");
}

void WiFiStationObtentionIP(WiFiEvent_t event, WiFiEventInfo_t info) {
  Serial.print("Obtention d'une adresse IP = ");
  Serial.println(WiFi.localIP());
  wifiOK = true;
}

void WiFiStationDeconnection(WiFiEvent_t event, WiFiEventInfo_t info) {
  wifiOK = false;
  Serial.print("Deconnexion du réseau. (raison : )");
  Serial.println(info.wifi_sta_disconnected.reason);
  Serial.println(")\nTentative de reconnexion.");
  WiFi.begin(ssid, wifiPassword); 
}

// fonction qui affiche le moment courant toutes les secondes (on peut forcer l'affichage en passant true)
bool afficherTemps(bool affichageForce = false) {
  static unsigned long dernierAffichage = -2000;
  if (synchroNtpEffectuee)
    if (affichageForce || (millis() - dernierAffichage >= 1000)) {
      time_t timestamp = time( NULL );
      struct tm *pTime = localtime(&timestamp );
      Serial.println(pTime, "%d/%m/%Y %H:%M:%S"); // https://github.com/espressif/arduino-esp32/blob/ccacb7e3d1dd0e58b309c83e5ebc2302ce97c7b2/cores/esp32/Print.h#L98
      dernierAffichage = millis();
    }
  return synchroNtpEffectuee;
}

void setup() {
  Serial.begin(115200);
  Serial.println("Démonstration de la gestion de l'heure par NTP");

  Serial.println("Connexion au réseau WiFi");
  // des callbacks pour savoir si le réseau est fonctionnel
  WiFi.onEvent(WiFiStationConnection, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_CONNECTED);
  WiFi.onEvent(WiFiStationObtentionIP, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_GOT_IP);
  WiFi.onEvent(WiFiStationDeconnection, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_DISCONNECTED);

  // configurer la lecture de l'heure courante par NTP, en fixant la timezone :
  // dernier dimanche de mars à 2:00 : GMT+2
  // dernier dimanche d'octobre à 3:00 : GMT+1
  configTzTime("CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00", ntpServer);

  // un callback pour être notifié qu'on a réussi à synchroniser le temps avec le serveur NTP
  sntp_set_time_sync_notification_cb(ntpSyncCallback);

  WiFi.begin(ssid, wifiPassword);
}

void loop() {
  afficherTemps();

  // Si votre code nécessite le WiFi on peut tester
  if (wifiOK) {
    // •••
  }

  // ici le code qui ne nécessite pas le WiFi


}

1 Like

Merci @J-M-L, ça servira souvent !
Une petite coquille ci-dessus, pas bien grave :wink:

merci c'est corrigé

Bonjour @J-M-L,

Un grand merci pour cette contribution. C'est simple et limpide. N'ayant pas encore goûté à l'ESP32, je vais essayer de la porter sur ESP8266.

Une petite question : je ne comprends pas l'initialisation à une valeur négative dans la ligne

static unsigned long dernierAffichage = -2000;

Super astuce ou faute de frappe ?

Merci d'avance,

MicroQuettas

La valeur négative est convertie en très grande valeur positive car c’est un unsigned long

C’est pour que la première comparaison au début du sketch alors qu’une seconde ne s’est peut être pas encore écoulée soit validée immédiatement

(J’aurais pu initialiser à -1000 ce serait suffisant)

1 Like

Bonsoir @J-M-L,

Merci beaucoup pour l'explication.

Bonne soirée

MicroQuettas

Merci !
Simple et concis, vive les callbacks !

Roland

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.