Problème réception WiFi avec ESP8285

Bonjour à tous, je suis nouveau dans le monde Arduino. Je viens de débuter avec un projet qui me permettait de réaliser ce que je cherchais, à savoir une commande horaire qui ne dérive pas dans le temps et qui montre une fiabilité à toute épreuve.

Comme suggéré à juste titre, je modifie l'exposé de mon problème, en sautant l'historique de mon parcours et en résumant la situation actuelle.

Mon cahier des charges:

  1. créer à chaque heure pleine (hh:00:00) l'activation d'une sortie pendant 1 minute (donc jusque hh:00:59)
  2. pendant cette minute, créer l'activation d'une seconde sortie pendant 1/2 seconde à partir de la 41ème seconde

Matériel utilisé: un module ESP8285 WiFi acheté ici [Horloge connectée avec Arduino - Tropratik]
Suite au problème de confinement du module dans un coffret partiellement métallique, j'ai modifié l'antenne WiFi selon ce tuto https://www.instructables.com/External-Antenna-for-ESP8266/

Tout fonctionne parfaitement et voici le code:


```cpp
// Déclaration des librairies utilisées
#include <ESP8266WiFi.h>
#include "LiquidCrystal_I2C.h"
#include <EasyNTPClient.h>
#include <WiFiUdp.h>
#include <Timezone.h>

// Déclaration globales
LiquidCrystal_I2C Afficheur_G(0x27, 16, 2); // Afficheur LCD1602 d'addresse 0x27 avec écran de 2 lignes de 16 caractères
WiFiUDP Udp_G; // Objet UDP permettant d'envoyer et recevoir des trames Wi-Fi selon le protocole UDP
EasyNTPClient ClientNtp_G(Udp_G, "fritz.box"); // Objet NTP synchronisé avec "fritz.box" qui a son propre serveur NTP disponible dans le réseau domestique
TimeChangeRule RegleHeureEteBelgique_G = {"RHEB", Last, Sun, Mar, 2, 120}; // Règle de passage à l'heure d'été en Belgique
TimeChangeRule RegleHeureHiverBelgique_G = {"RHHB", Last, Sun, Oct, 3, 60}; // Règle de passage à l'heure d'hiver en Belgique
Timezone ConvertirHeureBelgique_G(RegleHeureEteBelgique_G, RegleHeureHiverBelgique_G); // Objet de conversion de l'heure avec les caractéristiques de la Belgique
const int CommandeCF = 12; // définition de la broche "12" comme sortie commande coffret
const int CommandeDL = 13; // définition dela broche "13" comme sortie commande Data Logger
const int EntreeBP = 14; // définition de la broche "14" comme entrée bouton-poussoir
int etat_BP; // variable pour stocker l'état du BP
int memoire_BP = HIGH; // variable pour mémoriser l'état du BP, initialisée à 1 lorsque le BP est relaché
unsigned long temps_T0 = millis(); // variable intermédiaire pour stocker le temps Arduino pour la fonction chrono min:sec
unsigned long temps_S; // variable pour calculer la durée d'1 seconde
unsigned long chrono_BP; // variable pour mesurer une durée de 1 minute
unsigned long chrono_T0 = millis(); // variable intermédiaire pour stocker le temps Arduino pour la fonction BP
bool cond_Setup = false; // flag pour initier le chronomètre de la routine Lecture_Mesure
unsigned long TempsUnix; // variable permettant de lire la valeur du temps Unix
unsigned long TempsUnixMem = ClientNtp_G.getUnixTime(); // variable servant à mémoriser l'heure Unix précédente et initialisée au temps Unix la 1ère fois
bool TempsUnixVal = false; // flag permettant de valider la bonne réception de l'heure Unix
bool DL_Flag = false; // flag permettant de ne créer qu'une seule implusion vers le Data Logger (external trigger)
bool BL_Flag = true; // flag permettant d'éteindre le rétro-éclairage après les 60 premières secondes de mise sous tension, une seule fois
bool BP_Flag = false; // flag permettant de couper le 12V coffret uniquement après le cycle BP
int ValeurMinute;
int ValeurSeconde;

// Fonction de démarrage, s'exécute une seule fois:
void setup() 
{
  // Constantes de la fonction
   const char* SSID_L = "xxxxxxxx"; // Nom du réseau Wi-Fi
   const char* MOT_DE_PASSE_L = "xxxxxxxx"; // Mot De Passe du réseau
   pinMode(CommandeCF, OUTPUT); // configuration de la broche "12" comme sortie
   pinMode(CommandeDL, OUTPUT); // configuration de la broche "13" comme sortie
   pinMode(EntreeBP, INPUT); // configuration de la broche "14" comme entrée

   digitalWrite(CommandeCF, LOW); // initialisation de la sortie "12" à l'état repos = bas
   digitalWrite(CommandeDL, LOW); // initialisation de la sortie "13" à l'état repos = bas
   

   // Variables de la fonction
   wl_status_t StatutConnexion_L; // Pour mémoriser l'état de la connexion
   

   Afficheur_G.init(5,4); // Initialisation de l'afficheur, SDA=5, SCL=4
   Afficheur_G.backlight(); // Allumage du rétroéclairage

   WiFi.begin(SSID_L, MOT_DE_PASSE_L); // Fonction permettant d'initialiser le module WiFi et de lancer la connexion
   StatutConnexion_L = WiFi.status(); // Lecture de l'état de la connexion et stockage dans la variable "StatutConnexion_L"
   while ((StatutConnexion_L != WL_NO_SSID_AVAIL)&&(StatutConnexion_L != WL_CONNECTED)&&(StatutConnexion_L != WL_CONNECT_FAILED))
  {
    delay(500);
    Afficheur_G.print("."); // Affichage de points pendant la tentative de connexion
    StatutConnexion_L = WiFi.status(); // Lecture de l'état de la connexion et mémorisation dans la variable "StatutConnexion_L"
  }

      Afficheur_G.clear(); // Effacer l'écran avant l'affichage de l'heure
}

// Fonction principale du programme, s'exécute en boucle:
void loop() 
{
  // Variables de la fonction
  time_t HeureLocale_L;  

  if(WiFi.status()==WL_CONNECTED)
  {
    // Internet disponible
    HeureLocale_L = ConvertirHeureBelgique_G.toLocal(ClientNtp_G.getUnixTime());    
    AfficherHeureLocale(HeureLocale_L); // routine affichage heure locale
        
  }
  else
  {
    // Pas de connexion Internet
    Afficheur_G.setCursor(0,0);
    Afficheur_G.print("Erreur de       ");
    Afficheur_G.setCursor(0,1);
    Afficheur_G.print("       Connexion");
    LectureMesure(); // on laisse la possibilité de commander le coffret par BP hors connexion
  }
 delay(300);

 if((millis() > 60000) && BL_Flag) // on teste si on vient de mettre sous tension et le temps écoulé depuis
  {
    Afficheur_G.noBacklight(); // on éteint l'écran
    digitalWrite(CommandeCF, LOW); // et on coupe le 12V du coffret car ValeurMinute vaut 0 au démarrage
    BL_Flag = false; // et on bloque la commande d'extinction pour ne plus la reproduire par la suite
  }

  CommandeHoraire(HeureLocale_L); // routine de commande horaire pour démarrer les mesures et l'enregistrement des valeurs par le Data Logger, même sans connection internet

}

void AfficherHeureLocale(time_t Heure_P)
{
  char Buffer_L[50];
  char AbreviationMois_L[12][4]={"Jan","Fev","Mar","Avr","Mai","Jun","Jul","Aou","Sep","Oct","Nov","Dec"};
  char AbreviationJour_L[7][4]={"Dim","Lun","Mar","Mer","Jeu","Ven","Sam"};
  
  Afficheur_G.setCursor(0,0);
  sprintf(Buffer_L, " %.2d:%.2d:%.2d %.2d:%.2d", hour(Heure_P), minute(Heure_P), second(Heure_P), ValeurMinute, ValeurSeconde);
  Afficheur_G.print(Buffer_L);
  Afficheur_G.setCursor(0,1);
  sprintf(Buffer_L, "%s %.2d %s %d", AbreviationJour_L[weekday(Heure_P)-1], day(Heure_P), AbreviationMois_L[month(Heure_P)-1], year(Heure_P));
  Afficheur_G.print(Buffer_L);
}

void CommandeHoraire(time_t Heure_P)
{
  
  // vérification de la validité du temps Unix reçu qui doit s'incrémenter de 1 toutes les secondes
  TempsUnix = ClientNtp_G.getUnixTime(); // on charge la valeur Unix reçue par WiFi
  // et on compare à la valeur précédente; seule la même valeur (même seconde en cours) ou la valeur suivante sera valide
  if((TempsUnix == TempsUnixMem || TempsUnix == TempsUnixMem + 1) && TempsUnix != 0)
  {
    TempsUnixVal = true; // on valide le flag    
  }
  else
  {
    TempsUnixVal = false; // sinon on ne valide pas le flag
  }
  if(TempsUnix > TempsUnixMem)
  {
    TempsUnixMem = ClientNtp_G.getUnixTime(); // on sauvegarde la valeur en mémoire uniquement si celle-ci est supérieure à la précédente; cela permet d'échapper à une valeur WiFi non reçue ou intempestive
  } 

  // initialisation d'un compteur min:sec synchronisé avec le temps Unix si celui-ci est valide, sinon incrémentation automatique
  if(TempsUnixVal) // vérification du temps Unix valide
  {    
    ValeurMinute = minute(Heure_P); // synchronisation de la valeur minute dans le compteur min:sec
    ValeurSeconde = second(Heure_P); // synchronisation de la valeur seconde dans le compteur min:sec
    temps_T0 = millis(); // on maintient le chrono milliseconde à la valeur millis tant que le temps Unix est valide
  }
  else // dès que le temps Unix est perdu, on active l'incrémentation automatique
  {
    //temps_S = temps_T0 + 999; // calcul de la durée d'1 seconde
    if(millis() > (temps_T0 + 999))
    {      
      ValeurSeconde++; // on incrémente d'1 seconde toutes les 1000 millisecondes
      temps_T0 = millis(); // on réinitialise le chrono millisecondes  
    }
    if(ValeurSeconde > 59)
    {      
      ValeurMinute++; // on incrémente d'1 minute toutes les 60 secondes
      ValeurSeconde = 0; // on remet le compteur secondes à zéro
    }
    if(ValeurMinute > 59) // on remet le chrono min:sec à zéro après la valeur 59:59
    {
      ValeurMinute = 0;
      ValeurSeconde = 0;
      temps_T0 = millis();
      DL_Flag = true; // on remet le flag à 1 pour le cycle heure pleine
    }  
  }
  
  // on surveille le moment de l'heure pleine
  if(ValeurMinute == 00)
  {
    Afficheur_G.backlight(); // Allumage du rétroéclairage
    digitalWrite(CommandeCF, HIGH); // commande de la sortie "12" qui va alimenter le 12V du coffret pendant 1 minute
    if(ValeurSeconde == 01)
    {
      DL_Flag = true; // initialisation de l'autorisation de la commande DL à la seconde 01
    }

    // pendant la minute de l'heure pleine, on attend 45 secondes pour que les mesures soient stables
    if(ValeurSeconde == 45 && DL_Flag)
    {
      // à la 45ème seconde du chrono (00:45), commande de la sortie "13" qui envoie une impulsion de 1/2 s sur l'entrée External Trigger du Data Logger
      digitalWrite(CommandeDL, HIGH);
      delay(500);
      digitalWrite(CommandeDL, LOW);
      DL_Flag = false; // remise à zéro du flag pour ne générer qu'une impulsion vers le DL
    }    
  }
  else
  {
    LectureMesure(); // routine qui permet d'activer la sortie 12V coffret par BP et afficher la mesure pendant 1 minute, sans enregistrer dans le Data Logger

    // on coupe le 12V du coffret et le rétro-éclairage de l'écran    
    if(ValeurMinute == 01) // à la 1ère minute suivante, on coupe le 12V du coffret et on éteint l'écran
    {
      digitalWrite(CommandeCF, LOW);
      Afficheur_G.noBacklight();
    }      
  }
}

void LectureMesure()
{
  // Pour lancer la mesure lorsque le BP est relaché, il faut mémoriser son état
  etat_BP = digitalRead(EntreeBP); // lecture de l'état du BP
  
  // on vérifie si le bouton est relaché après avoir été activé
  if((etat_BP != memoire_BP) && (etat_BP == HIGH) && chrono_BP < 60000)
  {
     // setup conditionnel, commandes à n'éxécuter qu'une seule fois
    if(cond_Setup == false)
    { 
      digitalWrite(CommandeCF, HIGH); // commande du relais 12V coffret   
      chrono_T0 = millis(); // initialisation du temps T0
      cond_Setup = true; // blocage du flag
      BP_Flag = true; // autorisation de couper le 12V à la fin du cycle BP
    }
    // routine proprement dite
    else
    {
      chrono_BP = (millis() - chrono_T0); // calcul du temps écoulé depuis T0      
    }     
  }
  else  
  {
   cond_Setup = false; // remise à 0 du flag
   chrono_BP = 0; // remise à 0 du chrono       
   memoire_BP = etat_BP; // mémorisation de l'état du BP
   if(BP_Flag)
   {
      digitalWrite(CommandeCF, LOW); // extinction du coffret après le BP
      BP_Flag = false; // blocage du flag
   }
  }  
}

Sauf que... une fois le WiFi interrompu, l'affichage "erreur de connexion" ne s'affiche qu'au bout de 12 à 13 secondes, de même mon compteur incrémente 1 sec au bout de cette même période. Il faut plus de 600 sec pour arriver à incrémenter d'1 min.

Le code permettant la lecture du temps Unix depuis le serveur NTP, la transformation en date/heure humaine, la vérification de la connexion WiFi sont recopiés tels quels du tuto fourni avec le ESP8285. Des librairies spécifiques sont incluses pour y arriver. Et je ne sais absolument pas ce qui y est traité.

Ma question est: que fait le ESP8285 lorsque la connexion WiFi est interrompue??? Visiblement il n'exécute pas le else, sauf 1 fois toutes les 12 à 13 secondes.

Dès que je rétabli le WiFi, tout refonctionne normalement, les commandes horaires suivent leur cours.

J'ai essayé de résumer, car je suis sur ce projet depuis septembre dernier, donc j'y ai passé pas mal d'heures et je n'ai pas décrit toutes les étapes.

Le module ESP8285 est monté sur un support et intégré dans un montage électronique, sur un circuit imprimé, pour effectuer les adaptations vers le monde extérieur (alimentation 5VDC, transistors et bornier entrées/sorties vers du câblage).

Encore une fois je suis novice et mon vocabulaire ne correspond peut-être pas au vôtre, veuillez m'en excuser.

D'avance je remercie celle ou celui qui pourrait m'aider.

:warning:
Post mis dans la mauvaise section, on parle anglais dans les forums généraux. déplacé vers le forum francophone.

Merci de prendre en compte les recommandations listées dans Les bonnes pratiques du Forum Francophone

Bon soir @hachave et bienvenue sur ce forum francophone

Grâce à UKHelibob te voilà maintenant dans le Forum Arduino francophone, là où tu aurais dû poster initialement.

En consultant les incontournables Bonnes Pratiques du Forum tu découvriras toutes les informations qu'il est indispensable de fournir d'emblée pour toute demande d'aide.

Tu y découvriras également l'art et la manière du publier ici son code dans le corps des messages.

Ton message initial est par ailleurs très indigeste, trop tassé.
Ménages des espaces pour l'aérer et encourager sa lecture
Tu peux à cet effet rééditer le message initial pour le corriger.

J'ai fait l'acquisition d'un module ESP8285 WiFi (je ne sais pas si je peux mentionner le site?)

Non seulement tu peux, mais tu dois le faire pour que l'on sache quelle carte précise tu utilises, le micro-contrôlleur ESP8285 se trouve sur quelques cartes diverses.

Va falloir poster le code (en le formatant correctement - voir les bonnes pratiques du forum) parce qu'avec aussi peu d'indications, ça va être difficile...

Les antennes gravées ne sont pas les pires, j'ai eu une mauvaise expérience avec une antenne CMS céramique :frowning:
Cela dit, il y a aussi des cartes avec une vraie prise antenne, sans chirurgie audacieuse

Bonjour

Au cas où et si besoin, pour pouvoir obtenir la date/heure même quand internet n'est plus accessible, tu peux récupérer le signal radio DCF77

Il y a aussi les récepteurs GPS

On peut aussi utiliser un module RTC qui serait remis à l'heure par DCF77 ou GPS ou par l'accès au réseau local.


Mais bon, tout dépends du contexte : des fois une méthode convient mieux, et des fois c'est impossible à utiliser pour diverses raisons.

Bonjour et merci pour votre réponse rapide. Désolé pour avoir posté dans la mauvaise section, la procédure à suivre pour la création d'un nouveau poste, lorsque je suis sur la page Arduino en français, ne précise à aucun moment ce choix. Merci pour la rectification.

Bonjour et merci pour votre réponse rapide. Je viens de rééditer ma demande selon vos conseils.

Bonjour et merci pour votre réponse rapide. Le coffret dans lequel est monté le module se trouve dans un endroit hors de portée radio (cave/garage en sous-sol). Raison pour laquelle je m'étais orienté vers internet/serveur NTP.

Avant le module ESP8285, j'ai essayé plusieurs solutions, mais je ne connais pas de module RTC qui dispose de sorties programmables à volonté.

Bonjour et merci pour votre réponse rapide.

Je suis à l'aise au niveau manipulation électronique et je suis bien équipé pour des composants "discrets" (hors smd). Et je préfère de loin des connexions soudées à des connexions fichées.

Les câbles et les antennes utilisés en WiFi sont courants sur Amazon, je n'ai eu aucun mal à commander le matériel adéquat. Et franchement, c'est le jour et la nuit au point de vue réception, je n'ai plus jamais rencontré le "01 janvier 1970 00:00:00" depuis bientôt 3 semaines maintenant.

:+1:

Je suppose que @amic suggérait un module RTC à ajouter à ton ESP. L'ESP demandant l'heure au module quand la connexion est interrompue ou qu'une incohérence est détectée et réajustant les module RTC de temps à autre quand l'heure est certaine.

Pour info, si vous partiez sur un ESP32, j'ai mis dans les tutos un petit code de référence pour la gestion d'un serveur NTP ➜ Gestion d'un serveur de temps NTP sur ESP32 en WiFi

Oui merci, je pense que j'avais bien compris. Avez-vous une référence d'un module de ce type?

Les modifications finales que j'ai apportées à l'ESP8285 constituent en fait une RTC, qui commande les sorties dont j'ai besoin, et qui se met à l'heure avec le temps Unix tant que celui-ci est valide.

Le problème est que l'ESP8285 semble ne plus exécuter son code dès que la connexion WiFi est perdue. Comme si une instruction (peut-être contenue dans une des librairies) créait un "delay" de plusieurs secondes. Et c'est toute l'exécution du "loop" qui en est affectée.

Il y en a plein :
https://www.amazon.fr/s?k=rtc+arduino

J'ai pas regardé tout ton code mais l’utilisation d'un module supplémentaire permet de palier les coupures d'alim, reboots plus ou moins prévus, et éventuellement des problèmes de connexion au serveur NTP.

Je n'ai pas les compétences pour répondre mais c'est plausible : j'ai eu le même problème avec une bib pour des mesures de températures qui était bloquante (moins d'une seconde mais c'est assez pénalisant quand même)
Mot clé pour voir si une autre bibi te permet de ne pas bloquer le loop : "asynchrone"... C'est comme ça que j'ai résolu mon problème

en cas de déconnexion votre code n'essaye pas de se reconnecter au réseau WiFi donc toute la partie qui est dans ce if ne sera plus exécutée

De fait j'avais déjà vu ce genre de module. Mais je n'avais pas approfondi car il est incompatible avec le peu de GPIO que présente le ESP8285. En plus je devrais reconstruire une carte pour intégrer ses nouvelles connexions, qui forcément devra être plus grande et je n'ai déjà plus beaucoup de place dans mon coffret.

C'est une idée intéressante et qui me tente beaucoup. Mais comme débutant je ne vois pas vers quelle autre librairie m'orienter (et qui permettra les mêmes fonctions).

Merci pour votre réponse.

C'est exact, ce qui suit le if n'est plus exécuté. Mais ce qui suit le else ne l'est pas non plus, sauf au bout de plus de 10 secondes. Ensuite il y a des fonctions qui doivent être exécutées indépendamment du statut connexion, et elles ne le sont pas non plus.

Et lorsque le WiFi est rétabli, le ESP8285 reprend son fonctionnement normal, sans intervention.

ben la loop() appelle constamment CommandeHoraire() qui commence par faire

la fonction getUnixTime() déclenche un appel au serveur NTP (getServerTime()) toutes les 60s si ce n'est pas la première synchro (mUpdateInterval vaut 60000ms)

donc si vous n'avez plus de connexion vous appelez cette fonction getServerTime() qui contient une requête UDP qui aura des timeout liés à la couche réseau. C'est sans doute ce que vous voyez

Oui, c'est tout-à fait ça :

Le module RTC est alimenté par une simple pile (dont le support est intégrée au module),
donc, le module RTC est autonome => même si il y a une coupure totale de l'alimentation, le module RTC continuera à fonctionner.

Merci beaucoup J-M-L, je crois que c'est exactement ça qui se produit. Entretemps j'ai replongé dans mon code et je me rends compte de mon erreur :face_with_monocle: :frowning_face: .

Je suis en train de ré-écrire cette partie, en séparant la gestion du compteur min:sec selon que la connexion est OK ou NOK. Et ensuite j'adapte la CommandeHoraire() sans faire appel aux fonctions Unix, en se basant uniquement sur la valeur du compteur min:sec.

Toute la magie d'un oeil externe... :wink: Je posterai la nouvelle version de code et je vous tiendrai au courant du résultat. Encore merci !