ESP8266, récupérer l’heure depuis un serveur de temps (NTP), temps écoulé depuis dernier Reset, stocker l’heure en SPIFFS [Màj]

Dans ce troisième tutoriel consacré à la création d’une interface Web pour des projets DIY à base d’ESP8266, nous allons apprendre comment récupérer l’heure depuis un serveur de temps NTP (Network Time Protocole)L’Arduino ainsi que les modules WiFi ESP8266 et ESP32 ne disposent pas d’horloge temps réel (sauf quelques exceptions spécifiques).

Dès que l’on dispose d’une connexion internet, il est très facile de récupérer la date et l’heure courant depuis un serveur externe ou un serveur de temps NTP. On pourra ainsi horodater (timestamp) des mesures, connaître le temps écoulé entre deux événements, afficher l’heure courante sur l’interface WEB, déclencher une action programmée…

 

esp8266 esp32 ntp server date time

 

Si vous rencontrez des difficultés, vous pouvez utiliser ce sujet sur le forum.

Articles précédents du projet à relire

Partie Sujets abordés Dépôt GitHub Article
Partie 1
  • Comment préparer le code HTML de interface Web
    • Pour cela, nous utiliserons le langage Pug (auparavant appelé Jade) qui permet de simplifier l’écriture
    • Nouvelles notions d’HTML : menu de navigation, image, fixer le bas de page (footer), meilleure gestion “responsive” pour les petits écrans
  • Comment changer le thème de l’interface
    • Comment stocker le choix pour recharger le thème au prochain chargement de l’interface
  • Comment préparer et utiliser la zone SPIFFS pour stocker les fichiers HTML, JS, CSS, images
    • et les envoyer sur l’ESP8266
    • Premier test de l’interface WEB sur l’ESP8266
Partie 2 Comment interagir avec le code Arduino. Lire l’article

  • Intercepter les actions sur les boutons de l’interface et actualiser les affichages lorsque l’action a été réalisée (Javascript + jQuery)
    • Récupérer les requêtes sur l’Arduino, exécuter la demande et envoyer la réponse
  • Actualiser régulièrement (et automatiquement) le tableau de mesures et les afficheurs (Javascript)
    • Mettre à jour les symboles si la valeur actuelle est supérieure ou inférieure à la précédente
Partie 4 Comment créer un historique de mesures. Lire l’article

  • Manipuler les données à l’aide de la librairie ArduinoJSON
  • Enregistrer des données (historique de mesure) dans un fichier sur la zone SPIFFS
  • Recharger le fichier historique au démarrage de l’ESP8266
Partie 5 Comment ajouter des graphiques et des jauges Google Charts. Lire l’article

  • Evolution de la température et de l’humidité moyenne sur les 7 dernières heures (histogramme en barre)
  • Affichage de la mesure courante sous la forme d’une jauge thématique : thermomètre (température), goutte d’eau (humidité), jauge (pression atmosphérique)

Librairie NTPClient (la plus facile)

Si vous faites une recherche depuis le gestionnaire de bibliothèque de l’IDE Arduino, vous allez trouvez plusieurs librairies qui permettent de récupérer le temps depuis un serveur NTP

  • NTPClient de Fabrice Weinberg, puissante et simple d’utilisation
  • EasyNTPClient de Harsha Alva, une librairie ultra simplifiée qui ne propose que 3 méthodes. Get time offset, Set time offset et Get time qui permet de récupérer le temps au format  UNIX en secondes. C’est un peu trop limité à mon goût.
  • NTPClientLib de German Martin, elle est basée sur la librairie Time officielle pour Arduino. Elle fonctionne sur ESP8266, ESP32 et Arduino MKR1000
  • Time de Michael Margolis, la librairie initialement développée pour l’Arduino. Elle est difficile à utiliser (comparée au librairies récentes)

Après avoir testé quasiment toutes les librairies NTP sur ESP8266, je vous conseille d’utiliser la librairie NTPClient de Fabrice Weinberg. Elle est très simple à utiliser.

 

esp8266 ntp server ntpclient library

 

Lorsqu’on développe des objets connectés qui fonctionnent sur batterie, on peut souhaiter connaître l’heure rapidement pour déclencher pour appeler éventuellement une autre fonction. La librairie NTPClient permet de faire cela très facilement en forçant la récupération du temps en appelant la méthode forceUpdate().

