Open Sprinkler sur ESP8266
Depuis un moment, je suivais le projet OpenSprinkler, qui vise a faire un contrôleur d'arrosage Open Source. Initialement développé sur Arduino, puis porté sur Raspberry Pi, ce projet a eu une évolution inattendue en étant porté sur ESP8266 / Arduino.
Node MCU : ESP8266 plug and play
Le projet est assez bien document sur le repo Github. On y trouve notamment les sources du logiciel embarqué, et les sources de la carte électronique au format Eagle.
Bien sur il eu été possible d'acheter le matériel tout fait sur leur shop, mais autant le fabriquer c'est plus rigolo :).. Et plus hackable...
La force d'Open Sprinkler, c'est clairement son microprogramme. Il dispose d'une interface web responsive, utilisable aussi bien sur ordinateur que sur mobile, avec laquelle il n'est pas difficile de changer la programmation, lancer une station ponctuellement, etc..
Écran d'accueil d'Open Sprinkler :
Analyse du matériel
A minima, pour fonctionner, OpenSprinkler doit disposer d'une horloge temps réel sauvegardée par batterie, d'un afficheur LCD, de 3 boutons poussoirs pour le programmer, et d'entrées sorties permettant de commander les électrovannes. Optionnellement il est possible de raccorder un détecteur de pluviométrie, un buzzer, des entrées sorties vannes supplémentaires (il y en a 8 par défaut) sur le module lui même ou déportées sur un module esclave, une mesure de courant consommé, la communication par radio ou Wifi avec un module esclave, etc...
Coté commandes de vannes, il dispose de plusieurs options matérielles pour vannes monostables alternatives (AC) et continues (DC) ou bistables (commandes Latch). La version 3 gère ces fonctionnalités en utilisant une extension de ports I2C à 16 entrées sorties (PCF8575), alors que la version 2 utilise 2 extensions à 8 sorties (PCF8574). Il est également possible d'ajouter d'autres commandes de vannes en ajoutant des PCF8575.
Les entrées sorties utilisées sont de 2 types :
- Celles internes à l'ESP8266
- Celles ajoutées par des port expander I2C
L'I2C a en effet cela de super de permettre avec seulement 2 fils d'ajouter jusqu'à 128 périphériques... Magique! Cela va permettre d'outrepasser les limites d'entrées surties de l'ESP8266 en ajoutant au mois 16 entrées sorties, un afficheur LCD, une horloge temps réel.
Après analyse du code , voici les entrées/sorties internes de l'ESP8266 qui sont utilisées :
Broches utilisées pour les entrées sorties 0 (D3 / GPIO0 / Flash) Bouton 2 14 (D5 / GPIO14 / HSCLK PIN_RFRX 16 (D0 / GPIO16 / User / Wake) PIN_RFTX 12 (D6 / GPIO12 / HMISO) PIN_SENSOR1 13 (D7 / GPIO13 / RX2 / HMOSI) PIN_SENSOR2 15 (D8 / GPIO15 / TX2/ HCS) Buzzer A0 (ADC0) PIN_CURR_SENSE 5 (D1 / GPIO5 / SCL) SCL 4 (D2 / GPIO4 / SDA) SDA Broches utilisées en interne sur le node (pour la communication par USB): 3 (D9 / GPIO3 / RDX0) 1 (D10 / GPIO1 / TXD0) Broche libre 2 (D4 / GPIO2 / TX1 )
- RFRX et RFTX sont utilisés pour communiquer avec un port expandeur par radio fréquences. Il faut dans ce cas utiliser un module radio émetteur / récepteur connecté sur ces broches. Je n'ai pas trop creusé ces fonctions.
- Sensor 1 et Sensor 2 permettent de raccorder des capteurs de pluviométrie. Je n'ai pas trop creusé non plus.
- L'entrée analogique permet de mettre un capteur de courant pour mesurer la consommation du montage.
- Il est également possible de connecter un buzzer sur la sortie "Buzzer".
Mais comme l'ESP ne contient pas assez d'entrées sorties, l'I2C est utilisé à haute dose pour en ajouter, supporter l'afficheur et l'horloge temps réel. Les périphériques suivants sont supportés dans le code :
- 1 X expandeurs de port PCF8574 en 0x20 (les 3 bits d'adresse à zéro) (ATTENTION à ce que cela ne soit pas un PCF8574A qui est en 0x38)
0 Sortie PIN_PWR_RX Allume le récepteur 1 Entrée PIN_BUTTON_1 2 Sortie PIN_PWR_TX Allume l'émetteur 3 Entrée PIN_BUTTON_3 4 Sortie libre? 5 Sortie libre? 6 Sortie PIN_BOOST Allume le booster DC pour le charger 7 Sortie PIN_BOOST_EN Et le met dans le circuit
- 1 X expandeurs de port PCF8574 0x21 ACDR_I2CADDR pour les commandes ac OU
- 1 X expandeurs de port PCF8574 0x22 DCDR_I2CADDR pour les commandes dc OU
- 1 X expandeurs de port PCF8574 0x23 LADR_I2CADDR pour les commandes latch
- 1 X Oled display SSD1306 en 0x3c
- 1 X horloge temps réel DS1307_CTRL_ID en 0x68 OU
- 1 X horloge temps réel MCP7940_CTRL_ID en 0x6F
Il faut à minima 2 port expandeurs 8 bits (ou 1 x 16 bits) pour avoir un système fonctionnel :
- 8 IO pour les commandes annexes (PCF8574 en 0x20)
- 8 IO pour les commandes de vannes (0x21, 0X22 ou 0x23 selon la configuration AC/DC/Latch choisie)
Mon implémentation matérielle
Mon système d'arrosage est commandé par des électrovannes 24V alternatif mono stables. Il faut donc mettre une tension pour allumer l'arrosage et la maintenir pendant l'arrosage.
Le montage sera construit autour d'un NODEMCU, facile à mettre en oeuvre et à programmer. Il sera complété d'un module RTC à base de DS1307 et d'un afficheur OLED.
Ne disposant pas dans mes boites de PCF8574, je suis parti sur un MCP23017, qui est un porte expander I2C à 16E/S. Par contre le code sera à adapter, car ce composant n'est pas supporté dans Open Sprinkler.
Autre point, j'ai opté pour une sortie sur relais. Sur ce point il n’est nécessaire de s'étendre un peu. La plupart des modules relais disponibles fonctionnent en 5V. Or sur ce module relais, il y a 2 parties :
- La commande (avec isolation par opto)
- La puissance, qui déclenche le relais.
Lorsque les 4 relais sont activés, la puissance consommée est trop importante pour pouvoir alimenter le tout avec le 3.3V du nodeMCU qui n'est alimentée que par un tout petit régulateur.
Le relais étant 5V, la puissance devra rester en 5V.
Plusieurs cartes (comme par exemple celle-ci ) séparent bien les alimentations de la puisance de celles de la commande et disposent d'un cavalier (par défaut mis) qui permet d'alimenter la puissance avec la tension d'alimentation de commande.
Pour séparer les 2, il suffit d'enlever le cavalier et de mettre 5V sur l'une des deux broches du cavalier (celle qui va vers les relais) et 3V3 sur le VCC d'entrée de la carte. Ainsi la puissance consommée par les enroulements des relais sera prélevée sur le 5VUSB, sur lequel on peut tirer de 1 à 2 ampères. Sur le 3V3, ne sera prélevé que le courant nécessaire pour allumer la led de l'optocoupleur.. CQFD.
Câblage
Voici le schéma de l'ensemble réalisé (ou télécharger en PDF: OS23017.pdf)
Une bonne manière de vérifier que tout est bien câblé est d'utiliser i2cdetect. Il devrait indiquer la cartographie suivante :
Hardware détecté par i2cdetect (cf lib): 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: 20 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 23017 IO 30: -- -- -- -- -- -- -- -- -- -- -- -- 3C -- -- -- OLED display 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: 50 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 24C32 60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- -- DS1307 RTC 70: -- -- -- -- -- -- -- --
Adaptation logicielle
Avec la modification MCP23017, le port B sert aux commandes et entrées et remplace le "main PCF8574". Voir définitions sur l'usage des broches dans defines.h ligne 364 et suivantes. A ce jour, ne sont utilisées que les boutons 1 à 3. Le port A quand a lui fournit 8 IO inversées pour piloter jusqu'à 8 électrovannes.
Pour adapter logique positive ou négative sur les sorties (PortA du MCP23017), il faut modifier
- Ligne 802 de OpenSprinkler.cpp
- Ligne 70 de GPIO.cpp
L'adaptation au MCP23017 est relativement aisée. Il faut d'abord ajouter les fonctions qui vont permettre de piloter le composant dans GPIO.CPP
// Init MCP23017 void mcp_init(int addr) { // Port A Mixed Input Output. 1 Input, 0 output (0b00001010) Wire.beginTransmission(addr); Wire.write(0x00); // IODIRA register Wire.write(MAIN_INPUTMASK); // PORT A mixed Wire.endTransmission(); // pullup on inputs Wire.beginTransmission(addr); Wire.write(0x0c); // GPPUA register Wire.write(MAIN_INPUTMASK); // A acive / 0 inactive 0b00001010 Wire.endTransmission(); // Set output to zero /* mcp_writeA(addr,0);*/ // Port B full Output Wire.beginTransmission(addr); Wire.write(0x01); // IODIRB register Wire.write(0x00); // set entire PORT B to output Wire.endTransmission(); // pullup on inputs /* Wire.beginTransmission(addr); Wire.write(0x0D); // GPPUB register Wire.write(0x00); // No pullups Wire.endTransmission(); */ // Set output to zero mcp_writeB(addr,0x00); } // read a byte from MCP23017 port A byte mcp_readA(int addr) { Wire.beginTransmission(addr); Wire.write(0x12); // address PORT A Wire.endTransmission(); Wire.requestFrom(addr, 1); // request one byte of data byte data=Wire.read(); // store incoming byte return data; } // read a byte from MCP23017 port B byte mcp_readB(int addr) { Wire.beginTransmission(addr); Wire.write(0x13); // address PORT B Wire.endTransmission(); Wire.requestFrom(addr, 1); // request one byte of data byte data=Wire.read(); // store incoming byte return data; } // write a byte to MCP23017 port A void mcp_writeA(int addr, byte data) { Wire.beginTransmission(addr); Wire.write(0x12); // address port A Wire.write(data); // value to send Wire.endTransmission(); } // write a byte to MCP23017 port B void mcp_writeB(int addr, byte data) { Wire.beginTransmission(addr); Wire.write(0x13); // address port A Wire.write(data); // value to send Wire.endTransmission(); }
Dans le même fichier, les fonction digitalWriteExt et digitalReadExt devront également est adaptée. Ici, la commande des 4 relais devra être inversée :
void digitalWriteExt(byte pin, byte value) { // Pins 0 to 3 are connected to relay and needs to be inverted if(pin>=IOEXP_PIN) { // a pin on IO expander byte data=mcp_readA(MAIN_I2CADDR); // don't invert pins 2..7 if (pin>=IOEXP_PIN+4) { if(value) data|=(1<<(pin-IOEXP_PIN)); else data&=~(1<<(pin-IOEXP_PIN)); } // invert pins 0 to 3 else { if(value) data&=~(1<<(pin-IOEXP_PIN)); else data|=(1<<(pin-IOEXP_PIN)); } data |= MAIN_INPUTMASK; // make sure to enforce 1 for input pins mcp_writeA(MAIN_I2CADDR, data); } else { digitalWrite(pin, value); } } byte digitalReadExt(byte pin) { if(pin>=IOEXP_PIN) { // a pin on IO expander return mcp_readA(MAIN_I2CADDR)&(1<<(pin-IOEXP_PIN)); } else { return digitalRead(pin); } }
Les 5 fonctions ajoutées pour le MCP23017 devront être déclarées dans GPIO.H (ligne : 29, juste après le #if defined(ESP8266) )
void mcp_init(int addr); void mcp_writeA(int addr, byte data); void mcp_writeB(int addr, byte data); byte mcp_readA(int addr); byte mcp_readB(int addr);
Il faudra ensuite dans la fonction main(), ajouter l'initialisation du MCP23017 (fichier main.cpp ligne 299 juste avant os.begin(); )
DEBUG_PRINTLN(""); // Display i2c Perpherials i2cdetect(); // Init MCP23017 mcp_init(MAIN_I2CADDR); // Normal init
Dernier fichier à modifier, OpenSprinkler.cpp ligne 314 pour franciser les dates:
/** Weekday strings (stored in progmem, for LCD display) */ static const char days_str[] PROGMEM = "Lun\0" "Mar\0" "Mer\0" "Jeu\0" "Ven\0" "Sam\0" "Dim\0";
La fonction OpenSprinkler::lcd_print_time doit également est adaptée pour l'affichage des dates :
void OpenSprinkler::lcd_print_time(time_t t) { #ifdef ESP8266 lcd.setCursor(0, 0); lcd_print_2digit(hour(t)); lcd_print_pgm(PSTR(":")); lcd_print_2digit(minute(t)); lcd_print_pgm(PSTR(" ")); // each weekday string has 3 characters + ending 0 lcd_print_pgm(days_str+4*weekday_today()); lcd_print_pgm(PSTR(" ")); lcd_print_2digit(day(t)); lcd_print_pgm(PSTR("/")); lcd_print_2digit(month(t)); #endif }
Puis ligne 800 pour initaliser les IO :
if(hw_type == HW_TYPE_AC) // pcf_write(ACDR_I2CADDR, ~station_bits[0]); // with mcp23017, port b are valves mcp_writeB(MAIN_I2CADDR, station_bits[0]); else if(hw_type == HW_TYPE_DC) pcf_write(DCDR_I2CADDR, ~station_bits[0]);
Enfin, le spashscreen (OpenSprinkler::flash_screen()) peut être dapté pour montrer le support du MCP23017 :
void OpenSprinkler::flash_screen() { lcd.setCursor(0, -1); lcd.print(F("OSS JP/mc23017")); lcd.drawXbm(34, 24, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_image); lcd.display(); delay(2000); lcd.clear(); lcd.display(); }
J'ai eu également quelques soucis de conflit de nommage sur la librairie time.h, qui m'ont conduit à renommer les fichiers Time.h et Time.cpp en TTime.h et TTime.cpp, puis changer dans le fichier DS1307RTC.h
#include "Time.h"
en
#include "TTime.h"
Et enfin dans le fichier ttime.h adapter :
#ifndef _TTime_h #define _TTime_h
Last, but not least, il faut dans OpenSprinkler.ino, changer #include <OpenSprinkler.h> en #include "OpenSprinkler.h"
Configuration
Le fichier "defines.h" peut être personnalisé avec vos valeurs par défaut sur la clé d'API Wundergroup (prévisions météo) ou la localisation géographique.
#define DEFAULT_LOCATION "Boston,MA" #define DEFAULT_WEATHER_KEY ""
Compilation, flashage et tests
La compilation et le flashage ne posent pas de pb particuliers en utilisant la dernière version de l'IDE Arduino avec l'extension pour ESP8266. Il va ensuite falloir configurer le Wifi comme décrit dans le guide de mise en route.
Une documentation très abondante est disponible sur le site, aussi je vous laisserai la découvrir pour paramétrer votre produit et le tester.
Extensions
OpenSprinkler disposant d'un séquenceur facile à programmer, il m'a semblé intéressant de l'utiliser aussi pour commander le moteur de filtration de la piscine. Le problème est que chez moi, l'arrosage et la piscine n’habitent pas dans le même local....
OS dispose d'une fonction qui permet de gérer des entrées sorties déportées, en les commandant par le réseau. Pour déclarer une sortie ainsi, il suffit dans l'écran principal d'OpenSprinkler de cliquer sur l'engrenage à coté de la sortie choisie. Cela permet d'accéder à la définition de cette sortie. EN cliquant sur avancé, on découvre que l'on peut y associer un type, et que le type "HTTP" permet de mettre en œuvre une commande distante :
Ceci appellera une url http://192.168.20.248:80/On ou http://192.168.20.248:80/Off .
Il ne reste donc plus qu'à coder sur un autre ESP une commande distante. Pour faciliter la mise en oeuvre, j'ai fait ceci sur un Sonoff à 5 euros. C'est super, car le relais, un voyant et un poussoir s'y trouvent déjà,n ainsi qu'une alimentation 220V...
Le plus dur est de le programmer car il faut mettre un adaptateur série 3,3V > USB, mais ensuite c'est que du bonheur... Le programme disposant de la fonction de reprogrammation par le Wifi (OTA) il sera ensuite facile de le mettre à jour.
En voici le code source :
// Slave Open Sprinkler // Permet de commander 1 relays par interface commande web // // Appel : http://OSSlave.local/On & http://OSSlave.local/Off // // Fonctionnalités : // - Upgradable par OTA, // - S'annonce sur bonjour ("mdns_name".local) // - Debug sur serial, // - support optionnel d'un bouton pour forcage local du relais 1 (SOnoff) // - Affichage du status sur une Led // // Fonctions de la led // - eteint : wifi associé et operationnel // - clignotement 500 ms : recheche reseau wifi et association en cours // - allumé: pas de wifi // - 1 pulse de 100ms: action sur le relais 1 pendant 1s (/relay1) // #include <ESP8266WiFi.h> #include <ESP8266mDNS.h> #include <WiFiUdp.h> #include <WiFiClient.h> #include <ESP8266WebServer.h> #include <ArduinoOTA.h> // // WIFI char* ssid = "MON_SSID"; char* password = "MOTDEPASSE"; long port = 80; const char* mdns_name = "osslave"; // Nom en MDNS: http://"mdns_name".local // // Programmation OTA // cd /Users/Administrateur/Documents/Arduino/OpenSprinklerSlave // python /Users/Administrateur/Library/Arduino15/packages/esp8266/hardware/esp8266/2.3.0/tools/espota.py -i relayb.local -p 8266 -f OpenSprinklerSlave.ino.nodemcu.bin // ou pour le sonoff // python /Users/Administrateur/Library/Arduino15/packages/esp8266/hardware/esp8266/2.3.0/tools/espota.py -i 192.168.20.245 -p 8266 -f OpenSprinklerSlave.ino.generic.bin // // Brochages Arduino // Sonoff // // Attention, choix de carte : // Generic ESP8266 // Flashh mode: DOUT // Flash Size: 1M (64K SPIFFS) // BuiltinLed: 13 // Erase Flash: All flash content // Reflasher si erreur, car même à 115200 en DOut c'est parfois un peu dur.. // Penser à metter le nom de device en osslave. // // GPIO0: button // GPIO12: relay // GPIO13: green led / active Low // GPIO14: Red led //#define INVERTRELAY1; // permet de passer les relais ou la led en logique negative. #define INVERTLED; const int led = LED_BUILTIN; // led verte sur broche 13 const int relay1 = 12; // relais connecte au GPIO12 // Le sonoff dispose d'un bouton. #define BUTTON; const int button = 0; // Bouton intégré ESP8266WebServer server(port); // serveur HTTP /* Routine pour se connecter à un reseau wifi */ void connect(char *_SSID, char* _PWD) { Serial.println(""); Serial.print("Connecting "); Serial.print(_SSID); WiFi.mode(WIFI_STA); // pour éviter d'avoir aussi le relais en point d'accès... WiFi.begin(_SSID, _PWD); Serial.println(""); int h = 0; // Wait for connection while (WiFi.status() != WL_CONNECTED) { #ifdef INVERTLED digitalWrite(led, 0); // allume #else digitalWrite(led, 1); // allume #endif delay(50); #ifdef INVERTLED digitalWrite(led, 1); // eteint #else digitalWrite(led, 0); // eteint #endif delay(450); Serial.print("."); if (h++ > 40) { // si trop long on abandonne Serial.println(); Serial.println("Failed to connect"); return; } } Serial.println(""); Serial.print("Connected to "); Serial.println(ssid); Serial.print("IP address: "); Serial.println(WiFi.localIP()); Serial.print("Name: "); Serial.print(mdns_name); Serial.println(".local"); Serial.print("MAC adress: "); Serial.println(WiFi.macAddress()); } void on1() { Serial.println("On 1"); #ifdef INVERTRELAY1 digitalWrite(relay1, 0); // allumé #else digitalWrite(relay1, 1); // allumé #endif #ifdef INVERTLED digitalWrite(led, 1); // eteint #else digitalWrite(led, 0); // eteint #endif delay(50); #ifdef INVERTLED digitalWrite(led, 0); // allume #else digitalWrite(led, 1); // allume #endif delay(50); } void off1() { Serial.println("On 1"); #ifdef INVERTRELAY1 digitalWrite(relay1, 1); // eteint #else digitalWrite(relay1, 0); // eteint #endif #ifdef INVERTLED digitalWrite(led, 1); // eteint #else digitalWrite(led, 0); // eteint #endif delay(50); #ifdef INVERTLED digitalWrite(led, 0); // allume #else digitalWrite(led, 1); // allume #endif delay(50); } /* HTML code for response */ void handle_root() { /* page[] contient notre page web et renvoie vers le domaine /open si on appuie sur le bouton */ const char page[] = "<!DOCTYPE html><html><head></head><body>No action possible </body></html>"; server.send(200, "text/html", page); // repond avec la page web codee en HTML } void handle_on1() { const char page[] = "<!DOCTYPE html><html><head></head><body>On</body></html>"; server.send(200, "text/html", page); on1(); // actionne le relais 1 } void handle_off1() { const char page[] = "<!DOCTYPE html><html><head></head><body>Off</body></html>"; server.send(200, "text/html", page); off1(); // actionne le relais 1 } void setup(void) { /* Configuration des entree/sortie */ pinMode(relay1, OUTPUT); pinMode(led, OUTPUT); #ifdef BUTTON pinMode(button, INPUT); digitalWrite(button, 1); // activate pullup #endif #ifdef INVERTLED digitalWrite(led, 1); // eteint #else digitalWrite(led, 0); // eteint #endif #ifdef INVERTRELAY1 digitalWrite(relay1, 1); // eteint #else digitalWrite(relay1, 0); // eteint #endif Serial.begin(115200); // initialisation du port serie uint32_t realSize = ESP.getFlashChipRealSize(); uint32_t ideSize = ESP.getFlashChipSize(); FlashMode_t ideMode = ESP.getFlashChipMode(); Serial.printf("Flash real id: %08X\n", ESP.getFlashChipId()); Serial.printf("Flash real size: %u\n\n", realSize); Serial.printf("Flash ide size: %u\n", ideSize); Serial.printf("Flash ide speed: %u\n", ESP.getFlashChipSpeed()); Serial.printf("Flash ide mode: %s\n", (ideMode == FM_QIO ? "QIO" : ideMode == FM_QOUT ? "QOUT" : ideMode == FM_DIO ? "DIO" : ideMode == FM_DOUT ? "DOUT" : "UNKNOWN")); if (ideSize != realSize) { Serial.println("Flash Chip configuration wrong!\n"); } else { Serial.println("Flash Chip configuration ok.\n"); } connect(ssid, password); // connexion au reseau Wifi Serial.println(""); /* demarrage du serveur mDNS sur esp8266.local if (mdns.begin(mdns_name, WiFi.localIP())) { Serial.println("MDNS responder started"); }*/ MDNS.addService("http", "tcp", 80); // Start OTA server. ArduinoOTA.onStart([]() { Serial.println("Start"); }); ArduinoOTA.onEnd([]() { Serial.println("\nEnd"); }); ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { Serial.printf("Progress: %u%%\r\n", (progress / (total / 100))); }); ArduinoOTA.onError([](ota_error_t error) { Serial.printf("Error[%u]: ", error); if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); else if (error == OTA_END_ERROR) Serial.println("End Failed"); }); // Port defaults to 8266 // ArduinoOTA.setPort(8266); // No authentication by default // ArduinoOTA.setPassword((const char *)"123"); // Hostname defaults to esp8266-[ChipID] ArduinoOTA.setHostname(mdns_name); ArduinoOTA.begin(); Serial.println("OTA started"); /* ajout des actions du serveur et démarrage*/ server.on("/", handle_root); server.on("/On", handle_on1); server.on("/Off", handle_off1); server.begin(); // demarrage du serveur Serial.println("HTTP server started"); } void loop(void) { ArduinoOTA.handle(); server.handleClient(); // gestion du serveur #ifdef BUTTON if (digitalRead(button) != 1) { on1(); } #endif /* Si connecté au wifi alors la LED s'allume */ if (WiFi.status() == WL_CONNECTED) { #ifdef INVERTLED digitalWrite(led, 1); // eteint #else digitalWrite(led, 0); // eteint #endif } // Wifi deconnecté, on éteint la led et on resette. else { #ifdef INVERTLED digitalWrite(led, 0); // allume #else digitalWrite(led, 1); // allume #endif ESP.reset(); } }