diff --git a/src/main.cpp b/src/main.cpp index 29def3d..594e1e6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,680 +1,681 @@ -#include "main.h" -#include "NTPClient.h" - -#include -#include -#include -#include -#include -#include "Preferences.h" - -#define STORAGE_NAMESPACE "storage" -#define _OPEN_SYS_ITOA_EXT -#define RESTART 1 -#define REFRESHMEMBOOT 0 -#define RESTARTTIMES 2 -#define REFRESHTIMES 4 - -using namespace std; - -Preferences preferences; - -// Array of DHT sensors with their respective pins and types -DHT sensors[SENSORS_NUMBER] = {DHT(04, DHT22), DHT(18, DHT22), DHT(05, DHT22), DHT(17, DHT22), DHT(16, DHT22)}; - -// Arrays to store temperature and humidity readings -float temp[SENSORS_NUMBER]; -float hum[SENSORS_NUMBER]; - -// Pangodream_18650_CL battery object with ADC pin, conversion factor, and number of reads -Pangodream_18650_CL Battery(ADC_PIN, CONV_FACTOR, READS); - -// WiFi client object -WiFiClient WifiClient; - -// PubSubClient object for MQTT communication -PubSubClient MqttClient(WifiClient); - -// UDP object for NTP communication -WiFiUDP NtpUDP; - -// NTP client object with UDP and NTP server address -NTPClient TimeClient(NtpUDP, "192.168.23.254"); /*si vous utilisez le Wifi du fablab*/ -// NTPClient TimeClient(NtpUDP, "europe.pool.ntp.org"); /*si vous utilisez un réseau 4g ou autre */ - -//-------------------- CONDITION --------------------// - -/** - * @brief Test if the time is good for restart or refresh memory - * - * @param hours - * @param minutes - * @param restart bool to choose which the restart time bool or the refresh time bool will be returned - * @return true It's time to refresh or restart - * @return false It's not the time to refresh or restart - */ -bool testTime(int hours, int minutes, bool restart) -{ - // debug parts - // Serial.println('\n'); - // Serial.println((REFRESHTIMES * TIME_TO_SLEEP) / 3600,DEC); - // Serial.println(((REFRESHTIMES * TIME_TO_SLEEP) % 3600) / 60,DEC); - // Serial.println('\n'); - - if (restart) - { // restart parts - if (hours < (RESTARTTIMES * TIME_TO_SLEEP) / 3600) - return true; - else - { - if ((hours = (RESTARTTIMES * TIME_TO_SLEEP) / 3600) && (minutes <= ((RESTARTTIMES * TIME_TO_SLEEP) % 3600) / 60)) - return true; - return false; - } - } - else - { // refresh parts - if (hours >= (RESTARTTIMES * TIME_TO_SLEEP) / 3600) - { - if (hours < (REFRESHTIMES * TIME_TO_SLEEP) / 3600) - return true; - else - { - if ((hours = (REFRESHTIMES * TIME_TO_SLEEP) / 3600) && (minutes <= ((REFRESHTIMES * TIME_TO_SLEEP) % 3600) / 60)) - return true; - else - { - return false; - } - } - } - } - // Not needed but carefully habits - return false; -} - -//-------------------- FONCTIONS --------------------// - -//-------------- Connexion MQTT --------------// - -/** - * @brief Set up MQTT server with address and port - * - * @param address MQTT server address - * @param port MQTT server port - */ -void setupMQTT(const char *address, int port) -{ - MqttClient.setServer(address, port); -} - -/** - * @brief Set up WiFi connection with SSID and password - * - * @param wifi_name WiFi SSID - * @param password WiFi password - */ -void setupWIFI(const char *wifi_name, const char *password) -{ - Serial.println('\n'); - - WiFi.begin(wifi_name, password); - Serial.print("Connecting to "); - Serial.print(wifi_name); - - int cpttry = 10; - while (WiFi.status() != WL_CONNECTED) - { - delay(500); - Serial.print('.'); - - /* stop trying wifi connexion */ - cpttry--; - if (cpttry == 0) - { - Serial.print("wifi: 10 try go to sleep"); - sleep(); - } - } - Serial.println('\n'); - Serial.println("Connection established!"); - Serial.print("IP address:\t"); - Serial.println(WiFi.localIP()); -} - -void reconnect(void) -{ - Serial.println("\n Connecting to MQTT Broker..."); - int cpttry = 10; - while (!MqttClient.connected()) - { - /* stop trying mqtt connexion */ - cpttry--; - if (cpttry == 0) - { - Serial.print("Mqtt: 10 try go to sleep"); - LocalSave(); - sleep(); - } - - Serial.print("."); - - if (MqttClient.connect(ESPNAME, MQTT_USER, MQTT_MDP)) // MQTT_MDP (mot de passe) - { - Serial.println("Connected."); - } - } -} - -//-------------- Initialisation et lecture des capteurs --------------// - -/** - * @brief Initialize DHT sensors - * - * @param sensors array of DHT sensors - * @param number number of sensors - */ -void initSensors(DHT *sensors, int number) -{ - int i; - for (i = 0; i < number; i++) - { - sensors[i].begin(); - } -} - -/** - * @brief Read temperature and humidity from DHT sensors - * - * @param sensors array of DHT sensors - * @param temp array to store temperature readings - * @param hum array to store humidity readings - * @param number number of sensors - */ -void readSensors(DHT sensors[], float temp[], float hum[], int number) -{ - int i; - for (i = 0; i < number; i++) - { - *(temp + i) = sensors[i].readTemperature(); - *(hum + i) = sensors[i].readHumidity(); - } -} - -//-------------------- Sleep de l'ESP --------------------// - -/** - * @brief Put ESP into deep sleep mode - */ -void sleep() -{ - esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * US_TO_S_FACTOR); - esp_deep_sleep_start(); -} - -// exemple d'une triple utilisation de valeur pour une fonction utilise pour la date -std::tuple getDate() -{ - struct timeval synctime; - TimeClient.forceUpdate(); - time_t rawtime = TimeClient.getEpochTime(); - synctime.tv_sec = rawtime; - synctime.tv_usec = 0; - if (settimeofday(&synctime, NULL) != 0) - std::cout << "error\n"; - - // set timezone to France - setenv("TZ", "CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00", 2); // set at 2 or 3 (Third parameter) - tzset(); - - struct tm *ti; - ti = localtime(&rawtime); - int year = ti->tm_year + 1900; - int month = (ti->tm_mon + 1) < 10 ? 0 + (ti->tm_mon + 1) : (ti->tm_mon + 1); - int day = (ti->tm_mday) < 10 ? 0 + (ti->tm_mday) : (ti->tm_mday); - return std::make_tuple(year, month, day); -} - -// Function that gets current epoch time -unsigned long getTime() -{ - time_t now = TimeClient.getEpochTime(); - struct timeval synctime; - struct tm timeinfo; - - synctime.tv_sec = now; - synctime.tv_usec = 0; - if (settimeofday(&synctime, NULL) != 0) - std::cout << "error\n"; - // TimeClient.forceUpdate(); //cette ligne crée un bug avec l'heure quand on est connecté au réseau du fablab - vTaskDelay(1000); - if (!getLocalTime(&timeinfo)) - { - Serial.println("Failed to obtain time"); - return (0); - } - time(&now); - Serial.println(now); - return now; -} - -int getAverageChargeLevel() -{ - const int moyenne = 10; - int charge[moyenne]; - int total = 0; - - for (int i = 0; i < moyenne; i++) - { - charge[i] = Battery.getBatteryChargeLevel(); // Assurez-vous que Battery.getBatteryChargeLevel() est défini - total += charge[i]; - } - - int chargelvl = total / moyenne; - return chargelvl; -} - -//-------------------- Création de trames --------------------// - -/** - * @brief Create message frame with temperature and humidity readings - * - * @param txt message buffer - * @param temp array of temperature readings - * @param hum array of humidity readings - * @param number number of sensors - */ -void writeMessage(char *txt, float *temp, float *hum, int number) -{ - int chargelvl = getAverageChargeLevel(); - delay(2000); - - switch (number) - { - case 1: - sprintf(txt, "|%s|%0.2f|%0.2f", CLUSTER, temp[0], hum[0]); - break; - case 2: - sprintf(txt, "|%s|%0.2f %0.2f|%0.2f %0.2f", CLUSTER, temp[0], temp[1], hum[0], hum[1]); - break; - case 3: - sprintf(txt, "|%s|%0.2f %0.2f %0.2f|%0.2f %0.2f %0.2f", CLUSTER, temp[0], temp[1], temp[2], hum[0], hum[1], hum[2]); - break; - case 4: - sprintf(txt, "|%s|%0.2f %0.2f %0.2f %0.2f|%0.2f %0.2f %0.2f %0.2f", CLUSTER, temp[0], temp[1], temp[2], temp[3], hum[0], hum[1], hum[2], hum[3]); - break; - case 5: - sprintf(txt, "|%s|%0.2f %0.2f %0.2f %0.2f %0.2f|%0.2f %0.2f %0.2f %0.2f %0.2f|%d", CLUSTER, temp[0], temp[1], temp[2], temp[3], temp[4], hum[0], hum[1], hum[2], hum[3], hum[4], chargelvl); - break; - case 6: - sprintf(txt, "|%s|%0.2f %0.2f %0.2f %0.2f %0.2f %0.2f|%0.2f %0.2f %0.2f %0.2f %0.2f %0.2f", CLUSTER, temp[0], temp[1], temp[2], temp[3], temp[4], temp[5], hum[0], hum[1], hum[2], hum[3], hum[4], hum[5]); - break; - default: - Serial.println("Erreur, temp et hum sont trop longs : trop de capteurs"); - break; - } -} - -//-------------------- read, refresh and return the day boot state --------------------// - -/** - * @brief Read, refresh and return the day boot state - * - * @return bool at true if the esp was restarted or false if error or not restarted - * @inspired of https://github.com/espressif/esp-idf/blob/master/examples/storage/nvs_rw_blob/main/nvs_blob_example_main.c - */ -bool TestBoot(bool resOrRfsh) -{ - nvs_handle_t my_handle; - esp_err_t err; - bool booted = false; - // Serial.println("Test"); - - // Open - err = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &my_handle); - if (err != ESP_OK) - { - Serial.println("Erreur open nvs persistant storage"); - return false; - } - - /******** Refresh part ********/ - - if (resOrRfsh == REFRESHMEMBOOT) - { - int32_t restart = REFRESHMEMBOOT; - err = nvs_set_i32(my_handle, "restart", restart); - if (err != ESP_OK) - { - Serial.println("Erreur write nvs persistant storage"); - return false; - } - // Commit written value. - // After setting any values, nvs_commit() must be called to ensure changes are written - // to flash storage. Implementations may write to storage at other times, - // but this is not guaranteed. - err = nvs_commit(my_handle); - if (err != ESP_OK) - return err; - // Close - nvs_close(my_handle); - Serial.println("Refresh memory restart state "); - return false; - } - - // Read - int32_t restart = 0; // value will default to 0, if not set yet in NVS - err = nvs_get_i32(my_handle, "restart", &restart); - if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) - { - Serial.println("Erreur read nvs persistant storage"); - return false; - } - - if (!restart) - { - // Write - restart = 1; - err = nvs_set_i32(my_handle, "restart", restart); - if (err != ESP_OK) - { - Serial.println("Erreur write nvs persistant storage"); - return false; - } - // Commit written value. - // After setting any values, nvs_commit() must be called to ensure changes are written - // to flash storage. Implementations may write to storage at other times, - // but this is not guaranteed. - err = nvs_commit(my_handle); - if (err != ESP_OK) - return err; - // Close - nvs_close(my_handle); - Serial.println("Flag restart = 0 (not restarted"); - return true; - } - else - { - // Close - nvs_close(my_handle); - Serial.println("Flag restart = 1 (already restart)"); - return false; - } - - /* Not needed but finish properly the function */ - // Close - Serial.println("Maybe issue: last return"); - nvs_close(my_handle); - return false; -} - -//-------------------- Initialisation --------------------// - -/** - * @brief Setup function for the ESP - */ -void setup() -{ - Serial.begin(9600); - Serial.println("\n-----------------Start---------------------"); - - setupWIFI(SSID, PWD); - setupMQTT(MQTT_ADDRESS, MQTT_PORT); - initSensors(sensors, SENSORS_NUMBER); - - TimeClient.begin(); - TimeClient.update(); - - // int rawtime = (TimeClient.getEpochTime()) % 86400; // 86400 = 60sec*60minutes*24heures - unsigned long rawtime = getTime(); - - int hours = rawtime / 3600, minutes = (rawtime % 3600) / 60, secondes = rawtime % 60; - - if (nvs_flash_init() != ESP_OK) - { - Serial.println("Erreur flash init"); - } - /** - * @brief restart the ESP if there is the time restart - * - */ - if (testTime(hours, minutes, RESTART) && TestBoot(RESTART)) - { - Serial.println("-----------------RESSSSTTTAAAART---------------------"); - ESP.restart(); - delay(1000); - } - else if (testTime(hours, minutes, REFRESHMEMBOOT)) - { - TestBoot(REFRESHMEMBOOT); - } - - // // ######### test time part ######### - // Serial.println('\n'); - // Serial.println(hours,DEC); - // Serial.println(':'); - // Serial.println(minutes,DEC); - // Serial.println(':'); - // Serial.println(secondes,DEC); - // Serial.println('\n'); -} - -// Définir la capacité maximale de stockage -const int maxDataSets = 11; - -// Fonction pour sauvegarder les données localement -void LocalSave() -{ - // Ouvre les préférences sous le nom "capteurs" en mode lecture/écriture - preferences.begin("capteurs", false); - - // Lit l'indice de la prochaine sauvegarde, ou initialise à 0 s'il n'existe pas - int saveCounter = preferences.getInt("saveCounter", 0); - - // Efface les anciennes données si nécessaire - if (saveCounter >= maxDataSets) - { - saveCounter = 0; // Réinitialise le compteur si la limite est atteinte - } - - // Appelle la fonction pour effacer les données anciennes à l'index actuel - clearOldData(saveCounter); - - // Calcule l'heure actuelle en secondes depuis l'époque Unix - int chargelvl = getAverageChargeLevel(); // Obtient le niveau moyen de la batterie - time_t rawtime = TimeClient.getEpochTime(); // Obtient l'heure actuelle - - Serial.print("\n-----------------Save---------------------\n"); - - // Boucle pour sauvegarder les données de température et d'humidité - for (int i = 0; i < 5; i++) - { - char tempKey[16]; - char humiKey[16]; - - // Génère des clés uniques pour chaque valeur de température et d'humidité - snprintf(tempKey, sizeof(tempKey), "temp%d_%d", saveCounter, i); - snprintf(humiKey, sizeof(humiKey), "hum%d_%d", saveCounter, i); - - // Sauvegarde les valeurs de température et d'humidité dans les préférences - preferences.putFloat(tempKey, temp[i]); - preferences.putFloat(humiKey, hum[i]); - - // Affiche les valeurs de température et d'humidité pour vérification - Serial.printf("temp[%d]: %0.2f\n", i, temp[i]); - Serial.printf("hum[%d]: %0.2f\n", i, hum[i]); - } - - // Sauvegarde le temps et le niveau de charge - char timeKey[16]; - char chargeKey[16]; - snprintf(timeKey, sizeof(timeKey), "time%d", saveCounter); - snprintf(chargeKey, sizeof(chargeKey), "chargelvl%d", saveCounter); - - // Sauvegarde les valeurs de temps et de niveau de charge dans les préférences - preferences.putInt(timeKey, rawtime); - preferences.putInt(chargeKey, chargelvl); - - // Affiche les valeurs de temps et de niveau de charge pour vérification - Serial.printf("time: %d \n", rawtime); - Serial.printf("chargelvl: %d\n", chargelvl); - - // Incrémente le compteur de sauvegardes et le sauvegarde - saveCounter++; - preferences.putInt("saveCounter", saveCounter); - - // Termine l'accès aux préférences - preferences.end(); -} - -// Fonction pour effacer les anciennes données si l'espace est insuffisant -void clearOldData(int index) -{ - // Variables pour stocker les noms de clé formatés - char timeKey[16]; - char chargeKey[16]; - - // Formate les noms de clé pour le temps et le niveau de charge en fonction de l'index donné - snprintf(timeKey, sizeof(timeKey), "time%d", index); - snprintf(chargeKey, sizeof(chargeKey), "chargelvl%d", index); - - // Supprime les entrées correspondantes au temps et au niveau de charge - preferences.remove(timeKey); - preferences.remove(chargeKey); - - // Boucle pour supprimer les entrées de température et d'humidité associées - for (int i = 0; i < 5; i++) - { - // Variables pour stocker les noms de clé formatés pour la température et l'humidité - char tempKey[16]; - char humiKey[16]; - - // Formate les noms de clé pour la température et l'humidité en fonction de l'index et du sous-index - snprintf(tempKey, sizeof(tempKey), "temp%d_%d", index, i); - snprintf(humiKey, sizeof(humiKey), "hum%d_%d", index, i); - - // Supprime les entrées correspondantes à la température et à l'humidité - preferences.remove(tempKey); - preferences.remove(humiKey); - } -} - -// Fonction pour envoyer les données locales -void SendLocalData() -{ - // Ouvre les préférences sous le nom "capteurs" en mode lecture seule - preferences.begin("capteurs", true); - - // Lit le compteur de sauvegardes, ou initialise à 0 s'il n'existe pas - int saveCounter = preferences.getInt("saveCounter", 0); - - // Boucle pour envoyer les données sauvegardées - for (int counter = 0; counter < saveCounter; counter++) - { - char timeKey[16]; - char chargeKey[16]; - - // Génère des clés uniques pour le temps et le niveau de charge - snprintf(timeKey, sizeof(timeKey), "time%d", counter); - snprintf(chargeKey, sizeof(chargeKey), "chargelvl%d", counter); - - // Lit les valeurs de temps et de niveau de charge depuis les préférences - time_t rawtime = preferences.getInt(timeKey, 0); - int chargelvl = preferences.getInt(chargeKey, 0); - - // Boucle pour lire les données de température et d'humidité - for (int i = 0; i < 5; i++) - { - char tempKey[16]; - char humiKey[16]; - - // Génère des clés uniques pour chaque valeur de température et d'humidité - snprintf(tempKey, sizeof(tempKey), "temp%d_%d", counter, i); - snprintf(humiKey, sizeof(humiKey), "hum%d_%d", counter, i); - - // Lit les valeurs de température et d'humidité depuis les préférences - temp[i] = preferences.getFloat(tempKey, 0); - hum[i] = preferences.getFloat(humiKey, 0); - } - - // Prépare le message à envoyer - char msgsld[200]; - sprintf(msgsld, "%lu|%s|%0.2f %0.2f %0.2f %0.2f %0.2f|%0.2f %0.2f %0.2f %0.2f %0.2f|%d", rawtime, CLUSTER, temp[0], temp[1], temp[2], temp[3], temp[4], hum[0], hum[1], hum[2], hum[3], hum[4], chargelvl); - - // Envoie le message via MQTT - MqttClient.publish(TOPIC, msgsld); - Serial.println(msgsld); - - // Supprime les clés après envoi - clearOldData(counter); - } - - // Réinitialise le compteur de sauvegardes - preferences.putInt("saveCounter", 0); - - // Termine l'accès aux préférences - preferences.end(); -} - -//-------------------- Boucle principale --------------------// - -/** - * @brief Main loop function for the ESP - */ -void loop() -{ - int year, month, day; - int lenght; - unsigned long now; - char time[30]; - char date[30]; - char msg[70]; - bool horoIssue = false; - - readSensors(sensors, temp, hum, SENSORS_NUMBER); - writeMessage(msg, temp, hum, SENSORS_NUMBER); - - MqttClient.loop(); - if (!MqttClient.connected()) - { - reconnect(); - } - - // MqttClient.publish(TOPIC, msg, date); - - // TimeClient.update(); - // TimeClient.getFormattedTime().toCharArray(time, 30); - - // tie(year, month, day) = getDate(); - // /* debug print*/ - // Serial.println('\n'); - // Serial.println(year,DEC); - // Serial.println(':'); - // Serial.println(month,DEC); - // Serial.println(':'); - // Serial.println(day,DEC); - // Serial.println('\n'); - - now = getTime(); - lenght = sprintf(date, "%s", ultoa(now, date, 10) /* "|%d-%d-%d ", year, month, day */); - sprintf(date + lenght, time); - // Serial.println("msg1 : "); //debug print - // Serial.println(msg); //debug print - sprintf(date + strlen(date), msg); - // Serial.println("msg2 : "); //debug print - // Serial.println(msg); //debug print - // Serial.println("date : "); //debug print - // Serial.println(date); //debug print - Serial.println("\n"); - Serial.println("-----------------Local---------------------"); - SendLocalData(); - MqttClient.publish(TOPIC, date); - Serial.println("-----------------Brut---------------------"); - Serial.println(date); - Serial.println("-----------------DeepSleep---------------------"); - - delay(2000); - - sleep(); - delay(1000); +#include "main.h" +#include "NTPClient.h" + +#include +#include +#include +#include +#include +#include "Preferences.h" + +#define STORAGE_NAMESPACE "storage" +#define _OPEN_SYS_ITOA_EXT +#define RESTART 1 +#define REFRESHMEMBOOT 0 +#define RESTARTTIMES 2 +#define REFRESHTIMES 4 + +using namespace std; + +Preferences preferences; + +// Array of DHT sensors with their respective pins and types +DHT sensors[SENSORS_NUMBER] = {DHT(04, DHT22), DHT(18, DHT22), DHT(05, DHT22), DHT(17, DHT22), DHT(16, DHT22)}; + +// Arrays to store temperature and humidity readings +float temp[SENSORS_NUMBER]; +float hum[SENSORS_NUMBER]; + +// Pangodream_18650_CL battery object with ADC pin, conversion factor, and number of reads +Pangodream_18650_CL Battery(ADC_PIN, CONV_FACTOR, READS); + +// WiFi client object +WiFiClient WifiClient; + +// PubSubClient object for MQTT communication +PubSubClient MqttClient(WifiClient); + +// UDP object for NTP communication +WiFiUDP NtpUDP; + +// NTP client object with UDP and NTP server address +NTPClient TimeClient(NtpUDP, "192.168.23.254"); /*si vous utilisez le Wifi du fablab*/ +// NTPClient TimeClient(NtpUDP, "europe.pool.ntp.org"); /*si vous utilisez un réseau 4g ou autre */ + +//-------------------- CONDITION --------------------// + +/** + * @brief Test if the time is good for restart or refresh memory + * + * @param hours + * @param minutes + * @param restart bool to choose which the restart time bool or the refresh time bool will be returned + * @return true It's time to refresh or restart + * @return false It's not the time to refresh or restart + */ +bool testTime(int hours, int minutes, bool restart) +{ + // debug parts + // Serial.println('\n'); + // Serial.println((REFRESHTIMES * TIME_TO_SLEEP) / 3600,DEC); + // Serial.println(((REFRESHTIMES * TIME_TO_SLEEP) % 3600) / 60,DEC); + // Serial.println('\n'); + + if (restart) + { // restart parts + if (hours < (RESTARTTIMES * TIME_TO_SLEEP) / 3600) + return true; + else + { + if ((hours = (RESTARTTIMES * TIME_TO_SLEEP) / 3600) && (minutes <= ((RESTARTTIMES * TIME_TO_SLEEP) % 3600) / 60)) + return true; + return false; + } + } + else + { // refresh parts + if (hours >= (RESTARTTIMES * TIME_TO_SLEEP) / 3600) + { + if (hours < (REFRESHTIMES * TIME_TO_SLEEP) / 3600) + return true; + else + { + if ((hours = (REFRESHTIMES * TIME_TO_SLEEP) / 3600) && (minutes <= ((REFRESHTIMES * TIME_TO_SLEEP) % 3600) / 60)) + return true; + else + { + return false; + } + } + } + } + // Not needed but carefully habits + return false; +} + +//-------------------- FONCTIONS --------------------// + +//-------------- Connexion MQTT --------------// + +/** + * @brief Set up MQTT server with address and port + * + * @param address MQTT server address + * @param port MQTT server port + */ +void setupMQTT(const char *address, int port) +{ + MqttClient.setServer(address, port); +} + +/** + * @brief Set up WiFi connection with SSID and password + * + * @param wifi_name WiFi SSID + * @param password WiFi password + */ +void setupWIFI(const char *wifi_name, const char *password) +{ + Serial.println('\n'); + + WiFi.begin(wifi_name, password); + Serial.print("Connecting to "); + Serial.print(wifi_name); + + int cpttry = 10; + while (WiFi.status() != WL_CONNECTED) + { + delay(500); + Serial.print('.'); + + /* stop trying wifi connexion */ + cpttry--; + if (cpttry == 0) + { + Serial.print("wifi: 10 try go to sleep"); + sleep(); + } + } + Serial.println('\n'); + Serial.println("Connection established!"); + Serial.print("IP address:\t"); + Serial.println(WiFi.localIP()); +} + +void reconnect(void) +{ + Serial.println("\n Connecting to MQTT Broker..."); + int cpttry = 10; + while (!MqttClient.connected()) + { + /* stop trying mqtt connexion */ + cpttry--; + if (cpttry == 0) + { + Serial.print("Mqtt: 10 try go to sleep"); + LocalSave(); + sleep(); + } + + Serial.print("."); + + if (MqttClient.connect(ESPNAME, MQTT_USER, MQTT_MDP)) // MQTT_MDP (mot de passe) + { + Serial.println("Connected."); + } + } +} + +//-------------- Initialisation et lecture des capteurs --------------// + +/** + * @brief Initialize DHT sensors + * + * @param sensors array of DHT sensors + * @param number number of sensors + */ +void initSensors(DHT *sensors, int number) +{ + int i; + for (i = 0; i < number; i++) + { + sensors[i].begin(); + } +} + +/** + * @brief Read temperature and humidity from DHT sensors + * + * @param sensors array of DHT sensors + * @param temp array to store temperature readings + * @param hum array to store humidity readings + * @param number number of sensors + */ +void readSensors(DHT sensors[], float temp[], float hum[], int number) +{ + int i; + for (i = 0; i < number; i++) + { + *(temp + i) = sensors[i].readTemperature(); + *(hum + i) = sensors[i].readHumidity(); + } +} + +//-------------------- Sleep de l'ESP --------------------// + +/** + * @brief Put ESP into deep sleep mode + */ +void sleep() +{ + esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * US_TO_S_FACTOR); + esp_deep_sleep_start(); +} + +// exemple d'une triple utilisation de valeur pour une fonction utilise pour la date +std::tuple getDate() +{ + struct timeval synctime; + TimeClient.forceUpdate(); + time_t rawtime = TimeClient.getEpochTime(); + synctime.tv_sec = rawtime; + synctime.tv_usec = 0; + if (settimeofday(&synctime, NULL) != 0) + std::cout << "error\n"; + + // set timezone to France + setenv("TZ", "CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00", 2); // set at 2 or 3 (Third parameter) + tzset(); + + struct tm *ti; + ti = localtime(&rawtime); + int year = ti->tm_year + 1900; + int month = (ti->tm_mon + 1) < 10 ? 0 + (ti->tm_mon + 1) : (ti->tm_mon + 1); + int day = (ti->tm_mday) < 10 ? 0 + (ti->tm_mday) : (ti->tm_mday); + return std::make_tuple(year, month, day); +} + +// Function that gets current epoch time +unsigned long getTime() +{ + time_t now = TimeClient.getEpochTime(); + struct timeval synctime; + struct tm timeinfo; + + synctime.tv_sec = now; + synctime.tv_usec = 0; + if (settimeofday(&synctime, NULL) != 0) + std::cout << "error\n"; + // TimeClient.forceUpdate(); //cette ligne crée un bug avec l'heure quand on est connecté au réseau du fablab + vTaskDelay(1000); + if (!getLocalTime(&timeinfo)) + { + Serial.println("Failed to obtain time"); + return (0); + } + time(&now); + Serial.println(now); + return now; +} + +int getAverageChargeLevel() +{ + const int moyenne = 10; + int charge[moyenne]; + int total = 0; + + for (int i = 0; i < moyenne; i++) + { + charge[i] = Battery.getBatteryChargeLevel(); // Assurez-vous que Battery.getBatteryChargeLevel() est défini + total += charge[i]; + } + + int chargelvl = total / moyenne; + return chargelvl; +} + +//-------------------- Création de trames --------------------// + +/** + * @brief Create message frame with temperature and humidity readings + * + * @param txt message buffer + * @param temp array of temperature readings + * @param hum array of humidity readings + * @param number number of sensors + */ +void writeMessage(char *txt, float *temp, float *hum, int number) +{ + int chargelvl = getAverageChargeLevel(); + delay(2000); + + switch (number) + { + case 1: + sprintf(txt, "|%s|%0.2f|%0.2f", CLUSTER, temp[0], hum[0]); + break; + case 2: + sprintf(txt, "|%s|%0.2f %0.2f|%0.2f %0.2f", CLUSTER, temp[0], temp[1], hum[0], hum[1]); + break; + case 3: + sprintf(txt, "|%s|%0.2f %0.2f %0.2f|%0.2f %0.2f %0.2f", CLUSTER, temp[0], temp[1], temp[2], hum[0], hum[1], hum[2]); + break; + case 4: + sprintf(txt, "|%s|%0.2f %0.2f %0.2f %0.2f|%0.2f %0.2f %0.2f %0.2f", CLUSTER, temp[0], temp[1], temp[2], temp[3], hum[0], hum[1], hum[2], hum[3]); + break; + case 5: + sprintf(txt, "|%s|%0.2f %0.2f %0.2f %0.2f %0.2f|%0.2f %0.2f %0.2f %0.2f %0.2f|%d", CLUSTER, temp[0], temp[1], temp[2], temp[3], temp[4], hum[0], hum[1], hum[2], hum[3], hum[4], chargelvl); + break; + case 6: + sprintf(txt, "|%s|%0.2f %0.2f %0.2f %0.2f %0.2f %0.2f|%0.2f %0.2f %0.2f %0.2f %0.2f %0.2f", CLUSTER, temp[0], temp[1], temp[2], temp[3], temp[4], temp[5], hum[0], hum[1], hum[2], hum[3], hum[4], hum[5]); + break; + default: + Serial.println("Erreur, temp et hum sont trop longs : trop de capteurs"); + break; + } +} + +//-------------------- read, refresh and return the day boot state --------------------// + +/** + * @brief Read, refresh and return the day boot state + * + * @return bool at true if the esp was restarted or false if error or not restarted + * @inspired of https://github.com/espressif/esp-idf/blob/master/examples/storage/nvs_rw_blob/main/nvs_blob_example_main.c + */ +bool TestBoot(bool resOrRfsh) +{ + nvs_handle_t my_handle; + esp_err_t err; + bool booted = false; + // Serial.println("Test"); + + // Open + err = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &my_handle); + if (err != ESP_OK) + { + Serial.println("Erreur open nvs persistant storage"); + return false; + } + + /******** Refresh part ********/ + + if (resOrRfsh == REFRESHMEMBOOT) + { + int32_t restart = REFRESHMEMBOOT; + err = nvs_set_i32(my_handle, "restart", restart); + if (err != ESP_OK) + { + Serial.println("Erreur write nvs persistant storage"); + return false; + } + // Commit written value. + // After setting any values, nvs_commit() must be called to ensure changes are written + // to flash storage. Implementations may write to storage at other times, + // but this is not guaranteed. + err = nvs_commit(my_handle); + if (err != ESP_OK) + return err; + // Close + nvs_close(my_handle); + Serial.println("Refresh memory restart state "); + return false; + } + + // Read + int32_t restart = 0; // value will default to 0, if not set yet in NVS + err = nvs_get_i32(my_handle, "restart", &restart); + if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) + { + Serial.println("Erreur read nvs persistant storage"); + return false; + } + + if (!restart) + { + // Write + restart = 1; + err = nvs_set_i32(my_handle, "restart", restart); + if (err != ESP_OK) + { + Serial.println("Erreur write nvs persistant storage"); + return false; + } + // Commit written value. + // After setting any values, nvs_commit() must be called to ensure changes are written + // to flash storage. Implementations may write to storage at other times, + // but this is not guaranteed. + err = nvs_commit(my_handle); + if (err != ESP_OK) + return err; + // Close + nvs_close(my_handle); + Serial.println("Flag restart = 0 (not restarted"); + return true; + } + else + { + // Close + nvs_close(my_handle); + Serial.println("Flag restart = 1 (already restart)"); + return false; + } + + /* Not needed but finish properly the function */ + // Close + Serial.println("Maybe issue: last return"); + nvs_close(my_handle); + return false; +} + +//-------------------- Initialisation --------------------// + +/** + * @brief Setup function for the ESP + */ +void setup() +{ + Serial.begin(9600); + Serial.println("\n-----------------Start---------------------"); + + setupWIFI(SSID, PWD); + setupMQTT(MQTT_ADDRESS, MQTT_PORT); + initSensors(sensors, SENSORS_NUMBER); + + TimeClient.begin(); + TimeClient.update(); + + // int rawtime = (TimeClient.getEpochTime()) % 86400; // 86400 = 60sec*60minutes*24heures + unsigned long rawtime = getTime(); + + int hours = rawtime / 3600, minutes = (rawtime % 3600) / 60, secondes = rawtime % 60; + + if (nvs_flash_init() != ESP_OK) + { + Serial.println("Erreur flash init"); + } + /** + * @brief restart the ESP if there is the time restart + * + */ + if (testTime(hours, minutes, RESTART) && TestBoot(RESTART)) + { + Serial.println("-----------------RESSSSTTTAAAART---------------------"); + ESP.restart(); + delay(1000); + } + else if (testTime(hours, minutes, REFRESHMEMBOOT)) + { + TestBoot(REFRESHMEMBOOT); + } + + // // ######### test time part ######### + // Serial.println('\n'); + // Serial.println(hours,DEC); + // Serial.println(':'); + // Serial.println(minutes,DEC); + // Serial.println(':'); + // Serial.println(secondes,DEC); + // Serial.println('\n'); +} + +// Définir la capacité maximale de stockage +const int maxDataSets = 11; + +// Fonction pour sauvegarder les données localement +void LocalSave() +{ + // Ouvre les préférences sous le nom "capteurs" en mode lecture/écriture + preferences.begin("capteurs", false); + + // Lit l'indice de la prochaine sauvegarde, ou initialise à 0 s'il n'existe pas + int saveCounter = preferences.getInt("saveCounter", 0); + + // Efface les anciennes données si nécessaire + if (saveCounter >= maxDataSets) + { + saveCounter = 0; // Réinitialise le compteur si la limite est atteinte + } + + // Appelle la fonction pour effacer les données anciennes à l'index actuel + clearOldData(saveCounter); + + // Calcule l'heure actuelle en secondes depuis l'époque Unix + int chargelvl = getAverageChargeLevel(); // Obtient le niveau moyen de la batterie + time_t rawtime = TimeClient.getEpochTime(); // Obtient l'heure actuelle + + Serial.print("\n-----------------Save---------------------\n"); + + // Boucle pour sauvegarder les données de température et d'humidité + for (int i = 0; i < 5; i++) + { + char tempKey[16]; + char humiKey[16]; + + // Génère des clés uniques pour chaque valeur de température et d'humidité + snprintf(tempKey, sizeof(tempKey), "temp%d_%d", saveCounter, i); + snprintf(humiKey, sizeof(humiKey), "hum%d_%d", saveCounter, i); + + // Sauvegarde les valeurs de température et d'humidité dans les préférences + preferences.putFloat(tempKey, temp[i]); + preferences.putFloat(humiKey, hum[i]); + + // Affiche les valeurs de température et d'humidité pour vérification + Serial.printf("temp[%d]: %0.2f\n", i, temp[i]); + Serial.printf("hum[%d]: %0.2f\n", i, hum[i]); + } + + // Sauvegarde le temps et le niveau de charge + char timeKey[16]; + char chargeKey[16]; + snprintf(timeKey, sizeof(timeKey), "time%d", saveCounter); + snprintf(chargeKey, sizeof(chargeKey), "chargelvl%d", saveCounter); + + // Sauvegarde les valeurs de temps et de niveau de charge dans les préférences + preferences.putInt(timeKey, rawtime); + preferences.putInt(chargeKey, chargelvl); + + // Affiche les valeurs de temps et de niveau de charge pour vérification + Serial.printf("time: %d \n", rawtime); + Serial.printf("chargelvl: %d\n", chargelvl); + + // Incrémente le compteur de sauvegardes et le sauvegarde + saveCounter++; + preferences.putInt("saveCounter", saveCounter); + + // Termine l'accès aux préférences + preferences.end(); +} + +// Fonction pour effacer les anciennes données si l'espace est insuffisant +void clearOldData(int index) +{ + // Variables pour stocker les noms de clé formatés + char timeKey[16]; + char chargeKey[16]; + + // Formate les noms de clé pour le temps et le niveau de charge en fonction de l'index donné + snprintf(timeKey, sizeof(timeKey), "time%d", index); + snprintf(chargeKey, sizeof(chargeKey), "chargelvl%d", index); + + // Supprime les entrées correspondantes au temps et au niveau de charge + preferences.remove(timeKey); + preferences.remove(chargeKey); + + // Boucle pour supprimer les entrées de température et d'humidité associées + for (int i = 0; i < 5; i++) + { + // Variables pour stocker les noms de clé formatés pour la température et l'humidité + char tempKey[16]; + char humiKey[16]; + + // Formate les noms de clé pour la température et l'humidité en fonction de l'index et du sous-index + snprintf(tempKey, sizeof(tempKey), "temp%d_%d", index, i); + snprintf(humiKey, sizeof(humiKey), "hum%d_%d", index, i); + + // Supprime les entrées correspondantes à la température et à l'humidité + preferences.remove(tempKey); + preferences.remove(humiKey); + } +} + +// Fonction pour envoyer les données locales +void SendLocalData() +{ + // Ouvre les préférences sous le nom "capteurs" en mode lecture seule + preferences.begin("capteurs", true); + + // Lit le compteur de sauvegardes, ou initialise à 0 s'il n'existe pas + int saveCounter = preferences.getInt("saveCounter", 0); + + // Boucle pour envoyer les données sauvegardées + for (int counter = 0; counter < saveCounter; counter++) + { + char timeKey[16]; + char chargeKey[16]; + + // Génère des clés uniques pour le temps et le niveau de charge + snprintf(timeKey, sizeof(timeKey), "time%d", counter); + snprintf(chargeKey, sizeof(chargeKey), "chargelvl%d", counter); + + // Lit les valeurs de temps et de niveau de charge depuis les préférences + time_t rawtime = preferences.getInt(timeKey, 0); + int chargelvl = preferences.getInt(chargeKey, 0); + + // Boucle pour lire les données de température et d'humidité + for (int i = 0; i < 5; i++) + { + char tempKey[16]; + char humiKey[16]; + + // Génère des clés uniques pour chaque valeur de température et d'humidité + snprintf(tempKey, sizeof(tempKey), "temp%d_%d", counter, i); + snprintf(humiKey, sizeof(humiKey), "hum%d_%d", counter, i); + + // Lit les valeurs de température et d'humidité depuis les préférences + temp[i] = preferences.getFloat(tempKey, 0); + hum[i] = preferences.getFloat(humiKey, 0); + } + + // Prépare le message à envoyer + char msgsld[200]; + sprintf(msgsld, "%lu|%s|%0.2f %0.2f %0.2f %0.2f %0.2f|%0.2f %0.2f %0.2f %0.2f %0.2f|%d", rawtime, CLUSTER, temp[0], temp[1], temp[2], temp[3], temp[4], hum[0], hum[1], hum[2], hum[3], hum[4], chargelvl); + + // Envoie le message via MQTT + MqttClient.publish(TOPIC, msgsld); + Serial.println(msgsld); + + // Supprime les clés après envoi + clearOldData(counter); + } + + // Réinitialise le compteur de sauvegardes + preferences.putInt("saveCounter", 0); + + // Termine l'accès aux préférences + preferences.end(); +} + +//-------------------- Boucle principale --------------------// + +/** + * @brief Main loop function for the ESP + */ +void loop() +{ + int year, month, day; + int lenght; + unsigned long now; + char time[30]; + char date[30]; + char msg[70]; + bool horoIssue = false; + + readSensors(sensors, temp, hum, SENSORS_NUMBER); + writeMessage(msg, temp, hum, SENSORS_NUMBER); + + MqttClient.loop(); + if (!MqttClient.connected()) + { + reconnect(); + } + + // MqttClient.publish(TOPIC, msg, date); + + // TimeClient.update(); + // TimeClient.getFormattedTime().toCharArray(time, 30); + + // tie(year, month, day) = getDate(); + // /* debug print*/ + // Serial.println('\n'); + // Serial.println(year,DEC); + // Serial.println(':'); + // Serial.println(month,DEC); + // Serial.println(':'); + // Serial.println(day,DEC); + // Serial.println('\n'); + + now = getTime(); + lenght = sprintf(date, "%s", ultoa(now, date, 10) /* "|%d-%d-%d ", year, month, day */); + sprintf(date + lenght, time); + // Serial.println("msg1 : "); //debug print + // Serial.println(msg); //debug print + sprintf(date + strlen(date), msg); + // Serial.println("msg2 : "); //debug print + // Serial.println(msg); //debug print + // Serial.println("date : "); //debug print + // Serial.println(date); //debug print + Serial.println("\n"); + Serial.println("-----------------Local---------------------"); + SendLocalData(); + nvs_flash_erase(); // Supprime les données en local. + MqttClient.publish(TOPIC, date); + Serial.println("-----------------Brut---------------------"); + Serial.println(date); + Serial.println("-----------------DeepSleep---------------------"); + + delay(2000); + + sleep(); + delay(1000); } \ No newline at end of file