jeudi 24 juin 2021

ESP32 : HELTEC WiFi Kit 32

 


ESP32 : HELTEC WiFi Kit 32


Certains d'entre vous ont certainement remarqué ces petites cartes HELTEC équipées d'un ESP32 et d'un écran OLED 0.96".

On les trouve chez différents revendeurs, y compris sous d'autres marques.

Celle-ci par exemple, fabriquée par DIYMORE, que j'ai acheté :

https://fr.aliexpress.com/item/32847022581.html?spm=a2g0s.9042311.0.0.63436c37qjZ3Li

Il s'agit d'un clone, strictement identique à l'original : 

https://heltec.org/project/wifi-kit-32/

Ses caractéristiques :

  • ESP32 dual-core 240MHz
  • 4MB de mémoire FLASH
  • 520 KB de mémoire RAM
  • convertisseur USB CP2102
  • écran OLED I2C SSD1306 128x64
  • LED blanche utilisateur (LED_BUILTIN)

Sa largeur est la même que celle de la WEMOS D1 LOLIN32, ce qui permet de l'enficher sur une breadboard, tout en disposant de deux rangées de broches libres sur les côtés pour les raccordements de fils DUPONT. C'est appréciable.

D'autre part, cette carte est une bonne alternative d'affichage pour certaines personnes qui s'obstinent, encore à notre époque, à utiliser, y compris sur ESP8266 ou ESP32, des écrans LCD alphanumériques 2x16 ou 4x20, gros, moches et dépassés.

1. Le WIFI

Apparemment cette carte a un petit peu plus de mal à se connecter à mon réseau WIFI qu'une WEMOS D1 MINI. Même une carte ESP32 DevKit C fait mieux.

Depuis mon bureau, assez mal situé d'un point de vue WIFI, la connexion avec la HELTEC est impossible. Depuis la chambre voisine, 3 mètres plus près, c'est OK.

On va conclure que dans une situation normale, la carte fonctionne plutôt bien, sans être exceptionnelle.

2. L'écran OLED

On pourrait croire qu'il suffit d'installer le package ESP32 et une librairie SSD1306 pour faire fonctionner cette carte.

C'est possible, mais ce n'est pas la solution la plus aisée. Heureusement, HELTEC fournit un package pour sa carte. 

2.1. La méthode facile

Pour utiliser ces cartes, il suffit d'installer la librairie HELTEC. Elle est directement installable depuis le Gestionnaire de Bibliothèques de l'IDE ARDUINO : il suffit de rechercher Heltec ESP32 Dev-Boards.

Ensuite, redémarrer l'IDE pour voir apparaître les exemples dans le menu "Fichiers / Exemples / Heltec ESP32 Dev-Boards.

Il sera possible ainsi à peu de frais de commencer à s'amuser avec cette carte.

Il y a un inconvénient : le nombre de polices de caractères est très limité.

2.2. La méthode compliquée

Après tout, ces modules sont de vulgaires cartes ESP32 équipées d'un écran OLED SSD1306. Il n'y a pas de raison d'installer un package particulier pour si peu de chose. On devrait pouvoir s'en sortir avec le package ESP32 standard et la librairie SSD1306 de notre choix.

Tout d'abord, dans le menu "Outils / Type de Carte" choisissons la carte "Heltec WiFi Kit 32". Car elle existe dans le package ESP32 standard !

2.2.1. L'écran I2C

Le premier réflexe qui vient à l'esprit lorsque l'on connecte un écran I2C à un microcontrôleur est d'utiliser le logiciel I2C_Scanner pour vérifier que l'écran est bien visible sur le bus. Or, avec cette carte cela ne donne aucun résultat.

Le schéma nous apprend que SDA et SCL sont situés sur les GPIOs 4 et 15, au lieu de 21 et 22.

Un second essai en configurant Wire pour utiliser 4 et 15 ne donne aucun résultat non plus.

Surprise : dans les exemples Heltec il y a un exemple "Heltec ESP32 Dev-Boards / ESP32 / I2C_scanner. Ce sketch affiche bien un device I2C en 0x3C.

En examinant le code de la librairie, la broche RST de l'écran est configurée en sortie et basculée à HIGH. Essayons de faire la même chose avec un code classique I2C_Scanner légèrement modifié :

i2c_scanner.ino

Gagné !

I2C scanning with SDA=4, CLK=15

Found device at 0x3c
Found 1 I2C devices by scanning.

2.2.2. La librairie AdaFruit

Maintenant le but de la manip est de déterminer si la librairie Adafruit_SSD1306 est compatible avec cette carte. A priori il n'y a pas de raison d'en douter.

2.2.2.1. Installation

Il faut installer la librairie à l'aide du Gestionnaire de bibliothèques : rechercher Adafruit SSD1306.

Il faut également installer Adafruit GFX Library.

Cette librairie a pas mal évolué ces derniers temps et il n'est plus nécessaire de modifier le fichier Adafruit_SSD1306.h pour préciser quelle est la taille de l'écran utilisé : elle alloue son buffer écran dynamiquement, ce qui n'est pas plus mal.

Elle permet également l'utilisation d'un bus I2C autre que le bus par défaut, ce qui va grandement nous aider.

Les manipulations décrites ci-dessous ne fonctionneront qu'avec les dernières versions de la librairie.

2.2.2.2. Le sketch

Rappel : dans le menu "Outils / Type de Carte" choisir la carte "Heltec WiFi Kit 32".

L'exemple utilisé est : ssd1306_128x64_i2c.ino

Les modifications à apporter sont mineures :

