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

nodemcu.jpg

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 :

OS-mobile.png

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)

OS23017.png

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 : io-deportee.jpg

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();
  }
}