Rélfexion autour d'un ESP32

Discutions générales sur le DSPiy et tout ce qui s'y rattache
Avatar de l’utilisateur
alka
Administrateur du site
Messages : 3098
Enregistré le : mer. 15 juil. 2015 15:18
Localisation : 92
Contact :

Rélfexion autour d'un ESP32

Messagepar alka » sam. 8 juin 2024 17:10

On a commencé à aborder la question d'un ESP32 pour gérer bouton, trigger, CTNs et sortie relais + quelques leds dans le sujet d'ampli 6 voies de goodnoize https://www.dspiy.be/forum/viewtopic.php?p=10829#p10829
Pour ne pas polluer je redémarre ici.

Le projet décrit ici de manière pédagogique (j espère) consiste à piloter un relais qui allume un ampli avec micro-controleur (un ESP32). Il faut gérer un bouton poussoir et un trigger en entrée et faire quelques babioles supplémentaires.

Cela correspond très exactement à mon tout premier projet avec Arduino! Quand je le regarde aujourdh'ui je me dis que ça mérite que j'y revienne et j'en profite pour faire de la pédagogie à mon humble niveau :)

Pour bien faire, il faut du hardware! J'ai donc monté un banc de test rapide sur une breadboard avec une carte de dév ESP32, un bouton poussoir et une led rouge qui simulera le relais et quelques fils. La led bleue sur la carte servira pour diverses infos. Le trigger est simulé par un fil (le rouge) en l'air (trigger absent) ou au 0V (trigger présent).

Image

Schéma
Image

Sur l'image le trigger est présent (fil branché sur 0V) et la led rouge est allumée :)

J'utilise une carte ESP32 S2 mini que j'avais sous la main (carte que je ne recommande pas : trop de soucis de reconaissance sous arduino. Les cartes ESP32 wroom ou ESP32-S3 sont mieux)

Les specs pour la V1:

- Un appui sur le bouton active le relais. L'appui suivant le désactive.
- La led embarquée bleue est allumée pendant l'appui sur le bouton.
- Le relais suit l'entrée trigger, ie trigger présent=>relais activé et inversement
- Le bouton poussoir gagne sur le trigger: un appui bouton coupe le relais même si le trigger est présent, ou active le relais même si le trigger est absent.

Pour information, on mesure aussi la durée d'appui sur le bouton et durée de la boucle.

voilà le code de la v1

Code : Tout sélectionner

/*
Programme de gestion d'un bouton et d'un trigger pour piloter un relais.
Sur mon banc de test, 
- une led rouge simule le relais.
- l'entréee trigger est simulée par un fil au 0V
La led bleue embarquée est utilisée comme témoin.

v1  08 juin 2024 
    Un appui sur le bouton active le relais. L'appui suivant le désactive.
    La led embarquée bleue est allumée pendant l'appui sur le bouton.
    Le relais suit l'entrée trigger, ie trigger présent=>relais activé et inversement
    Le bouton poussoir gagne sur le trigger: il coupe le relais même si le trigger est présent,
    ou active le relais même si le trigger est absent.
    Pour information, donne la durée d'appui sur le bouton et durée de la boucle.
*/

#define FWVERSION "v1"        // version du programme, pour que je m'y retrouve dans les copies d'écran!

// GPIOs utilisés
#define LED_ESP 15            // led embarquée sur la carte ESP32-S2. Elle est sur GPIO 15
#define RELAY 5               // sortie relais sur GPIO 03

const int PushButton = 16;    // entrée PushButton sur GPIO 16. (Cette fois c'est défini avec une constante)
#define TRIGGER 39            // entrée trigger sur GPIO 39

// déclaration et initialisation des variables globales
bool relayState = LOW;         // état du relais. HIGH = activé, LOW = non activé
bool prevButtonState = HIGH;   // état précédent du bouton pour permetttre 
                               // de tester si l'état a changé. Initialisé HIGH, càd 'BP ouvert'.
bool prevTriggerState = HIGH;  // état précédent du trigger. initialisé HIGH, càd 'Trigger absent'.

ulong timeButtonPressed;       // timestamp quand bouton appuyé. En ms. Peut etre très grand => unsigned long
int maxLoopTime = 0;           // durée max de la boucle loop() en micro secondes


// *********************** SETUP ***********************************

void setup() {
  
  
// LED_ESP et RELAY declarés comme Output. 
  pinMode(LED_ESP, OUTPUT);             // pin HIGH : Led allumée   
  pinMode(RELAY, OUTPUT);               // pin HIGH : Relais activé

  // PushButton et Trigger déclarés comme  Input avec résistance pullup.  
  pinMode(PushButton, INPUT_PULLUP);    // pin LOW : Bouton appuyé
  pinMode(TRIGGER, INPUT_PULLUP);       // pin LOW : Trigger présent

  digitalWrite(LED_ESP, HIGH);          // allume la led pendant le setup

  Serial.begin(115200);                 // démarre la sortie série
  delay(1000);                          // attends 1sec que le port Série démarre (lent sur ma carte)

  Serial.println("Version " + String(FWVERSION));
  Serial.println("Prêt. Appuyer sur le bouton ou mettre le trigger.");
  Serial.println();

  digitalWrite(LED_ESP, LOW);           // éteins la led à la fin du setup
}


// *********************** LOOP ***********************************

void loop() {
  unsigned long timeStartLoop = micros();     // mémorise le timestamp au début de la boucle en microsecodnes
                                              // pour pouvoir mesurer la durée de la boucle

  // Gestion du BOUTON POUSSOIR 

  int buttonState = digitalRead(PushButton);  // LOW quand appuyé, HIGH sinon

  if (buttonState != prevButtonState) {
    // le bouton vient de changer d'état

    if (buttonState) {
      // bouton vient de passer à HIGH (ouvert)
      digitalWrite(LED_ESP, LOW);  // éteins la led embarquée
      int delayButtonPressed = millis() - timeButtonPressed;  // délai pendant lequel il est est resté appuyé
      Serial.print("Bouton relaché (durée appui: ");
      Serial.print(delayButtonPressed);
      Serial.println(" ms)."); 
      Serial
.println();                                       // saut ligne pour bien distinguer les appuis successifs

    } else {
      // bouton vient de passer à LOW (appuyé)
      digitalWrite(LED_ESP, HIGH);      // allume la led embarquée
      relayState = !relayState;         // mémorise que l'état de la sortie Relais est inversé
      digitalWrite(RELAY, relayState);  // maj pin sortie relais
      timeButtonPressed = millis();     // mémorise le timestamp du début de l'appui du bouton
      Serial.println("Bouton appuyé.");
    }

    prevButtonState = buttonState;                            // met l'état précédent à l'état courant pour le prochain test 
  }

  // Gestion de l'entrée TRIGGER

  int triggerState = digitalRead(TRIGGER);  // LOW si Trigger présent, HIGH sinon
  
  if 
(triggerState != prevTriggerState) {
    // le trigger vient de changer d'état
    relayState = !triggerState;           // l'état de la sortie Relais suit l'état du trigger mais en inversé.
                                          // Pin trigger LOW => pin sortie relais HIGH, et inversement.
    digitalWrite(RELAY, relayState);      // maj pin sortie relais (ce qui allume ou éteins la led rouge sur mon bench)

    Serial.print("Le trigger est ");
    if (triggerState) Serial.println("ABSENT"); else Serial.println("PRESENT");

    prevTriggerState = triggerState;      // mémorise l'état du trigger pour le prochain test
  }

  
  
// Mesure de la durée max de la boucle
  int loopTime = micros() - timeStartLoop;         // durée boucle courante en micro secondes
  if (loopTime > maxLoopTime) {                    // compare avec la durée max mémorisée
    maxLoopTime = loopTime;                        // cette boucle est plus longue: elle devient la durée max
    Serial.println("duréee max boucle: " + String(loopTime) + " micros.");
  }

}
  // end loop


et ce que cela donne sur la sortie série en utilisant le bouton. Dabord normalement puis quelques fois en appuyant le plus furtivement possible.

Code : Tout sélectionner

18:19.753 -> Version v1
18:19.753 -> Prêt. Appuyer sur le bouton ou mettre le trigger.
18:19.753 ->
18:19.753 -> Le trigger est PRESENT          <--- trigger présent au départ
18:19.753 -> duréee max boucle: 204 micros.
18:55.475 -> Bouton appuyé.
18:55.648 -> Bouton relaché (durée appui: 201 ms) <--- aucun rebond sur le bouton !
18:55.648 ->
18:55.648 -> duréee max boucle: 220 micros.
18:57.722 -> Bouton appuyé.
18:57.859 -> Bouton relaché (durée appui: 112 ms).
18:57.859 ->
19:01.206 -> Bouton appuyé.
19:01.381 -> Bouton relaché (durée appui: 146 ms).
19:01.381 ->
19:03.898 -> Bouton appuyé.
19:03.991 -> Bouton relaché (durée appui: 102 ms).
19:03.991 ->
19:05.894 -> Bouton appuyé.
19:05.939 -> Bouton relaché (durée appui: 72 ms).
19:05.939 ->
19:05.939 -> duréee max boucle: 288 micros.
19:07.325 -> Bouton appuyé.
19:07.361 -> Bouton relaché (durée appui: 37 ms).
19:07.361 ->
19:08.945 -> Bouton appuyé.
19:08.945 -> Bouton relaché (durée appui: 9 ms).
19:08.945 ->
19:11.326 -> Bouton appuyé.
19:11.359 -> Bouton relaché (durée appui: 26 ms).


Bon, c'est raté :mrgreen:
je comptais me servir de cet exemple pour illustrer un phénomène de rebond (bouncing) qui se passe avec les boutons poussoir si on n'y prend pas garde mais ce BP fonctionne trop bien.

Heureusement le trigger est là ! Comme il s'agit d'une fiche à enfoncer ou à retirer dans la breadboard, le contact sera bien moins franc.
Voyons ce qui se passe si je retire et remets le trigger :

Code : Tout sélectionner

19:30.146 -> Le trigger est ABSENT      <--- retrait trigger
19:30.146 -> Le trigger est PRESENT      <--- 20 rebonds en 109ms !!!
19:30.222 -> Le trigger est ABSENT
19:30.222 -> Le trigger est PRESENT
19:30.255 -> Le trigger est ABSENT
19:30.255 -> Le trigger est PRESENT
19:30.255 -> Le trigger est ABSENT
19:30.255 -> Le trigger est PRESENT
19:30.255 -> Le trigger est ABSENT
19:30.255 -> Le trigger est PRESENT
19:30.255 -> Le trigger est ABSENT
19:30.255 -> Le trigger est PRESENT
19:30.255 -> Le trigger est ABSENT
19:30.255 -> Le trigger est PRESENT
19:30.255 -> Le trigger est ABSENT
19:30.255 -> Le trigger est PRESENT
19:30.255 -> Le trigger est ABSENT
19:30.255 -> Le trigger est PRESENT
19:30.255 -> Le trigger est ABSENT
19:30.255 -> Le trigger est PRESENT
19:30.255 -> Le trigger est ABSENT


19:45.000 -> Le trigger est PRESENT      <--- remise trigger
19:45.000 -> Le trigger est ABSENT      12 rebonds en 89ms !!!
19:45.000 -> Le trigger est PRESENT
19:45.043 -> Le trigger est ABSENT
19:45.043 -> Le trigger est PRESENT
19:45.089 -> Le trigger est ABSENT
19:45.089 -> Le trigger est PRESENT
19:45.089 -> Le trigger est ABSENT
19:45.089 -> Le trigger est PRESENT
19:45.089 -> Le trigger est ABSENT
19:45.089 -> Le trigger est PRESENT
19:45.089 -> Le trigger est ABSENT
19:45.089 -> Le trigger est PRESENT


Là on voit bien que le programme lit 20 ou 12 changements d'états là ou je n'en voudrai qu'un seul.
Il faut faire un anti-rebond (debounce).

Tout ça pour justifier une V2 :mrgreen:
a suivre...

GoodNoize
Messages : 863
Enregistré le : mar. 27 nov. 2018 17:18
Localisation : Jura

Re: Rélfexion autour d'un ESP32, bouton, trigger et relais

Messagepar GoodNoize » dim. 9 juin 2024 09:46

Belle perf d'Alain et ses 9ms d'appuis sur un BP :hehe:

Merci pour ce topic c'est top :merci:
Je vais éplucher tout ça. Je devrais recevoir mon starter pack début début de semaine.

Avatar de l’utilisateur
alka
Administrateur du site
Messages : 3098
Enregistré le : mer. 15 juil. 2015 15:18
Localisation : 92
Contact :

Re: Rélfexion autour d'un ESP32, bouton, trigger et relais

Messagepar alka » lun. 10 juin 2024 11:50

GoodNoize a écrit :Belle perf d'Alain et ses 9ms d'appuis sur un BP :hehe:

c'est aussi le but du debounce : éviter qu'un appui involontaire allume l'ampli ;)
Dans le code plus haut, c'est pas top : n'importe quel glitch sur le trigger ou rebond sur le bouton change d'état. C'est pas optimal du tout.

Quand on cherche "arduino button debounce", on trouve des dizaines de pages, la plupart bien faites tant la question est connue.
Il y a même un exemple fourni avec Arduino (Examples > Digital > debounce). Curieusement, il est mal commenté !

Je trouve cette page particulièrement complète sur le sujet https://deepbluembedded.com/arduino-button-debouncing/

Que pourrai-je ajouter pour la pédagogie ? un dessin peut être.

Image

Deux cas dessinés : un bouton parfait et un bouton réel qui rebondit.
En haut la réalité de ce qui se passe sur la pin. Les points bleus sont les mesures effectuées dans les boucles successives de notre programme.
En dessous l'état stable du bouton enregistré dans la variable buttonState.

L'échelle n'est pas respectée et de loin!
Les points bleus sont séparés de quelques micro-secondes. Les points de mesure ne sont pas parfaitement réguliers comme sur le dessin car la boucle n'a pas une durée régulière, mais ça arrive tellement plus vite que les rebonds qu'il y a toujours beaucoup de points de mesure sur un état de la pin, même furtif.

Le debounce que j'ai choisi d'implémenter pour le bouton (basé sur l'exemple arduino) est de ne valider un changement d'état lu sur la pin que si ne nouvel état est présent pendant une certaine durée. Cette durée est matérialisée par le trait rouge marqué D.
Tout changement d'une durée inférieur à D n'est pas pris en compte. Les changements sont pris en compte sont sur les points blanc. (chaque point représente une boucle).
En général, on choisit D=50ms. Cela retarde la prise en compte de l'appui bouton de façon imperceptible pour un humain.

Pour le trigger, j'ai choisi une autre méthode. Il n'est pas censé rebondir mais il pourrait y avoir des "glitch". Pour les filtrer, je fais une double lecture séparée de quelques dizaines de ms pour confirmer que le changement qu'on a lu existe bien dans la durée.
Cette façon de procéder n'est pas géniale car elle bloque la boucle pendant l'attente de la deuxième lecture. Dans cette application et tout particulièrement pour un trigger qui n'est activé qu'occasionnellement (a l'échelle de la durée d'attente en ms), ce n'est pas gênant du tout.

code v2

Code : Tout sélectionner

/*
Programme de gestion d'un bouton et d'un trigger pour piloter un relais.
Sur mon banc de test, 
- une led rouge simule le relais.
- l'entréee trigger est simulée par un fil au 0V
La led bleue embarquée est utilisée comme témoin.

v1  08 juin 2024 
    - Un appui sur le bouton active le relais. L'appui suivant le désactive.
    - La led embarquée bleue est allumée pendant l'appui sur le bouton.
    - Le relais suit l'entrée trigger, ie trigger présent=>relais activé et inversement
    - Le bouton poussoir gagne sur le trigger: il coupe le relais même si le trigger est présent,
    ou active le relais même si le trigger est absent.
    - Pour information, donne la durée d'appui sur le bouton et durée de la boucle.
v2  10 juin 2024
    - anti rebond sur Bouton Poussoir 
    - double lecture pour entrée Trigger après délai pour ignorer les éventuels glitch
*/

#define FWVERSION "v2"         // version du programme

// GPIOs utilisés sur mon banc de test
#define LEDESP_PIN 15          // led embarquée sur la carte ESP32-S2. Elle est sur GPIO 15
#define RELAY_PIN 5            // sortie relais sur GPIO 05

#define PUSHBUTTON_PIN 16      // entrée pushButton sur GPIO 16. 
#define TRIGGER_PIN 39         // entrée trigger sur GPIO 39

// déclaration et initialisation des variables globales
bool relayState = LOW;         // état du relais. HIGH = activé, LOW = non activé

bool buttonState = HIGH;       // état stable du bouton. HIGH = ouvert, LOW = appuyé
bool prevReadingState = HIGH;  // état précédemment lu du bouton pour permetttre 
                               // de tester si l'état a changé. Initialisé HIGH, càd 'BP ouvert'.

bool triggerState = HIGH;      // état stable du trigger. initialisé HIGH, càd 'Trigger absent'.
const int triggerReadDelay = 100;  // délai pour double lecture du trigger, en ms. 50 par défaut.

const int debounceDelay = 50;  // délai pour anti-rebond en ms. 50 par défaut, augmenter jusqu'à 100ms si nécessaire
ulong lastButtonChangeTime = 0;// timestamp dernier changement lu pour le bouton. Utilisé pour le debounce

ulong timeButtonPressed;       // timestamp quand bouton appuyé. En ms. Peut etre très grand => unsigned long

int maxLoopTime = 0;           // durée max de la boucle loop() en micro secondes
int minLoopTime = 1000;        // durée min. initialisé assez grand.
int avgLoopTime = 0;           // durée moyenne sur 1000 dernières boucles
int loopCount   = 0;           // compteur de boucles
ulong sumLoopTime  = 0;        // cumul durées 
bool printLoopTime = false;    // flag pour imprimer les données boucle si appui bouton ou changement trigger

// ****************************************************************
//                         SETUP
// ****************************************************************

void setup() {
  
  
// LEDESP et RELAY declarés comme Output. 
  pinMode(LEDESP_PIN, OUTPUT);            // pin HIGH : Led allumée   
  pinMode(RELAY_PIN, OUTPUT);             // pin HIGH : Relais activé

  // pushButton et Trigger déclarés comme  Input avec résistance pullup.  
  pinMode(PUSHBUTTON_PIN, INPUT_PULLUP);  // pin LOW : Bouton appuyé
  pinMode(TRIGGER_PIN, INPUT_PULLUP);     // pin LOW : Trigger présent

  digitalWrite(LEDESP_PIN, HIGH);         // allume la led pendant le setup

  Serial.begin(115200);                   // démarre la sortie série
  delay(1000);                            // attends 1sec que le port Série démarre (lent sur ma carte)

  digitalWrite(LEDESP_PIN, LOW);           // éteins la led à la fin du setup

  Serial.println("Version " + String(FWVERSION));
  Serial.println("Prêt. Appuyer sur le bouton ou mettre le trigger.");
  Serial.println();
}

// ****************************************************************
//                         LOOP
// ****************************************************************

void loop() {
  unsigned long timeStartLoop = micros();     // mémorise le timestamp au début de la boucle en microsecodnes
                                              // pour pouvoir mesurer la durée de la boucle

  // Gestion du BOUTON POUSSOIR 

  // Lecture de l'état actuel
  int buttonReading = digitalRead(PUSHBUTTON_PIN);  // LOW quand appuyé, HIGH sinon

  if (buttonReading != prevReadingState) {
    // la  pin du bouton vient de changer d'état : on reset le timer de l'anti-rebond
    lastButtonChangeTime = millis();             
  
}

  if ((millis() - lastButtonChangeTime) > debounceDelay) {
    // le délai est écoulé. on revérifie si l'état lu a bien changé
    // si oui, on acte le nouvel état du bouton

    if (buttonReading != buttonState) {
      buttonState = buttonReading;        // assigne l'état stable du bouton à l'état lu

      if (buttonState == LOW) {
        // état stable bouton vient de passer à LOW (appuyé)
        Serial.println("Bouton appuyé.");
        digitalWrite(LEDESP_PIN, HIGH);   // allume la led embarquée
        relayState = !relayState;         // mémorise que l'état de la sortie Relais est inversé
        digitalWrite(RELAY_PIN, relayState);  // maj pin sortie relais
        Serial.print("Le relais est maintenant ");
        if (relayState) Serial.println("activé."); else Serial.println("coupé.");
        timeButtonPressed = millis();     // mémorise le timestamp du début de l'appui du bouton

      } else {
        // état stable bouton vient de passer à HIGH (ouvert)
        digitalWrite(LEDESP_PIN, LOW);                          // éteins la led embarquée
        int delayButtonPressed = millis() - timeButtonPressed;  // durée pendant lequel il est est resté appuyé
        Serial.print("Bouton relaché (durée appui: ");
        Serial.print(delayButtonPressed);
        Serial.println(" ms).");
        Serial.println();       // saut ligne pour bien distinguer les appuis successifs    
        printLoopTime = true;   // print durées boucles lors appui bouton   
      }
    }
  }

  prevReadingState = buttonReading;  // met l'état précédent du bouton à l'état courant pour le prochain test

  // Gestion de l'entrée TRIGGER
  
  
// Lecture de l'état actuel
  int triggerReading = digitalRead(TRIGGER_PIN);  // LOW si Trigger présent, HIGH sinon
  
  if 
(triggerReading != triggerState) {
    // le trigger vient peut être de changer d'état... 
    // attente puis deuxième lecture pour confirmation
    delay(triggerReadDelay);                 
    triggerReading 
= digitalRead(TRIGGER_PIN);

    if (triggerReading != triggerState) {
      // le changement d'état est bien confirmé
      triggerState = triggerReading;          // mémorise le nouvel état stable du Trigger
      Serial.print("Le trigger est ");
      if (triggerState) Serial.println("absent."); else Serial.println("présent.");

      relayState   = !triggerState;           // mémorise l'état de la sortie Relais qui suit l'état du trigger mais inversé:
                                              // Pin trigger LOW => pin sortie relais HIGH, et inversement.
      digitalWrite(RELAY_PIN, relayState);    // maj pin sortie relais (ce qui allume ou éteins la led rouge sur mon bench)
      Serial.print("Le relais est maintenant ");
      if (relayState) Serial.println("activé."); else Serial.println("coupé.");
      Serial.println();
      
      printLoopTime 
= true;                   // print durées boucles lors d'un changement Trigger
    }

  }

  
  
// Mesure de la durée min, max, moyenne de la durée de la boucle 
  int loopTime = micros() - timeStartLoop;     // durée boucle courante en micro secondes
  if (loopTime > maxLoopTime) maxLoopTime = loopTime;                      
  if 
(loopTime < minLoopTime) minLoopTime = loopTime; 
  loopCount   
+= 1;
  sumLoopTime += loopTime;
  avgLoopTime = sumLoopTime/loopCount;
  if (printLoopTime) {
    Serial.printf ("durée boucle min: %d micros, max: %d micros, moyenne: %d micros. \n", minLoopTime, maxLoopTime, avgLoopTime);
    printLoopTime = false;
  }
  if (loopCount == 1000) {
    // réinitialistaion après 1000 boucles
    loopCount   = 0;
    sumLoopTime = 0;
  }

}
  // end loop
 


et cette fois c'est propre en sortie, et plus d'appui de 9ms pris en compte ;)

Code : Tout sélectionner

11:21:46.579 -> Bouton appuyé.
11:21:46.579 -> Le relais est maintenant coupé.
11:21:46.752 -> Bouton relaché (durée appui: 185 ms).
11:21:46.752 ->
11:21:46.752 -> durée boucle min: 2 micros, max: 100113 micros, moyenne: 2 micros.
11:21:55.285 -> Bouton appuyé.
11:21:55.285 -> Le relais est maintenant activé.
11:21:55.514 -> Bouton relaché (durée appui: 208 ms).
11:21:55.514 ->
11:21:55.514 -> durée boucle min: 2 micros, max: 100113 micros, moyenne: 3 micros.
11:22:13.160 -> Le trigger est absent.
11:22:13.160 -> Le relais est maintenant coupé.
11:22:13.160 ->
11:22:13.160 -> durée boucle min: 2 micros, max: 100113 micros, moyenne: 339 micros.
11:22:20.547 -> Le trigger est présent.
11:22:20.547 -> Le relais est maintenant activé.
11:22:20.547 ->
11:22:20.547 -> durée boucle min: 2 micros, max: 100188 micros, moyenne: 11134 micros.
11:23:21.244 -> Bouton appuyé.
11:23:21.244 -> Le relais est maintenant coupé.
11:23:21.369 -> Bouton relaché (durée appui: 88 ms).
11:23:21.369 ->
11:23:21.369 -> durée boucle min: 2 micros, max: 100188 micros, moyenne: 2 micros.
11:23:21.403 -> Bouton appuyé.
11:23:21.403 -> Le relais est maintenant activé.
11:23:21.539 -> Bouton relaché (durée appui: 116 ms).
11:23:21.539 ->
11:23:21.539 -> durée boucle min: 2 micros, max: 100188 micros, moyenne: 2 micros.

Avatar de l’utilisateur
alka
Administrateur du site
Messages : 3098
Enregistré le : mer. 15 juil. 2015 15:18
Localisation : 92
Contact :

ESP32 et la date et l'heure

Messagepar alka » dim. 16 juin 2024 19:04

On passe à autre chose : mettre l'ESP3 à l'heure.

C'est un sujet galvaudé et pourtant! On trouve tout sur internet mais c'est disparate et pas toujours précis, ni complet.
Je me sers du forum comme blog pour mettre des informations que j'aurais aimé trouver avec des exemples :)

Ce sujet traite des dates et heures sur l'ESP32 pour faire des projets simples avec Arduino (core 2.7.4). On oublie horloge externe etc.
En anglais "time" regroupe les deux notions de date et heure. C'est plus pratique ! J'utiliserai "date" en pensant date+heure.

Dans nos programmes ESP on a souvent besoin de
- chronométrer des tâches
- connaitre la date et manipuler des dates

1. Chronométrer

L'exemple type est de mesurer la durée d'une tâche ou de faire quelquechose pendant une certaine durée.
La fonction millis() donne en milisecondes le temps écoulé depuis le dernier reset.
Remis à 0 à chaque reset.

La fonction retourne un entier positif, type unsigned long (en raccourci ulong) sur 32bits. Ca correspond à uint32_t.
Ca peut donc aller jusqu'à 2^32 ms, cad un peu plus de 49 jours.

Exemple: Ce code dans le setup() imprimera la durée du setup.

Code : Tout sélectionner

void setup() {
    // enregistre le timestamp au départ dans tdebut
    ulong tdebut = millis();    
    
    
... actions du setup...
    
    
// à la fin du setup, calcule et affiche la durée
    // duree contient le nb de milliseconde écoulées depuis tdebut                
    uint32_t duree = millis() - tdebut;     
    Serial
.println("Durée setup :" + String(duree) + "ms");
}



On peut chronométrer en microsecondes avec micros().


2. La date

L'ESP32 a plusieurs timers internes (au passage: ils ne sont pas utilisés pour millis() ou micros())
C'est décrit ici : https://docs.espressif.com/projects/esp ... _time.html

L'horloge RTC (real time clock) interne compte les secondes écoulées depuis le dernier reset.
Donc remis à 0 au reset.
Ce timer interne est accessible par les fonctions C standard comme time().
Le type pour ce timer est time_t qui sur ESP32 Arduino est un int (32 bits). Donc si rien n'est fait, un accident est prévisible en janvier 2038 connu comme le Y2K38 problem!

On a accès aux fonctions de time en mettant #include "time.h" (cette librairie est présente dans le core arduino pour ESP32.)

Ce code dans la boucle, affiche un compteur 1 2 3 4 etc au rythme de 1 par seconde

Code : Tout sélectionner

time_t now = time(&now);        // now contient le compteur RTC en secondes
Serial.println(now);
delay(1000); 


syntaxes valides pour time ():

Code : Tout sélectionner

time_t now = time(0);      // déclaration et initialisation variables now et now1
time_t now1 = time(NULL); // valides car argument 0 ou NULL accepté - NULL est défini dans time.h              

Code : Tout sélectionner

time_t now;              // déclaration variable now séparément de son assignation
time(&now);              // assignation du compteur RTC à la variable now             

Code : Tout sélectionner

time_t now = time(&now);// valide aussi, même si ça fait double emploi             


syntaxe invalide:

Code : Tout sélectionner

time_t now = time();      // invalide : time() a besoin d'un argument             



Jusque là, le timer RTC interne ne sert pas à grand chose !

La date de nos ordinateurs est un compteur de secondes depuis le 1er janvier 1970, 0h.
Cette façon de compter le temps a été créée par les gars qui ont fait unix dans les années 70.
On l'appelle EPOCH time.
Plein de sites internet donnent l'EPOCH courante ou convertissent une date en EPOCH et inversement.
par exemple Sunday 16 June 2024 12:29:14 c'est 1718540954.
https://www.epochconverter.com/