Cette portion du code d'origine :

#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3D ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

Est à remplacer par :

#define SCREEN_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire1, RST_OLED);

Ensuite dans la fonction setup() il faut ajouter cette ligne (en gras) :

void setup() {
  Serial.begin(115200);
  Wire1.begin(SDA_OLED, SCL_OLED);
  // etc.

Les constantes SDA_OLED, SCL_OLED et RST_OLED sont déclarées dans le package ESP32 pour la carte HELTEC WIFI KIT 32. Il n'y a pas à s'en soucier.

Explications : au lieu d'utiliser l'objet standard Wire, nous utilisons un autre objet, Wire1, qui sera initialisé à l'aide des bonnes broches SDA et SCL dans la fonction setup().

Le sketch complet :

adafruit-ssd1306.ino

Et l'image de départ de la démo Adafruit :

3. Hello server

Petit bonus : un petit serveur HTTP qui permet de se connecter à un point d'accès et d'afficher le résultat de la connexion sur l'écran OLED :

HelloServer.ino

Avant de compiler il faut renseigner ssid et password, et choisir la carte "Heltec WiFi Kit 32".

Son fonctionnement :

  • au démarrage :
    • il affiche "Connecting to SSID"
    • la LED blanche s'allume
  • à la connexion :
    • il affiche "Connected to SSID" et l'adresse IP
    • la LED blanche s'éteint
  • en cas de perte de connexion WIFI :
    • il affiche "Reconnecting to SSID"
    • la LED blanche se rallume
  • à la reconnexion :
    • il affiche "Connected to SSID" et l'adresse IP
    • la LED blanche s'éteint

Le mécanisme de reconnexion utilise les événements de connexion / déconnexion WIFI.

Accessoirement on peut se connecter à l'adresse IP avec un navigateur pour allumer ou étendre la LED.

Il sera ainsi facile de placer la carte à différents endroits de l'habitation pour vérifier que la couverture WIFI est correcte, et que le serveur a un temps de réaction raisonnable.

4. Téléchargements

Le projet se trouve ici :

https://bitbucket.org/henri_bachetti/heltec-wifi-kit-32.git

5. Conclusion

Nous voilà donc à même de pouvoir utiliser soit le package HELTEC, soit le package standard ESP32 + la librairie Adafruit.

La librairie Adafruit est intéressante à plus d'un titre :

Tout d'abord beaucoup de développeurs sur ARDUINO, ESP8266 ou ESP32 l'utilisent. Ils ne seront donc pas dépaysés.

La librairie Adafruit est maintenue et évolue fréquemment, et en bien, et même en très bien.

Elle propose un nombre de polices de caractères important, et il est possible de convertir des polices TTF existantes.

https://learn.adafruit.com/adafruit-gfx-graphics-library/using-fonts

Voici une petite carte bien sympathique qui devrait plaire à beaucoup. Son petit format est intéressant.


Cordialement

Henri

6. Mises à jour

26/06/2021 : 3. Hello server


samedi 12 juin 2021

ARDUINO : les Interruptions




ARDUINO : les Interruptions


Qu'est-ce qu'une interruption ? c'est un mécanisme présent sur beaucoup de processeurs, qui permet d'exécuter un code particulier, appelé routine d'interruption, quand une entrée change d'état, mais pas seulement dans ce cas.

Une interruption peut également être déclenchée par un timer, ou servir à réveiller un microcontrôleur endormi.

Dans cet article nous allons également aborder un point important : les sections critiques.

1. Les interruptions

1.1. Changement d'état d'une entrée

Le code d'une application peut demander à ce qu'une routine particulière soit exécutée si une entrée digitale change d'état, en attachant une routine à un vecteur d'interruption, à l'aide de la fonction attachInterrupt().

Lorsqu'une interruption survient le code principal de l'application est interrompu, le code de la routine d'interruption est exécuté, puis le code principal de l'application reprend son exécution là où elle s'était arrêtée.

Déclencher une interruption sur changement d'état d'une entrée digitale est facile. Ce code permet de changer l'état d'une LED en fonction des changements d'état de l'entrée D2 :

const byte ledPin = 13;
const byte interruptPin = 2;
volatile byte state = LOW;

void setup()
{
  pinMode(ledPin, OUTPUT);
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), blink, CHANGE);
}
void loop()
{
  digitalWrite(ledPin, state);
}
void blink() {
  state = !state;
}

La fonction blink() (c'est elle la routine d'interruption) est appelée à chaque fois que l'entrée change d'état.

Sur un ATMEGA328P seules les entrées 2 et 3 sont utilisables. Sur un MEGA2560 elles sont plus nombreuses : 2, 3, 18, 19, 20, 21.

1.1.1. Front montant ou descendant ?

La routine d'interruption peut être appelée uniquement sur les fronts montants ou descendant du signal, ou les deux, en modifiant le 3ème paramètre de la fonction attachInterrupt() :

  • CHANGE : front montant ou descendant
  • RISING : front montant uniquement
  • FALLING : front descendant uniquement

1.1.2. digitalPinToInterrupt()

A quoi peut bien servir la fonction digitalPinToInterrupt() ? c'est une fonction du core ARDUINO qui retourne le N° de l'interruption correspondant à une broche donnée :

digitalPinToInterrupt(2) retournera 0, et digitalPinToInterrupt(3) retournera 1.

  attachInterrupt(digitalPinToInterrupt(2), blink, CHANGE);

Est donc équivalent à :

  attachInterrupt(0, blink, CHANGE);

1.1.3. Pin change interrupts

