Mesure de tension déclenchant un relais

Dans le cadre de notre partenariat avec RTE (Réseau de Transport d’Electricité), nous avons réalisé un prototype permettant de relevé la tension aux bornes d’un lot de batterie.

L’objectif est de remettre en route un chargeur de batterie lorsque la tension aux bornes du lot de batterie tombe sous un certain niveau. De plus le relevé devra être enregistré dans un fichier CSV et visible sur un graphique en accédant à une page web contenu dans le micro-contrôleur.

Dans ce projet notre besoin matériel sera :

 

Notre besoin logiciel sera:

  • Un serveur web nous permettant de générer la page web.
  • Un serveur FTP nous permettant d’accéder au stockage des fichiers.
  • Un gestionnaire de fichier pour enregistrer les fichiers.

Le code:

// Vincent Pleux
// La Forge
// 29/06/2018

#include "ESP8266WiFi.h"
#include "ESP8266WebServer.h"
#include "ESP8266FtpServer.h"
#include "FS.h"

int analogInput = A0;                     //PIN qui mesurera la tension transformé vout
float R1 = 330000.0;                      // !! resistance de R1!!
float R2 = 6600.0;                        // !! resistance de R2 !!
float vout = 0.0;                         //Tension transformé par le pont diviseur de 0 à 0.98V
float vin = 0.0;                          //Tension que l'on souhaite mesuré de 0 à 50V

int CompteurAppel = 0;                    //Permettra d'incrémenter le nombre de mesure
long TempsPrecedent = 0;                  //Permettra de stocker la dernière valeur de temps
long Intervalle = 60000;                  //Permettra de donner l'intervalle de temps entre chaque mesure.  !! Attention à ne pas faire de relevé trop peu espacé. Sinon le système de fichier "basique" ne pourra pas suivre. CF Spécificatio SPIFFS
String BoardName = "Board1";              //Nous par défaut du point d'accès réseau qui servira également à nommer les fichiers
String FileName = "";

//Configuration du point d'accès.
IPAddress local_IP(192,168,4,22);
IPAddress gateway(0,0,0,0);
IPAddress subnet(255,255,255,0);
const char* ssid = BoardName.c_str();     //Le SSID par défaut sera le nom du micro-controleur
const char* password = BoardName.c_str(); //Le mot de passe par défaut sera le nom du micro-controleur
ESP8266WebServer httpSrv(80);             //On instancie un serveur ecoutant sur le port 80
FtpServer ftpSrv;                         //On instancie le serveur FTP

//Vérification si le fichier existe déja
String NewFile(String Name){
  Serial.println("Enter NewFile");
  int IncName = 0;
  bool Flag = true;
  String NewName = "";
  
  while(Flag){
    IncName++;
    String TestName = "/" + Name + "-" + IncName + ".csv";
    Serial.println(TestName);
    //File f = SPIFFS.exists(TestName);
    if(!SPIFFS.exists(TestName)){
        File f = SPIFFS.open(TestName, "a"); //Crée le fichier
        f.close();
      NewName = TestName;
      Flag = false;
      IncName=0;
    }
  }
  return NewName;
}

//Démarre le système de fichier dans la partie Flash du micro-controleur
bool StartFileSystem(void){
  bool Flag = false;
  if (SPIFFS.begin()) {
      FileName = NewFile(BoardName);
      Serial.println(FileName);
      File f = SPIFFS.open(FileName, "w");
      if (!f) {
        SPIFFS.format();
        Serial.println("Spiffs formatted");
        f.close();
        Serial.println("SPIFFS Active");
      }
      f.close();
      Flag = true;
  } else {
      Serial.println("Unable to activate SPIFFS");
      Flag = false;
  }
  //SPIFFS.remove(DATAFILE);              //A décommenter si l'on veut supprimer tous les fichiers au démarrage
  return Flag;
}

//Liste les fichiers contenu à la racine du système de fichier
void ListFileAll(void){
  Dir dir = SPIFFS.openDir("/");
  while (dir.next()) {
    Serial.print(dir.fileName());
    File f = dir.openFile("r");
    Serial.println(f.size());
  }
}

