Surveiller l'état et la charge d'une batterie 12 Volts
Par makoto doushite le vendredi, 27 décembre 2019, 19:53 - Energies Renouvelables - Lien permanent
- Monitorer la vitesse du vent et la rotation de l'éolienne, c'est fait !!
Cependant quid de l'état de la batterie ?
Combien d'énergie la génératrice produit-elle ?
Cela est-il suffisant pour recharger la batterie ?
J'avais acheté un appareil pour mesurer ça en local, mais en définitive je ne m'en sert pas… Pas utile à moins d'aller au fond du jardin pour le consulter…
- J'ai donc entrepris de construire un nouvel appareil de mesure, sur la même idée que l'Anémomètre Radio (Arduino + HC-12), avec transmission des données pour statistiques sur Grafana via InfluxDB (NodeMCU).
Attention cependant, Si on souhaite faire fonctionner l'Anémomètre et le Monitoring en même temps, il faudra prendre soin de différencier les canaux de fonctionnement des modules HC-12 (Voir la section Setup de chaque programmes)
Le monitoring Batterie, partie émetteur :
J'ai donc câblé un nouveau circuit :
- 1 Arduino Pro Mini 5V.
- Un ordi avec port USB et le soft Arduino IDE.
- 1 plaquette HC-12 et son antenne.
- 1 diode 1N4007.
- 1 Régulateur Négatif 9V L7909CV.
- 1 condensateur électrochimique polarisé de 220µF.
- 1 condensateur électrochimique polarisé de 2,2µF.
- 1 condensateur électrochimique polarisé de 1µF.
- 1 module ACS712 (20A) (Attention cependant !! > Je sais que mon générateur ne dépassera pas 5A, à moins d'une tempête de vent de fin du monde…).
Principe de fonctionnement :
- À intervalle régulier, l'Arduino va envoyer les valeurs lues sur les entrées analogiques A0 et A1, sur le port série du HC-12.
- A0 pour mesurer la valeur de la tension aux bornes de la batterie ( On a fabriqué un voltmètre très précis câblé en parallèle ).
- A1 pour mesurer le courant sortant du générateur ( Un Ampèremètre câblé en série ).
- Programme pour l’Arduino Pro Mini de transmission :
Édit du 15 mai 2020 !
Électro-Bidouilleur a enfin sorti une vidéo sur le module HC-12 et nous informe que la librairie <SoftwareSerial.h> est à éviter, ce que j'aurais bien voulu savoir à l'époque étant donné que j'ai effectivement rencontré des problèmes de réception. Il faut donc lui préférer la librairie <AltSoftSerial.h>. Je n'ai pas le temps de me pencher dessus, donc j'ai laissé tel quel le code suivant, sur github et en annexe du billet, à vous de changer la librairie.
#include <SoftwareSerial.h> /****************/ /* DÉCLARATIONS */ /****************/ SoftwareSerial HC12(10, 11); // HC-12 TX Pin, HC-12 RX Pin char MessageTensionBatterie[] = " VOL / "; // message to be sent; '\n' is a forced terminator char char MessageCourant[] = " AMP\n"; // message to be sent; '\n' is a forced terminator char String chaine; #define ATpin 7 // used to switch HC-12 to AT mode float Courant=0; float SupplyVoltage=12; const int Voie_0=0; // declaration constante de broche analogique int mesure_brute=0; // Variable pour acquisition résultat brut de conversion analogique numérique float mesuref=0.0; // Variable pour calcul résultat décimal de conversion analogique numérique float tension=0.0; // Variable tension mesurée float tension_batterie=0.0; // Variable tension batterie calculée float tension_regulateur=8925.0; // Variable tension réelle aux bornes du régulateur -9V (en mV) int PIN_ACS712 = A1; double Voltage = 0; double Current = 0; /*********/ /* SETUP */ /*********/ void setup() { // Serial.begin(9600); // Debug HC12.begin(9600); // Serial port to HC12 // Pin capteurs pinMode(PIN_ACS712, INPUT); pinMode(ATpin, OUTPUT); digitalWrite(ATpin, LOW); // Set HC-12 into AT Command mode delay(500); HC12.print("AT+C056"); // passer sur le canal 006 (433.4Mhz + 56x400KHz) delay(500); digitalWrite(ATpin, HIGH); // HC-12 en normal mode } /*************/ /* PROGRAMME */ /*************/ void loop() { MesureCourant(); MesureBrute(); TensionMesuree(); TensionBatterie(); chaine = String(tension_batterie,3) + MessageTensionBatterie + String(Current,3) + MessageCourant; // construction du message Serial.println ( "chaine String : " +chaine ); HC12.print(chaine); // send radio data delay(100); } /*************/ /* FONCTIONS */ /*************/ void MesureCourant() { // Serial.print("Courant : "); // Serial.print(Courant); // Serial.print(" A | Puissance : "); // Serial.print(Courant*SupplyVoltage); // Serial.println(" Watt"); // Voltage is Sensed 1000 Times for precision for(int i = 0; i < 1000; i++) { // ça ralentis tout, mais c'est indispensable ! Voltage = (Voltage + (0.004882812 * analogRead(PIN_ACS712))); // (5 V / 1024 (Analog) = 0.0049) which converter Measured analog input voltage to 5 V Range delay(1); } Voltage = Voltage /1000; Current = (Voltage -2.5)/ 0.100; // Sensed voltage is converter to current (0.100 pour modèle 20A) Serial.print("\n Voltage Sensed (V) = "); // shows the measured voltage Serial.print(Voltage,3); // the ‘2’ after voltage allows you to display 2 digits after decimal point Serial.print("\t Current (A) = "); // shows the voltage measured Serial.println(Current,3); // the ‘2’ after voltage allows you to display 2 digits after decimal point } void MesureBrute() { //-------- mesure brute -------- mesure_brute=analogRead(Voie_0); // Serial.print ("Valeur brute = "); // Serial.print (mesure_brute); // Serial.println (" "); // espace de propreté } void TensionMesuree() { //---------- tension mesurée --------- mesuref=float(mesure_brute)*5000.0/1024.0; tension=mesuref/1000.0; // en Volts // Serial.print ("Tension = "); // Serial.print(tension,3); // float avec 2 décimales // Serial.println(" V "); // unité et espace de propreté } void TensionBatterie() { //---------- tension batterie --------- tension_batterie=mesuref+tension_regulateur; tension_batterie=tension_batterie/1000.0; // en Volts // lcd.setCursor(0,1) ; // positionne le curseur à l'endroit voulu (colonne, ligne) // lcd.print ("Batt:"); // lcd.print (tension_batterie,2); // float avec 2 décimales // lcd.print ("V "); // unité et espace de propreté Serial.print ("Batterie = "); Serial.print(tension_batterie,3); // float avec 2 décimales Serial.println(" V "); // Serial.println(" "); }
Le boîtier client, partie récepteur avec affichage :
Voici le montage avec un afficheur LCD standard :
Matériel requis :
- 1 Arduino Pro Mini 5V.
- Un ordi avec port USB et le soft Arduino IDE.
- 1 plaquette HC-12 et son antenne.
- 1 diode 1N4007.
- 1 afficheur LCD type HD44780 16 ou 20 x 2.
- 1 NodeMCUv3 LoLin.
- 1 condensateur électrochimique polarisé de 220µF.
- J'ai simulé le générateur avec une alimentation 12V et la batterie par une ampoule :
Principe de fonctionnement :
- L'Arduino reçoit les valeurs transmises par le port série du HC-12 et les affiche instantanément sur l'écran LCD.
- Le NodeMCU reçoit les valeurs transmises par le port série du HC-12 et les transmet à la Base de données InfluxDB.
- Grafana s'occupera de tracer les graphiques à partir des données.
- Programme pour l’Arduino Pro Mini de réception :
Édit du 15 mai 2020 !
Électro-Bidouilleur a enfin sorti une vidéo sur le module HC-12 et nous informe que la librairie <SoftwareSerial.h> est à éviter, ce que j'aurais bien voulu savoir à l'époque étant donné que j'ai effectivement rencontré des problèmes de réception. Il faut donc lui préférer la librairie <AltSoftSerial.h>. Je n'ai pas le temps de me pencher dessus, donc j'ai laissé tel quel le code suivant, sur github et en annexe du billet, à vous de changer la librairie.
//———— Que fait ce programme ? ———— // ---> Récupére les données transmise par un module HC-12, // ---> Affichage la mesure de la tension d'une batterie au plomb 12V sur un écran LCD. // ---> Affichage l'intensité et la puissance fournie à la batterie par une génératrice 12V. //———— Fonctionnalités utilisées ———— // Utilise un afficheur LCD alphanumérique2x20 en mode 4 bits //———— Circuit à réaliser ———— // Broche 12 sur la broche RS du LCD // Broche 11 sur la broche E du LCD // Broche 5 sur la broche D4 du LCD // Broche 4 sur la broche D5 du LCD // Broche 3 sur la broche D6 du LCD // Broche 2 sur la broche D7 du LCD // Broche 7 sur HC-12 to AT mode // Broche 8 HC-12 TX Pin // Broche 9 HC-12 RX Pin //———— Réglages possibles ———— // Canal de réception (voir setup) // Voltage : Utilisé pour le calcul de la puissance en Watt. // MiniC : pour régler le courant minimum mesuré en dessous duquel la valeur est forcée à 0, pour prendre // en compte le fait que la mesure du zero ne tombe jamais pile. #include <LiquidCrystal.h> // lib LCD #include <SoftwareSerial.h> // lib Transmission série //################ //# DÉCLARATIONS # //################ //——— Radio HC-12 ———// SoftwareSerial HC12(8, 9); // HC-12 TX Pin, HC-12 RX Pin #define ATpin 7 // poir passer le HC-12 en AT mode char acquis_data; String chaine; float Voltage=12; float MiniC=0.08; // courant minimum mesuré en dessous duquel la valeur est forcée à 0. float tension_batterie_float; float Courant_float; //——— Écran LCD ———// const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2; // Déclaration LCD LiquidCrystal lcd(rs, en, d4, d5, d6, d7); // Pins LCD en mode 4 bits byte carre00[8] = { // caractères personnalisés B11111, B00000, B00000, B00000, B00000, B00000, B00000, B11111 }; byte carre01[8] = { B11111, B00000, B10000, B10000, B10000, B10000, B00000, B11111 }; byte carre02[8] = { B11111, B00000, B11000, B11000, B11000, B11000, B00000, B11111 }; byte carre03[8] = { B11111, B00000, B11100, B11100, B11100, B11100, B00000, B11111 }; byte carre04[8] = { B11111, B00000, B11110, B11110, B11110, B11110, B00000, B11111 }; byte carre05[8] = { B11111, B00000, B11111, B11111, B11111, B11111, B00000, B11111 }; byte crochetouvrant[8] = { B00011, B00010, B00010, B00010, B00010, B00010, B00010, B00011 }; byte crochetfermant[8] = { B11000, B01000, B01110, B01110, B01110, B01110, B01000, B11000 }; //——— Fonction pour pemettre le map avec variable float ——— float mapfloat(float x, float in_min, float in_max, float out_min, float out_max) { return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } //######### //# SETUP # //######### void setup() { // Serial.begin(9600); // Debug //——— HC12 ———// HC12.begin(9600); // Serial port to HC12 pinMode(ATpin, OUTPUT); digitalWrite(ATpin, LOW); // HC-12 en mode commande AT delay(500); HC12.print("AT+C056"); // passer sur le canal 056 (433.4Mhz + 56x400KHz) delay(500); digitalWrite(ATpin, HIGH); // HC-12 en normal mode //——— LCD ———// lcd.begin(20,2); // Initialise le LCD avec 20 colonnes x 2 lignes delay(10); lcd.print("LCD OK"); // affiche LCD OK delay(2000); lcd.clear(); delay(10); lcd.createChar(0, carre00); // Les 8 caractères personnalisés lcd.createChar(1, carre01); lcd.createChar(2, carre02); lcd.createChar(3, carre03); lcd.createChar(4, carre04); lcd.createChar(5, carre05); lcd.createChar(6, crochetouvrant); lcd.createChar(7, crochetfermant); } //############# //# PROGRAMME # //############# void loop() { Reception(); Jauge(); delay(200); } //############# //# FONCTIONS # //############# void Reception() { // surtout pas de delay dans cette boucle, sinon les data reçues sont erronnées. while (HC12.available()) { // If HC-12 has data acquis_data = HC12.read(); chaine = chaine + acquis_data; // Serial.println (chaine); // Attention, chaine est donc une String /* message reçu de la forme 12.665 VOL / 0.045 AMP pour chaque ligne on fait :*/ if (chaine.endsWith("\n")) { //détection de fin de ligne : méthodes String // Serial.println ("fin de ligne"); // debug // String phrase = "12.665 VOL / 0.045 AMP"; //debug char tension_batterie[5]; // chaine de 6 caractères pour stocker le texte avant le mot VOL char Courant[3]; // chaine de 4 caractères pour stocker le texte avant le mot AMP // sscanf(phrase.c_str(), "%s VOL / %s AMP", tension_batterie, &Courant); //debug sscanf(chaine.c_str(), "%s VOL / %s AMP", tension_batterie, &Courant); // la chaine à parser est dans une String, avec la méthode c_str() tension_batterie_float = atof(tension_batterie),3; // char convertie en Float, avec 3 décimales Courant_float = atof(Courant),2; // char convertie en Float, avec 2 décimales Serial.print("VOLTS: "); Serial.println(tension_batterie_float,3); // float avec 3 décimales Serial.print("AMPERES: "); Serial.println(Courant_float,3); Serial.print("Watt: "); Serial.println(Courant_float*Voltage,0); // float avec 0 décimales Serial.println(' '); // Affichage LCD courant et Puissance if ( Courant_float < MiniC ){ // remise à zero forcée si valeur mesurée très petite Courant_float = 0; } lcd.setCursor(0,0); lcd.print ("Power:"); lcd.print (Courant_float,2); // float avec 2 décimales lcd.print ("A "); // unité et espace lcd.setCursor(12,0); lcd.write(0b01111110); // caractère : fleche, depuis le Standard Character Pattern du LCD lcd.print (" "); lcd.print (Courant_float*Voltage,0); lcd.print ("Watt "); // Affichage LCD Batterie lcd.setCursor(0,1); lcd.print ("Batt:"); lcd.print (tension_batterie_float,3); lcd.print ("V "); chaine = ""; // vide la String chaine } } } void Jauge() { // Jauge de charge batterie lcd.setCursor(12, 1); lcd.write(byte(6)); // crochet ouvrant lcd.setCursor(19, 1); lcd.write(byte(7)); // crochet fermant float NiveauBatterie = mapfloat(tension_batterie_float, 11.4, 12.73, 0, 30); // Discrétise la valeur de la tension batterie int NiveauBatterieBarre = (int)NiveauBatterie; // conversion en entier if (NiveauBatterie > 30) { // en cas de dépassement des limites haute et basse et permettre l'affichage non-erroné NiveauBatterieBarre = 30; lcd.setCursor(13, 1); lcd.print ("Pleine"); delay (1000); } else if (NiveauBatterie < 0) { NiveauBatterieBarre = 0; lcd.setCursor(13, 1); lcd.print ("Erreur"); delay (1000); } // Serial.print ("NiveauBatterieBarre = "); // Serial.print(NiveauBatterieBarre); // Serial.println(" "); switch (NiveauBatterieBarre) { case 0: lcd.setCursor(13, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(14, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(15, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(16, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(17, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(18, 1); lcd.write(byte(0)); // carre00 break; case 1: lcd.setCursor(13, 1); lcd.write(byte(1)); // carre01 lcd.setCursor(14, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(15, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(16, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(17, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(18, 1); lcd.write(byte(0)); // carre00 break; case 2: lcd.setCursor(13, 1); lcd.write(byte(2)); // carre02 lcd.setCursor(14, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(15, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(16, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(17, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(18, 1); lcd.write(byte(0)); // carre00 break; case 3: lcd.setCursor(13, 1); lcd.write(byte(3)); // carre03 lcd.setCursor(14, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(15, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(16, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(17, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(18, 1); lcd.write(byte(0)); // carre00 break; case 4: lcd.setCursor(13, 1); lcd.write(byte(4)); // carre04 lcd.setCursor(14, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(15, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(16, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(17, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(18, 1); lcd.write(byte(0)); // carre00 break; case 5: lcd.setCursor(13, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(14, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(15, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(16, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(17, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(18, 1); lcd.write(byte(0)); // carre00 break; case 6: lcd.setCursor(13, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(14, 1); lcd.write(byte(1)); // carre01 lcd.setCursor(15, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(16, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(17, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(18, 1); lcd.write(byte(0)); // carre00 break; case 7: lcd.setCursor(13, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(14, 1); lcd.write(byte(2)); // carre02 lcd.setCursor(15, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(16, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(17, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(18, 1); lcd.write(byte(0)); // carre00 break; case 8: lcd.setCursor(13, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(14, 1); lcd.write(byte(3)); // carre03 lcd.setCursor(15, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(16, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(17, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(18, 1); lcd.write(byte(0)); // carre00 break; case 9: lcd.setCursor(13, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(14, 1); lcd.write(byte(4)); // carre04 lcd.setCursor(15, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(16, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(17, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(18, 1); lcd.write(byte(0)); // carre00 break; case 10: lcd.setCursor(13, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(14, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(15, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(16, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(17, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(18, 1); lcd.write(byte(0)); // carre00 break; case 11: lcd.setCursor(13, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(14, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(15, 1); lcd.write(byte(1)); // carre01 lcd.setCursor(16, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(17, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(18, 1); lcd.write(byte(0)); // carre00 break; case 12: lcd.setCursor(13, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(14, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(15, 1); lcd.write(byte(2)); // carre02 lcd.setCursor(16, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(17, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(18, 1); lcd.write(byte(0)); // carre00 break; case 13: lcd.setCursor(13, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(14, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(15, 1); lcd.write(byte(3)); // carre03 lcd.setCursor(16, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(17, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(18, 1); lcd.write(byte(0)); // carre00 break; case 14: lcd.setCursor(13, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(14, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(15, 1); lcd.write(byte(4)); // carre04 lcd.setCursor(16, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(17, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(18, 1); lcd.write(byte(0)); // carre00 break; case 15: lcd.setCursor(13, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(14, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(15, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(16, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(17, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(18, 1); lcd.write(byte(0)); // carre00 break; case 16: lcd.setCursor(13, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(14, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(15, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(16, 1); lcd.write(byte(1)); // carre01 lcd.setCursor(17, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(18, 1); lcd.write(byte(0)); // carre00 break; case 17: lcd.setCursor(13, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(14, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(15, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(16, 1); lcd.write(byte(2)); // carre02 lcd.setCursor(17, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(18, 1); lcd.write(byte(0)); // carre00 break; case 18: lcd.setCursor(13, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(14, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(15, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(16, 1); lcd.write(byte(3)); // carre03 lcd.setCursor(17, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(18, 1); lcd.write(byte(0)); // carre00 break; case 19: lcd.setCursor(13, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(14, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(15, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(16, 1); lcd.write(byte(4)); // carre04 lcd.setCursor(17, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(18, 1); lcd.write(byte(0)); // carre00 break; case 20: lcd.setCursor(13, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(14, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(15, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(16, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(17, 1); lcd.write(byte(0)); // carre00 lcd.setCursor(18, 1); lcd.write(byte(0)); // carre00 break; case 21: lcd.setCursor(13, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(14, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(15, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(16, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(17, 1); lcd.write(byte(1)); // carre01 lcd.setCursor(18, 1); lcd.write(byte(0)); // carre00 break; case 22: lcd.setCursor(13, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(14, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(15, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(16, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(17, 1); lcd.write(byte(2)); // carre02 lcd.setCursor(18, 1); lcd.write(byte(0)); // carre00 break; case 23: lcd.setCursor(13, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(14, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(15, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(16, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(17, 1); lcd.write(byte(3)); // carre03 lcd.setCursor(18, 1); lcd.write(byte(0)); // carre00 break; case 24: lcd.setCursor(13, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(14, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(15, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(16, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(17, 1); lcd.write(byte(4)); // carre04 lcd.setCursor(18, 1); lcd.write(byte(0)); // carre00 break; case 25: lcd.setCursor(13, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(14, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(15, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(16, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(17, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(18, 1); lcd.write(byte(0)); // carre00 break; case 26: lcd.setCursor(13, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(14, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(15, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(16, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(17, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(18, 1); lcd.write(byte(1)); // carre01 break; case 27: lcd.setCursor(13, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(14, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(15, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(16, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(17, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(18, 1); lcd.write(byte(2)); // carre02 break; case 28: lcd.setCursor(13, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(14, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(15, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(16, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(17, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(18, 1); lcd.write(byte(3)); // carre03 break; case 29: lcd.setCursor(13, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(14, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(15, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(16, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(17, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(18, 1); lcd.write(byte(4)); // carre04 break; case 30: lcd.setCursor(13, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(14, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(15, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(16, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(17, 1); lcd.write(byte(5)); // carre05 lcd.setCursor(18, 1); lcd.write(byte(5)); // carre05 break; default: // statements break; } }
- Programme pour le NodeMCU de réception :
Édit du 15 mai 2020 !
Électro-Bidouilleur a enfin sorti une vidéo sur le module HC-12 et nous informe que la librairie <SoftwareSerial.h> est à éviter, ce que j'aurais bien voulu savoir à l'époque étant donné que j'ai effectivement rencontré des problèmes de réception. Il faut donc lui préférer la librairie <AltSoftSerial.h>. Je n'ai pas le temps de me pencher dessus, donc j'ai laissé tel quel le code suivant, sur github et en annexe du billet, à vous de changer la librairie.
// Ressources : //https://forum.arduino.cc/index.php?topic=553743.0 //https://plaisirarduino.fr/moniteur-serie/ #include "ESPinfluxdb.h" // lib ESP InfluxDB #include <SoftwareSerial.h> // lib Transmission série #include <ESP8266WiFi.h> // lib ESP Wifi #include <ESP8266WiFiMulti.h> //################ //# DÉCLARATIONS # //################ //——— Radio HC-12 ———// SoftwareSerial HC12(D5, D6); // HC-12 TX Pin et RX Pin char acquis_data; String chaine; float Voltage=12; float MiniC=0.08; // courant minimum mesuré en dessous duquel la valeur est forcée à 0. float tension_batterie_float; float Courant_float; //——— InfluxDB ———// const char *INFLUXDB_HOST = "xx.org"; const uint16_t INFLUXDB_PORT = xx; const char *DATABASE = "xx"; const char *DB_USER = "xx"; const char *DB_PASSWORD = "xxxx"; Influxdb influxdb(INFLUXDB_HOST, INFLUXDB_PORT); //https://github.com/projetsdiy/grafana-dashboard-influxdb-exp8266-ina219-solar-panel-monitoring/tree/master/solar_panel_monitoring int compteur = 1; //——— WiFi ———// char ssid[] = "xx"; char password[] = "xxxxx"; ESP8266WiFiMulti WiFiMulti; //######### //# SETUP # //######### void setup() { Serial.begin(9600); // Debug //——— HC12 ———// HC12.begin(9600); // Serial port to HC12 //——— WiFi ———// WiFiMulti.addAP(ssid, password); // Connection au réseau WiFi while (WiFiMulti.run() != WL_CONNECTED) { delay(100); } Serial.println ("WiFi Connecté"); //——— InfluxDB ———// while (influxdb.opendb(DATABASE, DB_USER, DB_PASSWORD)!=DB_SUCCESS) { // Connexion à la base InfluxDB delay(10000); } Serial.println ("Connexion DataBase OK"); } //############# //# PROGRAMME # //############# void loop() { Reception(); if (compteur > 100) { // envoie la data toutes les 6 secondes (delay 10 x 100 = 1000 ms, et l'opération prend 5 secondes à transférer/écrire en base) SendDataToInfluxdbServer(); compteur = 1; } else { compteur++; } delay(10); // refresh la data sur l'afficheur toutes les 10 ms } //############# //# FONCTIONS # //############# void Reception() { // surtout pas de delay dans cette boucle, sinon les data reçues sont erronnées. while (HC12.available()) { // If HC-12 has data acquis_data = HC12.read(); chaine = chaine + acquis_data; // Serial.println (chaine); // Attention, chaine est donc une String /* message reçu de la forme 12.665 VOL / 0.045 AMP pour chaque ligne on fait :*/ if (chaine.endsWith("\n")) { //détection de fin de ligne : méthodes String // Serial.println ("fin de ligne"); // debug // String phrase = "12.665 VOL / 0.045 AMP"; //debug char tension_batterie[5]; // chaine de 6 caractères pour stocker le texte avant le mot VOL char Courant[3]; // chaine de 4 caractères pour stocker le texte avant le mot AMP // sscanf(phrase.c_str(), "%s VOL / %s AMP", tension_batterie, &Courant); //debug sscanf(chaine.c_str(), "%s VOL / %s AMP", tension_batterie, &Courant); // la chaine à parser est dans une String, avec la méthode c_str() tension_batterie_float = atof(tension_batterie),3; // char convertie en Float, avec 3 décimales Courant_float = atof(Courant),2; // char convertie en Float, avec 2 décimales Serial.print("VOLTS: "); Serial.println(tension_batterie_float,3); // float avec 3 décimales Serial.print("AMPERES: "); Serial.println(Courant_float,3); Serial.print("Watt: "); Serial.println(Courant_float*Voltage,0); // float avec 0 décimales Serial.println(' '); // Affichage LCD courant et Puissance if ( Courant_float < MiniC ){ // remise à zero forcée si valeur mesurée très petite Courant_float = 0; } chaine = ""; // vide la String chaine } } } void SendDataToInfluxdbServer() { //Writing data with influxdb HTTP API: https://docs.influxdata.com/influxdb/v1.5/guides/writing_data/ //Querying Data: https://docs.influxdata.com/influxdb/v1.5/query_language/ dbMeasurement rowDATA("Data"); rowDATA.addField("tension_batterie", tension_batterie_float); rowDATA.addField("Courant", Courant_float); // Serial.println(influxdb.write(rowDATA) == DB_SUCCESS ? " - rowDATA write success" : " - Writing failed"); influxdb.write(rowDATA); // Vide les données - Empty field object. rowDATA.empty(); }
Configuration de la Grafana :
- Concernant cette partie, je vous laisse voir la fin de ce billet, vu que c'est la même chose !
Vous pourriez obtenir ce genre de chose :
Le code est aussi documenté ici, et en annexe du billet.
Boîtiers 3D dispos sur Thingiverse
Ressources :
https://wiki.mchobby.be/index.php?title=SENSEUR-COURANT-ACS712
https://www.sparkfun.com/datasheets/BreakoutBoards/0712.pdf
http://www.mon-club-elec.fr/pmwiki_mon_club_elec/pmwiki.php?n=MAIN.ProjetBatterieMesureTension