Il faut mentionner aussi les Pin Change Interrupts (PCINT), qui permettent de déclencher une interruption sur une broche digital quelconque :

https://www.robot-maker.com/forum/topic/12765-pin-change-interrupt-sur-arduino-mega-et-uno/

Les PCINT sont un peu plus coûteuses en temps d'exécution si plusieurs entrées peuvent déclencher une interruption, car dans le code de la routine d'interruption il faut déterminer quelle entrée est responsable de l'interruption.

1.2. Timer interrupts

Une interruption peut être générée également sur expiration d'un timer, ou comparaison du compteur d'un timer à une certaine valeur.

On peut bien entendu manipuler directement les registres d'un timer :

https://www.locoduino.org/spip.php?article84

https://www.locoduino.org/spip.php?article88

https://www.locoduino.org/spip.php?article89

https://www.instructables.com/Arduino-Timer-Interrupts/

Heureusement, des librairies, bien plus faciles à utiliser, existent :

TimerOne

MsTimer2

Comme je le conseille souvent, essayer les exemples des librairies est un bon point de départ.

ATTENTION : certaines librairies utilisent les timers :

  • timer0 (8 bits) compte de 0 à 256 et commande la PWM des broches 5 et 6. Il est aussi utilisé par les fonctions delay(), millis() et micros().
  • timer1 (16 bits) compte de 0 à 65535 et est utilisé pour la commande PWM des broches 9 et 10. Il est utilisé également par la libriaire Servo.h
  • timer2 (8 bits) est utilisé par la fonction Tone() et la génération de la PWM sur les broches 3 et 11.

Par exemple la librairie IRREMOTE utilise également le timer2, et son utilisation entrera en conflit avec la librairie tone si elles sont utilisées dans le même logiciel :

core.a(Tone.cpp.o): In function tone(unsigned char, unsigned int, unsigned long)': /home/user/arduino-1.0.1/hardware/arduino/cores/arduino/Tone.cpp:230: multiple definition of__vector_7'

IRremote/IRremote.cpp.o:/home/user/arduino-1.0.1/libraries/IRremote/IRremote.cpp:70: first defined here

1.3. Analog interrupts

On peut même déclencher une interruption à l'aide du comparateur analogique intégré à l'ATMEGA328P :

http://www.gammon.com.au/forum/?id=11916

https://zestedesavoir.com/articles/2106/arduino-les-secrets-de-lanalogique/#4-comparaison-directe-avec-arduino

1.4. Pièges

Piège courant : que signifie cet attribut volatile dans l'exemple ci-dessus ? il faut savoir que lorsque l'on lit dans le programme principal une variable susceptible d'être modifiée par une routine d'interruption cet attribut est indispensable si l'on veut éviter que le compilateur procède à certaines optimisations. L'attribut volatile forcera le code généré par le compilateur à relire la variable à chaque fois.

Dans l'exemple donné ci-dessus au paragraphe 1.1. l'attribut volatile est inutile car la variable state n'est lue qu'une fois dans la fonction loop(). Si elle était lue répétitivement dans une boucle for() ou while() l'attribut volatile serait indispensable. Dans le doute, utiliser volatile systématiquement n'est pas une mauvaise idée.

Deuxième piège : une routine d'interruption doit être la plus courte possible. Dans l'exemple précédent la routine blink() se contente de modifier une variable. Il est hors de question de faire des traitements lourds dans une routine d'interruption (Serial.print() par exemple). Le risque est de rater une nouvelle interruption pendant que l'interruption courante est en cours de traitement.

1.5. Temps d'exécution

Il est possible de surveiller une entrée par une lecture répétitive dans le programme principal, ce que l'on appelle polling, ou à l'aide d'une interruption.

La gestion d'une interruption n'est pas un mécanisme gratuit. Son exécution prend du temps :

  • sauvegarde sur la pile du contexte d'exécution de l'application (un certain nombre de registres)
  • exécution de l'interruption
  • restitution du contexte d'exécution de l'application

Il convient donc d'utiliser les interruptions à bon escient. Il peut arriver que l'exécution répétitive d'interruptions à fréquence élevée consomme plus de temps CPU qu'un traitement classique dans le programme principal.

Il est parfois plus économique et plus efficace de travailler en polling plutôt que sous interruption.

2. Section critique

2.1. Code critique

Comme dit plus haut :

Lorsqu'une interruption survient le code principal de l'application est interrompu, le code de la routine est exécuté, puis le code principal de l'application reprend son exécution.

Il s'en suit donc que l'exécution du code principal peut être perturbé, d'un point de vue temporel, par une interruption. Si la précision d'exécution dans le temps est critique, il convient de protéger le code en désactivant les interruptions. Cela s'appelle une section critique :

void setup() {}

void loop()
{
  noInterrupts(); // désactivation des interruptions
  // portion de code critique, sensible au temps, ici
  interrupts();      // réactivation des interruptions
  // le reste du code ici
}

Dans l'exemple suivant (la librairie OneWire, écrite par un développeur très compétent), les interruptions sont désactivées pendant la lecture et l'écriture sur le bus, car la communication OneWire doit respecter des timings très précis :

https://github.com/PaulStoffregen/OneWire/blob/master/OneWire.cpp

Il faut savoir que même si le logiciel que l'on est en train d'écrire ne met en place aucune routine d'interruption, certaines routines d'interruption sont déjà installées par la librairie ARDUINO :

  • ligne série (Serial)
  • timer ZÉRO (utilisé entre autres par millis())

On n'est donc jamais totalement à l'abri.

2.2. Protection de variables

Une section critique peut également être utilisée lorsque l'on désire lire ou modifier des variables pouvant être également modifiées par une interruption.

Première règle : ne jamais travailler directement avec des variables modifiables par une interruption.

Pourquoi ? tout simplement parce que pendant le traitement, la variable peut changer de valeur suite à une interruption.

J'ai déjà travaillé sur un driver LIN (LIN est un protocole de communication largement utilisé dans l'industrie). Chaque trame LIN était reçue sous interruption et l'auteur n'avait pas pris la peine de recopier celle-ci avant de la traiter. Cela semblait fonctionner la plupart du temps, mais de temps à autre une nouvelle trame arrivait avant que la précédente ne soit complètement traitée. Le résultat était catastrophique !

Dans le programme principal il faut donc toujours copier les variables avant de les utiliser, et ceci doit être fait dans une section critique.

2.2.1. Cas simple

Le cas le plus simple est la modification d'une variable sur 32 bits. Si le programme principal consulte une variable de type long ou unsigned long susceptible d'être modifiée par une interruption, une lecture ou une écriture fiable n'est pas garantie.

Le microcontrôleur ATMEGA328P ne possède pas d'instructions de lecture ou d'écriture 32 bits. La lecture ou l'écriture sera donc effectuée en deux instructions. Si une interruption survient entre les deux instructions, le résultat peut être faussé.

const byte interruptPin = 2;
volatile uint32_t counter = 0;

void setup()
{
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), isr, CHANGE);
}

void loop()
{
  uint32_t cnt;
  if (counter >= 100000UL) {
    cnt = counter;
    counter = 0;
    // traitement de cnt
  }
}

void isr()
{
  counter++;
}

Lorsque counter est copié dans cnt dans la fonction loop(), l'opération est réalisée en réalité par deux copies de mots de 16 bits, LSB (octet de poids faible) et MSB (octet de poids fort) :

Exemple : 
  • counter = 0x0001FFFF
  • copie du LSB : cnt vaut donc 0x0000FFFF
  • interruption ! : counter est incrémenté : counter vaut maintenant 0x00020000
  • copie du MSB : cnt devient 0x0002FFFF
  • le résultat est totalement faux

Une section critique garantira que LSB et MSB soient copiés correctement :

void loop()
{
  uint32_t cnt;
  if (counter >= 100000UL) {
    noInterrupts();
    cnt = counter;
    counter = 0;
    interrupts();
    // traitement de cnt
  }
}

Avec cette modification le déroulement devient : 

  • counter = 0x0001FFFF
  • désactivation des interruptions
  • copie du LSB : cnt vaut donc 0x0000FFFF
  • pas d'interruption possible
  • copie du MSB : cnt devient 0x0001FFFF
  • réactivation des interruptions
  • le résultat est correct

Bien sûr on pourra objecter que cet exemple est extrême et qu'il y a très peu de chances pour que counter soit égal à 0x0001FFFF et qu'une interruption survienne pendant la copie. Mais sur un temps d'exécution très long cela peut parfaitement arriver. Mieux vaut prévenir ce genre de situation, car en cas de problème il sera très difficile d'en déterminer l'origine.

2.2.2. Cas plus complexe

Prenons un autre exemple, avec deux variables :

#define MAXBUFF      100

const byte interruptPin = 2;
volatile byte counter = 0;
byte buffer[MAXBUFF];

void setup()
{
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), isr, CHANGE);
}

void loop()
{
  byte localBuffer[MAXBUFF];

  if (counter >= MAXBUFF / 2) {
    // recopie de buffer
    memcpy(localBuffer, buffer, counter);
    counter = 0;
    // traitement différé du buffer localBuffer
  }
}

void isr()
{
  if (counter < MAXBUFF) {
    buffer[counter] = random(0, 200);
    counter++;
  }
}

Dans cet exemple la routine d'interruption isr() ajoute à un buffer un caractère aléatoire et incrémente un index nommé counter. Dans la réalité ce caractère pourrait parfaitement provenir de la lecture d'un port physique d'entrée / sortie, ou de la lecture d'un timer.

Dans la fonction loop(), si le buffer est à moitié plein il est recopié dans un autre buffer localBuffer pour être traité ensuite, puis counter est remis à ZÉRO.

Une interruption peut survenir pendant que loop() effectue cette opération, ce qui pourrait provoquer une incohérence entre le contenu du buffer et l'index.

Exemple : imaginons que counter soit égal à 55 :

  • loop() recopie le buffer
  • interruption ! la routine isr() place un octet supplémentaire à l'emplacement 55 et incrémente la variable counter. counter devient égal à 56
  • loop() remet counter à ZÉRO

Résultat : avant l'interruption counter vaut 55, et 56 après l'interruption. Après l'interruption, loop() remet la variable counter à ZÉRO. Le caractère à l'emplacement 55 est donc perdu, puisqu'il n'a pas été recopié.

Le remède est l'adoption d'une section critique :

void loop()
{
  byte localBuffer[MAXBUFF];

  if (counter >= MAXBUFF / 2) {
    noInterrupts();
    memcpy(localBuffer, buffer, counter);
    counter = 0;
    interrupts();
    // traitement du buffer localBuffer
  }
}

On préservera ainsi la cohérence entre les deux variables.

3. Le mode veille

Le mode veille est utilisé lorsque l'on désire consommer un minimum d'énergie dans une configuration alimentée par piles ou batterie. En pratique seule la carte PRO MINI est adaptée à ce genre d'utilisation, ainsi que certaines cartes ESP8266 et ESP32.

