Deux questions à propos du temps

Soit un montage (toujours mes volets mais peu importe en fait) avec un ESP32, un DS3231, synchroNTP du DS3231 au setup() (et périodiquement - mais ce n'est pas encore codé). Il est destiné à fonctionner longtemps de façon autonome (plusieurs semaines-mois...)

Je cherche à exécuter certains tâches toutes les minutes (pour le moment) mais aussi d'autres toutes les heures ou à certains moments de la journée (par exemple à 03h42 ou je ne sais quoi - mais ça ce n'est pas encore prêt)

Pour le moment je suis parti sur une structure classique du loop() :

void loop() {
  currentMillis = millis();

  // boucle des minutes
  if ((unsigned long)(currentMillis - previousMillisMinute) >= 60000) {

    previousMillisMinute = currentMillis;

    // Action à exécuter toutes les minutes
    Serial.println("Une MINUTE s'est écoulée !");
  }
  // boucle des heures
  if ((unsigned long)(currentMillis - previousMillisHeure) >= 3600000) { 
    previousMillisHeure = currentMillis;

    // Action à exécuter toutes les heures
    Serial.println("Une HEURE s'est écoulée !");
  }

  unTrucAChaqueTour();
}

Ça marche mais...

Si l'ESP32 a été démarré à 18h 34min et 42s les différentes actions auront lieu à 42s de chaque minute et à 34min 42s de chaque heure.
Ce que je souhaite c'est que les actions aient lieu 0 s (ou à peu près) de chaque minutes. C'est moins gênant pour les heures, je peux éventuellement garder le test sur millis

Ma première question : intuitivement je me dis que ce n'est pas une bonne idée de faire

  DateTime now = rtc.now();
  uint8_t secondeRTC = now.second();

pour ensuite tester si secondeRTC est repassé à 0 :

  • l'ESP32 va passer son temps à communiquer avec le DS3231
  • lors d'une synchro NTP il se peut très bien que l'on passe de 59s à 1s sans passer par 0.

Est-ce que ma réflexion est correcte ?

Alternative : utiliser l'horloge interne et un code du genre (pas testé)

struct tm tmNow = *localtime(&t);
int seconde = tmNow.tm_second;

et pareil, tester le passage à 0 de seconde

Sauf que, si je ne me trompe pas, l'horloge interne dérive plus vite que le RTC et pareil qu'avant, il est tout à fait possible, lors d'une resynchronisation, de louper un "0 s"

Donc question 2 : quelle est la manière propre et académique pour faire ce genre de choses ?

MErci d'avoir lu jusque là :wink:

Pour moi, mais je me trompe peut-être cela revient à faire une programmation. Je ferais simplement un code à partir de l'heure système. Si la valeur heure est changée ou si la valeur minute est changée faire "instruction". Si l'action est à une heure précise il suffit de faire un compare entre l'heure système et l'heure prévue du programme

Effectivement un truc du genre :

void loop() {
  time_t now;
  struct tm timeinfo;
  time(&now);
  localtime_r(&now, &timeinfo);

  if (timeinfo.tm_min != previousMinute) {
    previousMinute = timeinfo.tm_min;

    // Action à exécuter toutes les minutes
    Serial.println("Une MINUTE s'est écoulée !");
  }


  unTrucAChaqueTour();
}

devrait être OK
Il faut juste que je m'assure de ne pas effectuer deux fois une action si l'horloge est remise à l'heure et qu'elle est retardée d'une ou deux seconde
Merci !

Si vous voulez travailler au niveau des heures pleines ou secondes à zéro, vous pourriez organiser vos tâches par rapport au temps vrai (l’heure) et pas des delta T.

Pour éviter les « rebonds » de tâches au cas où le NTP remet à l’heure la RTC, quand une tâche récurrente est effectuée vous pouvez enregistrer la prochaine heure ou jour quand effectuer l’action. Par exemple lors de l'exécution d'une action qui a comme ∆t de répétition 24h et qui s'effectue à 8h du matin, vous stockez la date exacte de la prochaine activation (en utilisant les classes DateTime et TimeSpan de la bibliothèque RTCLib d'adafruit par exemple et en ajoutant le ∆t au temps de déclenchement de la tâche).