Pour que l'ESP32 soit à l'heure, il faut caler l'horloge RTC de l'ESP32 avec l'EPOCH actuelle.

On peut initialiser "à la main" avec la fonction setTime() ou utiliser ce qui existe dans l'ESP32 en synchronisant avec les serveurs NTP par internet.
Exemple:

Code : Tout sélectionner

#include <WiFi.h>
#include "time.h"

const char* ntpServer = "pool.ntp.org";
const char* time_zone = "CET-1CEST,M3.5.0,M10.5.0/3";   // TimeZone Paris avec infos DST

void setup() {
  Serial.begin(115200);
  configTzTime(time_zone, ntpServer);  
  wifiConnect
();                       // ma fonction de connexion Wifi
}
void loop() {
  time_t now = time(NULL);
  Serial.print("now is ");
  Serial.println(now);
  delay(1000);
}

Avec ce programme, on obtient en sortie
now is 1
now is 2
now is 1718551551
now is 1718551552
now is 1718551553
etc.

On voit que après les quelques secondes nécessaires à la syncronisation NTP, le timer RTC donne la date EPOCH.
La synchronisation NTP et la correction heure été/hiver (DST - Day Saving Time) se fait automatiquement grâce à configTzTime.
De ce que j'ai compris, dès lors que configTzTime (ou configTime) est dans le setup, l'appel à time() fait la synchonisation NTP avec les bons paramètres.
Le délai de resynchro NTP est de 60 minutes. Dans l'intervalle, ou aussi longtemps que la synchro NTP ne peut se faire, c'est le compteur RTC qui est utilisé.
Ca fait une horloge plutôt bien synchronisée.

La fonction getLocalTime() est souvent utilisée dans les exemples des tutoriels pour obtenir la date.
Elle fait appel en interne à time() et donc fait la synchronisation NTP si elle n'a pas eu lieu.
getLocalTime() met à jour une structure de date tm. définition: https://github.com/espressif/arduino-es ... hal-time.c

exemple pour getLocalTime:

Code : Tout sélectionner

tm nowtm;
if (!getLocalTime(&nowtm)) Serial.println("Failed to get local time !");
const char* pattern = "%d %m %y %H:%M:%S";        // pour le formatage lors du print de la date en jj mm aa hh:mm:ss
Serial.print("Local time is ");
Serial.println(&nowtm, pattern);  // Serial.println connait la structure tm et sait formater selon le pattern transmis



Pour manipuler les dates, on peut utiliser les fonctions standard du C définies dans time.h et aussi des spécifiques arduino comme setTime() adjustTime() getLocalTime()

Ca amène à parler de la structure tm qui vient dans time.h pour définir une date.

Code : Tout sélectionner

struct tm {
   int tm_sec;         /* seconds,  range 0 to 59          */
   int tm_min;         /* minutes, range 0 to 59           */
   int tm_hour;        /* hours, range 0 to 23             */
   int tm_mday;        /* day of the month, range 1 to 31  */
   int tm_mon;         /* month, range 0 to 11             */
   int tm_year;        /* The number of years since 1900   */
   int tm_wday;        /* day of the week, range 0 to 6    */
   int tm_yday;        /* day in the year, range 0 to 365  */
   int tm_isdst;       /* daylight saving time             */
};


Reste à parler de
- conversions de format EPOCH <-> tm
- l'exploitation des éléments de la structure tm
- formatage des dates pour les manipuler ou les imprimer. (Ca se fait soit directement dans Serial.println() comme dans l'exemple ou en utilisant strftime() )
- la lib ESP32Time

Avatar de l’utilisateur
thierryvalk
Administrateur du site
Messages : 3771
Enregistré le : jeu. 9 juil. 2015 20:08
Localisation : Belgique

Re: Rélfexion autour d'un ESP32, bouton, trigger et relais

Messagepar thierryvalk » dim. 16 juin 2024 19:53

Voilà qui tombe à pic vu que je devrais implanter un RTC pour ma sorcière.

note: un unsigned long est censé être sur 2 mots cad 64 bits sur un système 32 bits comme l'ESP32.
Il est pourtant bien en 32bits sur l'ESP32. Pas bien grave car on n'utilise évidemment jamais de délais aussi longs.)


C’est pas évident vu l’architecture des processeurs actuels : 8 16 ou 64 bits et l’héritage très anciens de ces conventions.
Pas bien grave si l’on écrit du code pour un type de processeur, mais bien plus problématique si l’on écrit une lib destinée à plusieurs types de processeurs.
Un int c’est un entier standard, sur un système à 64 bits, ce serait donc 64 bits ?
Pour faire plus clair et plus standard l’on utilise actuellement des déclarations du style :
uint8
uint16
uint32
uint64
Pas de confusion possible et ce n’est que quelques #define à modifier le cas échéant.

Et vu que l’on ait dans le sujet :
int duree = millis() - tdebut;

Voudrait dire que l’on peut remonter dans le temps.
Un unsigned serait logique, on double ainsi sa capacité, mais l’on simplifie aussi les calculs, bien que de soit pas un grand gain pour un ESP32.

La date de nos ordinateurs est un compteur de secondes depuis le 1er janvier 1970, 0h.

Non, pas forcément, n’oublions pas le bug de l’an 2000. (Qui a été en son époque une bonne relance de l’économie...)
Les RTC courants fonctionnent en BCD ( binaire codé décimal) et gèrent les mois et années bissextiles avec une résolution inférieure à la seconde.
Prochain bug 2100.
Par contre oui, dès que l’on veut faire des calculs de date, on converti les dates en durées.

Avatar de l’utilisateur
alka
Administrateur du site
Messages : 3098
Enregistré le : mer. 15 juil. 2015 15:18
Localisation : 92
Contact :

Re: Rélfexion autour d'un ESP32, bouton, trigger et relais

Messagepar alka » dim. 16 juin 2024 22:12

thierryvalk a écrit :Voilà qui tombe à pic vu que je devrais implanter un RTC pour ma sorcière.

note: un unsigned long est censé être sur 2 mots cad 64 bits sur un système 32 bits comme l'ESP32.
Il est pourtant bien en 32bits sur l'ESP32. Pas bien grave car on n'utilise évidemment jamais de délais aussi longs.)


C’est pas évident vu l’architecture des processeurs actuels : 8 16 ou 64 bits et l’héritage très anciens de ces conventions.
Pas bien grave si l’on écrit du code pour un type de processeur, mais bien plus problématique si l’on écrit une lib destinée à plusieurs types de processeurs.
Un int c’est un entier standard, sur un système à 64 bits, ce serait donc 64 bits ?
Pour faire plus clair et plus standard l’on utilise actuellement des déclarations du style :
uint8
uint16
uint32
uint64
Pas de confusion possible et ce n’est que quelques #define à modifier le cas échéant.
je me trompe. int64_t c'est "long long" dans arduino, ou "unsigned long long"pour uint64_t. Je vais corriger.
par défaut, int c'est la longueur d'un mot sur la machine : 64bits sur un processeur 64bits. Mais c'est le compilateur qui décide au final.
Arduino permet des types optimisés, du genre uint_fast8_t qu'il va mettre sur 32 bits parce que c'est plus rapide ou uint_least8_t qu'il va vraiment mettre sur 8bits au prix d'un ralentissement pour caster les bits au moment des traitements. C'est pour de l'optimisation aux petits oignons et je suis loin de ça.


Et vu que l’on est dans le sujet :
int duree = millis() - tdebut;

Voudrait dire que l’on peut remonter dans le temps.
Un unsigned serait logique, on double ainsi sa capacité, mais l’on simplifie aussi les calculs, bien que de soit pas un grand gain pour un ESP32.
oui ulong (uint32_t) serait mieux. Je vais modifier le code pour permettre 49jours de délai au lieu de 24,5 ;)
Je ne savais pas qu'un µC calcule plus vite en uint qu'en int.


La date de nos ordinateurs est un compteur de secondes depuis le 1er janvier 1970, 0h.

Non, pas forcément, n’oublions pas le bug de l’an 2000. (Qui a été en son époque une bonne relance de l’économie...)
Les RTC courants fonctionnent en BCD ( binaire codé décimal) et gèrent les mois et années bissextiles avec une résolution inférieure à la seconde.
Prochain bug 2100.
Par contre oui, dès que l’on veut faire des calculs de date, on converti les dates en durées.
Unix, Windows et iOS utilisent EPOCH et sont passés depuis un moment déjà au codage de l'EPOCH sur 64bits ce qui repousse le problème dans quelques millions d'années :mrgreen:
Sur les autres systèmes et RTC je ne sais pas.
Arduino sur ESP devrait suivre un jour aussi et supporter time() en 64bits.


Je suppose que tu veux une RTC pour les synchroniser entre eux. Peut etre que ESPNOW serait une option pour les faire échanger des données rapidement ?
https://randomnerdtutorials.com/esp-now ... duino-ide/

Avatar de l’utilisateur
thierryvalk
Administrateur du site
Messages : 3771
Enregistré le : jeu. 9 juil. 2015 20:08
Localisation : Belgique

Re: Rélfexion autour d'un ESP32, bouton, trigger et relais

Messagepar thierryvalk » lun. 17 juin 2024 08:12

Je suppose que tu veux une RTC pour les synchroniser entre eux.

Non, juste avoir la date et l'heure pour un seul ESP. Je n'ai pas à les synchroniser vu l'utilisation de MQTT.

Avatar de l’utilisateur
alka
Administrateur du site
Messages : 3098
Enregistré le : mer. 15 juil. 2015 15:18
Localisation : 92
Contact :