J'en parle ici :

ARDUINO PRO MINI & basse consommation

Consommation d'une carte ARDUINO, ESP8266 ou ESP32

Le mode veille est intimement lié aux interruptions, car lorsque le microcontrôleur est endormi, l'unique moyen de le réveiller est une interruption, et pas n'importe laquelle.

On peut sortir du mode veille en utilisant différentes méthodes :

  • réveil au bout d'un temps défini à l'aide du watchdog
  • réveil par une entrée digitale

Exemple : on peut parfaitement réveiller un ARDUINO à l'aide d'un capteur de présence sur une entrée digitale.

Sur un ATMEGA328P seules les entrées 2 et 3 sont utilisables pour le réveil. Sur un MEGA2560 elles sont plus nombreuses : 2, 3, 18, 19, 20, 21.

3.1. ESP8266 et ESP32

Contrairement à ce qui se passe sur un ARDUINO, le réveil par GPIO n'existe pas vraiment sur un ESP8266 ou un ESP32, en tous cas pas de la même façon. Un RESET est provoqué et le logiciel doit aller consulter les informations de RESET et lire l'état de la GPIO pour connaître la cause du réveil.

L'ESP32 possède une autre possibilité de réveil : le touchpad

voir cet article : ESP8266 et ESP32 sur batterie

3.2. Tutoriels

On trouve énormément de tutoriels sur le sujet :

TUTORIAL:A GUIDE TO PUTTING YOUR ARDUINO TO SLEEP

Waking up an Arduino with Input from a Sensor

Comme on peut le constater le code n'est pas toujours très abordable pour un débutant.

3.3. Librairie LowPower

Pour ma part j'utilise la librairie LowPower, très simple à mettre en oeuvre (voir les exemples) :

Réveil périodique : idleWakePeriodic

Réveil par une entrée digitale : powerDownWakeExternalInterrupt

Dans ce dernier exemple on aura avantage à remplacer la ligne suivante :

    attachInterrupt(0, wakeUp, LOW);

Par :

    attachInterrupt(0, wakeUp, RISING);

ou 

    attachInterrupt(0, wakeUp, FALLING);

Ceci afin de réveiller le microcontrôleur seulement sur un front montant ou descendant.

4. Conclusion

Maîtriser les interruptions peut être un atout dans certaines situations, mais en abuser serait une erreur.


Cordialement

Henri

mercredi 2 juin 2021

Contrôle de Niveau d'Eau

 

Contrôle de Niveau d'Eau

 

Bonjour amis jardiniers.

Pour ma centrale d'irrigation j'utilise soit de l'eau du réseau public, soit de l'eau de pluie quand celle-ci est disponible.

Un réservoir principal de 1500 litres récupère l'eau de pluie d'une toiture grâce à un collecteur :

Collecteur sur tuyau de descente

Un réservoir secondaire de 200 litres situé en hauteur (3m) dans le grenier d'un appentis est utilisé comme réserve intermédiaire. L'eau puisée dans le réservoir principal est aspirée par une pompe et remontée dans le réservoir secondaire. Un interrupteur à flotteur permet d'actionner et d'arrêter la pompe.

J'ai choisi cette solution car elle permet un arrosage en basse pression : 3m de hauteur d'eau correspondent à 0.3 bars.

Elle a un inconvénient : sauf s'ils sont situé dans un local hors gel, il faudra vidanger les réservoirs, la pompe et les tuyaux avant les gelées.

Il y a deux flotteurs :

  • un flotteur dans le réservoir secondaire
  • un flotteur dans le réservoir principal

Le premier flotteur active la pompe lorsque le niveau d'eau dans le réservoir secondaire est trop bas, et la coupe lorsque le niveau est proche du maximum.

Le deuxième flotteur désactive le système de remplissage du réservoir secondaire lorsque le niveau d'eau dans le réservoir principal est trop bas. Il faut donc basculer l'arrosage sur l'eau du réseau public.

Mon premier problème est que je n'ai aucune envie d'avoir du 230V sur des flotteurs immergés, susceptibles d'être peu étanches, et de fuir. Je préfère les alimenter sous 5V ou 12V, quitte à ajouter un peu d'électronique. Sécurité avant tout.

Mon second problème est le suivant : le contact des flotteurs que je possède se ferme lorsque le réservoir est plein. Si je le branche en série avec la pompe celle-ci va démarrer quand le réservoir sera plein et s'arrêter lorsque celui-ci sera vide.

C'est donc un flotteur de vidange qui serait plutôt adapté à la vidange d'un réservoir ou une cave. Il convient bien par contre pour le réservoir principal.

Pour le réservoir secondaire c'est exactement le contraire de ce que je veux. Plutôt que de racheter un flotteur de remplissage, j'ai créé un petit circuit inverseur à relais. De toutes façons si je veux alimenter les flotteurs en très basse tension ce circuit est obligatoire, quel que soit le modèle de flotteur..

Remarque : il existe des flotteurs assurant les deux fonctions :

  • un fil commun
  • un fil pour le contact servant au remplissage
  • un fil pour le contact servant à la vidange

Pour repérer les fils il suffit d'utiliser un ohmmètre : 

  • flotteur en bas :
    • résistance très faible : contact de remplissage
    • résistance infinie : contact de vidange
  • flotteur relevé :
    • résistance très faible : contact de vidange
    • résistance infinie : contact de remplissage

Bien entendu le montage proposé pourra être également être utilisé, entre autres, pour remplir un réservoir à partir d'un cours d'eau, ou d'un puits.

Différents sujets seront abordés dans cet article :

  • blocage et saturation d'un transistor NPN
  • protection et anti-parasitage à l'aide d'une varistance
  • conception d'une carte pour un boîtier DIN
  • tropicalisation

1. La théorie

Théoriquement, il est impossible d'aspirer de l'eau avec une pompe de surface sur une hauteur de colonne d'eau de plus de 10 mètres. Dans la pratique, les pompes de surface sont souvent limitées à 7 ou 8 mètres.

Par contre une pompe est capable de refouler de l'eau sur des hauteurs beaucoup plus importantes.

Si le réservoir à remplir est situé beaucoup plus haut que le réservoir à vider, il faudra donc placer la pompe au point le plus bas. Il faudra ensuite choisir une pompe ayant une hauteur de refoulement suffisante. Certains modèles ont une hauteur de refoulement de 40m, parfois plus.

Choisir entre une pompe de surface et une pompe immergée est une question pratique. La pompe de surface est difficile à amorcer, elle nécessite souvent l'ajout d'un clapet anti-retour. La pompe immergée est probablement la solution la moins problématique.

2. Matériel

2.1. Réservoir et pompe

Le réservoir secondaire est équipé de deux traversées de paroi  20/27 (3/4) en haut (remplissage) et en bas (arrosage) :

Il suffit d'ajouter deux nez de robinet 20/27 (3/4) pour deux tuyaux avec raccords rapides.

A propos de la pompe il y a deux solutions :

  • pompe de surface
  • pompe immergée dans le réservoir principal (avec son propre flotteur)

Dans le premier cas, un flotteur supplémentaire est nécessaire dans le réservoir principal. Il servira à couper l'alimentation du module relais quand le réservoir principal sera vide.

Dans le deuxième cas, le flotteur dont la pompe est équipée est suffisant. Quand le réservoir principal sera vide, il coupera l'alimentation de la pompe.

2.2. Flotteur

Le flotteur est un modèle Renkforce 2302386 acheté chez Conrad :

La hauteur d'eau n'est pas très facile à régler, étant donné qu'elle se fait en réglant la position du contrepoids sur le câble. D'autre part il est possible que le contact ne soit pas adapaté à de la très basse tension et faible courant. Voir le commentaire de jessie25 (au passage, merci à lui) en bas de page.

D'autres interrupteurs à flotteur existent, par exemple :

MEDER-LS03-1A66

Ce genre de capteur se monte sur la paroi de la cuve, à travers un trou de 16mm.

MEDER-LS02-1A66

Celui-ci se monte à la verticale, sous le couvercle de la cuve par exemple.

Le prix est faible : environ 8€.

Il y a de fortes chances que le contact soit du type interrupteur à ILS, et qu'un aimant soit logé dans la partie mobile. Il n'y a aucun risque de fuite électrique puisque le contact est totalement isolé.

Attention le courant maximal est faible : 500mA, mais comme le module relais proposé consomme environ 5mA sur son entrée cela convient parfaitement.

Je pense que ce genre de flotteur est préférable à un modèle à bille comme le Renkforce. La fiabilité est certainement supérieure, et la hauteur maximale d'eau sera plus facile à régler. J'ai profité d'une commande passée chez TME pour acheter le modèle MEDER-LS03-1A66 et je l'ai monté sur la cuve fin septembre :

Le flotteur a fermé le contact quelques centimètres plus bas que prévu, mais ce n'est pas bien important. Le réservoir est plein à 90%.

Il est important de percer le trou du capteur à la bonne hauteur, car le niveau d'eau maximal ne doit pas être trop haut, à cause du trop plein. Ici le niveau maxi est à deux centimètre au dessous de la sortie :

3. Les schémas

3.1. Principe

Le schéma de principe avec une pompe immergée est celui-ci :

Pour plus de clarté j'ai fait passer le trop plein du réservoir secondaire directement à la canalisation d'évacuation des eaux de pluie. Mais en réalité le tuyau de 50mm est relié à la descente de chéneau, au dessus du collecteur de remplissage, ce qui fait que s'il y a un problème de débordement (cas d'un flotteur défaillant par exemple) l'eau retourne au réservoir principal, et n'est pas perdue.

3.2. Module relais

Il y a deux schémas du module relais, un pour chaque type de flotteur.

3.2.1. Avec un flotteur de vidange


Il n'y a aucun microcontrôleur, pour une fois !

Le flotteur SW1 est branché entre la masse et la base d'un PN2222.

Lorsque le contact est ouvert la base du PN2222 est polarisée par la résistance R1, le transistor est saturé et la bobine du relais est alimentée. Le contact du relais se ferme. La pompe démarre.

Lorsque le contact est fermé la base PN2222 est à la masse, le transistor est bloqué et le relais s'ouvre. La pompe s'arrête.

Deux LEDs sont présentes :

  • verte : module alimenté
  • rouge : pompe alimentée 

La varistance VR1 (SIOV-S10K275) limite les surtensions aux bornes du moteur de la pompe (ou du contacteur qui va alimenter la pompe) et préserve le contact du relais. Elle permet en outre de limiter les parasites engendrés sur le secteur.

On peut ajouter un bouton-poussoir pour forcer le remplissage du réservoir secondaire :

  • bouton poussoir NF (Normalement Fermé)
  • en série avec le flotteur

3.2.2. Avec un flotteur de remplissage

On pourrait parfaitement utiliser un flotteur de remplissage en le branchant entre le +5V et la base du transistor à travers une résistance :

Ici aussi on peut ajouter un bouton-poussoir pour forcer le remplissage du réservoir :

  • bouton poussoir NO (Normalement Ouvert)
  • en parallèle sur le flotteur

3.2.3. Alimentation

Il est possible d'adopter une alimentation 12V au lieu de 5V, si l'on fait quelques modifications mineures :

  • remplacer le relais : SRD-12VDC-SL-C
  • remplacer l'alimentation : 12V
  • remplacer les résistances de 1KΩ par 2.2KΩ

3.2.3. Module relais

On pourrait remplacer ce montage par un module relais du commerce :

Si l'on possède un flotteur de vidange : si c'est un module relais activable sur niveau bas, brancher le flotteur entre l'entrée du module et GND. Si c'est un module activable sur niveau haut, brancher le flotteur entre l'entrée du module et +5V.

Si l'on possède un flotteur de remplissage, faire le contraire.

Mais mon but est d'intégrer le module relais sur un rail DIN dans un coffret électrique. Avec ce genre de module la chose ne sera pas possible.

3.3. Câblage général

3.3.1. Pompe de surface

Avec une pompe de surface le schéma électrique général est celui-ci :

On retrouve le module relais (K2), avec son entrée pour le flotteur (SENSOR1 et SENSOR2), son entrées 5V et les bornes d'entrée 230V et sortie du relais (OUT1 et OUT2).

Le flotteur SW2 du réservoir secondaire est câblé sur l'entrée du module relais.

Le flotteur SW3 du réservoir principal est en série avec l'alimentation 5V. Le module relais K2 ne sera donc plus alimenté lorsque le réservoir principal sera vide.

Dans ce cas les flotteurs SW2 et SW3 sont donc soumis à une tension de 5V, comme je le souhaitais, et non pas 230V.

3.3.2. Pompe immergée

Avec une pompe immergée le schéma électrique général est légèrement différent :

Le flotteur SW3 est inutile étant donné que la pompe possède le sien. La sortie 5V de l'alimentation sera reliée directement à l'entrée 5V du module relais :

Dans ce cas le flotteur de la pompe immergée sera soumis à une tension de 230V. Il est impératif de choisir une pompe de qualité.

Lorsque le réservoir principal sera vide, le module relais restera alimenté, le relais et le contacteur seront éventuellement fermés si le réservoir secondaire est également vide (la LED rouge restera allumée) . Cela n'est pas un gros problème, mais je préfère ajouter un disjoncteur pour couper le tout, jusqu'à la prochaine pluie. Ce disjoncteur servira également pour la mise hors tension l'hiver.

3.3.3. Le contacteur

Dans les deux cas la sortie du module relais (K2) est câblée sur la bobine d'un contacteur 25A (K3), car le petit relais interne du module relais (SRD-5VDC-SL-C) me paraît peu indiqué pour piloter une pompe de 1000W. Les contacts de ces petits relais chinois ont tendance à rester collés s'ils commutent une charge de puissance, spécialement si la charge est inductive.

Le contacteur K3 alimente la prise 230V de la pompe.

3.3.4. Surveillance du niveau d'eau

Pour l'instant je ne vois pas l'intérêt d'installer un indicateur de niveau pour le réservoir principal, ni de concevoir un basculement automatique entre alimentation en eau de pluie et eau du réseau public. Ce n'est pas un effort insurmontable de soulever le couvercle périodiquement pour vérifier le niveau.

3.3.5. Basculement entre eau de pluie et eau du réseau public

Il est légalement interdit d'opérer un basculement entre eau de pluie, potentiellement polluée, et eau du réseau public, y compris à l'aide d'une vanne 3 voies et de clapets anti-retour classiques. Le risque ZÉRO n'existant pas, un clapet peut rester bloqué en position semi-ouverte et l'eau de pluie peut alors contaminer le réseau public, ce qui peut coûter très cher au responsable.

A lire : Usage domestique d’eau de pluie

Remarque : la norme NF1717 impose des clapets anti-retour antipollution contrôlables (type EA).

Norme NF1717

Ne voulant pas m'engager dans cette voie risquée, en cas de manque d'eau de pluie je préfère donc maintenir un isolement total antre mon circuit d'arrosage et le réseau public, et remplir éventuellement, et manuellement, mon réservoir principal.

4. La conception

La carte est conçue pour être montée dans un boîtier pour rail DIN (tableau électrique) de largeur 35mm :

Kradex Z-106

Ce boîtier est pré-percé en haut et en bas :

  • 6 trous espacés de 4.8mm d'un côté
  • 4 trous espacés de 7.2mm de l'autre côté

Il faudra donc équiper la carte de borniers adéquats (5mm et 7.62mm).

L'empreinte Kicad du boîtier Kradex est incluse dans le projet.

5. La réalisation

Il est totalement déconseillé de fabriquer cette carte à l'aide d'une plaquette à pastilles. L'écartement entre pastilles est totalement insuffisant pour assurer une isolation correcte sous 230V. En conséquence un PCB s'impose.

J'ai utilisé un petit coffret de tableau prévu pour 8 modules qui recevra les différents composants :

  • une prise pour rail DIN pour la pompe
  • une alimentation 5V pour rail DIN
  • le module à relais
  • le contacteur
  • un disjoncteur
L'alimentation est une MEANWELL:

MEANWELL HDR-15-5

J'avais cette alimentation sous la main, sinon j'aurais utilisé un petit bloc secteur 5V, bien meilleur marché. Qui n'a pas un chargeur de téléphone inutilisé qui traîne dans un tiroir ?

Un câblage correct du flotteur est primordial. En effet, s'il se débranche la pompe tournera sans jamais s'arrêter.

Par sécurité mon réservoir secondaire est équipé d'une sortie trop-plein de 50mm afin d'éviter tout débordement.

6. Photos

Voici quelques images :

L'arrière de la carte

La carte vient de recevoir une couche de vernis de tropicalisation (elle va être installée dans un appentis et je préfère qu'elle soit insensible à l'humidité et à la poussière).

La carte dans son boîtier

Le module relais dans le coffret électrique

On a du mal à voir la LED verte allumée à travers le plexiglas rouge. Le boîtier Z-106 existe aussi avec plexiglas transparent.

De gauche à droite :

  • disjoncteur général, pour pouvoir couper le système en hiver
  • prise de la pompe
  • contacteur 25A
  • module relais (façade rouge)
  • alimentation 5V

Ces composants étaient déjà en ma possession (y compris le coffret électrique et le boîtier Kradex Z-106), et proviennent pour la plupart de récupération (prise, contacteur, disjoncteur). Si l'on n'a pas ce genre de matériel à disposition on peut réaliser le montage à moindre frais :

  • module relais du commerce
  • alimentation 5V de téléphone mobile
  • contacteur bon marché (on en trouve pour quelques € sur AliExpress)

J'ai ajouté dernièrement un bouton-poussoir NF (Normalement Fermé) en série avec le flotteur du réservoir secondaire. Cela me permet, en appuyant dessus, de remplir le réservoir à raz-bord afin de vérifier si le trop-plein fonctionne.

Si l'on utilise un flotteur de remplissage il faudra un bouton-poussoir NO (Normalement Ouvert), en parallèle sur le flotteur.

7. Coût

Le coût de la carte est très faible (achats réalisés sur AliExpress) :

  • 1 relais SRD-5VDC-SL-C : 0.20 (prix par 5 pièces)
  • 2 borniers au pas de 5mm : 0.10€ (prix par 20 pièces)
  • 2 borniers au pas de 7.62mm : 0.32€ (prix par 20 pièces)
  • 1 PN2222 : 0.02€ (prix par 20 pièces)
  • 3 résistances 1KΩ : 0.05€ (prix par 100 pièces)
  • 1 diode 1N4148 : 0.01€ (prix par 100 pièces)
  • 1 varistance SIOV-S10K275 : 0.12 (prix par 10 pièces)
  • 2 LEDs : 0.05€ (prix par 100 pièces)
  • total : 0.87€ hors PCB

Il est difficile d'acheter de petits composants à l'unité sur AliExpress ou ailleurs, mais souvent cela vaut la peine d'acheter par lot si l'on bricole souvent. Par exemple le même relais coûtera 1€ ou 1.50€ chez un revendeur local, et pratiquement 4€ les 5 pièces sur Amazon (Jeff Bezos n'est pas l'un des hommes les plus riches de ce monde sans raison).

8. Les essais

J'ai réalisé un essai avec une pompe de surface :

  • puissance 1000W
  • débit 3300 litres/heure
  • raccords 1" (tuyaux 25mm)

Le résultat n'est pas à la hauteur :

  • amorçage difficile (nécessite une crépine avec clapet anti-retour)
  • pression trop importante (grosses difficultés à étanchéifier les raccords 25mm de la pompe sur les tuyaux)

Je me suis rabattu sur une pompe immergée de petite puissance :

Mac Allister 400W
  • débit 2200 litres/heure pour 4m de hauteur de refoulement
  • la pompe est équipée d'un flotteur
  • elle est livrée avec le matériel suivant :
    • canne avec poignée et crochet de suspension (entrée et sortie 20/27 (3/4"))
    • adaptateur 20/27 pour raccord rapide
    • vanne
J'ai raccordé la pompe au réservoir secondaire à l'aide d'un simple tuyau d'arrosage 12.5mm avec raccords rapides, après avoir vissé sur la pompe l'adaptateur 20/27 livré, sans utiliser la vanne ni la canne.

Le résultat est bien plus intéressant :

    • amorçage systématique puisque c'est une pompe immergée
    • pression plus faible
    • pas de fuites

    Cette solution est en contradiction avec le cahier des charges que je m'étais fixé au départ :

    Mon premier problème est que je n'ai aucune envie d'avoir du 230V sur des flotteurs immergés, susceptibles d'être peu étanches, et de fuir. Je préfère les alimenter sous 5V ou 12V, quitte à ajouter un peu d'électronique. Sécurité avant tout.

    Reste à espérer que la fiabilité sera au rendez-vous avec cette pompe bon marché.

    L'idéal pour moi aurait été une pompe de surface auto-amorçable de faible puissance (difficile à trouver), avec un flotteur supplémentaire.

    Le trop-plein du réservoir secondaire fonctionne bien également. Comme le tuyau de 50mm est relié à la descente de chéneau, au dessus du collecteur de remplissage, l'eau retourne au réservoir principal, et n'est pas perdue.

    9. Téléchargements :

    Pour télécharger le projet : https://bitbucket.org/henri_bachetti/rainwater-tank.git

    Cette page vous donne toutes les informations nécessaires :

    https://riton-duino.blogspot.com/p/migration-sous-bitbucket.html

    10. Conclusion

    Voilà un petit montage qui va me permettre de ne pas me soucier du remplissage de mon réservoir.


    Cordialement

    Henri

    11. Mises à jour

    30/09/2021 : remplacement du flotteur par un MEDER-LS03-1A66