Lors des discussions et réflexions au sein de l'ADRASEC73, face aux risques de délestages électriques de l'hiver 2022, est revenu sur la table la surveillance de nos transpondeurs. En effet, pour le moment nous découvrons que nous les avons exploités via l'alimentation secourue que quand ils ne répondaient plus suite à la décharge des batteries.
=== J'ai donc réfléchi à un moyen de supervision permettant de nous remonter l'état de l'alimentation des équipements en cas de passage en mode secouru afin d'adapter notre usage dans un but d'économie d'énergie et donc de longévité de nos capacités de transmission.
Voici, en vrac, les éléments que j'ai inscrit au cahier des charges afin de guider le développement du projet :
1. sur site
J'ai retenu le choix d'utiliser une platine TTGO comprenant un ESP32 pour la partie logique, un module LoRa pour la partie transmission et des GPIO pour l’interfaçage et le relevé d'informations. De plus, le protocole LoRa est robuste et peu consommateur d'énergie.
Il est complété d'un relai permettant le changement d'état d'un GPIO à la perte de l'alimentation secteur, d'un pont diviseur de tension afin de pouvoir mesurer la tension des batteries de manière convenable pour l'ESP32, d'un cordon pour piloter les Motorola GM340 des transpondeurs sur un canal avec un bip de fin de transmission (indiquant le passage sur batteries) et d'un abaisseur de tension afin de fournir les 5V nécessaire à l'ESP32.
Voici l'ensemble sur la platine de développement :

