Système d’alarmes avec vidéo – Partie 2

Développement de la sonde

Comme je l’ai indiqué dans l’article précédent je vais expliquer ici le code contenu dans les sondes.

Les imports
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <WiFiClient.h>
#include <DHT.h>
Voici la liste des bibliothèques que nous utiliserons dans le projet.
  • La première permet d’utiliser le WIFI de l’ESP8266.
  • La seconde permet d’installer un serveur WEB sur la sonde.
  • La troisième donne la possibilité d’avoir un DNS pour joindre la joindre.
  • L’avant dernière concerne également le WIFI.
  • La dernière permet d’interpréter les données du capteur DHT.
Les constantes
Le code est très documenté. Les commentaires seront donc probablement redondant avec les explications e cet article. Une fois les imports réalisés je défini mes différentes variables et constantes. Par la suite j’initialise le serveur WEB sur le port 80. 
 
//Initialisation des constantes
const char* ssid      = "SSID";
const char* password  = "PASSWORD";
const char* host      = "10.10.5.10";
const int port        = 800;
const String url      = "/mesures/entree";
const String token    = "token: 736438497HG33H423HJG32RX37X30X320M359";
const long interval   = 3600000;
Voici dans l’ordre la liste des constantes :
  • L’identifiant du réseau ou la sonde va se connecter
  • Le mot de passe de ce réseau
  • L’IP du serveur qui héberge l’API (en Mode JS)
  • Le port utilisé par l’API
  • l’URL de l’API (cette partie change en fonction de la sonde)
  • Le jeton d’authentification demandé par l’API
  • Un intervalle d’envoi des données (en l’occurence la sonde envoie des données toutes les heures)  
La configuration des GPIO

La partie suivante indique les différents GPIO que je vais utiliser par la sonde (notation BCM) et pour la bibliothèque DHT une précision car il existe un DHT11 moins précis. Dans notre cas je précise que le type est unDHT22.

//Initialisation des constantes pour les capteurs
#define LIGHT_PIN   5
#define IR_PIN      14
#define DHT_PIN     4
#define GAZ_PIN     12
#define DHT_TYPE    DHT22
  • Le détecteur de luminosité prend le GPIO 5
  • Le détecteur d’infrarouge prend le GPIO 14
  • Le capteur de température et d’humidité est relié au GPIO 4
  • Le capteur MQ2 qui détecter la fumée se connecte au GPIO 12
Les variables de mesure
 Une fois ces initialisions faites je passe à des variables liées au programme de surveillance.
 
//Initialisation des variables d'alarme
bool alarm = false;
 
//Initialisation des variables de capteurs
unsigned long previousMillis  = 0; 
unsigned long currentMillis   = millis();
unsigned long timeout         = millis();
int switchLight               = LOW;
int switchSmoke               = LOW;
float humiditeValue           = 0;
float temperatureValue        = 0;
int calibrationTime           = 60;
int switchPIR                 = LOW;
int statePIR                  = LOW;
long fakePIR                  = 0;
long intervalFakePIR          = 6000;
 
//Définition des variables d'environnement
String temperature   = "temperature: 0";
String humidite      = "humidite: 0";
String luminosite    = "luminosite: 0";
String fumee         = "fumee: 0";
String presence      = "presence: 0";
 La variable « alarm » est un booléen indiquant si l’alarme de la sonde est active ou non. Ce booléen me servira plus tard lors d’une détection de présence. Si elle est activé alors l’API sera contactée. Dans le cas contraire elle ne sera contactée que toutes les heures comme l’intervalle le demande.
 
Les variables milles permettent de réaliser l’intervalle autrement qu’avec « Delay » qui bloque l’exécution du script. Les différentes variables suivantes permettent de récupérer les mesures des capteurs. Les 5 variables d’environnements seront utilisées pour l’appel de l’API. 
La récupération de l’adresse MAC
//Définition des variables de réseau
uint8_t MAC_array[6];
char MAC_char[18];
Comme je l’ai indiqué dans l’article sur l’installation des caméras j’utilise des ACL pour limiter l’accès aux réseaux WIFI eau équipements dont l’adresse MAC est listée. Pour connaitre l’adresse MAC d’un ESP il ne suffit pas de la chercher sur l’ESP en lui même mais il faut lui demander. Ces deux tableaux servent à ça. 
Le serveur WEB
Maintenant nous allons initialiser la bibliothèque pour le DHT et lancer le serveur WEB sur le port 80. Ce serveur permettra une communication bidirectionnelle. Ainsi la centrale (le Raspberry) pourra demander aux sondes leur statut, le modifier , demander une mise à jour immédiate… Ces demandent pourront être réalisées depuis le panneau de contrôle ou depuis le site WEB. Mais également depuis l’extérieur avec un système de sécurité avec un code par SMS.
 
//Initialisation du serveur WEB sur le port 80
ESP8266WebServer server(80);
 