PS: Dans une autre discussion sur des volets roulants j’avais proposé une classe qui permettait de dire « enregistre cette action à faire plus tard à tel moment ». Ça pourrait donner des idées.

Avec mon code d'exemple vous avez la fonction ntpSyncCallback() qui est appellée automatiquement quand le NTP a fait sa synchro donc vous pouvez rajouter la mise à jour de la DS3231 à cet endroit. Ce code vous donne aussi la config pour gérer la connexion et déconnexion du réseau WiFi avec les callbacks et une variable qui est maintenue à jour wifiOK, à tester avant de faire appel à des fonctions bloquantes pendant un moment de requêtes vers le web par exemple.

Je vais jouer mon relou de service en ne répondant pas à la question, mais plutôt chercher à trouver un argument pour te passer de cette contrainte :slight_smile:. Mais quel est l'intérêt d'être calé sur la première seconde de la minute.
Surtout si finalement tu te cale même pas sur un début d'heure ?

Je ne sais pas trop ce que tu veux faire, mais on pourrait peut être imaginer un système un peu "tordu".
C'est à dire que tu n'attends pas un temps fixe, mais un pourcentage du temps voulu, avec un passage à zéro, enfin pour les minutes.
Car pour les heures c'est quand même un peu bizarre, si tu as commencé à 30, de devoir pour te recaler, attendre 30 ou 1h30 ?

Oui, mais avec une structure comme celle-ci l'ESP tourne en permanence ce n'est pas très adapté à un système économe en énergie ayant une longue autonomie.

Pourquoi ne pas avoir l'ESP32 en deepsleep en permanence et réveillé par l'interruption de la RTC?
Là, plusieurs options:

  1. la RTC réveille l'ESP toute les minute et l'ESP regarde s'il a quelque chose à faire, l'exécute, puis retourne en deepsleep.
  2. le registre d'alarme de la RTC contient l'heure du prochain événement et ne réveillera l'ESP qu'à ce moment là. Il exécute celle-ci, il configure la RTC pour le prochain événement et repart en deepsleep.

Pour info, je me suis acheté un kit comme ci-dessous qui fonctionne suivant le principe exposé en 1. Le truc tourne depuis maintenant 13 jours et je pense qu'il tiendra 15 jours sur une batterie de 180mAh.
Ici, l'ESP est réveillé par la RTC, il met l'heure à jour, lit l'accéléromètre (il a une fonction compteur de pas) met à jour le nombre de pas, fait vibrer le moteur si les minutes sont à zéro, met à jour la jauge de la batterie. La précision de l'heure c'est donc celle de la RTC puisque c'est elle qui réveille l'ESP. Et comme il n'est actif qu'une seconde (voir moins d'une seconde) par minute la consommation est minime.

Merci pour cette stratégie, je vais étudier ça...

Merci aussi pour cette lecture, c'est pour ce soir

C'est cosmétique surtout : sur l'interface web il y a la liste des tâches prévues et l'heure. Si à 18h15 la tâche de 18h15 n'est pas lancée, tu te demande (éventuellement pendant 59s, tu vois le niveau de drame et d'angoisse :wink: ) si le système déconne. Non ce n'est pas plus grave que ça mais ce genre de détail m'agace.

Pas très important : le système est alimenté par le secteur avec un bloc alim qui sert aussi pour un RPi plus gourmand.

Sympa... C'est quoi la référence ?

https://fr.aliexpress.com/item/1005008519059686.html

La bibliothèque NTP de l'ESP32 autorise plusieurs mode de synchro du temps notamment des rattrapages progressifs si l'écart de temps est trop grand (plusieurs minutes par exemple) ainsi qu'une multitude de méthodes. Il faut regarder dans la bibliothèque avant de se lancer dans des opérations compliquées