Et le montage final avec la mise en boitier :
Je n'ai aucune connaissance en C++, mais en me documentant un peu j'ai quand même décidé de me lancer. Mon code est certainement très améliorable, je je serais ravi d'avoir des retours afin de l'améliorer.
J'ai donc développé l'ensemble du code sur Visual Studio et PlatformIO.
1. sur site
Voici les différentes briques de mon code. Je commence par déclarer les différentes librairies nécessaires ainsi que les variables globales :
#include <Wire.h>
#include <SSD1306Wire.h>
#include <SPI.h>
#include <LoRa.h>
SSD1306Wire display(0x3c, SDA, SCL); //Déclaration des pins de l'écran
const int pinRelais230 = 15; // GPIO relais présence 230V à surveiller
const int pinTension = 36; //GPIO Voltmetre
const int pinCmdTX = 25; //GPIO cmd TX
bool Secteur = true;
const long frequency = 430600000; //Fréquence LoRa : 430.600MHz
#define SCK 5 // GPIO5 -- SX1278's SCK
#define MISO 19 // GPIO19 -- SX1278's MISnO
#define MOSI 27 // GPIO27 -- SX1278's MOSI
#define SS 18 // GPIO18 -- SX1278's CS
#define RST 13.8 // GPIO13.8 -- SX1278's RESET
#define DI0 26 // GPIO26 -- SX1278's IRQ(Interrupt Request)
float TensionBatRead = 0;
float LastTension = 0;
Ensuite, je vais créer quelques fonctions que je pourrai appeler dans la suite du code. La première gère l'affichage sur le LCD en prenant en entré les différentes valeurs mesurées :
void Affichage(String ValSecteur, String ValTension) {
display.clear();
display.setFont(ArialMT_Plain_16);
display.drawString(0, 0, "Cime Caron");
display.setFont(ArialMT_Plain_10);
display.drawString(0, 30, ValSecteur);
display.drawString(0, 50, "Tension bat :" + ValTension + "V");
display.display();
}
La suivante gère l'envoi des trames LoRa :
void LoRa_sendMessage(String message) {
LoRa.beginPacket(); // Début du paquet
LoRa.print(message); // ajout des infos dans la trame
LoRa.endPacket(); //fin du paquet et envoi
}
Cette fonction mesure la tension en entrée sur la pin choisie de l'ESP32 qui ne peut varier que de 0 à 3,3V (d'où la présence du pont diviseur de tension). Je la convertie donc logiciellement pour la ramener à la vraie valeur de la batterie (aux environs de 12V). de plus, pour éviter des fausses valeurs, je fais une série de 10 mesures pour ne retourner que la plus élevée car, sur la série, j'obtiens plusieurs fois 0V.
float TensionBat() {
float TensionBat = 0;
float TableauMesures[] = {0};
for (size_t i = 0; i < 10; i++)
{
TensionBatRead = analogRead(pinTension) * (13.8 / 4095);
TableauMesures[i] = {TensionBatRead};
if (TableauMesures[i]>TensionBat)
{
TensionBat = TableauMesures[i];
}
delay(100);
}
return(TensionBat);
}
Vient ensuite, la fonction d'initialisation (setup). Celle-ci s’exécute une seule fois au démarrage de l'ESP32. Les commentaires sont normalement assez explicites :
void setup() {
Serial.begin(115200); //J'initialise la vitesse du port console qui me permet de suivre le déroulement de l’exécution du code
digitalWrite(pinCmdTX, LOW); //Initialisation à OFF du GPIO de changement de canal
//Pin relais présence 230V
pinMode(pinRelais230, INPUT_PULLUP); //mode INPUT_PULLUP
pinMode(pinCmdTX, OUTPUT); //mode OUTPUT
//voltmetre
pinMode(pinTension,INPUT_PULLUP); //mode INPUT_PULLUP
//Ecran de démarrage
Serial.println("Démarrage");
display.init();
display.clear();
display.flipScreenVertically();
display.setTextAlignment(TEXT_ALIGN_LEFT);
display.setFont(ArialMT_Plain_16);
display.drawString(0, 0, "Démarrage suveillance secteur");
display.drawString(0, 35, "Cime Caron");
display.display();
delay(3000);
//Module Lora - démarrage
SPI.begin(SCK,MISO,MOSI,SS);
LoRa.setPins(SS,RST,DI0);
if (!LoRa.begin(frequency)) {
Serial.println("Starting LoRa failed!");
while (1);
}
LoRa.setTxPower(20);
LoRa.receive(); //par défaut, en réception
}
La fonction loop va être exécutée en boucle, c'est donc ici que va se trouver le "coeur" de la supervision.
void loop() {
Serial.begin(115200);
int Presence230; // Déclaration de la variable du dernier état enregistré
int CompteurTemps; //Déclaration de la variable pour stoker le temps passé
String TxtPresenceSecteur; // Déclaration de la variable contenant le texte
//Vérification de la présence de 230V et actions en fonction de l'état retourné.
Presence230 = digitalRead(pinRelais230);
if (Presence230 == 0) { // Si on perd l'alim 230V
CompteurTemps = 0; //Initialisation du compteur de temps depuis la perte du 230V
Secteur = false;
digitalWrite(pinCmdTX, HIGH); //On passe sur le canal secours sur le TX
while (Presence230 == 0) //tant qu'on est sur batterie
{
String CTps_string = String(CompteurTemps);
TxtPresenceSecteur = "Sur Batterie : " + CTps_string + " min.";
Serial.println(TxtPresenceSecteur); //affichage sur la console
Affichage(TxtPresenceSecteur, String(TensionBat())); //affichage sur l'écran LCD
//Transmission LoRa
String MessageLora = "Caron\n" + TxtPresenceSecteur + "\nTension Batterie : " + String(TensionBat()) + "V.";
if ((CompteurTemps % 15) == 0) //si on est 1/4h plus tard
{
LoRa_sendMessage(MessageLora); //nouvelle transmission LoRa
}
Serial.println("TX Lora : " + MessageLora);
CompteurTemps++; //on incrémente le compteur de temps
delay(60000); //on attend 1 min
Presence230 = digitalRead(pinRelais230); //on lit a nouveau l'état de l'alimentation
}
}
else { //Si on retrouve l'alim 230V
digitalWrite(pinCmdTX, LOW); //On passe sur le canal normal sur le TX
TxtPresenceSecteur = "Sur Secteur 230V";
Serial.println(TxtPresenceSecteur);
Affichage(TxtPresenceSecteur, String(TensionBat()));
//on regarde le dernier état de la présence secteur (Secteur). Si on était sur batterie, on envoie
//un message Lora pour dire que le courant est revenu et d'autres 15, 30, 45 et 60min plus tard. Sinon rien.
if (Secteur == false)
{
for (size_t i = 0; i < 4; i++)
{
if (Presence230 == 1)
{
/* envoie message retour secteur + tension baterie toutes les 15min pendant 1h */
String MessageLora = "Caron\n" + TxtPresenceSecteur + "\nTension Batterie : " + TensionBat() + "V.";
LoRa_sendMessage(MessageLora);
Serial.println("TX Lora : " + MessageLora);
Affichage(TxtPresenceSecteur, String(TensionBat()));
for (size_t i = 0; i < 4; i++) // et on vérifie qu'on n'a pas une coupure entre 2 messages Lora
{
Presence230 = digitalRead(pinRelais230);
Affichage(TxtPresenceSecteur, String(TensionBat()));
if (Presence230 == 0) //Si coupure on sort de la boucle
{
break;
}
delay(60000); //sinon on attend 1min
}
if (Presence230 == 0) //si on est sur secteur, on sort de la boucle
{
break;
}
}
else {
//on sort de la boucle for
break;
}
}
Secteur = true;
Affichage(TxtPresenceSecteur, String(TensionBat()));
}
delay(60000); //on attend 1 min
}
//Code digipeater pour les autres supervisions de relais
//Si on reçoit une trame d'un autre transpondeur, on la ré-émet afin de maximiser les chances de réception par la gateway
//et on ajoute le chemin de la trame
int packetSize = LoRa.parsePacket();
if (packetSize) {
while (LoRa.available()) {
String messageRX = "via C Caron>> " + LoRa.readString(); //ajout du site qui à répété
Serial.println(messageRX);
display.setTextAlignment(TEXT_ALIGN_RIGHT);
display.drawString(0, 0, "RX>>TX"); //affichage sur l'écran LCD d'une ré-émission
display.display();
delay(1000);
LoRa_sendMessage(messageRX);
}
}
}
à venir après la mise en coffret
Elle fera l'objet du prochain article : Réception de la surveillance de relais