#include #include #include #include #include #include // Structures de données struct ParVal { String param; String value; }; struct Conf_WHP { char devname[16] = ""; char ip_ipx[16] = ""; char api_key[16] = ""; }; // Version du firmware const String FIRMWARE_VERSION = "PatLeHibou's WiFi Heater Pilot v.2"; String FilePath = __FILE__; // Récupère le chemin et le nom du fichier source String FileName; // Recevra le nom du fichier seul, sans extension, permettant d'avoir la version précise de la release // Suivi de version // v.1.0 : première version complètement opérationnelle basée sur la version v.0.6d de développement // l'état du fil pilote, la luminosité, la présence ou non du capteur DHT ainsi que le nom du device sont conservés dans l'EEPROM et rechargé en cas de reboot (après coupure par ex.) // V.2.0 : deuxième version intégrant les remontées d'informations vers l'IPX // 2.0a : prend en charge les paramètres de connexion à l'IPX et pour la surveillance de la T° et l'hygrométrie, et envoie les requêtes HTTP vers l'IPX // 2.1a : version faisant appel à SPIFFS pour solliciter moins l'EEPROM lors des changements d'état // 2.1b : utilisation de la librairie DHTesp // 2.2b : modification de la structure du JSON pour avoir une arborescence des paramètres de th_control et introduction d'un sous-paramètre "status" (même modif qu'en 2.1a et 2.2a) // 2.2c : si la valeur de DeviceConfig lue au démarrage est 0xFF (valeur usuelle avec une carte WeMos neuve) DeviceConfig est forcé à 0 // 2.5 : évolution majeure dans la gestion du Wifi en tirant parti de la mémorisation du SSID et du Password directement par l'ESP // suppression de ces informations dans la structure de données rebaptisée Conf_WHP (pour WiFi Heater Pilot) // suppression de la mémorisation d'un accès dans le byte DeviceConfig réduit à gérer la présence du capteur DHT // suppression du byte qui jusqu'à la version 2.0a servait à mémoriser l'état du Fil Pilote // nettoyage du code (suppression des commentaires inutiles, ajouts, corrections de commentaires...) // 2.5a : ajout de la surveillance de la connexion WiFi (extinction LED Link si perte du réseau) // avec auto-reconnexion en cas de perte du réseau // 2.6 : remplacement de la procédure d'initialisation via requêtes HTTP par celle de WiFiManager // 2.6a : modification du réglage de luminosité des LEDs pour éviter le scintillement // luminosité fixée à 1000 si valeur demandée supérieure // ajout d'un Wipe de l'EEPROM si bouton Hard Reset enfoncé au démarrage (les LEDs Link et Rouge cligntotent alternativement). Attention : luminosité fixée à 0 après // mise en commentaire de la ligne "if (Etat.substring(0, 3).equalsIgnoreCase("EP=")) {Val=9;}" pour désactiver les fonctions de Dump, Wipe et Fill sur l'EEPROM // 2.7 : ajout du retour de la température et de l'humidité dans des entrées virtuelles analogiques de l'IPX // 2.7a : correction d'un bug empêchant de désactiver le capteur DHT (oubli de modifier un test suite au passage en 2.6) et d'un autre dans la réponse en JSON lors de l'entrée de la configuration de l'IPX (virgule manquante) // 2.7b : modification de la gestion de la luminosité des LEDs : gestion individuelle des 3 valeurs R, V et B et comptage pour 256 tours de boucle au lieu de 1000 (et modifications relatives) // //-------------------------------------------------------// // LISTE DES COMMANDES VALIDES POUR LE PILOTAGE VIA HTTP // //-------------------------------------------------------// // CF : Mode 0 fait passer le Fil Pilote en mode Confort (pas de tension) // EC : Mode 1 fait passer le Fil Pilote en mode Eco (alternance complète) // HG : Mode 2 fait passer le Fil Pilote en mode Hors-Gel (1/2 alternance négative) // AR : Mode 3 fait passer le Fil Pilote en mode Arrêt ou Délestage (1/2 alternance positive) // ? : retourne l'état du dispositif, l'état du DHT avec T° et Hygrométrie s'il est activé, la valeur de la luminosité // VE : retourne la version du Firmware et celle du fichier source // LUM : suivi de '?' et un ou trois paramètres sous la forme // - soit RVB=Valeur : luminosité identique pour les 3 LEDs, valeur entre 0 (extinction complète) et 255 (luminosité max) // - soit R=Valeur_R&V=Valeur_V&B=Valeur_B : luminosité différenciée pour les 3 LEDs, valeurs entre 0 (extinction complète) et 255 (luminosité max) // NM : suivi de '=' et un mot, renomme le dispositif (éviter les espaces qui seront traduits en %22) // TH : suivi de '=' et ON pour activer le capteur DHT s'il est présent, OFF (ou n'importe quoi d'autre) pour le désactiver // A partir de v.2.0 // EP : suivi de '=' et DUMP pour voir le contenu de l'EEPROM en Hexa et en ASCII, WIPE pour vider l'EEPROM (mise à 0 de toutes les cellules) // IPX_CFG : suivi de '?' et une suite de paramètres sous la forme IP_IPX=AddrIP_IPX&APIKey=apikey, si APIKey est omis elle est fixée à "" et l'APIkey n'est pas utilisée // CTRL_TH : suivi de '?' et une suite de paramètres sous la forme PERIOD=Périodicité_en_Minutes&T_MINI=Val_T°_mini&T_MAXI=Val_T°_maxi&SV_TE=Num_SV_T°&H_MINI=Val_Hum_mini&H_MAXI=Val_Hum_maxi&SV_HU=Num_SV_Hum // - Si PERIOD=0, inhibe la surveillance et les autres paramètres peuvent être omis (ne sont pas évalués) // - PERIOD doit être inférieure à 1440 soit une journée // - T_MINI doit être compris entre 0 et 20 // - T_MAXI doit être compris entre 0 et 30 // - T_MINI doit être inférieur à T_MAXI // - H_MINI doit être compris entre 0 et 99 // - H_MAXI doit être compris entre 0 et 99 // - H_MINI doit être inférieur à H_MAXI // - SV_TE et SV_HU doivent être comprises entre 1 et 128, si 0, n'est pas utilisée, les 2 peuvent être égales pour conduire au même comportement // RTRN_TH : suivi de '?' et une suite de paramètres sous la forme PERIOD=Périodicité_en_Minutes&VA_TE=Num_VA_T°&VA_HU=Num_VA_Hum // - Si PERIOD=0, inhibe la surveillance et les autres paramètres peuvent être omis (ne sont pas évalués) // - PERIOD doit être inférieure à 1440 soit une journée // - VA_TE et VA_HU doivent être comprises entre 1 et 32, si 0, n'est pas utilisée, les 2 ne peuvent être égales (conflit de remontées) // toutes ces commandes retournent une réponse en JSON // toute autre valeur retournera un message "status" : "error" // Constantes // Structure du contenu de l'EEPROM const uint16_t EEPROM_SIZE=512; // Taille utilisée de l'EEPROM const uint8_t FLAG_DEVICECONFIG=0; // Byte 0 Configuration du device 1er bit = DHT actif ou non const uint8_t LUM_VALUE=1; // Byte 1, 2 et 3 Valeur des luminosité Rouge, Verte et Bleue (3 x uint8_t : 3 x 1 octet) const uint8_t PERIODE_CTRL=4; // Byte 4 et 5 Valeur de la périodicité de contrôle de franchissement des seuils de T° et d'Humidité const uint8_t T_MIN=6; // Byte 6 Valeur de T° mini const uint8_t T_MAX=7; // Byte 7 Valeur de T° maxi const uint8_t SV_TE=8; // Byte 8 N° de la Sortie Virtuelle de l'IPX recevant les changements d'état liés à la T° (1 si T° < seuil mini, 0 si T° > seuil maxi) const uint8_t H_MIN=9; // Byte 9 Valeur d'humidité mini const uint8_t H_MAX=10; // Byte 10 Valeur d'humidité maxi const uint8_t SV_HU=11; // Byte 11 N° de la Sortie Virtuelle de l'IPX recevant les changements d'état liés à l'humidité (0 si Humi < seuil mini, 1 si Humi > seuil maxi) const uint8_t PERIODE_RTRN=12; // Byte 12 et 13 Valeur de la périodicité de mesure de la T° et de l'Humidité const uint8_t VA_TE=14; // Byte 14 N° de l'Entrée Virtuelle Analogique de l'IPX recevant la température (doit être différente de VA_HU) const uint8_t VA_HU=15; // Byte 15 N° de l'Entrée Virtuelle Analogique de l'IPX recevant la température (doit être différente de VA_TE) // Les bytes 16 à 31 sont réservés pour un usage ultérieur const uint8_t POS_DEVICECONFIG=32; // Byte 32 et au-delà : Structure de donnée mémorisant la configuration du device (Nom, Infos IPX...) // Nom du fichier contenant le statut du radiateur const String STATUS_FILENAME = "/status.dat"; // Commandes de configuration du boitier // Nb max de paramètres dans les commandes const uint8_t MAX_PARAMS = 10; // Nb de paramètres maximum pour dimensionnement du tableau (7 utilisés pour CTRL_TH) // Statut par défaut du fil pilote à l'initialisation de la connexion au WiFi (0 = Confort, 1 = Eco, 2 = Hors Gel, 3 = Arrêt) const uint8_t DEFAULT_PILOTE_STATUS = 1; // Mode par défaut Eco de manière à ce que la LED passe en orange après entrée d'une configuration valide // Cycle à l'initialisation du dispositif : // Clignotement lent rouge : attend qu'une station se connecte sur l'adresse IP par défaut // Clignotement lent vert : une station est connecté sur l'AP, attend une commande de configuration // Vert fixe : commande reçue en cours d'interprétation (la LED bleue clignote pour indiquer les tentatives d'accès au WiFi, 20 clignotements rapides = réseau rejoint) // Rouge de courte durée : le réseau n'a pu être rejoint, réinitialisation du dispositif // Orange (et bleue) fixe : l'appareil est connecté au réseau WiFi et peut être piloté // Constantes d'affectation des broches aux fonctions (à modifier en cas de changement d'utilisation des broches) const uint16_t ALT_POS = D8; // Broche pilotant l'alternance positive const uint16_t ALT_NEG = D7; // Broche pilotant l'alternance négative const uint16_t LED_ROUGE = D1; // Broche pilotant la LED rouge de la LED Bicolore const uint16_t LED_VERTE = D2; // Broche pilotant la LED verte de la LED Bicolore const uint16_t LED_LINK = D4; // Broche pilotant la LED Link WiFi bleue const uint16_t MODE_BUTTON = D6; // Broche recevant le bouton de changement de mode const uint16_t HARD_RESET = D5; // Broche recevant le bouton de Hard Reset const uint16_t SENSOR_PIN = D3; // Broche sur laquelle le capteur de T° et d'humidité est branché (s'il est présent) // Autres constantes de base const uint16_t LUMIN_BASE = 10; // Valeur entre 0 et 1000 spécifiant le ‰ de temps que les LEDs doivent être allumées par boucle loop (si 0 : complètement éteintes, mode discret) const uint16_t NB_TENTATIVES = 20; // Nombre de tentatives de lecture de la T° et de l'hygrométrie const String SEPARATEUR= "------------------------------------------------------------------------------------------------------------"; // Création de l'instance dht DHTesp dht; // Paramètres du périphérique // MAC adress *** ATTENTION la WiFi.softAPmacAddress() n'a pas la même valeur que celle de connexion au réseau *** const String MACaddress = WiFi.macAddress(); // Nom du réseau Ad Hoc de configuration : Pilote_ les 6 derniers octets de l'adresse Mac de la carte en hexa const String AP_Network = "Pilote_" + MACaddress.substring(9, 11) + MACaddress.substring(12, 14) + MACaddress.substring(15, 17); // Port d'écoute du serveur Web WiFiServer server(80); // Variables de gestion du WiFi et des échanges HTTP boolean WiFiConnected; // Booléen mémorisant l'état de la conexion WiFi char ssid[32] = ""; // SSID char pass[64] = ""; // Mot de passe d'accès au WiFi char devname[16] = ""; // Nom donné au device char ip_ipx[16] = ""; // Adresse Ip de l'IPX char api_key[16] = ""; // APIkey de l'IPX String ClientRequest; String RequeteAnalyse; String Method; String Command; ParVal Params[MAX_PARAMS]; boolean CdeOK; uint16_t DeviceConfig = 0; // 1er bit : Flag pour indiquer la présence ou non du capteur DHT Conf_WHP ConfWHP; // Infos de config du boitier en EEPROM File Status_File; String Nom_Dispositif; String Commande; short Num_Cde, Num_Cde_Recue; String HttpResponse; // Variables pour le capteur DHT boolean Capteur_DHT; // Vrai si actif (suppose qu'il soit présent) float Temp; float Humi; uint16_t Num_Tentative; // Numéro de la tentative de lecture // Variables pour la gestion de la luminosité des LEDs uint8_t Luminosite_R = 4; // Valeur de luminosité rouge en cours uint8_t Luminosite_V = 4; // Valeur de luminosité verte en cours uint8_t Luminosite_B = 4; // Valeur de luminosité bleue en cours uint16_t Val_Lum_Dde; // Valeur de luminosité demandée uint16_t Cpt_Lum; // Compteur de boucle pour la luminosité des LEDs // Variables intermédiaires char Char_Tab[16]; // Tableau de char pour conversion String to int // Variables pour la connexion à un IPX800 String IP_IPX = ""; String API_Key = ""; String IPX_API_Prefix = ""; String IPX_API_Command = ""; // Variables pour la surveillance de la T° et de l'Humidité int T_min = 10; int T_Max = 12; int H_min = 70; int H_Max = 80; int SV_Te = 0; int SV_Hu = 0; short Periode_Ctrl = 0; // Variables pour la remontée périodique de la T° et de l'Humidité int VA_Te = 0; int VA_Hu = 0; short Periode_Rtrn = 0; short CAN_Te, CAN_Hu; // Variable recevant la conversion de la T° et de l'humidité mesurée sur 16 bits // CAN_Te = (Temp+40)/120*65535 // CAN_Hu = Humi/100*65535 // Variables recevant les nouvelles valeurs demandées int New_T_min, New_T_Max, New_H_min, New_H_Max, New_SV_Te, New_SV_Hu, New_VA_Te, New_VA_Hu; int New_Periode_Ctrl, New_Periode_Rtrn; // Plantage du module lors du sscanf dans le case CTRL_TH si short au lieu de int !!! // Fonction de requête HTTP GET String GetWithAnswer(String IP_Addr, String Commande) { String httpurl; String ClientAnswer; HTTPClient http; httpurl = "http://"; httpurl += IP_Addr; // httpurl+="/"; supprimé car le massage contient un "/" en début de chaîne httpurl += Commande; Serial.println(httpurl); http.begin(httpurl); http.GET(); ClientAnswer = (http.getString()); http.end(); return ClientAnswer; } // Cette fonction extrait les caractères d'une chaine jusqu'à un symbole de séparation spécifié, une espace ou la fin de la chaine String ExtractStr(String Entree, char Separateur) { uint16_t i = 0; boolean finChaine = false; String Sortie = ""; while (Entree[i] != Separateur && Entree[i] != ' ' && i != Entree.length()) { Sortie += Entree[i]; i++; } return Sortie; } // Cette fonction extrait les couples (Paramètre, Valeur) présents dans une chaîne de la forme param1=value1¶m2&value2&... à concurrence de NB_PARAMS //void ExtractParamsValues (String Requete, ParVal ParsVals[NB_PARAMS]) { void ExtractParamsValues (String Requete, ParVal ParsVals[], uint8_t Nb_Params) { uint8_t i; // Initialisation à vide du tableau des paramètres for (i = 0; i < Nb_Params ; i++) { ParsVals[i].param = ""; ParsVals[i].value = ""; } // Extrait les paramètres et les valeurs séparés par = et & String ChAnalyse = Requete; i = 0; while ((ChAnalyse != "") && (i < Nb_Params)) { ParsVals[i].param = ExtractStr (ChAnalyse, '='); ChAnalyse = ChAnalyse.substring(ParsVals[i].param.length() + 1); ParsVals[i].value = ExtractStr (ChAnalyse, '&'); ChAnalyse = ChAnalyse.substring(ParsVals[i].value.length() + 1); // Suppression de la " éventuelle en début de chaîne if (ParsVals[i].value[0] == '"') { ParsVals[i].value = ParsVals[i].value.substring(1); } // Suppression du %22 (") éventuel en début de chaîne if (ParsVals[i].value.substring(0, 3) == "%22") { ParsVals[i].value = ParsVals[i].value.substring(3); } // Suppression de la " éventuelle en fin de chaîne if (ParsVals[i].value[ParsVals[i].value.length() - 1] == '"') { ParsVals[i].value = ParsVals[i].value.substring(0, ParsVals[i].value.length() - 1); } // Suppression du %22 (") éventuel en fin de chaîne if (ParsVals[i].value.substring(ParsVals[i].value.length() - 3) == "%22") { ParsVals[i].value = ParsVals[i].value.substring(0, ParsVals[i].value.length() - 3); } i++; } } // Cette fonction teste l'appui sur le bouton Hard Reset void Test_Hard_Reset () { if (digitalRead(HARD_RESET) == 0) { Serial.println(); Serial.println(F("**************************************")); Serial.println(F("* HARD RESET, REINITIALISATION EN AP *")); Serial.println(F("**************************************")); // Lors d'un Hard Reset, déconnecte d'un éventuel réseau WiFi et efface un SSID précédemment mémorisé Serial.print(F("Lecture du SSID mémorisé : ")); Serial.println(WiFi.SSID()); WiFi.disconnect(); // Réinitialisation des paramètres DeviceConfig = 0; // Pas de capteur DHT Luminosite_R = 4; // Luminosité rouge à 4/256 Luminosite_V = 4; // Luminosité verte à 4/256 Luminosite_B = 4; // Luminosité bleue à 4/256 Nom_Dispositif = AP_Network; // Nom du dispositif au nom par défaut du boitier Nom_Dispositif.toCharArray(devname, 16); for (uint8_t i = 0; i < 16 ; i++) ConfWHP.devname[i] = devname[i]; IP_IPX = ""; // Mise à "" de l'adresse IP de l'IPX IP_IPX.toCharArray(ip_ipx, 16); for (uint8_t i = 0; i < 16 ; i++) ConfWHP.ip_ipx[i] = ip_ipx[i]; API_Key = ""; // Mise à "" de l'APIkey de l'IPX API_Key.toCharArray(api_key, 16); for (uint8_t i = 0; i < 16 ; i++) ConfWHP.api_key[i] = api_key[i]; Periode_Ctrl = 0; // Mise à 0 de la périodicité de contrôle du climat (la désactive) Periode_Rtrn = 0; // Mise à 0 de la périodicité de remontée du climat (la désactive) // Mémorisation dans l'EEPROM EEPROM.write(FLAG_DEVICECONFIG,DeviceConfig); EEPROM.write(LUM_VALUE,Luminosite_R); EEPROM.write(LUM_VALUE+1,Luminosite_V); EEPROM.write(LUM_VALUE+2,Luminosite_B); EEPROM.put(POS_DEVICECONFIG,ConfWHP); EEPROM.put(PERIODE_CTRL, Periode_Ctrl); EEPROM.put(PERIODE_RTRN, Periode_Rtrn); EEPROM.end(); delay(1000); // délai pour la mémorisation de l'effacement Serial.print(F("Lecture du SSID après réinitialisation : ")); Serial.println(WiFi.SSID()); // Fait rebooter l'équipement ESP.restart(); } } // Cette fonction fait clignoter la led additionnelle sur la GPIO 2 (broche D4) et la led intégrée à l'inverse l'une de l'autre. // La durée d'allumage et d'extinction est donnée par delai en 1000ème de seconde void ClignoteLED(uint8_t nbre, long delai) { uint8_t i; nbre = abs (nbre); for (i = 1; i <= nbre ; i++) { digitalWrite(LED_LINK, LOW); delay(delai); digitalWrite(LED_LINK, HIGH); delay(delai); } } // ************************************************************** // ******** Fonctions propres au pilotage d'un radiateur ******** // ************************************************************** // Cette fonction donne le n° correspondant à l'état demandé : 0=Confort, 1=Eco, 2=Hors-Gel, 3=Arrêt // ou à la commande passée : 4=Status, 5=Version de Firmware, 6=Réglage de la luminosité, 7=Renommage du device, // 8=activation/désactivation DHT, 9=Dump/Wipe, 10=Configuration IPX, 11=Paramètres de surveillance de la T° et de l'hygrométrie int NumEtat(String Etat) { int Val = -1; if (Etat.equalsIgnoreCase("CF")) {Val=0;} if (Etat.equalsIgnoreCase("EC")) {Val=1;} if (Etat.equalsIgnoreCase("HG")) {Val=2;} if (Etat.equalsIgnoreCase("AR")) {Val=3;} if (Etat.equalsIgnoreCase("?")) {Val=4;} if (Etat.equalsIgnoreCase("VE")) {Val=5;} if (Etat.substring(0, 4).equalsIgnoreCase("LUM?")) {Val=6;} if (Etat.substring(0, 3).equalsIgnoreCase("NM=")) {Val=7;} if (Etat.substring(0, 3).equalsIgnoreCase("TH=")) {Val=8;} // if (Etat.substring(0, 3).equalsIgnoreCase("EP=")) {Val=9;} if (Etat.substring(0, 8).equalsIgnoreCase("IPX_CFG?")) {Val=10;} if (Etat.substring(0, 8).equalsIgnoreCase("CTRL_TH?")) {Val=11;} if (Etat.substring(0, 8).equalsIgnoreCase("RTRN_TH?")) {Val=12;} return Val; } // Cette fonction pilote le changement d'état des sorties, de la LED bicolore d'état et le mémorise dans le fichier d'état void Pilote (int Status) { switch (Status) { case 0 : // aucune alternance en sortie, LED allumée en rouge : Confort digitalWrite(ALT_NEG,LOW); digitalWrite(ALT_POS,LOW); digitalWrite(LED_ROUGE,HIGH); digitalWrite(LED_VERTE,LOW); break; case 1 : // pleine alternance en sortie, LED allumée en orange (rouge+vert) : Eco digitalWrite(ALT_NEG,HIGH); digitalWrite(ALT_POS,HIGH); digitalWrite(LED_ROUGE,HIGH); digitalWrite(LED_VERTE,HIGH); break; case 2 : // demie alternance négative en sortie, LED allumée en vert : Hors Gel digitalWrite(ALT_NEG,HIGH); digitalWrite(ALT_POS,LOW); digitalWrite(LED_ROUGE,LOW); digitalWrite(LED_VERTE,HIGH); break; case 3 : // demie alternance positive en sortie, LED éteinte : Arrêt digitalWrite(ALT_NEG,LOW); digitalWrite(ALT_POS,HIGH); digitalWrite(LED_ROUGE,LOW); digitalWrite(LED_VERTE,LOW); break; } // Mémorisation dans le fichier Serial.print(F("Status écrit : ")); Serial.println(Status); Status_File = SPIFFS.open(STATUS_FILENAME, "w+"); Status_File.write(Status); Status_File.seek(0, SeekSet); Serial.print(F("Status relu : ")); Serial.println(Status_File.read()); Status_File.close(); } //******************************************************************************************************************// // Setup // //******************************************************************************************************************// void setup() { // Affiche les communications sur le port série Serial.begin(9600); Serial.println(); Serial.println(F("Initialisation du système")); // Initialisation du WiFiManager WiFiManager wifiManager; // Extraction du nom du fichier source en tant que version de Firmware reverse(FilePath.begin(),FilePath.end()); FileName = ExtractStr(FilePath.substring(4),'/'); reverse(FileName.begin(),FileName.end()); // Déclaration des broches utilisées // LED Link pinMode(LED_LINK, OUTPUT); // Alternance - pinMode(ALT_NEG, OUTPUT); // Alternance + pinMode(ALT_POS, OUTPUT); // LED bicolore côté Rouge pinMode(LED_ROUGE, OUTPUT); // LED bicolore côté Vert pinMode(LED_VERTE, OUTPUT); // Bouton de changement de mode pinMode(MODE_BUTTON, INPUT); // Bouton Hard Reset pinMode(HARD_RESET, INPUT); // Charge l'EEPROM en RAM et lit le premier octet pour savoir si un capteur DHT est déclaré présent EEPROM.begin(EEPROM_SIZE); // Si bouton Hard Reses appuyé, Wipe l'EEPROM if (digitalRead(HARD_RESET) == 0) { Serial.println(F("Hard Reset appuyé, relachez pour poursuivre.")); while (digitalRead(HARD_RESET) == 0) { // Attend que Hard Reset soit relâché pour éviter un reset de la carte digitalWrite(LED_LINK,LOW); digitalWrite(LED_ROUGE,HIGH); delay(250); digitalWrite(LED_LINK,HIGH); digitalWrite(LED_ROUGE,LOW); delay(250); } Serial.println(F("Wipe de l'EEPROM")); for (uint16_t x = 0; x < EEPROM_SIZE; x++) { //Loop end of EEPROM address if (!(EEPROM.read(x) == 0)) { EEPROM.write(x, 0); // if not 0 write 0 to clear, it takes 3.3mS ? } yield(); } EEPROM.commit(); Serial.println(F("Wipe terminé, vérification...")); // Dump en Hexa et en ASCII Serial.println(F("Dump de l'EEPROM")); for (uint16_t i = 0 ; i < (EEPROM_SIZE/16) ; i++) { for (uint8_t j = 0; j < 16 ; j++) { Serial.print(EEPROM.read(i*16+j) < 0x10 ? "0" : ""); Serial.print(EEPROM.read(i*16+j), HEX); Serial.print(F(" ")); } for (uint8_t j = 0; j < 16 ; j++) { Serial.print((EEPROM.read(i*16+j) < 0x20) || (EEPROM.read(i*16+j) > 0x7F) ? '^' : char(EEPROM.read(i*16+j))); } Serial.println(F("")); yield(); } } // lit le premier octet pour savoir si un capteur DHT est déclaré présent DeviceConfig = EEPROM.read(FLAG_DEVICECONFIG); Serial.print(F("Lecture du DeviceConfig dans l'EEPROM : ")); Serial.println(DeviceConfig); // Lecture du SSID mémorisé par l'ESP8266 Serial.print(F("Lecture du SSID mémorisé : ")); Serial.println(WiFi.SSID()); //Démarrage du File System SPIFFS.begin(); Serial.println(F("Démarrage du File System")); Serial.println(SEPARATEUR); // Initialisation de la connexion si pas de SSID connu if (WiFi.SSID() == "") { digitalWrite(LED_LINK,HIGH); digitalWrite(LED_ROUGE,HIGH); AP_Network.toCharArray(Char_Tab,14); wifiManager.autoConnect(Char_Tab); } //------------------------------------------------------------------------// // Une configuration valide a été entrée et l'accès au WiFi a été vérifié // //------------------------------------------------------------------------// // Récupère les infos de luminosité et de configuration du boitier Luminosite_R = EEPROM.read(LUM_VALUE); Luminosite_V = EEPROM.read(LUM_VALUE+1); Luminosite_B = EEPROM.read(LUM_VALUE+2); EEPROM.get(POS_DEVICECONFIG, ConfWHP); Serial.print(F("Device Config : ")); Serial.println(EEPROM.read(FLAG_DEVICECONFIG)); // Initialisation dans le mode précédemment mémorisé // Vérification de l'existence du fichier status.dat if (!SPIFFS.exists(STATUS_FILENAME)) { // Le crée et l'initialise avec la valeur par défaut s'il n'existe pas Status_File = SPIFFS.open(STATUS_FILENAME, "w"); Status_File.write(DEFAULT_PILOTE_STATUS); Status_File.close(); } Status_File = SPIFFS.open(STATUS_FILENAME, "r"); Num_Cde = Status_File.read(); Status_File.close(); Serial.print(F("Statut du radiateur (valeur lue dans le fichier Status) : ")); Serial.println(Num_Cde); // Bascule du radiateur dans son état antérieur Pilote(Num_Cde); // Définit les paramètres du capteur et positionne le booléen Capteur_DHT en fonction de la valeur du 1er bit de DeviceConfig dht.setup(SENSOR_PIN,DHTesp::DHT22); Capteur_DHT = DeviceConfig%2; if (Capteur_DHT) { // Effectue une première lecture de T° et d'Humidité pour que la première en surveillance ne soit pas à 0 Temp = dht.getTemperature( ); Humi = dht.getHumidity(); } // Nomme le périphérique Nom_Dispositif = ConfWHP.devname; if (Nom_Dispositif != "") WiFi.hostname(Nom_Dispositif); Nom_Dispositif = WiFi.hostname(); // Connecte au WiFi Serial.print(F("Recherche réseau WiFi : ")); Serial.println(WiFi.SSID()); WiFi.begin(); while ((!(WiFi.status() == WL_CONNECTED))) { delay(300); Serial.print(F(".")); ClignoteLED(2, 30); digitalWrite(LED_LINK, LOW); Test_Hard_Reset(); } WiFiConnected = true; // Passe le booléen à vrai WiFi.setAutoReconnect(true); // Permet la reconnexion automatique en cas de perte du réseau Serial.println(); Serial.print(F("Connecté au WIFI : ")); Serial.println(WiFi.SSID()); Serial.print(F("Puissance du signal ")); Serial.print(WiFi.RSSI()); Serial.println(F(" dBm")); ClignoteLED(5, 250); // Démarrage du Serveur Web server.begin(); // Affichage des informations sur le port série Serial.println(F("Serveur Web démarré")); Serial.print(F("L\'adresse IP du dispositif est ")); Serial.println((WiFi.localIP().toString())); Serial.print(F("Le nom du dispositif est ")); Serial.println((WiFi.hostname())); Serial.print(F("La version du firmware est ")); Serial.println(FIRMWARE_VERSION); Serial.print(F("Le fichier source est ")); Serial.println(FileName); Serial.print(F("La luminosité RVB est ")); Serial.print(Luminosite_R); Serial.print(F(", ")); Serial.print(Luminosite_V); Serial.print(F(", ")); Serial.println(Luminosite_B); Serial.print(F("Le capteur DHT est à ")); Serial.println(Capteur_DHT); // Charge et affiche les paramètres de surveillance de la T° et de l'humidité IP_IPX = ConfWHP.ip_ipx; if (IP_IPX != "") { Serial.print(F("IP IPX800 : ")); Serial.println(IP_IPX); IPX_API_Prefix = "/api/xdevices.json?"; if (ConfWHP.api_key != "") { IPX_API_Prefix = IPX_API_Prefix+"key="+ConfWHP.api_key+"&"; } Serial.print(F("Préfixe des requêtes HTTP pour IPX : http://")); Serial.println(IP_IPX+IPX_API_Prefix); } T_min = EEPROM.read(T_MIN); T_Max = EEPROM.read(T_MAX); H_min = EEPROM.read(H_MIN); H_Max = EEPROM.read(H_MAX); SV_Te = EEPROM.read(SV_TE); SV_Hu = EEPROM.read(SV_HU); Periode_Ctrl = EEPROM.read(PERIODE_CTRL); if (Periode_Ctrl == 0) { Serial.println(F("Surveillance T° et Hygrométrie désactivée, périodicité à 0")); } else { Serial.println(F("Surveillance T° et Hygrométrie activée")); Serial.print(F("Périodicité : ")); Serial.print(Periode_Ctrl); Serial.println(F(" minutes")); Serial.print(F("T° mini : ")); Serial.print(T_min); Serial.println(F(" °C")); Serial.print(F("T° Maxi : ")); Serial.print(T_Max); Serial.println(F(" °C")); Serial.print(F("La sortie virtuelle ")); Serial.print(SV_Te); Serial.println(F(" est sélectionnée pour la surveillance de la T°")); Serial.print(F("Hygro mini : ")); Serial.print(H_min); Serial.println(F(" %")); Serial.print(F("Hygro Maxi : ")); Serial.print(H_Max); Serial.println(F(" %")); Serial.print(F("La sortie virtuelle ")); Serial.print(SV_Hu); Serial.println(F(" est sélectionnée pour la surveillance de l'hygrométrie")); } VA_Te = EEPROM.read(VA_TE); VA_Hu = EEPROM.read(VA_HU); Periode_Rtrn = EEPROM.read(PERIODE_RTRN); if (Periode_Ctrl == 0) { Serial.println(F("Remontée T° et Hygrométrie désactivée, périodicité à 0")); } else { Serial.println(F("Remontée T° et Hygrométrie activée")); Serial.print(F("Périodicité : ")); Serial.print(Periode_Rtrn); Serial.println(F(" minutes")); Serial.print(F("L'entrée analogique virtuelle ")); Serial.print(VA_Te); Serial.println(F(" est sélectionnée pour la remontée de la T°")); Serial.print(F("L'entrée analogique virtuelle ")); Serial.print(VA_Hu); Serial.println(F(" est sélectionnée pour la remontée de l'hygrométrie")); } Serial.println(F("Pilote correctement démarré, en attente de commande...")); Serial.println(SEPARATEUR); } //******************************************************************************************************************// //******************************************************************************************************************// // Loop // //******************************************************************************************************************// //******************************************************************************************************************// void loop() { // Test du bouton Hard Reset Test_Hard_Reset(); // Test du bouton de changement d'état if (digitalRead(MODE_BUTTON) == 0) { delay(10);// Attend 10 ms que l'état du bouton soit stabilisé (pour éviter les commandes multiples en cas de rebond du poussoir) Serial.println(); Serial.print(F("Passage d'état ")); Serial.print(Num_Cde); Num_Cde = (Num_Cde+1)%4; Serial.print(F(" à ")); Serial.println(Num_Cde); Pilote(Num_Cde); while (digitalRead(MODE_BUTTON) == 0) { // Attend que le bouton soit relâché delay(1); } delay(10);// Attend 10 ms que l'état du bouton soit stabilisé (pour éviter les commandes multiples en cas de rebond du poussoir lors de sa réouverture) } // Teste une fois toutes les 10 secondes si le WiFi est bien connecté if ((millis()%10000) == 0) { WiFiConnected = WiFi.isConnected(); } // Clignotement des LEDs pour réduire l'intensité if ((Luminosite_R+Luminosite_V+Luminosite_B) != 0) { // Vérifie si toutes les luminosités ne sont pas à 0 Cpt_Lum=(Cpt_Lum+1)%256; // Luminosité de la LED Link (bleue) if (Luminosite_B != 0) { if (((Cpt_Lum%(256/Luminosite_B)) == 0) && (WiFiConnected)) { digitalWrite(LED_LINK,HIGH); // Allume la LED Link uniquement si le WiFi est connecté } else { digitalWrite(LED_LINK,LOW); } } else { digitalWrite(LED_LINK,LOW); } // Luminosité de la LED rouge if (Luminosite_R != 0) { if (((Cpt_Lum%(256/Luminosite_R)) == 0) && (Num_Cde <=1)) { digitalWrite(LED_ROUGE,HIGH); // Allume la LED rouge pour les modes 0 et 1 } else { digitalWrite(LED_ROUGE,LOW); } } else { digitalWrite(LED_ROUGE,LOW); } // Luminosité de la LED verte if (Luminosite_V != 0) { if (((Cpt_Lum%(256/Luminosite_V) == 0) && (((Num_Cde+3)%4) <=1))) { digitalWrite(LED_VERTE,HIGH); // Allume la LED verte pour les modes 0 et 1 } else { digitalWrite(LED_VERTE,LOW); } } else { digitalWrite(LED_VERTE,LOW); } } else { digitalWrite(LED_LINK,LOW); digitalWrite(LED_ROUGE,LOW); digitalWrite(LED_VERTE,LOW); } // Surveillance de la T° et de l'hygrométrie if ((IPX_API_Prefix != "") && (Periode_Ctrl != 0) && (Capteur_DHT)) { // Les paramètres de surveillance sont configurés et le capteur DHT est présent, on remonte les infos vers l'IPX à la périodicité demandée if ((millis()-30000) % (Periode_Ctrl * 60000) == 0) { // Diffère la mesure de 30 secondes pour éviter la simultanéité avec celle de remontée Serial.println(F("Mesure de la T° et de l'hygrométrie")); delay(1); // Pour s'assurer qu'il n'y aura pas de double déclenchement // Mesure de la T° et de l'hygrométrie Num_Tentative=0; Temp = dht.getTemperature( ); Humi = dht.getHumidity(); while ((isnan(Temp) || isnan(Humi)) && Num_Tentative < NB_TENTATIVES) { delay(100); Temp = dht.getTemperature( ); Humi = dht.getHumidity(); Num_Tentative++; } if (!(isnan(Temp)) && (SV_TE != 0) ) { // Une T° a bien été mesurée et la sortie virtuelle n'est pas 0 Serial.print(F("La T° mesurée est de : ")); Serial.print(Temp); Serial.println(F(" °C")); if (Temp < T_min) { // La T° est inférieure au seuil mini : on place la sortie virtuelle à 1 (Set) Serial.print(F("Elle est inférieure au seuil minimal de ")); Serial.print(T_min); Serial.print(F(" °C, la sortie virtuelle ")); Serial.print(SV_Te); Serial.println(F(" passe à 1")); IPX_API_Command = IPX_API_Prefix+"SetVO="+SV_Te; Serial.print(F("Requête HTTP envoyée à l'IPX : http://")); Serial.println(IP_IPX+IPX_API_Command); HttpResponse = (GetWithAnswer(IP_IPX, IPX_API_Command)); Serial.println(HttpResponse); } if (Temp > T_Max) { // La T° est supérieure au seuil maxi : on place la sortie virtuelle à 0 (Clear) Serial.print(F("Elle est supérieure au seuil maximal de ")); Serial.print(T_Max); Serial.print(F(" °C, la sortie virtuelle ")); Serial.print(SV_Te); Serial.println(F(" passe à 0")); IPX_API_Command = IPX_API_Prefix+"ClearVO="+SV_Te; Serial.print(F("Requête HTTP envoyée à l'IPX : http://")); Serial.println(IP_IPX+IPX_API_Command); HttpResponse = (GetWithAnswer(IP_IPX, IPX_API_Command)); Serial.println(HttpResponse); } } if (!(isnan(Humi)) && (SV_HU != 0)) { // Une humidité a bien été mesurée et la sortie virtuelle n'est pas 0 Serial.print(F("L'humidité mesurée est de : ")); Serial.print(Humi); Serial.println(F(" %")); if (Humi < H_min) { // L'humidité est inférieure au seuil mini : on place la sortie virtuelle à 0 (Clear) Serial.print(F("Elle est inférieure au seuil minimal de ")); Serial.print(H_min); Serial.print(F(" %, la sortie virtuelle ")); Serial.print(SV_Hu); Serial.println(F(" passe à 0")); IPX_API_Command = IPX_API_Prefix+"ClearVO="+SV_Hu; Serial.print(F("Requête HTTP envoyée à l'IPX : http://")); Serial.println(IP_IPX+IPX_API_Command); HttpResponse = (GetWithAnswer(IP_IPX, IPX_API_Command)); Serial.println(HttpResponse); } if (Humi > H_Max) { // L'humidité est supérieure au seuil maxi : on place la sortie virtuelle à 1 (Set) Serial.print(F("Elle est supérieure au seuil maximal de ")); Serial.print(H_Max); Serial.print(F(" %, la sortie virtuelle ")); Serial.print(SV_Hu); Serial.println(F(" passe à 1")); IPX_API_Command = IPX_API_Prefix+"SetVO="+SV_Hu; Serial.print(F("Requête HTTP envoyée à l'IPX : http://")); Serial.println(IP_IPX+IPX_API_Command); HttpResponse = (GetWithAnswer(IP_IPX, IPX_API_Command)); Serial.println(HttpResponse); } } Serial.println(SEPARATEUR); } } // Remontée de la T° et de l'hygrométrie if ((IPX_API_Prefix != "") && (Periode_Rtrn != 0) && (Capteur_DHT)) { // Les paramètres de remontée sont configurés et le capteur DHT est présent, on remonte les infos vers l'IPX à la périodicité demandée if (millis() % (Periode_Rtrn * 60000) == 0) { Serial.println(F("Remontée de la T° et de l'hygrométrie")); delay(1); // Pour s'assurer qu'il n'y aura pas de double déclenchement // Mesure de la T° et de l'hygrométrie Num_Tentative=0; Temp = dht.getTemperature( ); Humi = dht.getHumidity(); while ((isnan(Temp) || isnan(Humi)) && Num_Tentative < NB_TENTATIVES) { delay(100); Temp = dht.getTemperature( ); Humi = dht.getHumidity(); Num_Tentative++; } if (!(isnan(Temp)) && (VA_TE !=0)) { // Une T° a bien été mesurée et l'entrée analogique virtuelle n'est pas 0 Serial.print(F("La T° remontée est de : ")); Serial.print(Temp); Serial.println(F(" °C")); CAN_Te = (Temp+40)/120*65535; Serial.print(F("soit ")); Serial.print(CAN_Te); Serial.println(F(" sur 16 bits")); if (VA_Te < 10) { IPX_API_Command = IPX_API_Prefix+"SetVA0"+VA_Te+"="+CAN_Te; } else { IPX_API_Command = IPX_API_Prefix+"SetVA"+VA_Te+"="+CAN_Te; } Serial.print(F("Requête HTTP envoyée à l'IPX : http://")); Serial.println(IP_IPX+IPX_API_Command); HttpResponse = (GetWithAnswer(IP_IPX, IPX_API_Command)); Serial.println(HttpResponse); } if (!(isnan(Humi)) && (VA_HU !=0)) { // Une humidité a bien été mesurée et l'entrée analogique virtuelle n'est pas 0 Serial.print(F("L'humidité remontée est de : ")); Serial.print(Humi); Serial.println(F(" %")); CAN_Hu = Humi/100*65535; Serial.print(F("soit ")); Serial.print(CAN_Hu); Serial.println(F(" sur 16 bits")); if (VA_Hu < 10) { IPX_API_Command = IPX_API_Prefix+"SetVA0"+VA_Hu+"="+CAN_Hu; } else { IPX_API_Command = IPX_API_Prefix+"SetVA"+VA_Hu+"="+CAN_Hu; } Serial.print(F("Requête HTTP envoyée à l'IPX : http://")); Serial.println(IP_IPX+IPX_API_Command); HttpResponse = (GetWithAnswer(IP_IPX, IPX_API_Command)); Serial.println(HttpResponse); } Serial.println(SEPARATEUR); } } // Attente d'une connexion sur le serveur Web WiFiClient client = server.available(); if (!client) { return; } Serial.println(F("Client connecté sur le serveur web embarqué")); Serial.print(F("Adresse IP du client :")); Serial.println((client.remoteIP())); ClientRequest = (client.readStringUntil('\r')); Serial.println(ClientRequest); Commande=ClientRequest.substring(5,ClientRequest.length()-9); Serial.print("Commande \""+Commande+"\" : "); Num_Cde_Recue=NumEtat(Commande); Serial.println(Num_Cde_Recue); if (Num_Cde_Recue >= 4) { Serial.print(F("Status : ")); Serial.println(Num_Cde); } client.println(F("HTTP/1.1 200 OK")); client.println(F("Cache-Control: no-cache")); client.println(F("Connection: close")); client.println(F("Content-Type: application/json")); client.println(F("access-control-allow-origin: *")); client.println(F("")); client.println(F("{")); client.println(" \"device\" : \""+Nom_Dispositif+"\","); if (Num_Cde_Recue == -1) { ClignoteLED(5, 60); client.println(F(" \"status\" : \"error\"")); } else { ClignoteLED(1, 60); client.println(" \"command\" : \""+Commande+"\","); switch (Num_Cde_Recue) { case 4 : // Lecture de l'état client.print(F(" \"status\" : \"")); client.print((digitalRead(ALT_POS)*3+(digitalRead(ALT_NEG)*2))%4); client.println(F("\",")); client.print(F(" \"dht\" : \"")); client.print(Capteur_DHT); client.println(F("\",")); // Si présence du capteur DHT, mesure de la T° et de l'humidité if (Capteur_DHT) { Temp = dht.getTemperature( ); Humi = dht.getHumidity(); Serial.println(F("Lecture T° et Humidité")); Num_Tentative=0; while ((isnan(Temp) || isnan(Humi)) && Num_Tentative < NB_TENTATIVES) { delay(100); Temp = dht.getTemperature( ); Humi = dht.getHumidity(); Serial.print(Temp); Serial.print(F(" °C / ")); Serial.print(Humi); Serial.println(F("%")); Num_Tentative++; } Serial.print(F("Température : ")); Serial.print(Temp); Serial.println(F(" °C")); Serial.print(F("Humidité : ")); Serial.print(Humi); Serial.println(F(" %")); client.print(F(" \"temp\" : ")); if (!isnan(Temp)) { client.print(Temp); } else { client.print("\"nan\""); } client.println(F(",")); client.print(F(" \"humi\" : ")); if (!isnan(Humi)) { client.print(Humi); } else { client.print("\"nan\""); } client.println(F(",")); } client.print(F(" \"IP_IPX\" : \"")); client.print(IP_IPX); client.println(F("\",")); client.print(F(" \"IPX_API_Prefix\" : \"")); client.print(IPX_API_Prefix); client.println(F("\",")); client.println(F(" \"th_control\" : {")); if (Periode_Ctrl == 0) { client.println(F(" \"status\" : \"Off\"")); } else { client.println(F(" \"status\" : \"On\",")); client.print(F(" \"periodicity\" : \"")); client.print(Periode_Ctrl); client.println(F("\",")); client.print(F(" \"t_min\" : \"")); client.print(T_min); client.println(F("\",")); client.print(F(" \"t_max\" : \"")); client.print(T_Max); client.println(F("\",")); client.print(F(" \"sv_te\" : \"")); client.print(SV_Te); client.println(F("\",")); client.print(F(" \"h_min\" : \"")); client.print(H_min); client.println(F("\",")); client.print(F(" \"h_max\" : \"")); client.print(H_Max); client.println(F("\",")); client.print(F(" \"sv_hu\" : \"")); client.print(SV_Hu); client.println(F("\"")); } client.println(F(" },")); client.println(F(" \"th_return\" : {")); if (Periode_Rtrn == 0) { client.println(F(" \"status\" : \"Off\"")); } else { client.println(F(" \"status\" : \"On\",")); client.print(F(" \"periodicity\" : \"")); client.print(Periode_Rtrn); client.println(F("\",")); client.print(F(" \"va_te\" : \"")); client.print(VA_Te); client.println(F("\",")); client.print(F(" \"va_hu\" : \"")); client.print(VA_Hu); client.println(F("\"")); } client.println(F(" },")); client.print(F(" \"blue_luminosity\" : \"")); client.print(Luminosite_B); client.println(F("\",")); client.print(F(" \"red_luminosity\" : \"")); client.print(Luminosite_R); client.println(F("\",")); client.print(F(" \"green_luminosity\" : \"")); client.print(Luminosite_V); client.println(F("\",")); client.print(F(" \"RSSI\" : \"")); client.print(WiFi.RSSI()); break; case 5 : // Lecture de la version du firmware client.println(" \"version\" : \""+FIRMWARE_VERSION+"\","); client.print(" \"source\" : \""+FileName); break; case 6 : // Changement de luminosité des LEDs ExtractParamsValues(Commande.substring(4), Params, 3); // Extrait les 3 paramètres attendus : RVB ou R, V et B CdeOK = false; if (Params[0].param.equalsIgnoreCase("RVB")) { // Le paramètre est RVB : on fixe la même valeur pour les 3 luminosités Params[0].value.toCharArray(Char_Tab,16); int Value; sscanf(Char_Tab, "%i", &Value); Val_Lum_Dde=Value; Serial.print(F("Luminosité RVB demandée : ")); Serial.println(Val_Lum_Dde); if (Val_Lum_Dde <= 255) { Luminosite_R = Val_Lum_Dde; Luminosite_V = Val_Lum_Dde; Luminosite_B = Val_Lum_Dde; } else { Luminosite_R = 255; Luminosite_V = 255; Luminosite_B = 255; } CdeOK=true; } if ((Params[0].param.equalsIgnoreCase("R")) && (Params[1].param.equalsIgnoreCase("V")) && (Params[2].param.equalsIgnoreCase("B"))) { // Les 3 paramètres sont R, V et B (dans l'ordre) : on fixe des valeurs différenciées pour les 3 luminosités // Valeur de luminosité rouge Params[0].value.toCharArray(Char_Tab,16); int Value; sscanf(Char_Tab, "%i", &Value); Val_Lum_Dde=Value; Serial.print(F("Luminosité R demandée : ")); Serial.println(Val_Lum_Dde); if (Val_Lum_Dde <= 255) { Luminosite_R = Val_Lum_Dde; } else { Luminosite_R = 255; } // Valeur de luminosité verte Params[1].value.toCharArray(Char_Tab,16); sscanf(Char_Tab, "%i", &Value); Val_Lum_Dde=Value; Serial.print(F("Luminosité V demandée : ")); Serial.println(Val_Lum_Dde); if (Val_Lum_Dde <= 255) { Luminosite_V = Val_Lum_Dde; } else { Luminosite_V = 255; } // Valeur de luminosité bleue Params[2].value.toCharArray(Char_Tab,16); sscanf(Char_Tab, "%i", &Value); Val_Lum_Dde=Value; Serial.print(F("Luminosité B demandée : ")); Serial.println(Val_Lum_Dde); if (Val_Lum_Dde <= 255) { Luminosite_B = Val_Lum_Dde; } else { Luminosite_B = 255; } CdeOK=true; } // Indique le résultat if (CdeOK) { // Mémorisation des luminosités dans l'EEPROM EEPROM.write(LUM_VALUE,Luminosite_R); EEPROM.write(LUM_VALUE+1,Luminosite_V); EEPROM.write(LUM_VALUE+2,Luminosite_B); EEPROM.commit(); client.print(F(" \"blue_luminosity\" : \"")); client.print(Luminosite_B); client.println(F("\",")); client.print(F(" \"red_luminosity\" : \"")); client.print(Luminosite_R); client.println(F("\",")); client.print(F(" \"green_luminosity\" : \"")); client.print(Luminosite_V); } else { client.print(F(" \"status\" : \"LUMI Command error, incorrect or missing parameter(s)")); } break; case 7 : // Renommage du device Nom_Dispositif = Commande.substring(3,18); //---------------------------------------------------- Nom_Dispositif.toCharArray(devname, 16); for (uint8_t i = 0; i < 16 ; i++) ConfWHP.devname[i] = devname[i]; // Mémorisation du nouveau nom dans l'EEPROM EEPROM.put(POS_DEVICECONFIG,ConfWHP); EEPROM.commit(); Serial.print(F("Nouveau nom du device : ")); Serial.println(Nom_Dispositif); client.print(F(" \"newname\" : \"")); client.print(Nom_Dispositif); break; case 8 : // Activation/désactivation du DHT if (Commande.substring(3).equalsIgnoreCase("ON")) { if (!(DeviceConfig%2)) { DeviceConfig = DeviceConfig+1; // Positionne à 1 le 1er bit de DeviceConfig si "ON" Capteur_DHT = true; // Place Capteur_DHT à vrai } } else { if (DeviceConfig%2) { DeviceConfig = DeviceConfig-1; // Positionne à 0 le 1er bit de DeviceConfig si "OFF" Capteur_DHT = false; // Place Capteur_DHT à faux } } Serial.print(F("Config écrite : ")); Serial.println(DeviceConfig); EEPROM.write(FLAG_DEVICECONFIG,DeviceConfig); Serial.print(F("Config relue : ")); Serial.println(EEPROM.read(FLAG_DEVICECONFIG)); EEPROM.commit(); Serial.print(F("Le capteur DHT est à ")); Serial.println(Capteur_DHT); client.print(F(" \"dht\" : \"")); client.print(Capteur_DHT); break; case 9 : // Dump, Wipe (remplissage avec des 0) ou Fill (remplissage avec des 0xFF) de l'EEPROM if (Commande.substring(3).equalsIgnoreCase("WIPE")) { Serial.println(F("Wipe de l'EEPROM")); for (uint16_t x = 0; x < EEPROM_SIZE; x++) { //Loop end of EEPROM address if (!(EEPROM.read(x) == 0)) { EEPROM.write(x, 0); // if not 0 write 0 to clear, it takes 3.3mS ? } yield(); } EEPROM.commit(); Serial.println(F("Wipe terminé, vérification...")); Commande="EP=DUMP"; // Force un Dump de l'EEPROM après Wipe pour contrôle } if (Commande.substring(3).equalsIgnoreCase("FILL")) { Serial.println(F("Fill de l'EEPROM")); for (uint16_t x = 0; x < EEPROM_SIZE; x++) { //Loop end of EEPROM address if (!(EEPROM.read(x) == 0xFF)) { EEPROM.write(x, 0xFF); // if not 0 write 0 to clear, it takes 3.3mS ? } yield(); } DeviceConfig = 0; // Force le device Config à 0 EEPROM.write(FLAG_DEVICECONFIG,DeviceConfig); EEPROM.commit(); Serial.println(F("Fill terminé, vérification...")); Commande="EP=DUMP"; // Force un Dump de l'EEPROM après Fill pour contrôle } if (Commande.substring(3).equalsIgnoreCase("DUMP")) { // Dump en Hexa et en ASCII Serial.println(F("Dump de l'EEPROM")); for (uint16_t i = 0 ; i < (EEPROM_SIZE/16) ; i++) { for (uint8_t j = 0; j < 16 ; j++) { Serial.print(EEPROM.read(i*16+j) < 0x10 ? "0" : ""); Serial.print(EEPROM.read(i*16+j), HEX); Serial.print(F(" ")); } for (uint8_t j = 0; j < 16 ; j++) { Serial.print((EEPROM.read(i*16+j) < 0x20) || (EEPROM.read(i*16+j) > 0x7F) ? '^' : char(EEPROM.read(i*16+j))); } Serial.println(F("")); yield(); } client.print(F(" \"status\" : \"Successfull")); break; } Serial.println(F("Commande non reconnue")); client.print(F(" \"status\" : \"EP Command error")); break; case 10 : // Paramètres de l'IPX Serial.println(F("Configuration des paramètres de l'IPX")); ExtractParamsValues(Commande.substring(8), Params, 2); // Extrait les 2 paramètres attendus : Ip et, éventuellement, APIkey de l'IPX CdeOK = true; // Teste si le 1er paramètre est bien IP_IPX if (!Params[0].param.equalsIgnoreCase("IP_IPX")) { CdeOK = false; Serial.println(F("Le premier paramètre n'est pas IP_IPX")); Serial.println(F("Configuration non modifiée")); client.print(F(" \"status\" : \"IPX_CFG Command error, 1st param not IP_IPX")); Params[1].param = ""; // Efface l'éventuel deuxième paramètre pour ne pas rentrer dans le test suivant } else { IP_IPX = Params[0].value; } // Teste si le 2ème paramètre existe et est bien APIkey //API_Key = ""; if (Params[1].param != "") { if (!Params[1].param.equalsIgnoreCase("APIkey")) { CdeOK = false; Serial.println(F("Une second paramètre existe mais n'est pas APIkey")); Serial.println(F("Configuration non modifiée")); client.print(F(" \"status\" : \"IPX_CFG Command error, 2nd param present but not APIkey")); IP_IPX = ConfWHP.ip_ipx; // Restaure l'ancienne valeur de l'IP de l'IPX } else { API_Key = Params[1].value; } } IPX_API_Prefix = ""; if (IP_IPX != "") { Serial.print(F("IP IPX800 : ")); Serial.println(IP_IPX); IPX_API_Prefix = "/api/xdevices.json?"; if (API_Key != "") { IPX_API_Prefix = IPX_API_Prefix+"key="+API_Key+"&"; } } Serial.print(F("Préfixe des requêtes HTTP pour IPX : ")); Serial.println((IPX_API_Prefix=="") ? "Effacée" : "http://"+IP_IPX+IPX_API_Prefix); // Mémorisation des paramètres dans l'EEPROM if (CdeOK) { client.println(F(" \"status\" : \"IPX_CFG Success\",")); client.print(F(" \"IP_IPX\" : \"")); client.print(IP_IPX); client.println(F("\",")); client.print(F(" \"IPX_API_Prefix\" : \"")); client.print(IPX_API_Prefix); IP_IPX.toCharArray(ip_ipx, 16); for (uint8_t i = 0; i < 16 ; i++) ConfWHP.ip_ipx[i] = ip_ipx[i]; API_Key.toCharArray(api_key, 16); for (uint8_t i = 0; i < 16 ; i++) ConfWHP.api_key[i] = api_key[i]; EEPROM.put(POS_DEVICECONFIG,ConfWHP); EEPROM.commit(); } break; case 11 : // Paramètres de surveillance du climat Serial.println(F("Configuration des paramètres pour la surveillance du climat")); ExtractParamsValues(Commande.substring(8), Params, 7); // Extrait les 7 paramètres attendus : PERIOD, T_MINI, T_MAXI, SV_TE et optionnellement H_MINI, H_MAXI et SV_HU CdeOK = true; if (!Params[0].param.equalsIgnoreCase("PERIOD")) { CdeOK = false; Serial.println(F("Le premier paramètre n'est pas PERIOD")); Serial.println(F("Configuration non modifiée")); client.print(F(" \"status\" : \"CTRL_TH Command error, 1st param not PERIOD")); } else { Serial.println(F("Le premier paramètre est bien PERIOD")); // Duplication des anciennes valeurs New_T_min = T_min; New_T_Max = T_Max; New_H_min = H_min; New_H_Max = H_Max; New_SV_Te = SV_Te; New_SV_Hu = SV_Hu; New_Periode_Ctrl = Periode_Ctrl; // Evaluation de la périodicité demandée Params[0].value.toCharArray(Char_Tab,16); sscanf(Char_Tab, "%i", &New_Periode_Ctrl); if (New_Periode_Ctrl >= 1440) { // Périodicité supérieure à 24h refusée CdeOK = false; Serial.println(F("La périodicité demandée est supérieure à 24h")); Serial.println(F("Configuration non modifiée")); client.print(F(" \"status\" : \"CTRL_TH Command error, PERIOD more than 24h")); } else { if (New_Periode_Ctrl == 0) { // La périodicité est fixée à 0, inutile d'évaluer le reste Serial.println(F("Périodicité à 0, fonction de surveillance désactivée")); } else { Serial.println(F("La périodicité est comprise entre 0 et 24h")); if ((!Params[1].param.equalsIgnoreCase("T_MINI")) || (!Params[2].param.equalsIgnoreCase("T_MAXI")) || (!Params[3].param.equalsIgnoreCase("SV_TE"))) { CdeOK = false; Serial.println(F("Un des paramètres pour la T° est faux ou manquant")); Serial.println(F("Configuration non modifiée")); client.print(F(" \"status\" : \"CTRL_TH Command error, incorrect or missing Temperature parameter")); } else { Serial.println(F("Les paramètres de T° sont bien tous présents")); // Evaluation des paramètres de T° Params[1].value.toCharArray(Char_Tab,16); sscanf(Char_Tab, "%i", &New_T_min); Serial.print(F("T min = ")); Serial.println(New_T_min); Params[2].value.toCharArray(Char_Tab,16); sscanf(Char_Tab, "%i", &New_T_Max); Serial.print(F("T Max = ")); Serial.println(New_T_Max); if ((New_T_min <0) || (New_T_min > 20) || (New_T_Max <0) || (New_T_Max > 30) || (New_T_min >= New_T_Max)) { CdeOK = false; Serial.println(F("Les valeurs de T° min et Max sont incorrectes, vérifiez la consigne demandée")); Serial.println(F("Configuration non modifiée")); client.print(F(" \"status\" : \"CTRL_TH Command error, incorrect Temperature value(s)")); } else { Serial.println(F("Les températures mini et Maxi sont bien cohérentes")); Params[3].value.toCharArray(Char_Tab,16); sscanf(Char_Tab, "%i", &New_SV_Te); if (New_SV_Te > 128) { CdeOK = false; Serial.println(F("La sortie virtuelle demandée pour la température est au delà de 128")); Serial.println(F("Configuration non modifiée")); client.print(F(" \"status\" : \"CTRL_TH Command error, incorrect temperature VO value")); } else { Serial.print(F("La sortie virtuelle ")); Serial.print(New_SV_Te); Serial.println(F(" est sélectionnée pour la surveillance de la T°")); if ((Params[4].value != "") && (Params[4].param.equalsIgnoreCase("H_MINI")) && (Params[5].param.equalsIgnoreCase("H_MAXI")) && (Params[6].param.equalsIgnoreCase("SV_HU"))) { // Des paramètres d'hygrométrie sont présents on vérifie leur cohérence Serial.println(F("Les paramètres d'hygrométrie sont bien tous présents")); // Evaluation des paramètres d'hygrométrie Params[4].value.toCharArray(Char_Tab,16); sscanf(Char_Tab, "%i", &New_H_min); Serial.print(F("H min = ")); Serial.println(New_H_min); Params[5].value.toCharArray(Char_Tab,16); sscanf(Char_Tab, "%i", &New_H_Max); Serial.print(F("H Max = ")); Serial.println(New_H_Max); if ((New_H_min <0) || (New_H_min > 100) || (New_H_Max <0) || (New_H_Max > 100) || (New_H_min >= New_H_Max)) { CdeOK = false; Serial.println(F("Les valeurs d'humidité min et Max sont incorrectes, vérifiez la consigne demandée")); Serial.println(F("Configuration non modifiée")); client.print(F(" \"status\" : \"CTRL_TH Command error, incorrect Hygrometry value(s)")); } else { Serial.println(F("Les pourcentages d'hygrométrie mini et Maxi sont bien cohérents")); Params[6].value.toCharArray(Char_Tab,16); sscanf(Char_Tab, "%i", &New_SV_Hu); if (New_SV_Hu > 128) { CdeOK = false; Serial.println(F("La sortie virtuelle demandée pour l'hygrométrie est au delà de 128")); Serial.println(F("Configuration non modifiée")); client.print(F(" \"status\" : \"CTRL_TH Command error, incorrect hygrometry VO value")); } else { Serial.print(F("La sortie virtuelle ")); Serial.print(New_SV_Hu); Serial.println(F(" est sélectionnée pour la surveillance de l'hygrométrie")); } // endif Vérification SV Hygrométrie <=128 } // endif Contrôle des paramètres d'hygrométrie } else { // Réinitialisation des paramètres d'hygrométrie New_H_min = 0; New_H_Max = 100; New_SV_Hu = 0; } // endif présence des paramètres d'hygrométrie } // endif Vérification SV Température <=128 } // endif Contrôle des T° demandées } // endif Contrôle des paramètres de T° } // endif PERIOD à 0, désactivation } // endif PERIOD > 24h } // endif Premier paramètre non égal à PERIOD // Mémorisation des paramètres dans l'EEPROM if (CdeOK) { // La commande est Ok Serial.println(F("Configuration OK")); // Recopie des nouvelles valeurs demandées dans les variables correspondantes T_min = New_T_min; T_Max = New_T_Max; H_min = New_H_min; H_Max = New_H_Max; SV_Te = New_SV_Te; SV_Hu = New_SV_Hu; Periode_Ctrl = New_Periode_Ctrl; // Mémorisation dans l'EEPROM EEPROM.put(PERIODE_CTRL, Periode_Ctrl); if (Periode_Ctrl == 0) { client.print(F(" \"status\" : \"CTRL_TH Successfully deactivated")); } else { client.print(F(" \"status\" : \"CTRL_TH Successfull")); EEPROM.write(T_MIN, T_min); EEPROM.write(T_MAX, T_Max); EEPROM.write(SV_TE, SV_Te); EEPROM.write(H_MIN, H_min); EEPROM.write(H_MAX, H_Max); EEPROM.write(SV_HU, SV_Hu); } EEPROM.commit(); } break; case 12 : // Paramètres de remontée du climat Serial.println(F("Configuration des paramètres pour la remontée du climat vers l'IPX")); ExtractParamsValues(Commande.substring(8), Params, 3); // Extrait les 3 paramètres attendus : PERIOD, VA_TE et optionnellement VA_HU CdeOK = true; if (!Params[0].param.equalsIgnoreCase("PERIOD")) { CdeOK = false; Serial.println(F("Le premier paramètre n'est pas PERIOD")); Serial.println(F("Configuration non modifiée")); client.print(F(" \"status\" : \"RTRN_TH Command error, 1st param not PERIOD")); } else { Serial.println(F("Le premier paramètre est bien PERIOD")); // Evaluation de la périodicité demandée Params[0].value.toCharArray(Char_Tab,16); sscanf(Char_Tab, "%i", &New_Periode_Rtrn); if (New_Periode_Rtrn >= 1440) { // Périodicité supérieure à 24h refusée CdeOK = false; Serial.println(F("La périodicité demandée est supérieure à 24h")); Serial.println(F("Configuration non modifiée")); client.print(F(" \"status\" : \"RTRN_TH Command error, PERIOD more than 24h")); } else { if (New_Periode_Rtrn == 0) { // La périodicité est fixée à 0, inutile d'évaluer le reste Serial.println(F("Périodicité à 0, fonction de remontée désactivée")); } else { Serial.println(F("La périodicité est comprise entre 0 et 24h")); if (!Params[1].param.equalsIgnoreCase("VA_TE")) { CdeOK = false; Serial.println(F("Le paramètre du n° d'entrée virtuelle analogique est manquant")); Serial.println(F("Configuration non modifiée")); client.print(F(" \"status\" : \"RTRN_TH Command error, incorrect or missing temperature VA argument")); } else { Serial.println(F("Le paramètre du n° d'entrée virtuelle analogique est bien présent")); // Evaluation des paramètres de T° Params[1].value.toCharArray(Char_Tab,16); sscanf(Char_Tab, "%i", &New_VA_Te); if (New_VA_Te > 32) { CdeOK = false; Serial.println(F("L'entrée virtuelle analogique demandée pour la température est au delà de 32")); Serial.println(F("Configuration non modifiée")); client.print(F(" \"status\" : \"RTRN_TH Command error, incorrect temperature VA number (max 32)")); } else { Serial.print(F("L'entrée virtuelle analogique ")); Serial.print(New_VA_Te); Serial.println(F(" est sélectionnée pour la remontée de la T°")); if (Params[2].param.equalsIgnoreCase("VA_HU")) { // Une entrée virtuelle analogique est indiquée pour l'humidité Params[2].value.toCharArray(Char_Tab,16); sscanf(Char_Tab, "%i", &New_VA_Hu); if ((New_VA_Hu > 32) || (New_VA_Hu == New_VA_Te)) { CdeOK = false; Serial.println(F("L'entrée virtuelle analogique demandée pour l'hygrométrie est au delà de 32 ou égale à celle de la température")); Serial.println(F("Configuration non modifiée")); if (New_VA_Hu > 32) { client.print(F(" \"status\" : \"RTRN_TH Command error, incorrect hygrometry VA number (max 32)")); } else { client.print(F(" \"status\" : \"RTRN_TH Command error, hygrometry VA number same as temperature (must be different)")); } // endif Vérification VA Hygrométrie <=32 pour message } else { Serial.print(F("L'entrée virtuelle analogique ")); Serial.print(New_VA_Hu); Serial.println(F(" est sélectionnée pour la remontée de l'hygrométrie")); } // endif Vérification VA Hygrométrie <=32 et != VA T° } else { // Pas de troisième paramètre ou il n'est pas égal à VA_HU, mise à 0 New_VA_Hu = 0; } // endif Contrôle de la présence de VA d'hygrométrie } // endif Vérification VA Température <=32 } // endif Contrôle de la VA de T° } // endif PERIOD à 0, désactivation } // endif PERIOD > 24h } // endif Premier paramètre non égal à PERIOD // Mémorisation des paramètres dans l'EEPROM if (CdeOK) { // La commande est Ok Serial.println(F("Configuration OK")); // Recopie des nouvelles valeurs demandées dans les variables correspondantes VA_Te = New_VA_Te; VA_Hu = New_VA_Hu; Periode_Rtrn = New_Periode_Rtrn; // Mémorisation dans l'EEPROM EEPROM.put(PERIODE_RTRN, Periode_Rtrn); if (Periode_Rtrn == 0) { client.print(F(" \"status\" : \"RTRN_TH Successfully deactivated")); } else { client.print(F(" \"status\" : \"RTRN_TH Successfull")); EEPROM.write(VA_TE, VA_Te); EEPROM.write(VA_HU, VA_Hu); } EEPROM.commit(); } break; default : // Gestion d'un changement d'état Num_Cde=Num_Cde_Recue; client.print(F(" \"status\" : \"")); client.print(Num_Cde); Pilote(Num_Cde); } client.println(F("\"")); } client.println(F("}")); client.flush(); Serial.println(SEPARATEUR); }