La librairie NTPCLient utilise une connexion UDP pour se connecter à un serveur NTP. Pour cela, il faudra inclure la librairie standard WiFiUdp.h dans l’entête du croquis.

Ensuite, il est possible d’initialiser l’objet NTPClient avec la configuration par défaut :

  • Serveur : pool.ntp.org
  • Intervalle de mise à jour : 60 secondes
  • Décalage horaire (en secondes) : aucun
NTPClient timeClient(ntpUDP);

Ou de spécifier des paramètres. Ici, on applique un décalage horaire d’une heure

NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000);

Ensuite, il ne reste plus qu’à démarrer le service avec la méthode timeClient.begin(). Ensuite, on peut laisser la librairie actualiser l’heure en fonction de l’intervalle configuré avec la méthode update(), ou forcer l’actualisation manuellement avec la méthode forceUpdate(). C’est idéal pour les projets qui fonctionnent sur batterie.

Voici un petit exemple qui récupère et affiche l’heure toutes les 10 secondes

#include <ESP8266WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>

#define NB_TRYWIFI        10    // Nbr de tentatives de connexion au réseau WiFi - Number of try to connect to WiFi network

const char* ssid = "xxxx";
const char* password = "xxxx";

WiFiClient espClient;
WiFiUDP ntpUDP;

NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000);

void setup() {
  Serial.begin(115200);
  Serial.println("");
  Serial.print("Startup reason:");Serial.println(ESP.getResetReason());

  WiFi.begin(ssid, password);

  Serial.println("Connecting to WiFi.");
  int _try = 0;
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print("..");
    delay(500);
    _try++;
    if ( _try >= NB_TRYWIFI ) {
        Serial.println("Impossible to connect WiFi network, go to deep sleep");
        ESP.deepSleep(10e6);
    }
  }
  Serial.println("Connected to the WiFi network");
  
  // Démarrage du client NTP - Start NTP client
  timeClient.begin();
}

void loop() {
    // Met à jour l'heure toutes les 10 secondes - update time every 10 secondes
    timeClient.update();
    Serial.println(timeClient.getFormattedTime());
}

Stocker le temps dans la zone SPIFFS, calculer le temps écoulé depuis le dernier démarrage ou reset

Dans beaucoup de cas, on voudra conserver une trace du temps que l’on vient de récupérer pour d’autres usages :

  • Calculer le temps qui s’est écoulé depuis le dernier démarrage ou le dernier reset
  • Déclencher une action si le temps écoulé est supérieur à une consigne. Par exemple envoyé l’état d’un détecteur de mouvement si un second mouvement est détecté 30 secondes après le premier. Cela évitera de multiplier les notifications inutiles sur un smartphone
  • Déclencher une action pré-programmée. Par exemple, déclencher un relais qui ouvre une vanne d’arrosage du jardin durant 1 minute…

Pour stocker le temps, il est beaucoup plus facile de l’enregistrer dans un simple fichier texte dans la zone SPIFFS que de le stocker dans l’EEPROM. Pour cela, il suffit de déclarer la librairie fs.h au début du programme.

Ensuite il sera très facile de calculer le temps qui s’est écoulé depuis le dernier démarrage ou le dernier reset. Voici une adaptation du programme précédent. Ls fonctions suivantes ont été ajoutées :

  • saveLastEvent(), enregistre dans un fichier nommé lastEvent.txt l’heure NTP au format EPOCH Unix
  • loadLastEvent(), recharge le dernier temps NTP sauvegardé
  • timeSpent(), calcul le temps écoulé en secondes depuis le dernier démarrage
  • runEvent(), exécute un événement si le temps écoulé est supérieur au temps spécifié dans la variable DELAY_NEXT_EVENT

Vous pouvez également tester en activant le mode Deep-Sleep (passer la clé DEEP_SLEEP à true) du module ESP8266. Lisez ce tutoriel pour tout savoir sur la mise en veille et celui-ci si vous débutez avec les modules ESP8266. La méthode runEvent est exécutée si le temps écoulé dépasse DELAY_NEXT_EVENT.

#include <ESP8266WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include "FS.h"

#define NB_TRYWIFI        10    // Nbr de tentatives de connexion au réseau WiFi - Number of try to connect to WiFi network
#define DELAY_NEXT_EVENT  60    // délai entre deux événements en secondes - delay before new event in second 
#define DEEP_SLEEP        false
const char* ssid = "xxxx";
const char* password = "xxxx";

WiFiClient espClient;
WiFiUDP ntpUDP;

NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000);

int lastEvent;
int start = millis();
long int _now = 0;

bool loadLastEvent(){
  File f = SPIFFS.open("/lastEvent.txt", "r");
  if (!f) {
    Serial.println("Failed to open file");
    return false;
  }
  lastEvent = f.readStringUntil('\n').toInt();
  return true;
}

bool saveLastEvent(){
  File f = SPIFFS.open("/lastEvent.txt", "w");
  if (!f) {
      Serial.println("file open failed");
      return false;
  }
  f.println(_now);
  f.close();
  return true;
}

long int timeSpent(){
  loadLastEvent();
  timeClient.forceUpdate();
  _now = timeClient.getEpochTime();
  int timeSpent = _now - lastEvent;
  Serial.println("Time spent since last alarm (s): ");Serial.print(timeSpent);
  return timeSpent;
}
void runEvent(){
    
  if ( timeSpent() >= DELAY_NEXT_EVENT ) {
    Serial.println("RUN EVENT");
    saveLastEvent();
  } 
}

void setup() {
  Serial.begin(115200);
  Serial.println("");
  Serial.print("Startup reason:");Serial.println(ESP.getResetReason());

  SPIFFS.begin();

  WiFi.begin(ssid, password);

  Serial.println("Connecting to WiFi.");
  int _try = 0;
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print("..");
    delay(500);
    _try++;
    if ( _try >= NB_TRYWIFI ) {
        Serial.println("Impossible to connect WiFi network, go to deep sleep");
        ESP.deepSleep(10e6);
    }
  }
  Serial.println("Connected to the WiFi network");
  
  // Démarrage du client NTP - Start NTP client
  timeClient.begin();
   
  
  if ( DEEP_SLEEP){ 
    runEvent();
    Serial.println("Go to deep sleep");
    ESP.deepSleep(10e6);
  }  
}

void loop() {
  if ( !DEEP_SLEEP ){
    timeClient.update();
    Serial.println(timeClient.getFormattedTime());
    delay(10000); 
  }
}

Librairies NTPClientLib et Time (la plus ancienne, pour mémoire)

Les librairies Time et NTPClientLib sont deux librairies très anciennes. La librairie Time est la librairie officielle qui permet de gérer le temps avec du code Arduino. La librairie NTPClientLib est une sur-couche adaptée aux modules ESP8266. Elle supporte maintenant les cartes Arduino MKR1000 ainsi que les ESP32.

J’ai toutefois rencontré des problèmes de synchronisation de l’heure. La librairie NTPClientLib n’est pas adaptée aux projets qui fonctionnent sur batterie. La synchronisation de l’heure peut prendre quelques instants. Le temps est récupéré via une procédure callback qui peut demander du temps et donc consommer la batterie inutilement. J’ai toutefois préféré conserver cette partie du tutoriel pour mémoire et au cas ou la librairie NTPClient serait inadaptée à votre projet.

 

Il est très facile de récupérer le temps sur internet à l’aide de la librairie NTPClientLib développée par gmag11. Pour fonctionner, il faudra également installer et déclarer la librairie TimeLib standard de l’Arduino. Ces deux librairies sont disponibles depuis le gestionnaire de bibliothèque de l’IDE Arduino.

 

esp8266 ntp server ntpclientlib timelib arduino

Un fois les librairies installées, il ne reste plus qu’à les déclarer dans le sketch.

#include <TimeLib.h>
#include <NtpClientLib.h>

La librairie NtpClient met à disposition toutes les méthodes nécessaires à la gestion de temps mais elle utilise la librairie TimeLib pour fonctionner. Vous trouverez de nombreux tutoriels (anciens) sur internet qui décodent directement les messages UDP envoyés par le serveur de temps. Avec la librairie développée par gmag11, tout se travaille est devenu inutile.

Avant de pouvoir récupérer le temps sur internet, on doit déjà démarrer l’objet NTP. On le démarre avec la méthode begin() qui prend 3 paramètres dans le setup() :

  • l’URL du serveur de temps. Par défaut, on pourra interroger  pool.ntp.org
  • Décalage horaire qui correspond à votre fuseau horaire
  • Heure d’été. Mettre à True pour gérer le mode heure d’été, heures d’hivers

La méthode setInterval permet de définir la fréquence d’interrogation du serveur de temps. Restez raisonnable, inutile de re-synchroniser l’horloge de l’ESP8266 10 fois par secondes !  Vous allez surcharger inutilement le serveur NTP. Ici, le temps sera re-synchronisé chaque minute (ce qui est déjà beaucoup trop).

NTP.begin("pool.ntp.org", 1, true);
NTP.setInterval(60);

On peut créer un fonction callback qui sera appelée à chaque fois qu’un événement se produit (serveur inaccessible, mauvaise URL, demande de temps).

NTP.onNTPSyncEvent([](NTPSyncEvent_t error) {
  if (error) {
    Serial.print("Time Sync error: ");
    if (error == noResponse)
      Serial.println("NTP server not reachable");
    else if (error == invalidAddress)
      Serial.println("Invalid NTP server address");
    }
  else {
    Serial.print("Got NTP time: ");
    Serial.println(NTP.getTimeDateString(NTP.getLastNTPSync()));
  }
});

Maintenant que tout est prêt, on peut demander le temps à n’importe quel moment dans tour le code Arduino. La librairie NtpClientLib propose de nombreuses fonctions qui vont nous faciliter le travail en plus de synchroniser le temps avec un serveur NTP :

  • TimeZone (get ou set) : décalage horaire -11 à + 13
  • stop() : arrête la synchronisation du temps
  • Interval (get ou set) : temps en milli-secondes avant la prochaine synchronisation du temps avec le serveur NTP
  • DayLight (get ou set) : gestion de l’heure d’été
  • getTimeStr(temps unix) : permet de convertir directement le temps unix en chaine, pratique pour créer un affichage
  • getDateStr()
  • getDateStr(temps unix) : chaine contenant la date
  • getTimeDateString() : chaine contenant la date et heure
  • getLastNTPSync() : dernière synchronisation

Comme vous pouvez le voir, il existe presque toutes les combinaisons. Seul problème, les chaines ne sont pas (encore) dans un standard ISO. Vous risquez donc de rencontrer des problèmes si vous devez traiter ces dates dans du code navigateur (Javascript). Pour le moment, le mieux est donc de conserver le timestamp unix retourné par la fonction getTime(). Le timestamp (tampon horaire) présente aussi plusieurs avantages :

  • On peut facilement comparer deux dates par une simple soustraction. La différence de temps sera en secondes.
  • C’est un nombre assez court, donc économe en mémoire. C’est important dans un projet Arduino / ESP8266.
  • Il sera facile à traiter dans du code Javascript. La fonction new Date(timestamp) permet de créer une date très simplement.

Il existe  également quelques fonctions bien pratiques pour gérer le système

  • getUptimeString() : durée de fonctionnement de l’ESP8266
  • getUptime() : idem mais sous la forme d’un timestamp Unix
  • getLastBootTime() : temps écoulé depuis le dernier boot de l’ESP8266
  • getFirstSync() : première synchronisation avec le serveur depuis le démarrage

Comparaison des librairies

Voici un petit tableau récapitulatif des fonctions proposées par les principales librairies disponibles

NTPClient EasyNTPClient NTPCLientLib
WiFi UDP X X
Port UDP X
Type de carte NETWORK_ESP8266, NETWORK_ESP32,  ARDUINO_ARCH_SAMD ou NETWORK_W5100 dans begin()
Paramètres disponibles
Serveur de temps création objet ou setPoolServerName X création objet ou setNtpServerName
Décalage horaire création objet ou setTimeOffset SetTimeOffset
Intervalle de synchronisation création objet ou setUpdateInterval création objet ou setInterval
Indiquer zone horaire setTimeZone
Heure d’été (daylight) setDayLight
Méthodes
Récupérer l’heure NTP udpate getUnixTime
Forcer synchronisation forceUpdate getUnixTime
Décalage horaire getTimeOffset
Zone horaire getTimeZone ou getTimeZoneMinutes
Suspendre la synchronisation stop
Formatage du temps
Temps UNIX (Epoch) getEpochTime getUnixTime
Jours getDay
Heures getHours
Minutes getMinutes
Secondes getSecondes
HH:MM:SS getFormattedTime getTimeStr
Date JJ:MM:AAAA getDateStr
Chaine date + heure getTimeDateString
Fonctions avancées
Temps écoulé depuis le démarrage* getLastBootTime
Première synchronisation getFirstSync
Période d’été isSummerTimePeriod

(*) ne fonctionne pas en cas de RESET ou reboot manuel

Compatibilité

Voici également un liste des cartes de développement supportées par les librairies. Les développeurs ne peuvent pas tester toutes les plateformes. Certaines cartes pourraient être supportées sans pour autant se trouver dans le tableau ci-dessous. N’hésitez pas à partager votre expérience si c’est le cas.

NTPClient EasyNTPClient NTPCLientLib
Arduino X X X
Arduino MKR1000 X X X
ESP8266 X X X
ESP32 X probable X
AVR ? ? X

Remarque sur le temps Unix (Unix timestamp)

Le temps unix a débuté le 1er janvier 1970 à minuit. C’est un compteur en secondes. Vous pouvez directement convertir un temps unix dans le code Arduino pour en déduire une date ou une heure (ou le tout) à l’aide des fonctions getTimeStr(temps unix) et getDateStr(temps unix). Il existe également de nombreux convertisseurs sur internet (http://www.unixtimestamp.com/ ou en français http://www.timestamp.fr/).

Autres serveurs NTP

Voici une petite liste de serveurs de temps (en France et dans le monde). Il en existe des centaines (probablement plus encore !)

Dans le prochain tutoriel, nous utiliserons cette base de temps pour créer un historique des mesures qui servira à la création des graphiques.

 

 

Inscrivez-vous à la newsletter hebdomadaire

Aucun spam et aucun autre usage ne sera fait de votre email. Vous pouvez vous désinscrire à tout moment.

Promos à ne pas louper

Tags :

9
Poster un Commentaire

Laisser un commentaire

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.

  S’abonner  
Notifier de
Michael Carre

Bonjour, Merci d’essayer de m’aider en fait j’ai fait des essais avec le code exemple livré avec la librairie afin de comprendre le fonctionnement je joint quand meme le code: #include #include #include #include #include #include #include // Enter a MAC address for your controller below. // Newer Ethernet shields have a MAC address printed on a sticker on the shield byte mac[] = { 0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02 }; EthernetClient client; void setup() { Serial.begin(115200); if (Ethernet.begin(mac) == 0) { Serial.println(“Failed to configure Ethernet using DHCP”); // no point in carrying on, so do nothing forevermore: for… Lire la suite »

Bonjour Michael. J’espère que vous avez avancé sur votre problème. Vous pouvez maintenant en discuter avec tout le monde sur le forum ici https://projetsdiy.fr/forums/topic/probleme-librairie-ntpclientlib/. A très bientôt

Michael Carre

0 00:00:05 01/01/1970. Winter Time. Uptime: 0 days 00:00:05 since Time not set

1 00:00:10 01/01/1970. Winter Time. Uptime: 0 days 00:00:10 since Time not set

2 00:00:15 01/01/1970. Winter Time. Uptime: 0 days 00:00:15 since Time not set

3 00:00:20 01/01/1970. Winter Time. Uptime: 0 days 00:00:20 since Time not set

4 00:00:25 01/01/1970. Winter Time. Uptime: 0 days 00:00:25 since Time not set

voici ce que j’obtient pour être plus clair
Merci si vous avez une solution, moi je galère pour l’instant

Bonsoir Michael, vous pourriez m’envoyer votre code depuis le formulaire de contact si c’est possible. C’est plus facile pour reproduire et trouver l’erreur. Merci

Michael Carre

bonjour,
j’ai essayé cette librairie mais sa ne m’affiche que la date du 01/01/1970
puis un décompte de temps depuis que c’est en marche je ne comprend pas
si j’ai bien compris je devrais avoir le temps reel?

Germán Martín

Nice article. Regards!

Thank you very much. I’m really happy to see you here.

Clemzo

Bonjour,

Et merci pour vos très bon articles.

D’après mes tests, il semble que le paramètre de réglage de la fréquence d’interrogation, s’exprime en secondes.
Donc un paramètre de 60000 est équivalent à 1000 minutes.

Cordialement.

Bonjour Clemzo. Effectivement, vous avez raison. Merci beaucoup. C’est corrigé.

Domotique et objets connectés à faire soi-même