Re: Rélfexion autour d'un ESP32, bouton, trigger et relais

Messagepar alka » lun. 17 juin 2024 10:41

thierryvalk a écrit :
Je suppose que tu veux une RTC pour les synchroniser entre eux.

Non, juste avoir la date et l'heure pour un seul ESP. Je n'ai pas à les synchroniser vu l'utilisation de MQTT.

ah j'ai mal compris. Je pensais que tu voulais RTC externe. Si ta sorcière voit du wifi de temps en temps, c'est facile avec les quelques lignes de code plus haut.

Avatar de l’utilisateur
alka
Administrateur du site
Messages : 3098
Enregistré le : mer. 15 juil. 2015 15:18
Localisation : 92
Contact :

Re: Rélfexion autour d'un ESP32, bouton, trigger et relais

Messagepar alka » lun. 17 juin 2024 11:57

Je continue en présentant une manière de mettre à la date.
Il s'avère que time() récupère parfois au début un nombre non nul mais fantaisiste avant d'avoir le bon EPOCH.
Une manière de s'assurer d'avoir synchronisé la date :

fonction appelée dans setup() après avoir la connection wifi

Code : Tout sélectionner

#define NTP_MIN_VALID_EPOCH 1533081600    // August 1st, 2018. pourquoi cette date ? pourquoi pas.
time_t now_;    // stocke la date actuelle

//a appeler dans setup()
bool syncTime() {
 unsigned long t0 = millis();
 Serial.print ("Synching Time with NTP...");
  while ((now_ = time(NULL)) < NTP_MIN_VALID_EPOCH) {
        delay(200);
        Serial.print(".");
        if  ((millis() - t0) > 5000) {
            Serial.println(" failed. Timeout 5s !\nTime is not synched !!!");
            return false;          
        
}      
  
}
  Serial.printf(" done in %lu ms. \n", millis()-t0);
  return true;
}
 

Puis au début de loop():

Code : Tout sélectionner

now_ = time(nullptr);   // pour maintenir la date à jour pour la loop.   

Comme ma loop est < 1sec, now_ est toujours correct et c'est plus joli que d'appeler time(NULL) à chaque fois qu'on en a besoin.
Si d'aventure le contexte fait que la loop peut durer plus d'une seconde, il suffit de remettre now_ = time(NULL); à l'endroit adéquat

Au passage, c'est équivalent d'appeler time() avec comme paramètre 0 NULL ou nullptr

Avatar de l’utilisateur
alka
Administrateur du site
Messages : 3098
Enregistré le : mer. 15 juil. 2015 15:18
Localisation : 92
Contact :

Re: Rélfexion autour d'un ESP32, bouton, trigger et relais

Messagepar alka » lun. 17 juin 2024 11:58

et j'ai vérifié sur mon ESP32 :
sizeof time_t: 4 bytes
sizeof int8: 1 bytes
sizeof int: 4 bytes
sizeof ulong: 4 bytes
sizeof u u long: 8 bytes

Avatar de l’utilisateur
alka
Administrateur du site
Messages : 3098
Enregistré le : mer. 15 juil. 2015 15:18
Localisation : 92
Contact :

Re: Rélfexion autour d'un ESP32

Messagepar alka » lun. 17 juin 2024 15:21

suite...

pour mettre à jour l'horloge interne "à la main", par exemple si pas d'accès internet ou si l'heure vient d'un autre système, on peut utiliser setTime()
Ca marche en passant en argument soit un time_t soit les données élémentaires..
on peut aussi utiliser adjustTime() pour ajuster l'horloge en + ou en -
je n'ai jamais utilisé ces fonctions. Je suppose qu'il suffit de suivre la doc.

Code : Tout sélectionner

setTime(hours, minutes, seconds, days, months, years) to set the time and date, using the 6 parameters.
adjustTime(number) to Adjust the time, adding (positive numbers) or removing (negative numbers) seconds.


J'ai une préférence pour la représentation de la date en format EPOCH (time_t). Je n'utilise la représentation struct tm que pour des manipulations de date et les impression.
Pour passer d'une représentation à l'autre :
- localtime() passe de time_t à tm heure locale
- mktime() passe de tm à time_t

Code : Tout sélectionner

// aller-retour
time_t nowt = 1718631782; // date du 17 06 24 15:43:02 
tm *nowtm   = localtime(&nowt); 
time_t nowt2 
= mktime(nowtm); 

En sortie:
nowt : 1718631782
nowtm: 17 06 24 15:43:02
nowt2: 1718631782

pour la mise en forme humainement lisible, a partir d'une struct tm,
- pour imprimer on peut directement utiliser Serial.print() comme dans les exemples plus haut
- pour mettre dans un String, on utilise strftime()
Les options de formatage sont nombreuses. https://www.tutorialspoint.com/c_standa ... rftime.htm

je l'utilise comme ça par exemple:

Code : Tout sélectionner

String tmToString(tm* tmTime) {
  // retourne String de la date sous forme dd mm yy hh:mm:ss
  char buff[30];
  strftime(buff, 30, "%d %m %y %H:%M:%S", tmTime);
  return buff;


Et pour finir, je cite la librairie ESP32Time de fbiago encapsule pas mal de fonctions pour faciliter la gestion de time. Elle s'appuie sans surprise sur les fonctions présentées plus haut et donne une certaine homogénéité et facilité.
https://github.com/fbiego/ESP32Time/tree/main

Avatar de l’utilisateur
thierryvalk
Administrateur du site
Messages : 3771
Enregistré le : jeu. 9 juil. 2015 20:08
Localisation : Belgique

Re: Rélfexion autour d'un ESP32

Messagepar thierryvalk » lun. 17 juin 2024 19:00

Et pour l’heure d’été ?

Avatar de l’utilisateur
alka
Administrateur du site
Messages : 3098
Enregistré le : mer. 15 juil. 2015 15:18
Localisation : 92
Contact :

Re: Rélfexion autour d'un ESP32

Messagepar alka » lun. 17 juin 2024 22:06

C'est pris en compte dans la timezone avec DST.

edit: comme en belgique c'est pareil qu'en france, tu peux utiliser la meme timezone
"CET-1CEST,M3.5.0,M10.5.0/3"

Avatar de l’utilisateur
alka
Administrateur du site
Messages : 3098
Enregistré le : mer. 15 juil. 2015 15:18
Localisation : 92
Contact :

Re: Rélfexion autour d'un ESP32

Messagepar alka » mar. 18 juin 2024 10:55

une autre conversion que j'avais faite car on trouve parfois l'heure exprimée au format ISO 8601
YYYY-MM-DDTHH:MM:SS

Code : Tout sélectionner

// Returns time_t time from ISO8601 time String "YYYY-MM-DDTHH:MM:SS"
// -1 if error
time_t epochFromISOString(const String &timestr) {

  // minimal error checking
  if (timestr.length() != 19 )          return -1;  
  if 
(timestr.substring(10,11) != "T")  return -1;

  // prepare tm struct 
  struct tm tm;  
  tm
.tm_year = timestr.substring(0,4).toInt() - 1900;    // tm_year starts in 1900
  tm.tm_mon  = timestr.substring(5,7).toInt() - 1;       // tm_month jan is 0
  tm.tm_mday = timestr.substring(8,10).toInt();
  tm.tm_hour = timestr.substring(11,13).toInt();
  tm.tm_min  = timestr.substring(14,16).toInt();
  tm.tm_sec  = timestr.substring(17,19).toInt();
  tm.tm_isdst = -1;                                     // DST On? 1 = Yes, 0 = No, -1 = Unknown
  
  
// mktime does the conversion tm -> epoch
  return mktime(&tm);                                   // mktime returns -1 if it fails


la même chose fait différemment avec strptime pour la pédagogie.

Code : Tout sélectionner

time_t epochFromISOString2(const String &timestr) {

  struct tm tm;
  char timecharArray[20];
  timestr.toCharArray(timecharArray,20);
  
  if 
(strptime(timecharArray, "%Y-%m-%dT%H:%M:%S", &tm) == NULL)  return -1;

  tm.tm_isdst = -1;     // Not set by strptime(). Tells mktime() whether daylight saving time is in effect (1) or not (0) or unknown (-1)

  return  mktime(&tm);      // -1 if it fails
}

Avatar de l’utilisateur
alka
Administrateur du site
Messages : 3098
Enregistré le : mer. 15 juil. 2015 15:18
Localisation : 92
Contact :

Re: Rélfexion autour d'un ESP32

Messagepar alka » ven. 28 juin 2024 20:08

une v3 du code pour la gestion bouton-trigger-températures
ca suit plus ou moins les exigences de goodnoize dans son sujet mais pas exactement.
Pas mal de changements :
- connecté au wifi il est a l'heure
- gère les températures des 6 CTN
- fait clignotter la led plus ou moins vite
- reprise complète de la logique du code et refactoring

j'ai refait la logique en partant sur l'idée de Thierry de séparer la partie prise en compte des entrées et ensuite traitements pour les sorties. Thierry pensait a un entier qui coderait tous les états du système mais je ne suis pas allé jusque là. Combinatoire trop complexe et le système doit aussi réagir aux événements, pas seulement aux états.
Je n'avais jamais réfléchi comme ça et ça a son intérêt.
L'état de AmpController (oui c'est son nom parce que un projet sans nom n'existe pas vraiment) est dans une variable controllerState : -1 (unset), 0 (fault) il faut tout couper, 1 (OK) , 2 (warning)
Un json contient tous les états. Ce sera utile pour l'interface web quand elle viendra.

exemple:
{"controller": 1, "button": 1, "trigger": 0, "powerCtrl": 1, "led": 0, "TH1": 14.43, "TH2": 14.56, "TH3": 13.51, "TH4": 14.43, "TH5": 13.77, "TH6": 13.90}

Evidemment dans mon cas les 6 températures sont quasi identiques vu qu'il n'y a qu'une seule CTN simulée sur mon breadbord lue 6 fois !

Voci. Ce serait plus simple d'attacher le fichier mais le quota du formu est atteint et je suis un admin fainéant :mrgreen:

Code : Tout sélectionner

/*

AmpController : Programme de gestion d'un bouton, d'une entrée trigger, de 6 sondes de température
pour piloter une sortie PowerControl et une led.
Si la température dépasse un seuil, la sortie PowerCtrl est coupée.
La led est témoin de l'état du système et clignotte plus ou moins.

Sur mon banc de test, 
- une led rouge simule la sortie PowerControl.
- la led builtin sur la carte est utilisée comme led témoin
- l'entréee Trigger est simulée par un fil au 0V
- l'entrée bouton est un bouton poussoir tact
- une unique CTN simulée par un potentiomètre

v1  08 juin 2024 
    - Un appui sur le bouton active Power Control. L'appui suivant le désactive.
    - La led embarquée bleue est allumée pendant l'appui sur le bouton.
    - PowerCtrl suit l'entrée trigger, ie trigger présent=>PowerCtrl activé et inversement
    - Le bouton poussoir gagne sur le trigger: il coupe Power Control même si le trigger est présent,
    ou active Power Control même si le trigger est absent.
    - Pour information, donne la durée d'appui sur le bouton et durée de la boucle.
v2  10 juin 2024
    - anti rebond sur Bouton Poussoir 
    - double lecture pour entrée Trigger après délai pour ignorer les éventuels glitch
v3  27 06 2024
    - handleLed qui fait clignoter la led (blink)
    - ajout wifi 
    - gestion état du controller et séparation gestion des entrées / gestion des sorties
    - traitement des 6 CTN alerte température (overHeat)
TO DO
    - lib WiFiManager
    - interface web
*/

#define FWVERSION "v3"         // version du programme

// GPIOs utilisés 
#define LEDESP_PIN    15       // led. La led embarquée sur la carte ESP32-S2 est sur GPIO 15
#define POWERCTRL_PIN  5       // sortie powerCtrl sur GPIO 05

#define BUTTON_PIN    16       // entrée Button sur GPIO 16. 
#define TRIGGER_PIN   39       // entrée Trigger sur GPIO 39

#define   NB_TH 6              // nombre de sondes de température
const int TH_PINS[NB_TH] = {9,9,9,9,9,9}; // pins des sondes température CTN TH0 à TH5. Sur ma breadbord une seule CTN sur GPIO9 !

// pour améliorer la lisibilité 
// pour le bouton
#define PRESSED  LOW            
#define RELEASED HIGH 
// pour le trigger          
#define PRESENT  LOW            
#define ABSENT   HIGH 
// pour la sortie PowerControl
#define POWERCTRL_ON  HIGH
#define POWERCTRL_OFF LOW
// pour la led          
#define LED_OFF   0            // arrete le blink. éteins la led.
#define LED_ON    1            // toujours allumé
#define LED_BLINK_SLOW  1000   // durée en ms du blink lent 
#define LED_BLINK_FAST  200    // durée en ms du blink rapide
// pour l'état du controller
#define CTL_UNKNOWN -1         // inconnu. seulement au reset.
#define CTL_FAULT    0         // erreur: tout arreter !
#define CTL_OK       1         // tout va bien
#define CTL_WARNING  2         // warning 


// includes 
#include "config.h"                // fichier de configuation pour les credentials
// libs du core
#include <WiFi.h>                  // lib Wifi incluse dans le core arduino esp32


#include <ESPmDNS.h>
#include <LittleFS.h>

// WiFi credentials
const char* SSID = "";                        // SSID du WiFi 2.4GHz
const char* PASSPHRASE = "";                  // passphrase wifi
const char* HOSTNAME = "AmpController";       // hostname de la carte. Pas sur que ce soit pris en compte !

// déclaration et initialisation des const et variables globales
int controllerState = CTL_UNKNOWN; // Etat du système. Unknonw(-1), Fault(0), OK(1), Warning(2)

bool powerCtrlState = LOW;         // état de la sortie powerCtrl. HIGH = activé, LOW = non activé

bool buttonState = RELEASED;       // état stable du bouton. HIGH = ouvert, LOW = appuyé
bool buttonChanged = false;        // true quand l'état stable du bouton a changé
bool buttonReadingPrev = RELEASED; // état précédemment lu du bouton pour permetttre 
                                   // de tester si l'état a changé. Initialisé HIGH, càd 'BP ouvert'.
ulong timeButtonPressed;           // timestamp début bouton appuyé. En ms. Peut etre très grand => unsigned long
const int debounceDelay = 50;      // délai pour anti-rebond du bouton en ms. 50 par défaut, augmenter jusqu'à 100ms si nécessaire
ulong lastButtonChangeTime = 0;    // timestamp dernier changement lu pour le bouton. Utilisé pour le debounce

bool triggerState = ABSENT;        // état stable de l'entrée trigger. initialisé HIGH, càd 'Trigger absent'.
bool triggerChanged = false;       // true quand l'état stable de l'entrée Trigger a changé
const int triggerReadDelay = 100;  // délai pour double lecture du trigger, en ms. 50 par défaut.


bool overHeatState = false;        // true when one of the sensors exceeds threshold
const uint TEMP_TRESHOLD = 55;     // temperature threshold to net exceed, in °C 
float thTemps[NB_TH] = {};         // table des températures des sondes. initialisé à 0.0
int   thPinReadings[NB_TH];  // table des valeurs lues par l'ADC sur 12bits encodées sur 16bits. init à 0

uint  ledState = 0;                // état led (0 éteinte, 1 allumée ou vitesse du blink 
ulong blinkLastChange = 0;         // timestamp quand blink led a changé d'état pour la dernièrefois

int maxLoopTime = 0;               // durée max de la boucle loop() en micro secondes
int minLoopTime = 1000;            // durée min. initialisé assez grand.
int avgLoopTime = 0;               // durée moyenne sur 1000 dernières boucles
int loopCount   = 0;               // compteur de boucles
ulong sumLoopTime  = 0;            // cumul durées boucles pour calcul moyenne
bool printFlag = false;            // flag pour demander d'imprimer les données timing boucle

File fsUploadFile;                 // fichier temporaire pour les uploads

// pour la date
const char* ntpServer1 = "pool.ntp.org";
const char* time_zone  = "CET-1CEST,M3.5.0,M10.5.0/3";  // TimeZone for Europe/Paris including daylight adjustment rules
time_t nowt;                       // date actuelle au format time_t (unsigned long)



// déclarations des prototypes des fonctions
void   handleLed();
String epochToString(time_t epochtime, char* pattern = (char*)"%d/%m/%y %H:%M:%S");
bool   syncTime();
String formatBytes(size_t bytes);

// ****************************************************************
//                         SETUP
// ****************************************************************

void setup() {
  
  
// sorties POWERCTRL et LED
  pinMode(POWERCTRL_PIN, OUTPUT);            // pin HIGH : PowerCtrl activé
  pinMode(LEDESP_PIN, OUTPUT);              // pin HIGH : Led allumée
  
  
// Entrées Button et Trigger avec résistance pullup.  
  pinMode(BUTTON_PIN, INPUT_PULLUP);        // pin LOW : Bouton appuyé
  pinMode(TRIGGER_PIN, INPUT_PULLUP);       // pin LOW : Trigger présent

  // Entrées sondes CTN sur ADC : rien à déclarer

  digitalWrite(LEDESP_PIN, HIGH);           // allume la led pendant le setup
  
  Serial
.begin(115200);                     // démarre la sortie série
  delay(1500);                              // attends 1.5sec que le port Série démarre (il est lent sur ma carte)
 
  Serial
.println("\n_____ Début Setup _____");
  Serial.println("FW version: " + String(FWVERSION));

  // LittleFS
  if (LittleFS.begin())  Serial.println("LittleFS démarré."); 
  else Serial
.println("Impossible d'ouvrir le file system LittleFS !");

  // connexion au point d'accès WiFi
  WiFi.mode(WIFI_STA);                    // mode STA par défaut mais autant le préciser
  WiFi.setHostname("HOSTNAME");           // hostname de la carte. Utile ?
  WiFi.begin(SSID, PASSPHRASE);           // credentials définis dans config.h
  Serial.print("Connexion au Wifi ...");
  ulong timerW = millis();                // pour mesurer la durée de connexion au Wifi

  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    digitalWrite(LEDESP_PIN, !digitalRead(LEDESP_PIN)); // inverse l'état de la led pour faire clignoter
    delay(200);
  }
  digitalWrite(LEDESP_PIN,HIGH);          // rallume la led au cas ou

  Serial.printf("\nConnecté à %s en %d ms.\n", SSID, millis() - timerW);
  Serial.printf("  RSSI: %d dB \n", WiFi.RSSI());
  Serial.print("  MAC Address: "); Serial.println(WiFi.macAddress());
  Serial.print("  IP Address : "); Serial.println(WiFi.localIP());
  Serial.print("  HostName: ");    Serial.println(HOSTNAME);

  // time NTP avec TimeZOne et DST
  configTzTime(time_zone, ntpServer1);
  syncTime();                 // ma fonction qui synchronise la date par NTP

  // mDSN
  if (!MDNS.begin("HOSTNAME")) Serial.println("Error starting mDNS");   // marche vraiment ce truc ?

  
  
// fin setup
  Serial.println("nowt is " + epochToString(nowt));
  Serial.println("_____ Fin Setup _____");
  Serial.println("Appuyer sur le bouton ou mettre le trigger ou tourner le potentiomètre CTN.");
  Serial.println();
  digitalWrite(LEDESP_PIN, LOW);   // éteins la led à la fin du setup     

}

// ****************************************************************
//                         LOOP
// ****************************************************************

void loop() {

  // maj de la date
  nowt = time(NULL);
  
  
// --------------------------------------------------------
  //                       ENTREES
  // --------------------------------------------------------
  

  
// BOUTON POUSSOIR 
  // --------------------------

  // Lecture de l'état actuel
  int buttonReading = digitalRead(BUTTON_PIN);  // LOW quand appuyé, HIGH sinon

  if (buttonReading != buttonReadingPrev) {
    // la  pin du bouton vient de changer d'état : on reset le timer de l'anti-rebond
    lastButtonChangeTime = millis();             
  
}

  if ((millis() - lastButtonChangeTime) > debounceDelay) {
    // le délai est écoulé. on revérifie si l'état lu a bien changé
    // si oui, on acte le nouvel état du bouton
    if (buttonReading != buttonState) {
      buttonState   = buttonReading;        // assigne l'état stable du bouton à l'état lu
      buttonChanged = true;
    }
  }
  buttonReadingPrev = buttonReading;  // enregistre l'état précédent du bouton à l'état courant pour le prochain test


  // entrée TRIGGER 
  // ---------------------------
  
  
// Lecture de l'état actuel
  int triggerReading = digitalRead(TRIGGER_PIN);  // PRESENT (LOW) si Trigger présent
  
  if 
(triggerReading != triggerState) {
    // le trigger vient peut être de changer d'état... 
    // attente puis deuxième lecture pour confirmation
    delay(triggerReadDelay);                 
    triggerReading 
= digitalRead(TRIGGER_PIN);

    if (triggerReading != triggerState) {
      // le changement d'état est bien confirmé
      triggerState = triggerReading;             // mémorise le nouvel état stable du Trigger
      triggerChanged = true;
    }
  }

  // entrées sondes de température CTN
  // ---------------------------------
  const float T0 = 298.0;       // Température de référence de la CTN en degrés Kelvin (25°C)
  const float R0 = 10000.0;     // Résistance de la CTN à 25°C
  const float Rs = 4500.0;      // Résistance série du diviseur de tension
  const float beta = 3435.0;    // Valeur bêta de la CTN
  overHeatState = false;                                  // réinitialise le statut avant lecture des sondes
  for(int i=0;i<NB_TH;i++) {
    thPinReadings[i] = analogRead(TH_PINS[i]);            // lecture valeur ADC de la pin TH. 
    float k = (Rs/R0) * (3.3 / (thPinReadings[i] * 3.1 / 4096) - 1);  // calcul intermédiaire
    thTemps[i] = -273 + 1 /( 1/T0 + (1/beta) * log(k) );  // résultat en °C
    if(thTemps[i] > TEMP_TRESHOLD) overHeatState = true;  // vérifie si seuil dépassé
  }
  

  
// --------------------------------------------------------
  //            TRAITEMENT DES SORTIES
  // --------------------------------------------------------
  
  if 
(buttonChanged) {
    if (buttonState == PRESSED) {
      // l'état stable du bouton vient de passer à LOW (appuyé)
      Serial.println("Bouton appuyé.");
      if(controllerState != CTL_FAULT) {
        powerCtrlState = !powerCtrlState;             // l'état de la sortie Relais est inversé
        digitalWrite(POWERCTRL_PIN, powerCtrlState);  // maj pin sortie powerCtrl 
        Serial.print("PowerControl  est maintenant ");
        if (powerCtrlState) Serial.println("activé.");
        else Serial.println("coupé.");
      } else {
        powerCtrlState = POWERCTRL_OFF;               //normalement il l'est déjà, mais ca ne fait pas de mal d'insister !
        Serial.print("PowerControl reste coupé car le controleur est en défaut! ");
      }
      timeButtonPressed = millis();                   // mémorise le timestamp du début de l'appui du bouton

    } else {
      // l'état stable du bouton vient de passer à HIGH (ouvert)
      //digitalWrite(LEDESP_PIN, LOW);                          // éteins la led embarquée
      ledState = LED_OFF;
      int delayButtonPressed = millis() - timeButtonPressed;  // durée pendant lequel le btn est resté appuyé
      Serial.print("Bouton relaché (durée appui: ");
      Serial.print(delayButtonPressed);
      Serial.println(" ms).");
      Serial.println();  // saut ligne pour bien distinguer les appuis successifs
      printFlag = true;  // print durées boucles lors appui bouton
    }
  }

  if (triggerChanged) {
    Serial.print("Le trigger est maintenant: ");
    if (triggerState == PRESENT) Serial.println("Présent.");
    else Serial.println("Absent.");
    if (controllerState != CTL_FAULT) {
      powerCtrlState = !triggerState;              // mémorise l'état de la sortie Relais qui suit l'état du trigger mais inversé:
                                                   // Pin trigger LOW => pin sortie powerCtrl HIGH, et inversement.
      digitalWrite(POWERCTRL_PIN, powerCtrlState); // maj pin sortie powerCtrl (ce qui allume ou éteins la led rouge sur mon bench)
      Serial.print("PowerControl  est maintenant ");
      if (powerCtrlState) Serial.println("activé.");
      else Serial.println("coupé.");
      Serial.println();
    } else {
      powerCtrlState = POWERCTRL_OFF;              //normalement il l'est déjà, mais ca ne fait pas de mal d'insister !
      Serial.println("PowerControl reste coupé car le controller est en défaut!");
    }

    printFlag = true;  // print infos lors d'un changement Trigger 
  }
  
  
  
// Cas d'anomalie
  // -------------------------------

  if (overHeatState) {   
    
// il faut tout arrêter ! 
    // un redémarrage au bouton ou au trigger est nécessaire! 
    controllerState = CTL_FAULT;
    powerCtrlState = POWERCTRL_OFF;
    digitalWrite(POWERCTRL_PIN, POWERCTRL_OFF);
    ledState = LED_BLINK_FAST;

  } else {
    // si le trigger est présent et que PowerControl est désactivé, la led blink lentement.
    // c'est que l'ampli a été arreté avec le bouton alors que le Trigger est présent.
    if (powerCtrlState == POWERCTRL_OFF && triggerState == PRESENT) {
      ledState = LED_BLINK_SLOW;
      controllerState = CTL_WARNING;
    } else {
      ledState = LED_OFF;
    }
  }
  // Si bouton appuyé, led allumée. Sera éteinte quand bouton relaché
  if (buttonState == PRESSED) ledState = LED_ON; 

  handleLed
();           // gestion de la led : éteins, allume ou blink selon ledState


  // Impression  
  // ------------------------------------------- 
  if (printFlag) {
    printFlag = false;
    // date
    Serial.println("Date: " + epochToString(nowt));
    // états des entrées/sorties
    Serial.println("States: " + getStatesJson());
    // CTN et températures
    for (int i=0;i<NB_TH;i++) {
      Serial.printf("adc CTN%d: %d ,TH%d %f°C\n",i+1,thPinReadings[i],i+1,thTemps[i]);
    }
  }

  // réinitialise les variables de changement des entrées et l'état du controler pour la boucle suivante
  buttonChanged   = false;
  triggerChanged  = false;
  controllerState = CTL_OK;

}
  // end loop

// ****************************************************************
//                         FUNCTIONS
// ****************************************************************

// retourne un Json de l'état du controller
// controllerState, buttonState, triggerState, powerCtrlState, ledState
String getStatesJson() {
  String json;
  json  = "{";
  json += "\"controller\": "   + String(controllerState);  // -1 0 1 2
  json += ", \"button\": "     + String(buttonState);      // 0: Appuyé,  1: relaché
  json += ", \"trigger\": "    + String(triggerState);     // 0: Présent, 1: absent
  json += ", \"powerCtrl\": "  + String(powerCtrlState);   // 0: éteint,  1: allumé
  json += ", \"led\": "        + String(ledState);         // 0: éteint,  1: allumé, valeur: rythme blink
  for(int i=0;i<NB_TH;i++) {
    json += ", \"TH" + String(i+1) + "\": " + String(thTemps[i]);  // temperature des 6 sondes THn avec n de 1 à 6
  }
  json += "}";

  return json;
}

// fait clignotter la led sur la carte selon ledState
// si 0 (LED_OFF) on arrete, si 1 (LED_ON) on allume
// sinon on alterne à la vitesse en ms de ledState
// Clignottement exécuté pendant le cours de la boucle sans l'arreter
void handleLed() {
  if (ledState == LED_OFF) {
    // il faut arreter mais si blink était en cours, on éteint la led et on s'arrete
    digitalWrite(LEDESP_PIN, LOW);
    
  
} else if (ledState == LED_ON) {
    // led on
    digitalWrite(LEDESP_PIN, HIGH);
    
  
} else {
    // blink a faire. ledState donne le timing.
    
    if 
((millis() - blinkLastChange) > ledState) {
      // délai atteint 
      digitalWrite(LEDESP_PIN, !digitalRead(LEDESP_PIN)); // inverse l'état de la led
      blinkLastChange = millis();                         // mémorise timestamp dernier changement
    }
  }
}
 // end function


String formatBytes(size_t bytes) {
  if (bytes < 9999) {
    return String(bytes) + " B";
  } else if (bytes < (1048576)) {
    return String(bytes / 1024.00) + " KB";
  } else if (bytes < (41943404)) {
    return String(bytes / 1048576.00) + " MB";
  } else {
    return "trop grand! " + String(bytes);
  }
}


time_t getEpochTime() {
  time_t now;
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    //Serial.println("Failed to obtain time");
    return (0);
  }
  time(&now);
  return now;
}

String epochToString(time_t epochtime, char* pattern) {
  // Output formats : https://www.cplusplus.com/reference/ctime/strftime/
  // default output pattern format is declared in prototype as "DD-MM-YY HH:MM:SS"
  tm* localTime;
  localTime = localtime(&epochtime);
  char buff[30];
  strftime(buff, 30, pattern, localTime);
  return buff;
}

String tmToString(tm* tmTime) {
  char buff[30];
  strftime(buff, 30, "%d %m %y %H:%M:%S", tmTime);
  return buff;
}

// Synchronise la date par NTP et met dans la var globale nowt
bool syncTime() {
  #define NTP_MIN_VALID_EPOCH 1533081600  // August 1st, 2018. pourquoi cette date ? pourquoi pas.
  unsigned long t0 = millis();
  Serial.print("Synching Time with NTP...");
  while ((nowt = time(NULL)) < NTP_MIN_VALID_EPOCH) {
    delay(200);
    Serial.print(".");
    if ((millis() - t0) > 5000) {
      Serial.println(" failed. Timeout 5s !\nTime is not synched !!!");
      return false;
    }
  }
  Serial.printf(" done in %lu ms. \n", millis() - t0);
  return true;
}
 

Avatar de l’utilisateur
alka
Administrateur du site
Messages : 3098
Enregistré le : mer. 15 juil. 2015 15:18
Localisation : 92
Contact :

projet Arduino multi fichiers

Messagepar alka » mar. 9 juil. 2024 14:52

dans la série j'utilise le forum comme mon blog, je rajoute un chapitre sur comment faire un projet Arduino multi fichiers.
Découper en plusieurs fichiers rend le code plus digeste à lire et plus simple à maintenir.

Arduino a été fait pour être simple : tout le code dans un seul fichier .ino avec 4 sections

1) entête avec directives pré-processeur (#define, #include des libs externes) et déclaration + initialisation des variables et objets globaux.

2) setup() : code exécuté une seule fois. En général des initialisations.

3) loop() : boucle avec le code exécuté de manière répétitive.

4) définition des fonctions utilisées dans le code.

En principe, on devrait mettre 4) avant 2) pour que le compilateur connaisse les fonctions avant qu'elles soient utilisées dans le code, mais ça rend le code moins lisible alors on repousse à la fin.
Il faudrait déclarer au moins les prototypes des fonctions avant 2) pour que le compilateur sache au moins a quoi s'attendre, mais le compilateur est devenu malin et sait que s'il trouve une fonction inconnue sur son chemin, elle sera déclarée plus tard et il fera le boulot lors du link. Donc tant mieux, c'est simple pour nous.

Cela dit, un long fichier monolithique c'est pas cool a éditer.
Alors Arduino a prévu qu'on pouvait découper en plusieurs fichiers .ino, pourvu qu'ils se trouvent dans le même répertoire. Chaque fichier apparait dans un onglet (tab) de l'éditeur.

Par exemple, répertoire nommé /test, avec dedans plusieurs fihciers .ino dont un test.ino nommé du même nom que le répertoire.
Celui ci est chargé en premier et les autres fichiers .ino seront chargés derrière par ordre alphabétique.
En fait, Arduino commence par concaténer tous les .ino dans un seul fichier qui sera soumis au préprocesseur et compilateur.
Le premier fichier test.ino doit contenir la section 1) mais les autres sections peuvent être séparées dans autant de fichiers qu'on veut, du moment que le chargement dans l'ordre alphabétique soit cohérent.

On peut avoir l'organisation:

Code : Tout sélectionner

test/
  test.ino
  a_setup.ino
  b_loop.ino
  f_fonctions.ino

avec les fichiers nommés artificiellement pour être chargés dans le bon ordre. On peut multiplier les fichiers pour séparer le code par entités qui ont un sens et rendre le code plus maintenable.

C'est super cool, permet de pas se prendre la tête comme en C/CPP avec les includes et les .h

ET... Arduino IDE 2.x est arrivé.

Arduino IDE 2.x a un éditeur beaucoup plus riche, hérité d'un fork de VSCode.
Ce nouvel éditeur permet par exemple de voir directement en survolant avec la souris la valeur d'une constante ou d'un define, la définition d'une fonction et naviguer direct vers elle, de faire du refactoring, etc.

SAUF que l'éditeur Arduino IDE 2.x ne connait pas le découpage en .ino !!

Dès qu'on découpe en plusieurs fichiers .ino il est perdu et ne retrouve pas ce qui est défini ailleurs.
Cet éditeur connait le C et Cpp et comprend le code avec les .h et .cpp et #includes habituels du C/Cpp.

On pourrait penser que les dév qui font Arduino IDE vont corriger ça, mais il faut avouer que c'est un projet qui avance à la vitesse d'un escargot fatigué. Il y a tellement de choses à faire sur cet IDE que je n'y compte pas.

Donc c'est parti pour découper un projet Arduino en fichiers .ino .h .cpp d'une manière qui fonctionne.
Et c'est pas si simple quand on ne connait pas super bien les règles du C.

Quelques bricoles à savoir:
- il faut toujours une paire de fichers .h et .cpp. Le .h contient les définitions et prototypes de fonctions, le .cpp contient le code
- chaque .cpp doit avoir #include <Arduino.h>
- on savait déjà que la syntaxe pour inclure une lib externe (du core arduino ou importée) c'est #include <lib.h> et inclure une lib locale c'est #include "lib.h"
- pour rendre une variable globale, il faut la déclarer en extern
- on déclare une variable dans le .h, on l'initialise dans le .cpp correspondant
- dans chaque .h on n'oublie pas le guard pour éviter qu'elle soit chargé plusieurs fois (#ifndef #define.....#endif)
- si une fonction doit être uilisée sans que ce soit possible de faire l'include du .h où elle est déclarée, on peut la déclarer en extern dans le .cpp où elle sera utilisée. extern marche pour const/var et fonctions.

Exemple dans le répertoire /test :
test.ino :
comme c'est celui qui sera ouvert en premier par l'utilisateur, on commence en haut par les paramètres globaux (#define ou const) comme par exemple SSID/PWD wifi a saisir,
puis tous les #includes de librairies externes utilisées par le projet (<WiFi.h>, ...)
Puis #include des libs locales utilisées, ici "globals.h" et "fun.h"
il contient setup() et loop()

globals.h contient tous les #define, déclarations de const et variables globales du projet (déclarées en extern)
globals.cpp contient #include <Arduino.h> et "globals.h" et initialise les var globales.
C'est important de séparer déclaration dans le .h et initialisation dans le .cpp

fun.h : contient les prototypes des fonctions définies dans fun.cpp
fun.cpp : contient le code des fonctions, généralement d'un domaine spécifique
en haut de fun.cpp il y aura les include de <Arduino.h> "globals.h" et "fun.h".

C'est le principe. Ensuite on adapte à son code.
En poussant le maximum de code dans des fonctions, on peut faire un setup() et loop() très courts.
Mais si on veut quand même sortir loop() de test.ino par exemple, il faudra créer loop.h et loop.cpp
Si on se contente de mettre loop() dans b_loop.ino, ça compile mais l'éditeur Arduino IDE se perd dans ce fichier; pas dans les autres.


un monsieur qui fait ça en vidéo en direct : https://www.youtube.com/watch?v=3SMfqtGutCA

Avatar de l’utilisateur
thierryvalk
Administrateur du site
Messages : 3771
Enregistré le : jeu. 9 juil. 2015 20:08
Localisation : Belgique

Re: Rélfexion autour d'un ESP32

Messagepar thierryvalk » mar. 9 juil. 2024 15:45

Tu n'a jamais testé Visual Studio Code ?

Avatar de l’utilisateur
alka
Administrateur du site
Messages : 3098
Enregistré le : mer. 15 juil. 2015 15:18
Localisation : 92
Contact :

Re: Rélfexion autour d'un ESP32

Messagepar alka » mar. 9 juil. 2024 17:44

thierryvalk a écrit :Tu n'a jamais testé Visual Studio Code ?

oui, c'est mon éditeur préféré que j'utilise a coté. Il est génial pour des comparaison de dossiers et fichiers par exemple.
J'avais aussi utilisé VSCode avec une extension arduino sur un projet a une époque où Arduino 2 n'existait pas.
J'ai le souvenir d'avoir eu quelques galère pour mettre en place. Et platformIO vraiment trop galère à configurer pour moi. Ce sont des outils pour des gens qui ne font que ça. Pour moi qui fait de temps à autres, Arduino IDE me va bien.
J'avais un peu l'espoir que Arduino 2.x devienne au niveau de VSCode mais c'est pas vraiment pour demain.

edit: VSCODE avec l'extension Arduino faite par microsoft c'est bien, donc sans platformio. Pas parfait mais mieux pour développer : intellisense est mieux, il comprend vraiment le code complet et les instructions propres à Arduino (que Arduino IDE ne reconnait pas curieusement!), navigation plus aisée et plus complète, et VsCode est plutot complet avec des bons outils pour dév (comparer, gestion version, etc.)
On finit par avoir les deux : Adrduino pour installer les libs, gérer les cartes, upload de data, etc.
VSCODE pour le dév de code.
Deux soucis :
- au lancement, il met dans le répertoire source un dossier .vscode qui contient les settings. il manque le setting pour l'emplacement des fichiers de travail pour compiler etc. Dans arduino.json, ajouter "output": "chemin", en remplaçant chemin par celui qu'on veut. Par exemple ./build .
- le port série sur ma machine n'écrit pas les caractères accentués correctement. Impossible de trouver comment régler ça. J'ai déjà mis encodage UTF8 partout où c'est possible et ça ne suffit pas. c'est un bug corrigé dans la pre-release du Serial Monitor de VSCODE.


Retourner vers « DSPiy général »

Qui est en ligne

Utilisateurs parcourant ce forum : Aucun utilisateur enregistré et 8 invités