Décembre 2018 - Mai 2019:

Malgré une mise à jour en janvier 2018, je n'ai pas eu le temps de vérifier à nouveau cet article. 
Il faudra donc prendre son contenu AS IS (comme il est)!

------------------------------------

Généralités

Dès que j'ai commencé à mettre un ou deux composants autour de ce ESP8266, il m'est venu cette idée de plateforme.

Au lieu de penser à une application bien spécifique, comme par exemple celle d'allumer une lampe au passage d'une personne, celle de faire clignoter la LED si l’humidité est trop haute ou trop basse, ou encore celle d'enclencher un radiateur électrique si la température est trop basse, pourquoi ne pas implémenter du code pour en faire un objet connecté beaucoup plus général.

L'ESP8266 ayant une interface Wifi et donc la possibilité de dialoguer avec l'extérieur au moyen d'un Web serveur, pourquoi ne pas laisser le contrôle et la logique à l'extérieur.
Une application sur un PC, un Smartphone, un Raspberry Pi, ou mieux encore sur un serveur NAS ouvert, pourrait implémenter du code pour, par exemple, vérifier une température, en faire des statistiques, voir générer une alarme ou envoyer un Email (un peu trop compliqué pour un ESP8266).

Commençons par la présentation des éléments:

  1. L'ESP8266, ici un NodeMCU ( Wikipedia ) qui s'alimente avec un cable USB standard 
  2. Un capteur température humidité DHT11
  3. Un relais 220V
  4. Un détecteur de mouvement
  5. Une LED

L'idée est aussi de mettre tous ces composants, ou une partie seulement, dans une jolie petite boîte!
Il manque quelques câblage, ici, comme la partie 220V du relais ou des fils de prolongation de la LED. 


Mise en route

La première chose est de connecter son NodeMCU sur son PC et de mettre en place l'outil de développement.
Le premier exercice sera de vérifier l'intégration de la LED pour pouvoir la faire clignoter.

Afin de vérifier cette partie, j'ai refait une installation complète du logiciel lors de l'écriture de ce document!
Certaine librairie (par exemple pour le DHT11) nécessite une installation particulière. 

Le document en anglais de Benn Thomsen va nous aider à configure l'IDE (logiciel de développement) de l'Arduino pour l'utiliser avec l'ESP8266.
Ma première difficulté sur mon PC a été la reconnaissance du module connecté à l'USB. J'ai dû installer le driver USB

On trouvera une description plus détaillée ici, mais les point essentiels sont: l’installation du logiciel Arduino (j'ai pris la version 1.8.1 et sans installeur) et sous Préférence dans IDE (arduino.exe), après son démarrage, il faudra s'assurer d'entrer l'Url de gestion des ESP8266: http://arduino.esp8266.com/stable/package_esp8266com_index.json.

Pour pouvoir se connecter au NodeMCU, il faudra spécifier dans l'IDE, sous Outil / Type de carte: NodeMCU 1.0 (ESP 12E Module).

On utilisera ici un DHT11 et il faudra y intégrer les librairies d'Adafruit associées.
Comme j'installe en général mes outils sur un disque séparé, on trouvera ces librairies dans le sous-répertoire D:\arduino-1.8.1\libraries sous par exemple Adafruit_Sensor et DHT_sensor.
Ne pas mettre de - dans le nom (contenus dans https://github.com/adafruit/DHT-sensor-library et https://github.com/adafruit/Adafruit_Sensor.
Dès qu'on a créer ces deux répertoires avec leur fichier, il faudra redémarrer l'IDE si actif (pour moi D:\arduino-1.8.1\arduino.exe). 

Si c'est vraiment trop compliqué, il faudra reprendre les bases sans nécessairement faire l'achat d'un Arduino, quoique!  >>>>>  Ici un très bon site de référence et très général 


Premier test avec la LED

Ce fichier de test en anglais est disponible dans l'IDE de l'Arduino sous Fichier/Exemples/ESP8266/Blink
Où cela se complique, c'est d'identifier la location de Blink.ino (le fichier de données de l'arduino.exe): C:\Users\jb\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.3.0. Cet exemple est installé lors de l'intégration de l'ESP8266 dans l'IDE expliqué ci-dessus.

Si on veut le modifier, il est conseillé de créer son propre répertoire de fichiers .ino, par exemple sous D:\arduinoDataXx avec des sous répertoires bien nommés. 

/*
 ESP8266 Blink by Simon Peter
 Blink the blue LED on the ESP-01 module
 This example code is in the public domain

 The blue LED on the ESP-01 module is connected to GPIO1 
 (which is also the TXD pin; so we cannot use Serial.print() at the same time)

 Note that this sketch uses LED_BUILTIN to find the pin with the internal LED
*/
void setup() {
  pinMode(LED_BUILTIN, OUTPUT);     // Initialize the LED_BUILTIN pin as an output
}

// the loop function runs over and over again forever
void loop() {
  digitalWrite(LED_BUILTIN, LOW);   // Turn the LED on (Note that LOW is the voltage level
                                    // but actually the LED is on; this is because 
                                    // it is active low on the ESP-01)
  delay(1000);                      // Wait for a second
  digitalWrite(LED_BUILTIN, HIGH);  // Turn the LED off by making the voltage HIGH
  delay(2000);                      // Wait for two seconds (to demonstrate the active low LED)
} 


Les scripts Arduino sont assez facile à comprendre avec la partie setup() pour l'initialisation et celle de loop() pour la boucle infinie.

LED_BUILTIN est une constante Arduino correspondant au GPIO 1, une petite LED bleu sur le circuit lui-même.

Dès qu'on a chargé ce fichier on pourra le compiler et le télécharger avec une connexion établie avec un câble USB (qui alimentera aussi le NodeMCU.

Je n'ai pas trop détaillé ici ces divers aspects, il y en a plein sur Internet, comme en français sur https://zestedesavoir.com/tutoriels/686/arduino-premiers-pas-en-informatique-embarquee/742_decouverte-de-larduino/3416_le-logiciel/#3-10784_approche-et-utilisation-du-logiciel.

On pourra modifier le code, voir connecter une grosse LED rouge (5) comme sur l'image en début d'article.
Il ne faudra pas oublier de mettre une résistance de 1K au moins entre la LED et la terre pour ne pas risquer de griller la LED voir la carte elle-même.
La LED est une diode! Il faudra donc la montée dans le bon sens, donc éventuellement la retourner si elle ne s'allume pas quand elle le devrait.

En consultant https://iotbytes.wordpress.com/nodemcu-pinout/ on trouvera que le GPIO5 correspondant au pin D1 du NodeMCU, où j'ai connecté ma grosse LED.

On utilisera donc ce code à intégrer:

const int ledPin   = 5;    //D1 grosse LED rouge (code avant setup()) 

pinMode(ledPin, OUTPUT); //Dans setup()

digitalWrite(ledPin, ...); //A modifier

Serial.println("Début de loop()"); //Dans la boucle pour vérifier l'interface Console


Dans le menu Outil / Moniteur on pourra initialisé une console pour vérifier le déroulement du programme lors d'adaptation de code.


Notre plateforme complète 

Mon code n'étant pas encore terminé, je dépose quand même ici l'état actuel début Janvier 2017.

Il est tout à fait fonctionnel, mais nécessitera encore des adaptations. Comme je dois encore développer un application client pour dialoguer avec le serveur Web de cette plateforme, bien des changements seront nécessaire. 

#include <ESP8266WiFi.h>

#include "DHT.h"

#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>

#define DHTTYPE DHT11

const char* ssid     = ".....";      //Router name
const char* password = ".....";      //Router password 

//The time we give the sensor to calibrate (10-60 secs according to the datasheet)
int calibrationTime = 10;        

boolean isHigh = false;

const int relayPin = 2;    //D4 relay
const int DHTPin   = 0;    //D3 humidity / temperature sensor
const int pirPin   = 4;    //D2 mvt sensor
const int ledPin   = 5;    //D1 big LED

const int led = 13;  //It seems we cannot remove it

String texttosend = "Hi";
int nbofmoves   = 0;
int ledactive   = 1;
int relayactive = 0;

const  int   asize = 10;    //array size
static float myTemps[asize];
static float myHumids[asize];
static int   pos = 0;

static char celsiusData[7];
static char humidityData[7];

int loop1  = 0;
int count1 = 0;
int count2 = 0;

ESP8266WebServer server(80);

//Init DHT sensor.
DHT dht(DHTPin, DHTTYPE);

void handleRoot() {
  digitalWrite(led, 1);

  texttosend = "Nb of mouvements: ";
  texttosend += nbofmoves;
  
  server.send(200, "text/plain", texttosend);
  digitalWrite(led, 0);
}

void handleNotFound(){
  digitalWrite(led, 1);
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET)?"GET":"POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i=0; i<server.args(); i++){
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }
  server.send(404, "text/plain", message);
  digitalWrite(led, 0);
}

void setup(void){
  Serial.begin(115200);
  Serial.println("Setup started");
  
  pinMode(relayPin, OUTPUT); 
  digitalWrite(relayPin, HIGH);
  
  pinMode(pirPin, INPUT);
  digitalWrite(pirPin, LOW);
  
  pinMode(ledPin, OUTPUT);
  
  WiFi.begin(ssid, password);

  //Config static IP
  IPAddress ip(192, 168, 1, 70);
  IPAddress gateway(192, 168, 1, 1);
  IPAddress subnet(255, 255, 255, 0);  
  WiFi.config(ip, gateway, subnet);
  
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  if (MDNS.begin("esp8266")) {
    Serial.println("MDNS responder started");
  }

  server.on("/", handleRoot);

  server.on("/inline", [](){
    server.send(200, "text/plain", "This works as well");
  });

  server.on("/temperature", [](){
    String separator = " - ";
    String both = celsiusData + separator + humidityData; 
    server.send(200, "text/plain", both);
  });

  server.on("/relayon", [](){
    server.send(200, "text/plain", "Relay on");
    digitalWrite(relayPin, HIGH);
  });

  server.on("/relayoff", [](){
    server.send(200, "text/plain", "Relay off");
    digitalWrite(relayPin, LOW);
  });

  server.on("/relayactive", [](){
    server.send(200, "text/plain", "Relay active");
    relayactive = 1;
  });

  server.on("/relaynotactive", [](){
    server.send(200, "text/plain", "Relay not active");
    relayactive = 0;
  });

  server.on("/ledon", [](){
    server.send(200, "text/plain", "Led on");
    ledactive = 1;
  });
  
  server.on("/ledoff", [](){
    server.send(200, "text/plain", "Led off");
    ledactive = 0;
  });

  server.onNotFound(handleNotFound);

  server.begin();
  Serial.println("HTTP server started");

  //give the sensor some time to calibrate
  Serial.print("Calibrating sensor (");
  Serial.print(calibrationTime);
  Serial.print(" secs) ");
  for (int i = 0; i < calibrationTime * 2; i++){
     Serial.print(".");
     delay(500);
  }
  Serial.println(" calibrating done");
  Serial.println("THE SENSOR IS ACTIVE");

  strcpy(celsiusData, "0");
  strcpy(humidityData, "0");
              
  delay(1000);    
  blink(4);
  delay(1000);
}

void blink(int nbtimes) {
  if (ledactive == 1) {
    for (int i = 0; i < nbtimes; i++) {
      digitalWrite(ledPin, HIGH);
      delay(110);
      digitalWrite(ledPin, LOW);
      delay(110);
    }
  }
}

void sort(float a[], int size) {
    for (int i=0; i<(size-1); i++) {
        for (int o=0; o<(size-(i+1)); o++) {
                if(a[o] > a[o+1]) {
                    int t = a[o];
                    a[o] = a[o+1];
                    a[o+1] = t;
                }
        }
    }
}

void computeData(float oneTemp, float oneHumid, int nbignored) {
  myTemps[pos]  = oneTemp;
  myHumids[pos] = oneHumid;
  pos += 1;

  if (pos == asize) {
    //showAll();
      
    sort(myTemps, asize);
    sort(myHumids, asize);

    //Serial.println("Sorted done and restart from 0");

    //showAll();

    float total1 = 0;
    float total2 = 0;
    for (int i = nbignored; i < (asize - nbignored); i++) {
      total1 += myTemps[i];
      total2 += myHumids[i];
    }

    total1 = total1 / (asize - (2*nbignored));
    total2 = total2 / (asize - (2*nbignored));
    
    dtostrf(total1, 6, 1, celsiusData);               
    dtostrf(total2, 6, 1, humidityData);

    String result = "Average: ";
    String separator = " - ";
    String both = separator + celsiusData + separator + humidityData; 
    //Serial.print(result);
    //Serial.println(both);

    for (int i = 0; i < asize; i++) {
      myTemps[i]  = 0;
      myHumids[i] = 0;
    }
    pos = 0;
  }
}

void showAll() {
  for (int i = 0; i < asize; i++) {
    Serial.print(myTemps[i]);
    Serial.print(" ");
  }
  Serial.println("");

  for (int i = 0; i < asize; i++) {
    Serial.print(myHumids[i]);
    Serial.print(" ");
  }
  Serial.println("");
}


void loop(void){
  server.handleClient();

  if (digitalRead(pirPin) == HIGH) {
    if (isHigh == false) {
       if (ledactive == 1) {
         digitalWrite(ledPin, HIGH);   //the led visualizes the sensors output pin state
       }
       Serial.print("Motion detected !!!   ");
       isHigh = true;
       nbofmoves += 1;

       if (relayactive == 1) {
         digitalWrite(relayPin, HIGH);
       }
       
       blink(3);      
       delay(500); 
     }
    }

    if (digitalRead(pirPin) == LOW) {     
      if (isHigh == true) {  
        if (ledactive == 1) {
          digitalWrite(ledPin, LOW);   //the led visualizes the sensors output pin state
        }
        
        Serial.println("End of motion detected !!!");
        isHigh = false;

        if (relayactive == 1) {
          digitalWrite(relayPin, LOW);
        }
         
        blink(1);      
        delay(500);
      }
    }

    loop1 += 1;
    if (loop1 == 100000){
      loop1 = 0;
      
      //Sensor readings may also be up to 2 seconds (slow sensor)
      float h = dht.readHumidity();

      //Read temperature as Celsius (default)
      float t = dht.readTemperature();

      //Check if any reads failed
      if (isnan(h) || isnan(t)) {
        //Serial.println("isnan() failed to read from DHT sensor!");
        count2 += 1;
        h = 0;       
      }
      else {
        if (h == 0) {  //Failure: no humidity
          //Serial.println("Humidity 0 from DHT sensor!");
          count2 += 1;  
        }
        else {
          // Computes values in Celsius and Humidity
          float hic = dht.computeHeatIndex(t, h, false); 
                
          //dtostrf(hic, 6, 2, celsiusData);               
          //dtostrf(h, 6, 2, humidityData);
          count1 += 1;

          String separator = " - ";
          String both = separator + celsiusData + separator + humidityData; 
          //Serial.print(count1);
          //Serial.print(" ");
          //Serial.print(count2);
          //Serial.println(both);

          computeData(hic, h, 2);
       }
    }
  }
}

Le code n'a pas trop de documentation. Ce n'est pas trop mon habitude mais j'étais trop impatient de voir ma plateforme fonctionnée.

Le senseur est délicat: j'ignore les valeurs extrêmes. 
Le code aussi: il faut systématiquement sauver le fichier avec un autre nom ou nouvelle version. Une petite correction malveillante peu entraîner un désastre. L'IDE et le compilateur ne sont pas trop à la hauteur. Une simple variable non initialisée peut entraîner une catastrophe.  

En activant une console avant la recompilation, on pourra voir les messages correspondant à ce code pendant le démarrage et après avoir passer trois fois devant le senseur de mouvements: 

Connected to vdl-84353
IP address: 192.168.1.70
MDNS responder started
HTTP server started
Calibrating sensor (10 secs) .................... calibrating done
THE SENSOR IS ACTIVE
Motion detected !!!   End of motion detected !!!
Motion detected !!!   End of motion detected !!!
Motion detected !!!   End of motion detected !!!

Le code actuel compte le nombre de passage devant notre senseur de mouvement, et fait clignoter la LED 3 fois à chaque passage.

Nous voyons que l'adresse IP 192.168.1.70 est utilisée et qu'un accès à http://192.168.1.70 (code server.on("/", handleRoot);) nous retournera ceci 

Nb of mouvements: 3

Pour vérifier le code server.on("/temperature", [](){ on utilisera doc http://192.168.1.70/temperature

  21.1 -   33.7

C'est à dire la température en Celsius et l'humidité un peu basse (besoin de calibration quoique il fait un froid de canard dehors, -5).

J'accepte volontiers toutes questions ou commentaires pour améliorer cette partie pas encore terminée: Cette adresse e-mail est protégée contre les robots spammeurs. Vous devez activer le JavaScript pour la visualiser.