DHT dht(DHT_PIN, DHT_TYPE);
Fonction setup
La première chose à préciser dans cette fonction c’est la vitesse du port série que nous utiliserons. Les Node MCU recommandent une vitesses basse de 9600 bauds/s. Je n’ai jamais eu de problème en allant jusqu’à 115200. Par la suite configurer l’ESP pour qu’il fonctionne en mode station uniquement ce qui permet de ne pas émettre de réseau depuis la sonde.
 
//Initialisation de la sonde
void setup() {
  Serial.begin(115200);
 
  WiFi.mode(WIFI_STA);
 
  //Affichage de l'adresse MAC de la sonde
  WiFi.macAddress(MAC_array);
  for (int i = 0; i < sizeof(MAC_array); ++i){
    sprintf(MAC_char,"%s%02x:",MAC_char,MAC_array[i]);
  }
  
  Serial.println();
  Serial.print("Adresse MAC : ");
  Serial.println(MAC_char);
 
  //Connexion au réseau
  Serial.println();
  Serial.println();
  Serial.print("Connexion à ");
  Serial.println(ssid);
  
  WiFi.begin(ssid, password);
 
  //Affichage d'un point tant que la sonde n'est pas connectée
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
 
  //Affichage de l'IP obtenue
  Serial.println("");
  Serial.println("WiFi connecté");  
  Serial.print("L'adresse IP obtenue est : ");
  Serial.println(WiFi.localIP());
 
  //Activation du mDNS
  if(!MDNS.begin("nom_DNS")) {
      Serial.println("Erreur mDNS");
      
      while(1) { 
        delay(1000);
      }
  }
  
  Serial.println("mDNS activé");
    
  //Démarrage du serveur
  server.begin();
  Serial.println("Serveur TCP démarré");
 
  server.on("/activation", activation);
  server.on("/desactivation", desactivation);
  server.on("/check", check);
  server.on("/update", updateData);
  
  server.onNotFound(noRoute);
    
  //Ajout du service mDNS en TCP sur le port 80
  MDNS.addService("http", "tcp", 80);
 
  //Initialisation de la LED
  pinMode(LED_BUILTIN, OUTPUT);
 
  //Initialisation du capteur PIR
  Serial.print("Calibration du détecteur IR (");
  Serial.print(calibrationTime);
  Serial.println(" secondes)");
  for(int i = 0; i < calibrationTime; i++){
    Serial.println(i+1);
    delay(1000);
  }
 
  //Lancement du capteur DHT
  dht.begin();
}
 Une fois la configuration réalisée il faut passer à la boucle infinie de l’ESP.
Fonction loop
Cette fonction est exécutée  en boucle et dans mon cas un appel à l’API se fait toutes les minutes. Pour le PIR et éviter les faux positifs un con trôle est réaliser sur la durée de la détection. Si la détection est inférieure à ce délai il s’agit alors d’un faux positif et l’alarme n’est pas déclenchée.
 
//Actions répétées indéfiniment toutes les 60 secondes
void loop() {
  currentMillis = millis();

  //Vérification d'un appel de la centrale
  server.handleClient();

  //Lecture de la luminosité
  switchLight = digitalRead(LIGHT_PIN);
   
  if(switchLight == LOW)
  {
    Serial.println("Lumière allumée");
    luminosite    = "luminosite: 1";
  } else {
    Serial.println("Lumière éteinte");
     luminosite    = "luminosite: 0";
  }

  //Lecture de la fumée
  switchSmoke = digitalRead(GAZ_PIN);

  if(switchSmoke == LOW)
  {
    Serial.println("Présence de fumée");
    fumee    = "fumee: 1";
  } else {
    Serial.println("Absence de fumée");
     fumee   = "fumee: 0";
  }

  //Lecture de la température et de l'humidité
  humiditeValue    = dht.readHumidity();
  temperatureValue = dht.readTemperature();
  
  if (isnan(humiditeValue) || isnan(temperatureValue)) {
    Serial.println("Erreur de lecture du capteur DHT22");
    return;
  }
  
  Serial.print("Humidité: ");
  Serial.print(humiditeValue);
  Serial.print(" %\t");
  Serial.print("Température: ");
  Serial.print(temperatureValue);
  Serial.println(" °C ");

  temperature = "temperature: " + String(temperatureValue);
  humidite    = "humidite: " + String(humiditeValue);

   //Lecture des infrarouges
  switchPIR = digitalRead(IR_PIN);

  if(switchPIR == HIGH) {
    delay(150);
    if (statePIR == LOW) {
      fakePIR = currentMillis;
      Serial.println("Présence détectée (non vérifiée)");
      presence      = "presence: 1";
      statePIR      = HIGH;
    }
  } else {
    delay(300);
    if (statePIR == HIGH){
      if(currentMillis - fakePIR > intervalFakePIR) { 
        Serial.println("Vraie présence");
        if(alarm) {
          Serial.println("Alarme active API appelée");
          callAPI();
        }
      } else {
        Serial.println("Fausse détection");
      }

      long trueInterval = currentMillis - fakePIR;
        Serial.print(trueInterval);
        Serial.println(" millisecondes d'intervalle");
      
      presence      = "presence: 0";
      statePIR      = LOW;
    }
  }
  
  //Comparaison du temps courant par rapport au dernier passage et s'il est supérieur à l'intervale on entre dans la condition
  if(currentMillis - previousMillis >= interval) { 
    previousMillis = currentMillis;
    callAPI();
  }
}
 Fonction activation
Cette fonction permet d’activer l’alarme sur la sonde (message reçu de la centrale).
 
//Ecouteur du serveur sur l'activation de la sonde
void activation() {
  Serial.println("----Début du traitement du message de la centrale----");
  alarm = true;
  server.send(200, "text/plain", "Alarme active");
  Serial.println("Réponse envoyée avec confirmation de l'activation de l'alarme");
  Serial.println("----Fin du traitement du message de la centrale----");
}
Fonction activation
Cette fonction permet de désactiver l’alarme sur la sonde (message reçu de la centrale).
//Ecouteur du serveur sur la désactivation de la sonde
void desactivation() {
  Serial.println("----Début du traitement du message de la centrale----");
  alarm = false;
  server.send(200, "text/plain", "Alarme en veille");
  Serial.println("Réponse envoyée avec confirmation de l'activation de l'alarme");
  Serial.println("----Fin du traitement du message de la centrale----");
}
Fonction updateData
Cette fonction permet de forcer une mise à jour des don nées de la sonde (message reçu de la centrale).
//Ecouteur du serveur sur la mise à jour des données
void updateData() {
  Serial.println("----Début du traitement du message de la centrale----");
  callAPI();
  server.send(200, "text/plain", "Actualisation API");
  Serial.println("Réponse envoyée avec confirmation de l'activation de l'alarme");
  Serial.println("----Fin du traitement du message de la centrale----");
}
Fonction check
Cette fonction permet de connaître l’état de la sonde (message reçu de la centrale). Dans la version finale cette fonction retourne en plus le temps d’activité et le nom de la sonde.
 
//Ecouteur du serveur sur l'état de la sonde
void check() {
  Serial.println("----Début du traitement du message de la centrale----");
  String response;
  if(alarm == true) {
      response = "active";
  } else {
      response = "en veille";
  }
  server.send(200, "text/plain", "Alarme " + response + "");
  Serial.println("Réponse envoyée avec confirmation de l'activation de l'alarme");
  Serial.println("----Fin du traitement du message de la centrale----");
}
Fonction noRoute 
Cette fonction permet d’indiquer un e erreur 404 si l’URL appelée n’est pas disponible.
 
void noRoute(){
  server.send(404, "text/plain", "URL inconnue");
}
Fonction callAPI
Cette fonction permet l’appelle de la centrale en affichant sa réponse (utile en mode debug). La LED de l’ESP s’allume également lors de la réponse ce qui permet en mode production de voir la bonne réception par la centrale des mesures. Il est également possible de se connecter à la centrale ou ou site pour consulter les mesures mais la LED permet visuellement de voir une bonne mesure lors du redémarrage du système.
 
//Appel de l'API
void callAPI() {
  digitalWrite(LED_BUILTIN, LOW);
  Serial.print("Connexion à ");
  Serial.println(host);
  //Utilisation de la classe WiFiClient pour créer des connexions TCP
  WiFiClient client;
  if (!client.connect(host, port)) {
    Serial.println("Connexion échouée");
    digitalWrite(LED_BUILTIN, HIGH);
   return;
 }
  Serial.print("Appel de l'API sur l'URL : ");
  Serial.println(url);
  //Préparation de la requête
  client.println("POST " + url + " HTTP/1.1");
  client.println("Host: " + String(host));
  client.println(token);
  client.println(temperature);
  client.println(humidite);
  client.println(luminosite);
  client.println(fumee);
  client.println(presence);
  client.print("Content-Length: ");
  client.println(token.length() + temperature.length() + humidite.length() + luminosite.length() + fumee.length() + presence.length() );
  client.println();
  //Vérification de la réponse de l'API
  timeout = millis();
  while (client.available() == 0) {
    if (millis() - timeout > 5000) {
      Serial.println("Timeout de l'API");
      client.stop();
      return;
    }
  }
  //Affichage de la réponse du serveur
  Serial.println();
  Serial.println("----Début de la réponse----");
  Serial.println();
  while(client.available()){
    String line = client.readStringUntil('\r');
    Serial.print(line);
  }
  Serial.println();
  Serial.println("----Fin de la réponse----");
  Serial.println();
  digitalWrite(LED_BUILTIN, HIGH);
}

Maintenant que le code des sondes est expliqué ainsi que celui de l’API je vais passer au site qui présente les données et qui permet d’interagir avec les sondes. Par la suite je rédigerai des articles sur la centrale physique (avec clavier et écran reliés au Rapsberry via des GPIO) et du système de ventilation. Puis des ajouts comme la synthèse vocale.

Posted in Banana Pi, Domotique, ESP8266, Odroid, Orange Pi, Raspberry Pi and tagged , , , , , , , , , , , , , , , , , , .

Fondateur du site Single Blog Computer. Je suis captivé par toutes les nouveautés high tech permettant de faciliter la vie du quotidien. Les SBC me semblent la solution idéale dans ce cas de figure.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *