Hi,
I started a new project and just started learning Arduino a few days ago.
The code I wrote seem to work smoothly, though I wonder whether it is fail-proof enough, because the microcontrollers are intended to be left unsupervised.
Here is the context:
We have several buildings that may get a power outage at any time in the common sections of a any of the buildings.
So the idea is to put a client microcontroller in each building, and a gateway microcontroller in a private apartment (private apartments are less prone to power outages).
The gateway pings each client sequencially via LoRa, the client answers the gateway via LoRa again, and then the gateway sends an HTTP POST request to a server via WiFi, and the server timestamps the request.
If a building has a power outage, the client does not answer, the HTTP POST request is skipped, and therefore we know that a building has a power outage.
I would like to get opinions about whether the code is fail-proof enough or not in that context, and also if it can be improved in general.
My background is that I started to learn Python since December, so I kind of have a beginner-intermediate level.
I am using TTGO ESP32-Paxcounter LoRa32 V2.1 1.6 Version 868 MHz LoRa ESP-32 OLED 0,96" MCUs.
They are plugged to power sockets via a USB cable and a power adaptor.
Here is the code for the gateway:
//Bibliothèques pour LoRa
#include <SPI.h>
#include <LoRa.h>
//Bibliothèques pour l'écran OLED
#include <Wire.h>
#include "SSD1306.h"
//Bibliothèques pour la requête HTTP
#include <WiFi.h>
#include <HTTPClient.h>
//Définition des pins utilisés par l'émetteur/receveur LoRa
#define SCK 5 // GPIO5 -- SCK
#define MISO 19 // GPIO19 -- MISO
#define MOSI 27 // GPIO27 -- MOSI
#define SS 18 // GPIO18 -- CS
#define RST 23 // GPIO14 -- RESET (If Lora does not work, replace it with GPIO14)
#define DI0 26 // GPIO26 -- IRQ(Interrupt Request)
#define BAND redacted // 868E6
// Décision 2006/771/CE modifiée
// Par coefficient d’utilisation, on entend le rapport de temps, sur une heure,
// durant lequel un dispositif particulier émet effectivement.
// Les conditions moins restrictives au sens de l’article 3, paragraphe 3 signifient que
// les États membres peuvent autoriser une valeur supérieure pour le coefficient d’utilisation.
SSD1306 display(0x3c, 21, 22);
String rssi = "RSSI --";
String packSize = "--";
String packet ;
const int csPin = 18; // LoRa radio chip select
const int resetPin = 23; // LoRa radio reset
const int irqPin = 26; // change for your board; must be a hardware interrupt pin
long unsigned lastSendTime = 0;
#define WIFI_NETWORK_1 "redacted"
#define WIFI_PASSWORD_1 "redacted"
#define WIFI_NETWORK_2 "redacted"
#define WIFI_PASSWORD_2 "redacted"
const char* host = "https://redacted.redacted"; // hostname of web server:
const char* path = "/redacted/";
const char* liveToken = "redacted";
#define WIFI_TIMEOUT_MS 20000
byte localAddress = 0xREDACTED0;
String destinationName[] = {"a", "b", "c", "d", "e", "f", "sg"} ;
byte destinationAddress[] = {0xREDACTED1, 0xREDACTED2, 0xREDACTED3, 0xREDACTED4, 0xREDACTED5, 0xREDACTED6, 0xREDACTED7};
int destinationAddressLength;
int isInDestinationAddress;
int destinationAddressCheck;
int interval = 5000;
String aucunMessage = "0";
int i = 0;
byte incomingSize;
byte outgoingSize;
void sendMessage(String outgoing, byte destinationAddress) {
LoRa.beginPacket();
LoRa.write(destinationAddress);
LoRa.write(localAddress);
LoRa.write(outgoing.length());
LoRa.print(outgoing);
LoRa.endPacket();
display.clear();
display.drawString(0, 0, "redacted_email");
display.drawString(0, 10, " .redacted@redacted.com");
display.drawString(0, 30, "Envoi au bâtiment : " + String(outgoing));
display.display();
outgoingSize = sizeof(outgoing);
Serial.print("Message envoyé : " + String(outgoing));
Serial.print(" depuis 0x" + String(localAddress, HEX));
Serial.println(" à 0x" + String(destinationAddress, HEX));
Serial.println("Longueur du message envoyé : " + String(outgoingSize) + " bytes");
}
void receiveMessage(int packetSize, String destName) {
if (packetSize == 0) return;
int recipient = LoRa.read();
byte sender = LoRa.read();
byte incomingLength = LoRa.read();
String incoming = "";
while (LoRa.available()) {
incoming += (char)LoRa.read();
}
if (incomingLength != incoming.length()) {
Serial.println("Erreur : La longueur du message ne correspond pas");
return;
}
if (recipient != localAddress) {
Serial.println("Erreur: L'adresse du destinataire ne correspond pas");
return;
}
isInDestinationAddress = 0; // L'envoyeur n'est pas autorisé.
for(int destinationAddressCheck = 0; destinationAddressCheck < destinationAddressLength; destinationAddressCheck++){
if (sender == destinationAddress[destinationAddressCheck]){
isInDestinationAddress = 1; // L'envoyeur est autorisé.
break;
} // Sinon isInDestinationAddress reste sur 0, l'envoyeur n'est pas autorisé.
}
if (isInDestinationAddress == 0) { // Si l'envoyeur n'est pas autorisé.
Serial.println("Expéditeur non autorisé : 0x" + String(sender, HEX));
display.clear();
display.drawString(0, 0, "Expéditeur non autorisé : \n0x" + String(sender, HEX));
display.drawString(0, 30, "redacted_email");
display.drawString(0, 40, " .redacted@redacted.com");
display.display();
return;
}
int rssi = LoRa.packetRssi();
float snr = LoRa.packetSnr();
long freqErr = LoRa.packetFrequencyError();
display.clear();
display.drawString(0, 0, "redacted_email");
display.drawString(0, 10, " .redacted@redacted.com");
display.drawString(0, 20, "Message reçu : " + String(incoming));
display.drawString(0, 33, "RSSI : " + String(rssi));
display.drawString(0, 43, "SNR : " + String(snr));
display.drawString(0, 53, "Erreurs : " + String(freqErr));
display.display();
incomingSize = sizeof(incoming);
Serial.println("Longueur du message reçu : " + String(incomingSize) + " bytes");
Serial.println() * 2;
Serial.print("Message reçu : " + String(incoming));
Serial.print(" depuis 0x" + String(sender, HEX));
Serial.println(" à 0x" + String(recipient, HEX));
Serial.println("RSSI : " + String(rssi));
Serial.println("SNR : " + String(snr));
Serial.println("Erreurs : " + String(freqErr));
post(destName, incoming);
}
void post(String batiment, String message){
HTTPClient http;
String url = String(host) + String(path);
http.begin(url);
http.setConnectTimeout(1500); // Durée maximale de la tentative de connexion
http.setTimeout(1500); // Durée maximale lorsqu'on est déjà connecté au serveur
http.addHeader("Content-Type", "application/x-www-form-urlencoded");
String payload = String("token=") + String(liveToken) + String("batiment=") + String(batiment) + String("message=") + String(message);
// + String("&") + String("int_temp_c=") + String(temp_c);
// + String("&") + String("int_temp_f=") + String(temp_f);
int httpResponseCode = http.POST(payload); //Make the request
if (httpResponseCode > 0) { //Check for the returning code
String response = http.getString(); //Get the response to the request
Serial.println(httpResponseCode); //Print return code
Serial.println(response); //Print request answer
display.drawString(85, 33, "HTTP");
display.drawString(85, 43, "POST");
display.drawString(85, 53, String(httpResponseCode));
display.display();
}else{
Serial.print("Erreur dans l'envoi HTTP POST : ");
Serial.println(httpResponseCode);
}
http.end(); //Free the resources
}
bool connectToWiFi(){
Serial.print("Connexion au WiFi en cours");
unsigned long startAttemptTime = millis();
while (WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < WIFI_TIMEOUT_MS) {
delay(100);
Serial.print(".");
}
if(WiFi.status() != WL_CONNECTED){
Serial.println("Connexion au WiFi échouée");
// take action
}else{
Serial.print("Connecté au WiFi");
Serial.println(WiFi.localIP());
}
}
void screenInit() {
pinMode(16,OUTPUT);
pinMode(2,OUTPUT);
digitalWrite(16, LOW); // set GPIO16 low to reset OLED
delay(50);
digitalWrite(16, HIGH); // while OLED is running, must set GPIO16 in high
}
void setup() {
screenInit();
Serial.begin(115200);
Serial.println("Démarrage LoRa duplex");
SPI.begin(SCK,MISO,MOSI,SS);
LoRa.setPins(csPin, resetPin, irqPin);
if (!LoRa.begin(BAND)) {
Serial.println("L'initialisation du LoRa a échoué. Veuillez vérifier vos connexions.");
while (true) {}
}
display.init();
display.flipScreenVertically();
display.setFont(ArialMT_Plain_10);
display.drawString(0, 0, "Démarrage");
display.drawString(0, 10, "LoRa duplex avec retour");
display.drawString(0, 20, "Connection au WiFi");
display.drawString(0, 40, "redacted_email");
display.drawString(0, 50, " .redacted@redacted.com");
display.display();
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_NETWORK_1, WIFI_PASSWORD_1);
connectToWiFi();
if(WiFi.status() != WL_CONNECTED){
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_NETWORK_2, WIFI_PASSWORD_2);
connectToWiFi();
}else{
Serial.println("Connecté");}
destinationAddressLength = sizeof(destinationAddress);
delay(500);
lastSendTime = millis();
}
void loop() {
if (millis() - lastSendTime > interval) {
lastSendTime = millis();
sendMessage(destinationName[i], destinationAddress[i]);
i++;
if (i == destinationAddressLength) {i = 0;}
}
receiveMessage(LoRa.parsePacket(), destinationName[i]);
}
And here is a client:
//Bibliothèques pour LoRa
#include <SPI.h>
#include <LoRa.h>
//Bibliothèques pour l'écran OLED
#include <Wire.h>
#include "SSD1306.h"
//Définition des pins utilisés par l'émetteur/receveur LoRa
#define SCK 5 // GPIO5 -- SCK
#define MISO 19 // GPIO19 -- MISO
#define MOSI 27 // GPIO27 -- MOSI
#define SS 18 // GPIO18 -- CS
#define RST 23 // GPIO14 -- RESET (If Lora does not work, replace it with GPIO14)
#define DI0 26 // GPIO26 -- IRQ(Interrupt Request)
#define BAND redacted // 868E6
// Décision 2006/771/CE modifiée
// Par coefficient d’utilisation, on entend le rapport de temps, sur une heure,
// durant lequel un dispositif particulier émet effectivement.
// Les conditions moins restrictives au sens de l’article 3, paragraphe 3 signifient que
// les États membres peuvent autoriser une valeur supérieure pour le coefficient d’utilisation.
SSD1306 display(0x3c, 21, 22);
String rssi = "RSSI --";
String packSize = "--";
String packet ;
const int csPin = 18; // LoRa radio chip select
const int resetPin = 23; // LoRa radio reset
const int irqPin = 26; // change for your board; must be a hardware interrupt pin
byte localAddress = 0xREDACTED6;
String localAddressString = "f";
byte destinationAddress = 0xREDACTED0;
String aucunMessage = "0";
String incoming;
int yy = 0;
String erreur;
void setup() {
Serial.begin(115200);
Serial.println("Démarrage LoRa duplex");
Serial.print("Adresse locale : ");
Serial.println(String(localAddress, HEX));
Serial.print("Adresse de destination : ");
Serial.print(String(destinationAddress, HEX));
display.init();
display.flipScreenVertically();
display.setFont(ArialMT_Plain_10);
display.drawString(0, 10, "LoRa duplex : client " + localAddressString);
display.drawString(0, 40, "redacted_email");
display.drawString(0, 50, " .redacted@redacted.com");
display.display();
LoRa.setPins(csPin, resetPin, irqPin);
if (!LoRa.begin(BAND)) {
Serial.println("L'initialisation du LoRa a échoué. Veuillez vérifier vos connexions.");
while (true) {}
}
}
void loop() {
receiveMessage(LoRa.parsePacket());
}
void sendMessage(String outgoing) {
LoRa.beginPacket();
LoRa.write(destinationAddress);
LoRa.write(localAddress);
LoRa.write(outgoing.length());
LoRa.print(outgoing);
LoRa.endPacket();
Serial.println("Sending " + outgoing);
}
void receiveMessage(int packetSize) {
if (packetSize == 0) return;
int recipient = LoRa.read();
byte sender = LoRa.read();
byte incomingLength = LoRa.read();
String incoming = "";
while (LoRa.available()) {
incoming += (char)LoRa.read();
}
if (incomingLength != incoming.length()) {
String erreur = "La longueur du message \nne correspond pas";
displayErreur(erreur);
return;
}
if (recipient != localAddress) {
String erreur = "L'adresse du destinataire \nne correspond pas";
displayErreur(erreur);
return;
}
Serial.print("Données reçues : " + incoming);
Serial.print(" depuis 0x" + String(sender, HEX));
Serial.println(" vers 0x" + String(recipient, HEX));
sendMessage(aucunMessage);
displayMessage(incoming);
}
void displayErreur(String erreur) {
Serial.println("Erreur : " + erreur);
display.clear();
display.drawString(0, yy, erreur);
display.drawString(0, 30 + yy, "redacted_email");
display.drawString(0, 40 + yy, " .redacted@redacted.com");
display.display();
yyCalc();
}
void yyCalc() {
// On fait défiler le texte vers le bas
// pour éviter de laisser une image statique sur l'écran OLED.
yy += 5;
if (yy >= 20) {
yy = 0;
}
}
void displayMessage(String incoming) {
display.clear();
display.drawString(0, yy, "Message reçu : " + incoming);
display.drawString(0, 20 + yy, "redacted_email");
display.drawString(0, 30 + yy, " .redacted@redacted.com");
display.display();
yyCalc();
}
Thanks!