//Lit tout le contenu du fichier
void ReadFileAll(void){
  String Data;
  File f = SPIFFS.open(FileName, "r");
  if (f && f.size()) {
    while (f.available()){
      Data += char(f.read());
    }     
  }
  Serial.println(Data);
  f.close();
}

//Lit le contenu d'un fichier ligne par ligne
void ReadFileLineByLine(void){
  String Line;
  File f = SPIFFS.open(FileName, "r");

  while (f.available()) {
    Line = f.readStringUntil('\n');
    Serial.println(Line);
    delay(1000);
  }

  f.close();
}

//Pour mesurer de 0 à 50 Volts nous utiliserons un pont de résistance avec R1 = 330000 Ohms et R2 = 16600 Ohms
//Ce qui nous permettra de mesurer un voltage allant de 0 à 0.98 Volts, ainsi pas de risque de bruler le feather Huzzah dont la seule entrée analogique mesure de 0 à 1V
float volt(void){
  //Calcul du pont diviseur de tension http://www.calculatoredge.com/electronics/voltage%20divide.htm
  //Calcul des couleurs de résistance https://www.digikey.fr/fr/resources/conversion-calculators/conversion-calculator-resistor-color-code-4-band
  float value = analogRead(analogInput);
  Serial.println("Value: " + String(value));
  vout = (value * 0.98) / 1024.0;
  vin = vout / (R2/(R1+R2));  
  if (vin < 0.1) {   //Suppression des relevé parasite
    vin=0.0;
  }
  
  Serial.println("Volt out: " + String(vout));
  Serial.println("Volt in: " + String(vin));
  return vin;
}

//Permet d'analyser le fichier CSV pour extraire les valeurs séparer par des virgules
String getValue(String data, char separator, int index)
{
    int found = 0;
    int strIndex[] = { 0, -1 };
    int maxIndex = data.length() - 1;

    for (int i = 0; i <= maxIndex && found <= index; i++) {
        if (data.charAt(i) == separator || i == maxIndex) {
            found++;
            strIndex[0] = strIndex[1] + 1;
            strIndex[1] = (i == maxIndex) ? i+1 : i;
        }
    }
    return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
}

//Ajout d'une ligne dans le fichier CSV contenant le numéro de ligne + la mesure de la tension
void savedata(void){
  CompteurAppel ++;
  float mesure=volt();
  File f = SPIFFS.open(FileName, "a"); //append
  f.println(String(CompteurAppel) + "," + String(mesure));
  ActionRelay(mesure);
  f.close();
}

//Active ou désactive le relais selon la mesure du voltage
void ActionRelay(float seuil){
  //Seuil utile à définir
  if (seuil > 45.0){      //Si le voltage de la batterie est au dessus de 45 Volts alors on laisse le relais éteint et l'on ne fait rien
    digitalWrite(14, LOW);
    Serial.println("Relais état bas, ne laisse pas passer le courant");
  }
  
  if (seuil < 30.0) {   //Si le voltage de la batterie passe en dessous de 30 Volts alors on active le relais et on recharge la batterie
      digitalWrite(14, HIGH);
      Serial.println("Relais état haut, laisse passer le courant"); 
  }
}

//Page web principale 
void handleRoot() {
  String message;
  String Line;
  
  
  //Construction du message
  message += "<!DOCTYPE html><html><head>";
  message += "<meta charset=\"UTF-8\">\n";
  message += "<title>Etat Batterie "+ BoardName + "</title>";
  
  message += "<style>";
  message += ".row{ display: flex; flex-direction: row; flex-wrap: wrap; align-items: center; border: 1px solid black; box-shadow: 0 0 5px rgba(0,0,0.5); position: relative;}\n";
  message += ".row::before {content: \"↑ Volts\"; position: absolute; margin-left: 5px; margin-top: 5px;}\n";
  message += ".row::after {content: \"Temps →\"; position: absolute; /* margin-right: 5px; */ /* margin-top: 5px; */ right: 5px; bottom: 5px;}\n";
  message += ".column {display: flex; flex-direction: column; align-items: center; justify-content: flex-end; color: white; width: 10px; transform: rotate(180deg);}\n";
  message += ".column:hover{color:#ec008c; background: rgba(0,0,0,0.05); cursor: pointer;}\n";
  message += ".column:nth-child(1) {margin-left: 20px; }\n";
  message += "p.value{transform: rotate(180deg); /* position: absolute; */ opacity: 0; background: #fff; color: #ec008c; box-shadow: 0 0 5px rgba(0,0,0,0.2); padding: 5px; top: 20px; position: absolute;}\n";
  message += ".column:hover .value {opacity: 1;}\n";
  message += ".column:hover .point{fill: #ec008c;}\n";
  message += "</style>";
  
  message += "</head><body><center>";
  message += "<h1> " + BoardName + "</h1>";
  message += "<p> Fichier: " + FileName + "</p><p>Nombre d'enregistrements: " + CompteurAppel +"</p></br></center>";
  message += "<div class='row'>";


  //Pour lire le fichier ligne par ligne, on parse chaque ligne pour en extraire le contenu
  File f = SPIFFS.open(FileName, "r");
  while (f.available()) {
    Line = f.readStringUntil('\n');
    Serial.println(Line);
    //parsing
    message += "<div class='column'> <p class='value'>"+ getValue(Line,',',1) + "</p><svg width=\"10\" height=\"500\" ><circle class='point' cx=\"5\" cy=\"" + getValue(Line,',',1) + "\" r=\"5\" fill=\"blue\"/></svg></div>\n";
  
  }
  f.close();

  message += "</div><script>function refresh(){document.location.reload();}setTimeout(refresh,10000);</script></body></html>";
  Serial.println(message);
  httpSrv.send(200, "text/html", message);
}


void setup(void){
  //Démarrage
  Serial.begin(115200);
  pinMode(14, OUTPUT);                  //Pin du relais que l'on commande
  digitalWrite(14, LOW);                //Par défaut on éteint le relais
  pinMode(analogInput, INPUT);
  StartFileSystem();

  //Démarrage serveur HTTP
  httpSrv.begin();
  Serial.print("Setting soft-AP configuration : ");
//  Serial.println("IP: " + String(local_IP));
//  Serial.println("Passerelle: " + gateway);
//  Serial.println("Sous-réseau: " + subnet);
  Serial.println(WiFi.softAPConfig(local_IP, gateway, subnet) ? "Ready" : "Failed!");

  Serial.println("Setting soft-AP : ");
  Serial.println("Login: " + BoardName);
  Serial.println("Password: " + BoardName);
  WiFi.softAP("Board1","Board1");
  //Serial.println(WiFi.softAP(BoardName.c_str(),BoardName.c_str()) ? "Ready" : "Failed!");
  //Serial.println(WiFi.softAP("Board1","Board1") ? "Ready" : "Failed!");

  Serial.print("Soft-AP IP address = ");
  Serial.println(WiFi.softAPIP());
  Serial.printf("MAC address = %s\n", WiFi.softAPmacAddress().c_str());

  Serial.println("HTTP server started");
  
  //Configuration des routes http
  httpSrv.on("/",         handleRoot);
  //httpsrv.on("/bonjour",  [](){httpsrv.send(200, "text/plain", "helloooo!");});

  //Démarrage FTP
  ftpSrv.begin(BoardName,BoardName);
}

void loop(void){
  ftpSrv.handleFTP();           //En attente de connexion FTP
  httpSrv.handleClient();       //En attente de connexion HTTP

  //Déclenchement de la sauvegarde de données à intervalle régulier
  unsigned long TempsCourant = millis();
  if(TempsCourant - TempsPrecedent > Intervalle) {
    TempsPrecedent = TempsCourant;
    savedata();  
  }
}
ESP8266BatteryRTE

 

Le montage:

Une fois le microcontrôleur en route l’on peut:

  • Se connecter au réseau WiFi « Board1 »
  • Login/Password : Board1/Board1
  • Se connecter au FTP en mode normal sans TLS

  • Voir les fichiers via le FTP

  • Se connecter à la page Web en 192.168.4.22 depuis le réseau « Board1 »

La page web s’adaptera à un format tablette ou téléphone.

Ce que l’on ne vois pas ici est que lorsque l’on passe la souris au dessus des points de la courbe ceux-ci indique leur valeur.

(Ici le test est réalisé avec un pile de 1,5V, la valeur afficher est correct.)

 

Voilà.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *