vendredi 3 avril 2020

ARDUINO : NTP sur Ethernet



ARDUINO : NTP sur Ethernet


Le problème se pose souvent lorsque l'on travaille avec un ARDUINO et une carte Ethernet W5100 ou W5500 : comment récupérer l'heure sans horloge RTC (Real Time Clock) ?

Cette petite librairie va être très utile. Elle permet de récupérer l'heure UTC (Coordinated Universal Time) ou locale à partir d'un serveur (pool.ntp.org par défaut).

Elle possède un mécanisme permettant d'appliquer une correction en fonction du fuseau horaire et des règles de changement d'heure été / hiver (Timezones).

Sur ESP8266 ou ESP32 on utilisera une autre solution. Voir en fin d'article.


1. Le besoin

Cette librairie a été conçue dans le but de pouvoir utiliser les 3 systèmes de gestion de l'heure les plus courants sur ARDUINO :
  • librairie standard C :
    • time()
    • gmtime()
    • localtime()
    • ctime()
    • strftime()
    • etc.
  • librairie TimeLib de Paul Stoffregen (optionnelle) :
    • year()
    • month()
    • day()
    • hour()
    • minute()
    • second()
    • etc.
  • librairie AdaFruit RtcLib (optionnelle)
La possibilité d'adopter une librairie connue permettra d'une part de satisfaire les besoins et habitudes spécifiques de chacun et également d'intégrer au besoin une horloge RTC DS3231.

On peut se poser la question de l'utilité d'une horloge RTC lorsque l'on dispose d'une connexion Ethernet.
Cette possibilité peut être utile lorsque cette connexion n'est pas permanente. Bien sûr dans ce cas on imagine assez bien que l'ARDUINO ne sera pas utilisé en tant que serveur WEB.
Pour cette raison j'ai choisi d'offrir la possibilité d'intégrer uniquement un DS3231, bien plus précis qu'un DS1307.

Un ordinateur du type PC dispose également d'une horloge RTC, et cette horloge est mise à jour périodiquement lorsque le PC est relié au réseau. J'ai repris le même principe ici.

    1.1. Heure UTC et locale

    L'heure UTC est l'heure au méridien de Greenwich (Greenwich Mean Time, abrégé en GMT). Elle correspond au fuseau horaire UTC+0.

    L'heure locale est l'heure correspondant à un fuseau horaire donné. Cette heure locale, suivant les pays, peut être corrigée en fonction d'horaires été / hiver.

    Afin de ne pas avoir à remettre à l'heure une horloge à chaque changement d'heure on utilise des règles de changement d'heure. En France elles sont les suivantes :
    • dernier dimanche de mars à 2:00 : GMT+2
    • dernier dimanche d'octobre à 3:00 : GMT+1
    Comme beaucoup de systèmes UNIX existants l'heure stockée en interne l'est sous la forme UTC. Elle est récupérable directement à l'aide de la fonction time() de la Librairie standard C.
    L'heure est ensuite corrigée par la fonction localtime() selon la zone déterminée lors de la lecture NTP ou RTC.

    1.2. Librairie standard C

    On peut récupérer l'heure UTC ou locale à l'aide des fonctions de la librairie standard C :

        time_t t;
        // heure UTC en nombre de secondes depuis le 01/01/1970
        time(&t);
        // heure UTC (année, mois, jour, heure, minutes, secondes, etc.)
        struct tm *current = gmtime(&t);
        const char *format = "%A %d/%m/%Y, %H:%M:%S";

        // formatage : jour jj/mm/aaaa, hh:mm:ss
        strftime(buf, sizeof(buf), format, current);

         // heure locale (année, mois, jour, heure, minutes, secondes, etc.) 
        current = localtime(&t); 
        // formatage : jour jj/mm/aaaa, hh:mm:ss 
        strftime(buf, sizeof(buf), format, current);

    La librairie standard C possède bien d'autres fonctions.

    1.3. Librairie optionnelles

    Les librairies TimeLib et RtcLib sont optionnelles. Il suffit de les activer par une option de compilation.
    Si ces librairies ne sont activée elles n'auront aucun besoin d'être installées.

    Il est possible d'ajouter une horloge RTC DS3231 comme source de temps supplémentaire. A chaque fois que le serveur NTP pourra être joint, la RTC sera mise à jour. Ensuite si le serveur NTP ne peut plus être joint, l'heure sera récupérée à partir de la RTC.

    Attention : la RTC est mise à jour à l'aide de l'heure UTC. Si l'on cherche à récupérer l'heure et la date à partir des méthodes de la RtcLib dans l'application, elle ne sera pas corrigée en fonction de l'heure été / hiver.

    Une méthode rtcNow() permet de récupérer l'heure de la RTC corrigée. Voir l'exemple plus bas.

    1.4. Horaires été / hiver

    La librairie w5100-ntp possède bien entendu tout le code nécessaire pour tenir compte de la TimeZone dans laquelle on se trouve, avec une gestion d'heure été / hiver.

    1.5. Mise en œuvre

    Dans la fonction setup() d'un sketch ARDUINO il suffit de peu de choses pour l'utiliser. Voici un exemple typique pour une gestion de l'heure en TimeZone Europe/Paris. La majeure partie du code est associée au démarrage du serveur Ethernet et à la recherche du serveur NTP par DNS :

    void setup()
    {
      EthernetNtp *ntp = EthernetNtp::getInstance();
      Serial.begin(115200);
      Serial.println(F("Network Time Protocol Example"));
      Ethernet.begin(mac, ip, dnsIp);
      if (Ethernet.hardwareStatus() == EthernetNoHardware) {
        Serial.println(F("Ethernet hardware was not found.  Sorry :("));
        while (true) {
          delay(1);
        }
      }
      if (Ethernet.linkStatus() == LinkOFF) {
        Serial.println(F("Ethernet cable is not connected."));
      }
      // Europe/Paris : last sunday of october at 3:00 : 1H offset
      ntp->std("CET", Last, Sun, Oct, 3, 60);
      // Europe/Paris : last sunday of march at 2:00 : 2H offset
      ntp->dst("CEST", Last, Sun, Mar, 2, 120);
      dns.begin(dnsIp);
      // get NTP server using DNS
      Serial.print(F("DNS (")); Serial.print(ntpUrl); Serial.print(F("): "));
      Serial.println(dns.getHostByName(ntpUrl, ntpIp) == true ? "SUCCESS" : "FAILED");
      // update time every 60 seconds
      ntp->begin(ntpIp, 60);
    #ifdef USE_RTCLIB
      ntp->addDS3231();#endif
      Serial.print(F("server is at "));
      Serial.println(Ethernet.localIP());
    }


    Ensuite il suffira de récupérer l'heure et la date à l'aide des fonction habituelles de la librairie standard C ou TimeLib. Voici un bout de code affichant l'heure et la date UTC ou locale sur une page WEB :

        char buf[30];
        client.println(F("HTTP/1.1 200 OK"));
        client.println(F("Content-Type: text/html"));
        client.println(F("Connection: close"));
        client.println();
        client.println(F("<!DOCTYPE HTML>"));
        client.println(F("<html>"));
        client.print("Using standard C library:");
        client.println(F("<br />"));
        client.print("UTC time & date: ");
        time_t t;
        time(&t);
        struct tm *current = gmtime(&t);
        const char *format = "%A %d/%m/%Y, %H:%M:%S";
        strftime(buf, sizeof(buf), format, current);
     

        client.print(buf);
        client.println(F("<br />"));
        client.print("Local time & date: ");
        current = localtime(&t);
        strftime(buf, sizeof(buf), format, current);
     

        client.print(buf);
        client.println(F("<br /><br />"));
        client.print("Using formattedTime:");
        client.println(F("<br />"));
        EthernetNtp *ntp = EthernetNtp::getInstance();
        ntp->formattedTime(buf, sizeof(buf), format);
     

        client.print("Local time & date: ");
        client.print(buf);
    #ifdef USE_RTCLIB
        client.println(F("<br /><br />"));
        client.print("Using RTC:");
        client.println(F("<br />"));
        DateTime now(ntp->rtcNow());
        strcpy(buf, "DDD DD/MM/YYYY hh:mm:ss");
        now.toString(buf);
        client.print("Local time & date: ");
        client.print(buf);
    #endif
        client.println(F("<br />"));
        client.println(F("</html>"));


    Le fait de récupérer l'heure de la RTC avec la méthode rtcNow() offre peu d'intérêt, étant donné que l'heure système est forcément à jour par rapport au serveur NTP.
    Le seul intérêt réside dans l'utilisation de la classe DateTime qui offre certaines facilités.
    Personnellement je trouve que les fonctions standards de la librairie C sont plus nombreuses et bien plus puissantes.

    1.6. Horloge de la librairie standard C

    Si l'on utilise les fonctions standards de la librairie C, dans la fonction loop() il ne faudra pas oublier de mettre à jour l'heure interne toutes les secondes :

    void loop()
    {
      static char request[REQUEST_MAX + 1];
      static unsigned long lastTick;
      int reqIndex = 0;
      char c;
      EthernetClient client = server.available();

      unsigned long tick = millis();
      if (tick - lastTick > 1000) {
        EthernetNtp::getInstance()->tick();
        lastTick = tick;
      }


    1.7. Exemple

    L'exemple de la librairie utilise 3 méthodes de lecture et de formatage de l'heure :
    • librairie standard C : time(), gmtime(), localtime(), strftime()
    • méthode formattedTime() de la classe EthernetNtp
    • méthode rtcNow() de la classe EthernetNtp et méthode toString() de la classe DateTime (librairie RtcLib).
    L'URL suivante est utilisée : http://xxxx.xxx.xxx.xxx/time
    L'adresse IP est affichée sur la console au démarrage.

    Le résultat :

    Using standard C library:
    UTC time & date: Saturday 04/04/2020, 08:39:35
    Local time & date: Saturday 04/04/2020, 10:39:35

    Using formattedTime:
    Local time & date: Saturday 04/04/2020, 10:39:35

    Using RTC:
    Local time & date: Sat 04/04/2020 10:39:35


    Dans l'exemple l'heure NTP est demandée toutes les 60 secondes :

      ntp->begin(ntpIp, 60);

    On pourra augmenter cette période en fonction de la précision de l'oscillateur de la carte.

    2. La librairie

    La librairie est disponible ici :
    https://bitbucket.org/henri_bachetti/w5100-ntp.git

    On pourra activer les options TimeLib et RtcLib dans w5100-ntp.h comme ceci :

    // uncomment to use TimeLib
    #define USE_TIMELIB

    // uncomment to use RtcLib
    //#define USE_RTCLIB


    La librairie Ethernet :
    https://github.com/arduino-libraries/Ethernet

    On peut également installer les librairies TimeLib et RtcLib si l'on a choisi l'une ou l'autre de ces options, ou les deux :
    https://github.com/PaulStoffregen/Time
    https://github.com/adafruit/RTClib

    l'exemple complet :
    https://bitbucket.org/henri_bachetti/w5100-ntp/src/master/examples/ntp/ntp.ino

    3. ESP32 & ESP8266

    Avec un ESP32 récupérer l'heure à partir d'un serveur NTP et configurer la TimeZone sont beaucoup plus simples :

    const char* ntpServer = "pool.ntp.org";
    configTzTime("CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00", ntpServer);

    La chaîne de caractères "CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00" est une "timezone string".

    Avec cette configuration le changement d'heure sera donc automatique le dernier dimanche de mars à 2:00 et octobre à 3:00.

    Les explications ici : https://www.di-mgt.com.au/wclock/tz.html

    Je n'ai pas encore étudié le cas de l'ESP8266, mais je sais qu'il existe une librairie :
    https://platformio.org/lib/show/5438/NTP


    Cordialement
    Henri BACHETTI

    4. Mises à jour

    05/04/2020 : correction d'un bug de la mise à l'heure système.

    jeudi 2 avril 2020

    ARDUINO : l'alimentation (VCC, VIN, etc.)


    ARDUINO : l'alimentation

    (VCC, VIN, etc.)




    Après avoir écrit de multiples articles sur l'alimentation d'un ARDUINO, d'un ESP8266 ou ESP32 en mode basse consommation par batterie, je vais examiner pour vous les différents moyens d'alimentation d'une carte lorsque l'on a besoin de puissance, ce qui malgré les apparences, n’est pas aussi simple que cela.

    1. Examen du montage

    Avant toutes choses, il faut bien être conscient qu’alimenter un ARDUINO sans parler des composants périphériques ne rime pas à grand-chose. En effet les circuits qui entourent le microcontrôleur consomment eux aussi du courant et suivant leur tension d'alimentation et leur consommation en courant, la solution d’alimentation sera probablement différente.

    Il faut avant tout examiner les caractéristiques électriques de chaque composant ou module, et faire la somme des courants consommés par ceux-ci afin d’obtenir un bilan de consommation.
    Sans ce bilan de consommation, il sera difficile de trouver une solution rationnelle.

    1.1. Tension d'alimentation

    La tension d'alimentation générale sera choisie en fonction de la tension d'alimentation de chaque module.

    Si tous les modules doivent être alimentés sous 5V le microcontrôleur sera de préférence un modèle 5V.

    Un montage basé sur un ARDUINO 3.3V sera beaucoup plus facile à concevoir avec des modules alimentés sous 3.3V.

    Si l'on doit commander un module relais avec un microcontrôleur 3.3V on prendra bien garde à choisir un modèle que l'on puisses commander par une tension de 3.3V. En général ces modules, même s'il sont alimentés en 5V au niveau de la bobine, acceptent une tension de commande beaucoup plus faible.

    Également cela ne pose aucun problème de commander un relais nu 5V, 12V ou 24V avec un transistor commandé par une sortie 3.3V.
    Voir ici :
    https://riton-duino.blogspot.com/2018/08/alimenter-un-relais-transistor.html

    1.2. ESP8266 et ESP32

    Si un ESP8266 ou un ESP32 a été choisi pour ses capacités de communication WIFI il sera préférable d'adopter des modules 3.3V afin d'éviter des adaptations de niveaux de tension entre modules 5V et microcontrôleur 3.3V.
    Si l'on n'a pas le choix, deux tensions d'alimentations seront nécessaires ainsi qu'une éventuelle adaptation de niveaux.

    La datasheet des ESP8266 et ESP32 précise que la tension sur les entrées ne doit pas dépasser 3.3V + 0.3V.
    Un simple pont diviseur peut suffire pour réduire la tension de sortie d'un module 5V à 3.3V.

    1.3. Questions courantes

    Pour répondre à une question souvent posée, dont la réponse paraîtra évidente à certains : oui, il est possible d'alimenter un montage consommant un courant donné avec une alimentation pouvant débiter un courant largement supérieur.
    Le montage prélèvera uniquement le courant nécessaire à son fonctionnement.

    Pour répondre à un autre problème souvent posé : Fritzing propose parmi ses sources d'alimentation une pile de 9V. Est-ce viable ? non, ce n'est pas une solution viable. Une pile 9V a une capacité ridiculement faible par rapport à la majorité des besoins.
    D'autre part si l'on veut consommer peu d'énergie, une tension de 9V est absolument inadaptée.

    2. Caractéristiques électriques

    Avant de parler alimentation il convient de parler du microcontrôleur et de ses capacités à produire du courant sur ses sorties, ainsi que de sa broche d'alimentation.

    La datasheet des microcontrôleurs doit être examinée afin de déterminer plusieurs valeurs :
    • le courant maximal sur chaque sortie
    • le courant maximal d’alimentation
    Le courant maximal d’alimentation représente la somme du courant consommé par le microcontrôleur lui-même, ainsi que la somme des courants consommés sur ses sorties.

    2.1. Courants maximaux

    Voici une table des courants maximaux admissibles :

    Microcontrôleur Courant maximal sur chaque sortie Courant d’alimentation maximal
    ATMEGA328P 40mA 200mA
    ATMEGA2560 40mA 200mA
    ESP8266 12mA 500mA
    ESP32 40mA (source), 28mA (sink) 1200mA

    Par exemple pour un ATMEGA328P la limitation globale de 200mA inclut la consommation du microcontrôleur lui-même : 14mA. Il nous reste donc 186mA disponibles sur les sorties.

    On voit immédiatement que si l’on a l’intention de débiter 20mA sur les sorties d’un ATMEGA328P, la limitation globale nous interdira de débiter ce courant sur plus de 9 sorties.

    Il faudra donc des interfaces de puissances adaptées si l'on dépasse un de ces critères.

    2.2. Tension sur une sortie

    La tension sur la sortie d’un microcontrôleur varie en fonction du courant débité. Si un ATMEGA328P est bien capable de débiter 40mA, sa tension de sortie ne sera plus que de 4.4V. S’il débite seulement 20mA elle sera de 4.8V.

    Si cela peut convenir pour piloter une LED, cela peut être gênant dans d'autres cas.

    3. Les différentes possibilités d'alimentation

    L'examen d'une carte UNO, NANO, ESP8266 ou ESP32 laisse entrevoir plusieurs possibilités d'alimentation.

    3.1. Broche VCC

    La broche VCC porte différents noms en fonction de la carte :
    • ARDUINO UNO, NANO, MEGA : 5V
    • ARDUINO PRO MINI : VCC (5V ou 3.3V)
    • ESP8266 ou ESP32 : 3.3V
    Dans tous les cas, cette broche alimente directement le microcontrôleur. C’est la technique d’alimentation la plus directe. La limitation en courant ne dépendra que de la puissance de l’alimentation. Le microcontrôleur consommera du courant selon ses besoins et les composants annexes raccordés sur cette ligne (modules relais, capteurs, etc.), auront également des besoins en courant qu'il faudra satisfaire.

    L'avantage d'alimenter le microcontrôleur et d'autres modules en parallèle sur la même alimentation 5V ou 3.3V est que chacun absorbe le courant qui lui est nécessaire, sans que cela handicape la carte microcontrôleur comme cela peut se produire lorsque l'on alimente celle-ci par sa broche VIN (voir paragraphe suivant).

    Il conviendra donc d’utiliser une alimentation suffisante pour l’application. Le courant maximal de l’alimentation devra être au moins égal au bilan des consommations calculé, majoré d'une marge de sécurité d’au moins 20%.

    3.1.1. Sortie 3.3V
    Il s’agit de ne pas confondre la broche 3.3V d’une carte ARDUINO UNO ou NANO avec la broche 3.3V d’un ESP8266 ou ESP32.

    La broche 3.3V d’une carte ARDUINO UNO ou NANO est une sortie prévue pour alimenter des composants extérieurs sous 3.3V. Elle provient en général d’une broche du convertisseur USB / série (CH340, CP2102, etc) implanté sur la carte.

    Elle fournit peu de courant. Un CH340, selon différentes sources, serait capable de délivrer seulement 30mA.

    3.2. Broche VIN

    Ici aussi la broche VIN porte différents noms en fonction de la carte :
    • ARDUINO UNO, NANO, MEGA : VIN (ou JACK sur UNO ou MEGA)
    • ARDUINO PRO MINI : RAW (5V ou 3.3V)
    • ESP8266 ou ESP32 : 5V
    Cette broche est une entrée d’alimentation pouvant accepter une tension supérieure à VCC. Elle est raccordée à l’entrée d’un régulateur dont la sortie alimente le microcontrôleur.

    On retrouve cette tension de sortie également sur la broche VCC, c'est à dire que le courant consommé sur la sortie VCC passera forcément par le régulateur.
    La broche VCC est donc une entrée si on l'utilise pour alimenter la carte, et une sortie si l'on alimente la carte par la broche VIN.

    La tension maximale sur la broche VIN dépend de deux facteurs :
    • tension maximale admise par le régulateur
    • courant consommé
    Afin de déterminer la limite à ne pas dépasser il est impératif d’identifier le régulateur équipant la carte :

    Régulateur Tension maximale en entrée Courant maximal Drop-out voltage (VDROP)
    UA78M05 35V 500mA 2V
    MC33269D 20V 800mA 1.35V
    AMS1117 15V 1000mA 1.3V
    ME6211 6.5V 600mA 260mV

    Le MC33269D est présent sur les cartes UNO officielles.
    L’AMS1117 équipe la majeure partie des cartes clones ARDUINO chinoises et pas mal d'ESP8266 ou ESP32.
    Le ME6211 équipe certaines cartes ESP8266 ou ESP32 de qualité.

    3.2.1. Tension maximale et minimale
    On voit assez rapidement que la tension d’alimentation va fortement dépendre du régulateur. Par exemple appliquer 7.5V ou 9V sur l’entrée 5V d’un ESP32 ne sera pas possible si la carte est équipée d’un régulateur ME6211, qui grillerait instantanément.

    Dans le tableau ci-dessus un paramètre VDROP permet de connaître la différence minimale admissible entre VIN et VOUT pour que le régulateur puisse assurer sa fonction de régulation.
    Par exemple la tension en entrée d’un AMS1117 ne pourra être inférieure à 5V + 1.3V donc 6.3V.

    3.2.2. Courant maximal
    Chaque régulateur est prévu pour fournir un courant maximal en sortie. Au delà il entrera en protection et sa tension de sortie chutera.

    3.2.3. Puissance maximale
    Chaque régulateur peut dissiper une puissance maximale. Cette puissance maximale dépend de 2 paramètres :
    • résistance thermique du boîtier
    • surface de dissipation du circuit imprimé sur lequel il est soudé
    La puissance dissipée est facilement calculable :

    Pmax = UI = (VIN – VOUT) * I

    VIN étant la tension d’entrée du régulateur et VOUT sa tension de sortie, on constate facilement que la puissance dissipée sera forcément proportionnelle à la tension d’entrée.

    Il y a donc deux limites qui nous permettront de choisir la tension d’alimentation : VDROP et Pmax.

    Pmax pourra t-elle être dissipée par le régulateur ? c’est ici que les choses se corsent.

    Si l’on prend comme exemple une carte ARDUINO NANO équipée d’un AMS1117 :

    La datasheet du régulateur explique comment calculer la surface de dissipation en fonction de la puissance dissipée (voir 10.1.1. HeatsinkRequirements).

    La résistance thermique peut être calculée comme suit :

    RθJA= TR(max) / Pmax

    RθJA est la résistance thermique entre la jonction (la puce) et l’air ambiant.

    TR(max) représente la différence maximale de température entre la puce du régulateur (125°) et l'air ambiant.

    La dissipation du LM1117 est assurée par une piste sous la carte. Cette piste fait à vue de nez environ 0.3 inch carré de surface.

    D’après la table 2 en page 18 la résistance thermique de cette piste de 0.3 inch carré est de 84 °C / W (boîtier SOT-223).

    La puissance maximale à ne pas dépasser pour le LM1117 à une température ambiante de 25°C sera de :

    Pmax = TR(max) / RθJA = (125° - 25°) / 84 = 1.2W

    Si l’on alimente la carte en 7.5V, la différence de tension entre VIN et 5V sera de 2.5V. Le courant maximal en sortie du régulateur sera de :

    I = 1.2W / 2.5V = 480mA

    Si l’on alimente la carte en 6.3V, la différence de tension entre VIN et 5V sera de 1.3V. Le courant maximal en sortie du régulateur sera de :

    I = 1.2W / 1.3V = 920mA

    3.2.4. Expérience

    Alimentons une carte UNO ou NANO avec une tension de 6.5V à 9V sur VIN. Une résistance de 10Ω est branchée entre la broche 5V et GND. Elle va donc consommer 5V / 10Ω = 500mA.

    Voici un petit tableau donnant la valeur de la tension VCC en fonction de la tension VIN :

    Carte VIN=6.5V (à vide) VIN = 6.5V (charge 10Ω) VIN = 7.5V (charge 10Ω) VIN = 9V (charge 10Ω)
    UNO 5.01V 4.91V 4.9V 3.6V
    NANO 4.96V 4.7V 3.1V

    Remarques :

    La UNO tient mieux la charge que la NANO, malgré que le régulateur soit identique. Ceci est normal, la surface de dissipation allouée au régulateur de la UNO est supérieure.

    La tension sur la broche 5V ne chute pas immédiatement lorsque la puissance dissipée augmente, mais au bout de quelques dizaines de secondes. Cela indique que cette limitation est thermique. Si la puissance à dissiper est plus importante ce temps peut être bien plus court.

    3.3. Plusieurs alimentations

    Est-il possible d'alimenter une carte par VCC, VIN et USB simultanément ?

    Oui, il est possible de laisser le cordon USB branché tout en ayant une alimentation sur VIN ou VCC. Les cartes sont équipées de mécanismes permettant d'éviter le conflit.

    Par contre il est absolument déconseillé d'alimenter une carte par sa broche VIN et par sa broche VCC simultanément, car cela revient à appliquer une tension sur la sortie du régulateur de la carte alors que son entrée est alimentée. Le mécanisme de régulation va essayer de combattre la différence entre la tension présente sur sa sortie et la tension de sortie théorique du régulateur. Cela risque d'avoir pour conséquence une sur-consommation plus ou moins importante et une surchauffe du régulateur.

    4. Alimentations

    4.1. Alimentation 3.3V ou 5V

    L'alimentation directe d'un microcontrôleur et de modules en 3.3V ou 5V ou d'un ESP8266 ou ESP32 par sa broche 5V peut être facilement mise en œuvre à l'aide de modules ou blocs secteurs du commerce :
    Hi-Link 5V/3W
    Alimentation 3.3V 1A
    Si l'alimentation doit alimenter simultanément un microcontrôleur 3.3V et un relais ou un moteur 5V, une tension de 3.3V sera insuffisante pour le relais ou le moteur.
    On pourra donc adopter une alimentation 5V sur laquelle on viendra ajouter un régulateur LDO linéaire 3.3V pour alimenter le microcontrôleur :

    HT-7333-1 3.3V 250mA
    MCP1825 ajustable 500mA
    On parle souvent de cartes ESP8266 ou ESP32 mais pour des raisons d'encombrement on peut préférer implanter un module nu :

    Un module nu est dépourvu de régulateur et dans ce cas l'alimentation sous 3.3V est obligatoire.
    Les  MCP1725, MCP1825, LM3940, HT7833-1 sont de bons choix.

    Les régulateurs linéaires peuvent être intéressants car peu encombrants.

    Il est impératif de consulter leur datasheet car ils nécessitent en général un condensateur d'entrée et de sortie.

    Cet article propose une liste de régulateurs LDO :
    https://riton-duino.blogspot.com/2018/11/les-regulateurs-ldo.html
     

    4.2. Alimentation par VIN 6.5V, 7.5V ou 9V

    L'alimentation d'une carte par la broche VIN pourra être effectuée par le même moyen que précédemment, une alimentation ou un bloc secteur. Les modèles 6.5V sont rares. Il faudra respecter la tension maximale du régulateur de la carte microcontrôleur ainsi que sa puissance maximale dissipée.

    Dans tous les cas une tension faible sera préférable, afin de limiter la dissipation du régulateur de la carte microcontrôleur, surtout s'il s'agit d'un ESP8266 ou un ESP32, leur consommation étant importante lors du démarrage (connexion au réseau WIFI).

    4.3. Alimentation 12V ou 24V

    Il est relativement courant d'avoir besoin d'une tension d'alimentation de 12V ou 24V pour alimenter par exemple des relais, un moteur, une pompe, etc.

    On devra donc choisir une alimentation adéquate, en fonction de la puissance réclamée par ces composants mécaniques :

    Alimentation MeanWell 12V 30W
    Alimentation 12V / 1A

    Une carte ARDUINO pourra être alimentée en 12V par sa broche VIN si le courant consommé sur ses sorties ne dépasse pas une limite raisonnable.
    On peut l'estimer facilement :

    I = 1.2W / (12V - 5V) = 170mA

    Par contre s'il s'agit d'un ESP8266 ou ESP32 alimenté par sa broche 5V, il sera plus prudent de passer par un régulateur 5V supplémentaire, surtout si la carte est équipée d'un régulateur ME6211.
    On pourra également adopter un régulateur 3.3V pour alimenter une carte par sa broche VCC, ou alimenter un module nu.
    Il faut savoir qu'un ESP8266 ou un ESP32 consomme plus de 400mA dans sa phase de connexion au réseau WIFI. Si la carte est équipée d'un AMS1117 la puissance dissipée sera excessive :

    Pmax = UI = (VIN – VOUT) * I = (12V - 5V) * 0.4A = 2.8W

    Dans le cas d'une alimentation 24V ce régulateur sera bien entendu indispensable dans tous les cas.

    Ce régulateur sera de préférence un modèle à découpage, un LM2596 ou un MP1584 par exemple :
    LM2596
    MP1584 3.3V
    Pour des courants faibles on pourra comme précédemment utiliser un régulateur linéaire, un LM7805, LM7833 ou LM78L33 par exemple.

    Attention, beaucoup de régulateurs LDO ont une tension d'entrée faible :
    https://riton-duino.blogspot.com/2018/11/les-regulateurs-ldo.html
     
    4.3.1. Régulateur élévateur
    On peut également choisir l'option inverse : il est possible de choisir une alimentation 3.3V ou 5V adaptée au microcontrôleur et d'élever la tension à 12V ou 24V, pour piloter un relais par exemple, à l'aide d'un régulateur élévateur (step-up) :
    MT3608
    Cette solution pourra être utilisée pour piloter des relais ou de petits moteurs, mais le courant consommé sur l'alimentation 5V ou 3.3V sera bien plus important.

    Cet article en dit plus long sur ces régulateurs :
    https://riton-duino.blogspot.com/2019/11/convertisseurs-stepup-stepdown.html

    En particulier il évoque le rapport existant entre le courant d'entrée et le courant de sortie d'un régulateur à découpage (paragraphe 1).

    6. Conclusion

    Alimenter une carte par la broche VIN semble être la solution la plus simple à première vue. Il n'en est rien, elle s’avère beaucoup plus complexe qu’il n’y paraît.

    Lorsque l’on veut pouvoir disposer d’une puissance même moyennement élevée sur le 5V il est beaucoup plus intéressant d’alimenter la carte directement par la broche VCC à l'aide d'une alimentation dont on choisit la puissance en fonction d'un bilan de consommation sérieux.


    Cordialement
    Henri

    jeudi 26 mars 2020

    Serveur ESP32 : tests automatisés (2ème partie)


     Serveur ESP32 : tests automatisés

    (2ème partie)


    Ceci est la suite et fin des six articles précédents :
    Serveur ESP32 : implémentation
    Serveur ESP32 : implémentation (2ème partie)
    Serveur ESP32 : implémentation (3eme-partie)
    Serveur ESP32 : implémentation (4eme-partie)
    Serveur ESP32 : implémentation (5eme-partie)
    Serveur ESP32 : tests automatisés

    Le but de cette dernière évolution est :
    • ajouter la délivrance d'un service aux abonnés
    • améliorer le serveur 
    • tester certains cas nominaux ou non nominaux
    • tests particuliers
    • tests interactifs
    Rappel : comme vu dans la 4ème partie seul l'ESP32 pourra offrir une possibilité de partitionnement entre SPIFFS et FAT.
    Pour un ESP8266 il faudra forcément utiliser une SD.

    1. Evolutions

    1.1. Service aux abonnés

    Ce service va être simple, afin de rajouter aussi peu de matériel que possible :
    • un bouton-poussoir
    • une LED (la LED de la carte ESP32)
    L'abonné présente sa carte et appuie sur le bouton poussoir.
    Si l'abonné n'est pas enregistré la LED clignote 1 fois.
    Si l'abonné est désactivé la LED clignote 3 fois.
    Si l'abonné a un crédit nul la LED clignote 6 fois.
    S'il a du crédit la LED s'allume 10 secondes et son crédit est décompté.

    On pourrait imaginer beaucoup de solutions hardware :
    • écran LCD alphanumérique
    • écran tactile
    • gâche électrique
    • vanne de distribution de boisson
    • distributeur de barres vitaminées
    • imprimante de tickets
    • etc.
    Le but final de cet exercice sera d'ouvrir une porte. Le bouton-poussoir disparaîtra à terme. La notion de crédit ne sera pas nécessaire non plus, mais cela peut servir pour d'autres applications.

    1.2. Historique

    L'historique fonctionne maintenant à l'envers. Il est plus pratique d'avoir les dernières opérations affichées en premier.
    L'affichage de l'historique est maintenant limité à 20 lignes et un lien "More history ..." permet de visualiser le fichier complet.

    Une nouvelle ligne a été ajoutée : la date d'enregistrement de l'abonné.

    1.3. Les détails

    Le changement d'heure
    Comme le changement d'heure est intervenu cette nuit (29 mars), le serveur est en retard d'une heure.

    L'heure était configurée comme ceci :

      configTime(3600, 0, ntpServer);

    Aujourd'hui il faudrait la configurer avec une heure de plus :

      configTime(7200, 0, ntpServer);

    Mais il y a moyen de faire cela automatiquement, avec une "timezone string" :

      configTzTime("CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00", ntpServer);

    Le changement d'heure sera donc automatique le dernier dimanche de mars et octobre.
    Les explications ici : https://www.di-mgt.com.au/wclock/tz.html

    Les image et logos
    Les deux pages HTML affichent maintenant un petit logo (un cadenas) et l'icône favori (favicon.ico) a également été ajouté :

    Ceci pour démontrer qu'il ne suffit pas d'écrire <img src="/lock.jpg"> dans une page pour que l'image s'affiche.
    Cela n'est pas forcément une évidence lorsque l'on a l'habitude de travailler sur un serveur Apache, mais avec un ESP32 et la librairie AsyncWebServer, il faut servir cette image.

    Mémoire libre
    Une URL supplémentaire a été ajoutée afin de pouvoir afficher la mémoire dynamique libre :
    /freeram.html

    La quantité de mémoire RAM libre est également affichée dans la boîte de dialogue "About ...".

    2. Tests

    2.1. Cas nominaux et non nominaux

    Quelques tests nominaux ont été ajoutés :
    • recherche par nom
    • recherche par UID
    Ces tests ont permis de corriger un bug.

    Dans tout développement sérieux il ne suffit pas de tester quelques cas nominaux. Il faut aussi tester les cas d'erreurs :
    • carte non lue
    • abonné inexistant
    • abonné désactivé
    • crédit nul
    Pour tous ces cas, du code a été écrit. Il serait assez peu judicieux de faire confiance au code écrit sans le tester, car en dehors d'assurer une fonctionnalité, tout cas d'utilisation, nominal ou non, est susceptible de produire un crash du logiciel.
    Tant que l'on n'a pas testé le plus de cas possible rien ne permet d'affirmer que le logiciel fonctionne correctement.

    Ces tests paraissent simples. Vérifier que le serveur affiche bien "Subscriber NOT FOUND !!!" si l'abonné n'existe pas ne devrait poser aucune difficulté.

    Pour rappel cette suite de test s'exécute comme ceci :

    $ python3 test.py AutomatedTest

    2.2. Cas particuliers

    Partition pleine
    Un test en particulier est absolument indispensable : le test de remplissage de la partition FAT.Pour tester ce cas, il va être difficile de créer un abonné ou plusieurs et de réaliser un grand nombre d'actions (ajout de crédit par exemple) jusqu'à remplir la partition de 1.44 méga-octets. Chaque ligne d'historique faisant environ 30 octets. Cela nous obligerait à réaliser 48059 actions.

    Faire ce test en automatique sera long également. Même si chaque opération ne dure que 500ms, il nous faudrait tout de même 24000 secondes soit plus de 6 heures !

    Une astuce consiste à remplir artificiellement la partition de fichiers de taille assez importante.
    La serveur se voit donc ajouter une nouvelle URL /create.html permettant de créer un fichier X de taille donnée. La durée du test sera nettement raccourcie.
    Une deuxième URL /freebytes.html est ajoutée afin que le logiciel de test puisse récupérer l'espace disponible.

    Le logiciel de test va donc demander au serveur de créer N fichiers de 100Ko jusqu'à ce que l'espace soit presque plein.On ne peut pas simplement diviser l'espace disponible au départ par 100Ko et créer N fichiers. L'allocation des secteurs par le système de fichiers n'est pas prédictible.

    La taille restante sera d'environ 4096 octets, la taille d'un secteur de la FLASH.
    En dessous de cette valeur la méthode FFat.freeBytes() utilisée pour déterminer l'espace disponible retourne ZÉRO, ce qui ne nous arrange pas car on ne saurait quand s'arrêter.

    Il ne nous restera plus qu'à effectuer une centaine d'opérations pour remplir le dernier secteur.

    Test intensif
    Ce test enchaîne 5000 ajouts de crédit.
    Cela m'a permis de m'apercevoir qu'après l'ajout de 5000 lignes dans le fichier historique, lorsque l'on utilise la fonction explorer,  la lecture du fichier historique échoue. L'ESP32 n'a plus assez de mémoire.
    Donc les fichiers sont affichés par page de 200 lignes avec un lien "More ...".
    Les lignes sont maintenant numérotées.

    Cette suite de test s'exécute comme ceci :

    $ python3 test.py SpecialTest

    2.3. Tests interactifs

    Deux tests interactifs ont été également ajoutés :
    • ajout d'un abonné
    • débit
    Le test d'ajout d'un abonné demande au testeur de passer par la page "Nouvel abonné" d'entrer un nom et de présenter une carte.

    Le test de débit nécessite la présentation de la carte et l'appui sur le bouton-poussoir.

    Il est à noter que chacun de ces test, s'il est effectué un peu avant le passage à la minute suivante, échouerait, d'où une petite attente, afin de laisser au testeur le temps de réaliser les opérations manuellement :

        def wait_minute_00(self) :
            dt = datetime.now()
            if dt.second < 30:
                return
            print("wait %s seconds please" % (60 - dt.second))
            time.sleep(60 - dt.second)


    Cette suite de test interactifs s'exécute comme ceci :

    $ python3 test.py InteractiveTest

    3. La suite de test

    Celle-ci est disponible ici :
    https://bitbucket.org/henri_bachetti/webserver-form/src/v2.3/esp32-subscriber/test/test.py

    On voit donc de nouveaux tests apparaître :
    • remise à ZÉRO du crédit
    • désactivation de l'abonné
    • réactivation de l'abonné
    • test de remplissage de la partition
    Une nouvelle classe de base apparaît : BaseTest. Elle contient les méthodes de base qui seront utilisées par les classes qui en hériteront :
    • setUp() et tearDown() : voir article précédent
    • nettoyage du serveur
    • remplissage de la partition
    • récuparation de l'heure
    La récupération de l'heure comporte une petite astuce particulière : elle fait une pause de 2 secondes au moment où les secondes arrivent à 59 afin d'éviter les problèmes de comparaison d'heure dans les tests.
    Le PC peut en effet demander au serveur d'effectuer une opération qu'il effectuera à 10:44:59, et il est probable que le logiciel de test récupère ensuite une heure légèrement différente : 10:45:00. La comparaison échouerait.

    L'heure du PC et de l'ESP32 ne sont pas parfaitement synchronisées à la seconde près. C'est pour cette raison que l'on évite généralement de contrôler des heures à la seconde près.

    Le test de remplissage de la partition est situé dans une classe à part SpecialTest afin de permettre son exécution séparément des autre tests, car son exécution prend pas mal de temps : 2 minutes.

    $ python3 test.py SpecialTest
    .
    ----------------------------------------------------------------------
    Ran 1 test in 122.269s

    OK


    L'exécution des tests nominaux est plus courte : 10 secondes.

    $ python3 test.py AutomatedTest
    ......
    ----------------------------------------------------------------------
    Ran 7 tests in 10.686s

    OK


    4. Téléchargement

    Cette version est disponible ici :
    https://bitbucket.org/henri_bachetti/webserver-form/src/v2.3/esp32-subscriber/

    5. Conclusion

    Une fois de plus le test automatisé permet de fiabiliser le logiciel. Sans test automatisé il m'aurait été impossible de m'apercevoir de ce que j'ai observé en test intensif (voir 2.2).

    On voit également que souvent le code à tester évolue au fur et à mesure que les tests sont écrits, ce qui est parfaitement normal dans une démarche de test automatisé.

    Il existe même une méthode dite "Test-driven development" (Développement piloté par les tests) qui préconise l'écriture des tests avant l'écriture du code.

    Bien entendu la suite de tests n'est pas totalement terminée, mais cela ne justifie pas un article de plus.Les suppléments seront ajoutés dans celui-ci.

    Pour conclure, je dirais que le test automatisé nécessite une investissement non négligeable, certes, mais cet investissement est largement compensé par le temps gagné par rapport à des tests effectués manuellement, surtout si on les répète souvent.
    En fait on s'aperçoit très vite que l'on a tendance à lancer les tests après chaque journée ou semaine de travail, avant le commit (partage de son travail). Lorsque l'on travaille en équipe cela permet de ne pas perturber le travail des autres membres de l'équipe.
    J'ai tellement pris goût à ces méthodes que même quand je travaillais seul, je continuais à faire de même.

    J'espère que cette suite d'articles vous aura éclairé quant à la manière de concevoir un serveur ESP32, et surtout de le tester.


    Cordialement
    Henri

    6. Mises à jour

    27/03/2020 : ajout de tests
                         corrections
    29/03/2020 : 1.3. Les détails / le changement d'heure

    mardi 24 mars 2020

    Serveur ESP32 : tests automatisés

    Serveur ESP32 : tests automatisés


    Ceci est la suite des cinq articles précédents :
    Serveur ESP32 : implémentation
    Serveur ESP32 : implémentation (2ème partie)
    Serveur ESP32 : implementation (3eme-partie)
    Serveur ESP32 : implementation (4eme-partie)
    Serveur ESP32 : implementation (5eme-partie)

    Maintenant que notre serveur est bien avancé il serait temps de démarrer une séance de test. Nous allons pour cela utiliser PYTHON et le framework UNITTEST.

    Ce serveur est l'occasion de démontrer l'intérêt des tests automatisés. Il sera facile d'envoyer des requêtes à celui-ci et de vérifier que tout se passe comme prévu.

    1. Tests automatisés

    Pourquoi des tests automatisés ?
    J'ai l'habitude de travailler comme cela depuis 20 ans et ces tests comportent tellement d'avantages que je ne pourrais plus travailler autrement.

    1.1. Gain de temps

    Une fois que les tests sont écrits, le temps d'exécution est ridicule par rapport à celui que prendrait l'exécution des mêmes tests faits à la main.

    1.2. Répétabilité

    Les tests logiciels sont répétables à volonté. On peut les exécuter autant de fois que nécessaire.

    1.3. Régressions

    A chaque modification de code une régression est possible. Une évolution peut introduire un bug, y compris dans du code déjà existant, par effet de bord par exemple.
    L'exécution des tests automatisés permet de s'assurer que le code fonctionne comme avant la modification.

    Il est beaucoup plus facile de modifier du code en profondeur (refactoring) quand on dispose de tests déjà écrits. Une fois le code modifié, le déroulement des tests permet de s'assurer que le code fonctionne toujours.
    Un refactoring permet de réécrire des portions de code mal conçues, et le fait de redevoir refaire les tests à la main est souvent dissuasif, donc le refactoring n'est pas effectué, ce qui est une erreur.

    1.4. Tests finaux

    Le déroulement des tests automatisés ne dispense pas de réaliser des tests finaux (intégration, validation).
    Ces tests finaux permettront aussi de constater des bugs. Souvent un bug est dû à l'absence d'un test unitaire.
    Idéalement, chaque bug doit être l'occasion d'écrire un nouveau test, et ce test doit être OK après correction du bug.

    1.5. Sérénité

    Lorsqu'un projet est terminé et que l'on doit installer une version terrain, le déroulement des tests automatisés permet de livrer son travail avec beaucoup plus d'assurance et moins d'appréhension.

    2. Logiciels nécessaires

    Pour réaliser nos tests nous allons avoir besoin de quelques logiciels sur PC.

    Il existe plusieurs solutions très élaborées, dont SELENIUM, qui nécessitent une mise en œuvre plus lourde.

    SELENIUM procède par pilotage en direct du navigateur à l'aide d'un WebDriver approprié (Firefox, Safari, Edge, Chrome, Internet Explorer, etc.).
    Le navigateur est donc actif et on peut voir en direct la navigation opérer pendant le test, comme une vidéo :
    • clicks sur les boutons, liens
    • entrée de caractères au clavier
    • changements de page HTML
    • etc.
    La technique que je propose permet d'utiliser des composants simples, à la portée de tout un chacun. Elle est assez facile à comprendre par toute personne, même peu habituée au développement WEB :
    • envoi d'une requête à un serveur WEB
    • réception de la réponse
    • examen de la réponse (parsing)
    • comparaison du résultat par rapport à un résultat attendu
    • affichage des éventuels problèmes
    SELENIUM est un sujet à part entière, plus complexe, qui fera sans doute l'objet d'un autre article.

    2.1 PYTHON

    PYTHON est un langage interprété très moderne dont j'ai déjà parlé sur ce blog.

    2.1.1. PYTHON 2
    La version 2.7 commence à dater, mais elle est toujours maintenue. La dernière release date d'octobre 2019.
    PYTHON 2.7 a été très utilisé par le passé, d'où un support encore très actif.

    2.1.2. PYTHON 3
    PYTHON 3 est sorti en 2008, et comme il s'agit ici d'un nouveau projet, nous allons plutôt opter pour ce choix.

    La reprise d'un ancien projet PYTHON 2.7 pour le porter en PYTHON 3 nécessiterait pas mal de travail, en fonction de la taille du projet.

    Pour installer PYTHON3 sous Ubuntu :

    sudo apt-get install python3

    Pour installer PYTHON3 sous Windows :

    https://www.python.org/downloads/

    Choisir la dernière version (3.7.7 actuellement) :

    https://www.python.org/downloads/release/python-377/

    En bas de page choisir "Windows x86 executable installer" ou "Windows x86-64 executable installer", en fonction de l'architecture 32bits ou 64bits du PC.

    2.2. UNITTEST

    UNITTEST est un framework de tests unitaires PYTHON.
    Il permet d'exécuter des tests automatisés. Les tests peuvent être organisés :
    • module (un programme PYTHON)
      • suite de test (une classe)
        • test (une méthode de la classe)
    # test.py
    import unittest

    class TestStringMethods(unittest.TestCase):

        def test_upper(self):
            self.assertEqual('foo'.upper(), 'FOO')

        def test_isupper(self):
            self.assertTrue('FOO'.isupper())
            self.assertFalse('Foo'.isupper())

        def test_split(self):
            s = 'hello world'
            self.assertEqual(s.split(), ['hello', 'world'])
            # check that s.split fails when the separator is not a string
            with self.assertRaises(TypeError):
                s.split(2)

    if __name__ == '__main__':
        unittest.main()


    Il est possible d'exécuter tous les tests d'un même module, une seule classe ou un test en particulier :

    python3 test.py
    python3 test.py TestStringMethods
    python3 test.py TestStringMethods.test_upper

    2.3. URLLIB et HTMLParser

    URLLIB est une librairie permettant d'envoyer une requête à un serveur et d'attendre sa réponse :

    from urllib import request, parse
     

    # envoi d'une requête GET
    html = request.urlopen('http://192.168.1.18').read()
     

    # envoi d'une requête POST
    data = parse.urlencode({'action': 'add1'}).encode("ascii")
    html = request.urlopen('http://192.168.1.18/', data=data).read()


    URLLIB fait partie des librairies de base de PYTHON.

    HTMLParser va nous permettre de décortiquer la réponse du serveur :

    from html.parser import HTMLParser
     

    class MyHTMLParser(HTMLParser):

      def __init__(self):
            HTMLParser.__init__(self)

            self.state = 'idle'
            self.title = ''

        def handle_starttag(self, tag, attrs):

            print("Encountered a start tag:", tag, attrs)
            if (tag == 'title') :
                self.state = 'title'

        def handle_endtag(self, tag):
            print("Encountered an end tag :", tag)
            self.state = 'idle'

        def handle_data(self, data):
            print("Encountered some data  :", data)

            if self.state == 'title':
                self.title = data


    parser = MyHTMLParser()
    parser.feed(str(html))

    Les méthodes de la classe MyHTMLParser doivent être complétées pour récupérer les informations qui nous intéressent :
    • textes de label, de bouton
    • contenu et couleur d'un champ de saisie
    Les lignes print() peuvent être laissées dans un premier temps pour y voir plus clair, et retirées ensuite.

    L'exemple fourni est complet : voir 8. Téléchargement
    Il s'agit d'un petit automate à états finis très simple à comprendre.

    HTMLParser fait également partie des librairies de base de PYTHON.

    Avec UNITTEST + URLLIB et HTMLParser nous avons donc tout le matériel nécessaire pour élaborer nos tests.

    5. Exemple

        def test_first_launch(self):
            html = request.urlopen('http://192.168.1.18').read()
            self.parser.feed(str(html))
            self.assertEqual(self.parser.title, 'Subscribers')
            self.assertEqual(self.parser.page_title, 'SUBSCRIBERS')
            self.assertEqual(self.parser.subscriber_name, 'Unknown')
            self.assertEqual(self.parser.subscriber_id, '0')
            self.assertEqual(self.parser.subscriber_credit, '0 credits')
            self.assertEqual(self.parser.subscriber_history, '\\n')
            self.assertEqual(self.parser.status, 'No Subscribers registered')
            self.assertEqual(self.parser.status_color, 'background-color:red;color:white;')


    Ici nous testons le premier lancement de notre serveur :
    • la requête est envoyée et la réponse reçue
    • les informations dont vérifiées :
      • le titre de la page : "Subscribers"
      • un titre en gras : "SUBSCRIBERS"
      • le nom de l'abonné "Unknown"
      • l'ID de l'abonné "0"
      • le solde de l'abonné "0 credits"
      • l'historique : vide
      • le status : "No Subscribers registered"
      • la couleur du status : blanc sur fond vert
    Exécution :

    $ python3 test.py Test.test_first_launch
    .
    ----------------------------------------------------------------------
    Ran 1 test in 0.662s

    OK


    L'exécution a réussi.

    5. Quelques règles :

    UNITTEST considère qu'une méthode est une méthode de test si son nom commence par "test". Les autres méthodes sont ignorées. Bien entendu les méthodes de test peuvent faire appel à toute méthode classique, ne sesait-ce que pour des raisons de factorisation.

    Deux méthodes setUp() et tearDown() peuvent être appelées automatiquement par UNITTEST, si elles existent, en début et fin de chaque test :
    • setUp() : initialisations communes à tous les tests de la classe
    • tearDown() : terminaisons communes à tous les tests de la classe
    La méthode spéciale setUp() est exécutée avant chaque test :

        def setUp(self):
            self.parser = MyHTMLParser()


    Elle nous sert simplement à créer notre objet MyHTMLParser.

    L'autre méthode spéciale tearDown() est exécutée après chaque test :

        def clear_all(self):
            request.urlopen('http://192.168.1.18/wipe.html').read()

       def tearDown(self):
            self.clear_all()


    Elle est utilisée pour nettoyer le serveur après chaque test.

    Pourquoi nettoyer après chaque test ? parce que UNITTEST lance les tests les uns à la suite des autres sans ordre précis.

    Un test ne doit pas dépendre des conditions finales du test qui a été exécuté précédemment.

    Par exemple le test de l'exemple du paragraphe 4 échouerait s'il était exécuté après un autre test ayant créé un abonné.
    Le status en particulier ne serait pas ègal à "No Subscribers registered", forcément.

    6. La testabilité

    Le serveur ESP32 a été développé dès le départ pour être logiciellement testable.

    Par exemple les champs d'un formulaire ont un nom explicite qui permet de les retrouver facilement dans la réponse à une requête.

    Quelques requêtes supplémentaires ont été ajoutées :
    • visialisation des fichier sur le serveur
    • destruction des fichier sur le serveur
    Dans cette version j'ai ajouté une requête permettent l'ajout d'un abonné en fournissant son nom et son ID. Cette méthode permet de se passer de la présentation de la carte RFID.

    S'il fallait présenter une carte pour exécuter certains tests, cela ne s'appellerait plus du test automatisé.

    Mais cela n'empêche pas d'écrire une suite de test spéciale dite "suite interactive" avec une interaction homme machine.

    Exemple :
    • le test demande la présentation d'une carte
    • le testeur présente la carte et appuie sur <RETURN>
    • le test continue
    Cette suite interactive sera déroulée suivant les besoins, probablement moins souvent que la suite de tests automatisés.

    7. La suite de test

    Celle-ci est disponible ici :
    https://bitbucket.org/henri_bachetti/webserver-form/src/v2.2/esp32-subscriber/test/test.py

    Elle comporte 3 tests :
    • test de premier lancement du serveur
    • test d'ajout d'un abonné
    • test d'ajout de 1 crédit + 5 crédits
    Exécution :

    $ python3 test.py
    ...
    ----------------------------------------------------------------------
    Ran 3 tests in 2.003s

    OK


    L'exécution prend 2 secondes. Ce temps est bien inférieur à celui qu'il aurait fallu pour faire la même chose manuellement avec la souris et le clavier.

    8. Téléchargement

    Cette version 2.2 est disponible ici :
    https://bitbucket.org/henri_bachetti/webserver-form/src/v2.2/esp32-subscriber/

    9. Conclusion

    Dans le cadre d'un développement d'un serveur ESP32 cette méthode de test est facile à mettre en place, car le canal de communication permettant de dérouler les tests existe déjà : HTTP.

    Faire la même chose avec un ARDUINO par la ligne série n'est pas impossible. On peut s'inspirer de cet article :
    https://riton-duino.blogspot.com/2019/12/commander-un-arduino-par-la-ligne-serie_21.html

    Cette suite de tests m'a déjà permis de trouver un bug dans gestion de la requête de nettoyage. La fiabilité ne peut qu'augmenter en continuant.

    Bien sûr les tests sont loin d'avoir été tous écrits. Par exemple le contenu des fichiers n'est pas vérifié. Le travail continue donc.

    J'espère vous avoir éveillé votre curiosité avec cette démarche de test.


    Cordialement
    Henri

    lundi 23 mars 2020

    Serveur ESP32 : implémentation (5ème partie)


    Serveur ESP32 : implémentation

    (5ème partie)


    Ceci est la suite des quatre articles précédents :
    Serveur ESP32 : implémentation
    Serveur ESP32 : implémentation (2ème partie)
    Serveur ESP32 : implementation (3eme-partie)
    Serveur ESP32 : implementation (4eme-partie)

    Dans cette 5ème partie nous allons nous occuper de la gestion des données des abonnés.
    Comme vu dans l'article précédent ces données seront gérées sous forme de fichiers dans une partition FAT.

    Rappel : seul l'ESP32 pourra offrir une possibilité de partitionnement entre SPIFFS et FAT.
    Pour un ESP8266 il faudra forcément utiliser une SD.

    1. Le fichier abonnés principal

    Ce fichier contient les données principales :
    • nom de l'abonné
    • ID de l'abonné (N° de carte)
    • son état (actif, désactivé)
    • son solde
    Ce fichier comporte une ligne par abonné :

    Unknown:0,0,0
    Henri Bachetti:1158992794,1,15
    Martin Hirsch:1436301657,1,10
    Michelle Tournier:1436648937,1,5
    Jean Lain:1437378937,1,10
    Arthur Leroy:1437071625,1,10
    Sarah Humbert:-1011506900,1,5


    Il est lu au démarrage et les données sont placées en mémoire RAM.

    A chaque opération (ajout de crédit, désactivation, réactivation, etc.) le fichier est mis à jour.

    2. Les fichiers historique

    Pour chaque abonné il y un fichier historique à son nom.
    Ce fichier retrace toutes les opérations effectuées sur son compte, avec les dates :

    23/03/2020 15:16 : +10 credits
    23/03/2020 15:19 : deactivated
    23/03/2020 15:19 : activated
    23/03/2020 15:19 : 0 credits
    23/03/2020 15:19 : +5 credits


    3. Mode opératoire

    3.1. Nouvel abonné

    Pour ajouter un nouvel abonné, un nouveau bouton "Add a new subsciber" est ajouté :

    Cliquer sur ce bouton provoque l'affichage d'une nouvelle page :

    Il suffit d'entrer le nom, d'approcher la carte du lecteur et cliquer sur le bouton "Read media".

    Un message d'erreur sur fond rouge est affiché dans le champ status si :
    • l'utilisateur existe déjà
    • la carte n'est pas présentée ou illisible
    • il ne reste plus de place dans la partition FAT
    • une erreur d'écriture se produit
    Sinon, on revient au formulaire principal et les données de l'abonné sont affichées avec un message "Subscriber is OK" sur fond vert.

    3.2. Opérations abonné

    Pour effectuer une opération pour un abonné il est possible de procéder de différentes manières :
    • approcher la carte du lecteur et cliquer sur le bouton "Read media". Le formulaire présentera les données de l'abonné.
    • recherche l'abonné par son nom ou son N° de carte
    • utiliser le bouton "abonnés.
    Un message d'erreur sur fond rouge est affiché dans le champ status si :
    • l'utilisateur n'existe pas
    • la carte n'est pas présentée ou illisible
    Ensuite il faut choisir l'opération à effectuer et cliquer sur OK. Le formulaire affrichera les nouvelles données de l'abonné.
    Un message d'erreur sur fond rouge est affiché dans le champ status si :
    • il ne reste plus de place dans la partition FAT
    • une erreur d'écriture se produit
    Sinon un message est affiché sur fond vert :
    • 1 credit added
    • 5 credit added
    • 10 credit added
    • Credits = 0
    • Subscriber is OK
    En cas de désactivation de l'abonnement, un message "Subscriber is DEACTIVATED !!!" est affiché sur fond rouge.

    4. Améliorations et petits trucs

    4.1. Libellés cliquables

    Les libellée des boutons radios sont maintenant cliquables :

    <input type="radio" name="action" id=add1 value="add1" checked>
    <label for="add1">Add 1 credit</label>


    4.2. Explorateur

    Un explorateur de fichier est accessible au moyen d'une URL cachée.
    Elle n'est pas accessible depuis le formulaire d'accueil (pas de bouton, pas de lien hypertexte, il faut la taper manuellement dans la barre du navigateur) :
    http://xxx.xxx.xxx.xxx/explore.html :

    Cette page permet de visualiser les différents fichiers présents, et même de les détruire.
    Elle est très utile pour contrôler le contenu des fichiers en cours de développement.

    4.3. Nettoyage

    Une autre URL cachée permet d'effacer tous les fichiers et de recréer un fichier abonnés vide :
    http://xxx.xxx.xxx.xxx/wipe.html :

    Cette URL est utilisée pour nettoyer le serveur en cours de développement, si l'on a écrit de mauvais contenus dans un plusieurs fichiers, donc en cas de bug, et que l'on veut repartir de ZÉRO.

    5. BITBUCKET

    Comme on l'a vu tout au long de ces quelques articles j'ai utilisé les services du serveur BITBUCKET pour partager mon travail.
    BITBUCKET est un serveur git, au même titre que GITHUB.

    A chaque nouvel article une nouvelle version est née (v1.0, v1.1, v2.0, v2.1).
    Toutes ces versions sont encore diponibles, alors que si j'avais travaillé en local sur son PC, faire la même chose aurait été bien plus fastidieux.
    Avec git le partage est grandement facilité.

    git offre des facilités qui rendent la vie du développeur bien plus agréable. Par exemple comparer deux versions d'un fichier pour examiner les différences est un jeu d'enfant.

    git nécessite un petit apprentissage, mais cela en vaut vraiment la peine. Il peut s'interfacer avec certains IDE, comme VisualStudioCode ou Eclipse.

    6. Téléchargement

    Cette version 2.1 est disponible ici :
    https://bitbucket.org/henri_bachetti/webserver-form/src/v2.1/esp32-subscriber/

    7. Conclusion

    Le développement d'un petit serveur de ce genre sur ESP32 est très amusant et il est possible de lui associer très facilement une partie matérielle, ici un lecteur RFID.
    Comme on le voit au paragraphe 4, il ne faut pas hésiter à doter le serveur de quelques petits outils qui facilitent la mise au point et le contrôle du résultat, et rendent donc les tests bien plus faciles.


    Cordialement
    Henri