DOMOTIQUE : Détecteur d'ouverture porte de garage avec ESP32 et LORAWAN

30 mars 2019 rdorigny 0 commentaires

Dans cet article, je vais vous montrer comment j'ai réalisé un détecteur d'ouverture de porte pour mon garage avec transmission de la détection LORA.

J'ai bien essayé de le faire en WIFI avec un raspberry Zero W, mais cette solution n'est pas assez stable (perte de réseau, plantage du raspberry). Donc j'ai opté pour l'utilisation d'un microcontrôleur (ESP32) et l'utilisation d'un protocole de transmission aérien fiable (LORA)





Principe

Je souhaite être averti lorsque la porte de mon garage est ouverte. En sachant qu'il est un peu écarté et donc que la réception WIFI n'est pas terrible. Et la bande passante en 433Mhz est très encombrée. Donc le protocole LORA en 868Mhz m'avait semblé une bonne solution. Pour cela j'utilise des modules RFM95.

Pour la détection, j'ai opté pour le sonar HY-SRF05 qui va permettre de détecter la distance entre l'émetteur et un obstacle, la porte en l’occurrence, et donc de sa position ouverte ou fermée.

La tâche étant assez simple et répétitive, un microcontrôleur est tout indiqué pour notre petit système. J'ai choisi, l'ESP32.

Le matériel

Pour notre système il faudra donc:
  • 2 ESP32 (Espressif ESP32 DevKitC V4 avec ESP32-WROOM-32 Rev. 1L);
  • 2 RFM95 Ultra-Long Range Transceiver Module/Lora 868Mhz ;
  • 1 hy-srf05 comme sonar.

  • Le schéma de câblage


    Pour l'émetteur du garage:


    Pour le récepteur de la maison:

    La réalisation

    Voici quelques photos de la réalisation de l’émetteur du garage, au départ tests sur breadboard, puis construction sur d'une plaquette PCB de prototypage. Le tout dans un boite de dérivation électrique.
    Le récepteur maison est encore assez sommaire, il va falloir que je pense à lui trouver un boitier!

    Le code

    On va distinguer trois parties: le code de l’émetteur, celui du récepteur de la maison et enfin celui de la mise à jour de la base de données pour mémoriser le changement de position.

    Code de l'émetteur du garage:
    L'émetteur mesure distance avec la porte et la transmet sur le réseau LORA.
    /*********
      Robert Dorigny le 14 février 2019
      www.doritique.fr
    *********/
    
    #include (SPI.h) //remplacer les () par <>
    #include (LoRa.h)
    
    //define the pins used by the transceiver module
    #define ss 5
    #define rst 14
    #define dio0 2
    const unsigned int TRIG_PIN=13;
    const unsigned int ECHO_PIN=12;
    int counter = 0;
    
    //Calcul de la distance en cm
    int dist() { 
      digitalWrite(TRIG_PIN, LOW);
      delayMicroseconds(2);
      digitalWrite(TRIG_PIN, HIGH);
      delayMicroseconds(10);
      digitalWrite(TRIG_PIN, LOW);
     const unsigned long duration= pulseIn(ECHO_PIN, HIGH);
     return(duration/29/2); 
    }
    
    void setup() {
      //Initialisation entrées/sorties sonar
      pinMode(TRIG_PIN, OUTPUT);
      pinMode(ECHO_PIN, INPUT);
      //Initialisation série
      Serial.begin(115200);
      while (!Serial);
      Serial.println("LoRa Sender");
      //setup LoRa transceiver module
      LoRa.setPins(ss, rst, dio0);
      //replace the LoRa.begin(---E-) argument with your location's frequency 
      //433E6 for Asia
      //866E6 for Europe
      //915E6 for North America
      while (!LoRa.begin(866E6)) {
        Serial.println(".");
        delay(500);
      }
       // Change sync word (0xF3) to match the receiver
      // The sync word assures you don't get LoRa messages from other LoRa transceivers
      // ranges from 0-0xFF
      LoRa.setSyncWord(0xF3);
      Serial.println("LoRa Initializing OK!");
    }
    
    void loop() {
      Serial.print("Sending packet: ");
      Serial.println(counter);
    
      //Send LoRa packet to receiver
      LoRa.beginPacket();
      LoRa.print("dpg: "); //distance porte garage
      LoRa.print(dist());
      LoRa.endPacket();
    
      counter++;
    
      delay(10000);
    }
    

    Code du récepteur de la maison:
    Le récepteur réceptionne la trame LORA et récupère la distance. Au bout de 3 dépassements du seuil, la porte est considérée comme ouverte et donc il y a mis à jour de la base de données avec une api en PHP; puis transmission d'un SMS (en utilisant l'api de l'opérateur Free).
    /*********
    Robert DORIGNY le 10 mars 2019
    rdorigny@free.fr - www.doritique.fr
    *********/
    #include (WiFi.h)  //remplacer les () par <>
    #include (HTTPClient.h)
    #include (SPI.h)
    #include (LoRa.h)
    #include "esp_system.h"
    
    //Definition des pins du module de transmission
    #define ss 5
    #define rst 14
    #define dio0 2
    //Pour le wifi
    const char* ssid = "votreréseau";
    const char* password =  "votremotdepasse";
    
    int position=0;     //Conserve en memoire la position de la porte, 0 pour fermee et 1 pour ouverte
    int Mem[] = {0, 0, 0}; //Conserve en memoire les dernieres mesures
    int Longueur_Seuil=300;
    int cpt=0;
    String LoRaData;
    
    //Cette fonction initialise la variable position +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    int Recup_position(){
        if(WiFi.status()== WL_CONNECTED) //Check WiFi connection status
         {   
         //Emission de la requete vers l'API
         HTTPClient http;   
         //Recup dans la base de données
         http.begin("http://www.votresite.fr/monapi.php?action=get&var=garage");  
         http.addHeader("Content-Type", "text/plain");             //Specify content-type header
         int httpResponseCode = http.POST("POSTING from ESP32");   //Send the actual POST request
         if(httpResponseCode>0)
           {
           String response = http.getString();                       
           Serial.println(httpResponseCode);   //Print return code
           Serial.println("Position intiale:"+response); 
           return(String(response).toInt());          
           }
         }
      }
    
    //Cette fonction est appelée lorsqu'un changement de position de la porte est détecté+++++++++++++++++++++++++++++++ 
    void Send_Data(){
       if(WiFi.status()== WL_CONNECTED) //Check WiFi connection status
         {   
         //Emission de la requete vers l'API
         HTTPClient http;   
         //Mise à jour de la base de données
         http.begin("http://www.votresite.fr/monapi.php?action=set&var=garage&data="+String(position));  
         http.addHeader("Content-Type", "text/plain");             //Specify content-type header
         int httpResponseCode = http.POST("POSTING from ESP32");   //Send the actual POST request
         if(httpResponseCode>0)
           {
           String response = http.getString();                       //Get the response to the request
           Serial.println(httpResponseCode);   //Print return code
           Serial.println(response);           //Print request answer
           }
         else
           { 
           Serial.print("Error on sending POST: ");
           Serial.println(httpResponseCode); 
           }
         delay(2000);
         //Envoi du SMS
         String str="";
         if (position==1) 
           str="ouverte"; 
         else 
           str="fermée"; 
         http.begin("https://smsapi.free-mobile.fr/sendmsg?user=codeuser&pass=clefreeK&msg=Porte%20garage%20"+str);  
         http.addHeader("Content-Type", "text/plain");             //Specify content-type header
         httpResponseCode = http.POST("POSTING from ESP32");   //Send the actual POST request
         if(httpResponseCode>0)
           {
           String response = http.getString();                       //Get the response to the request
           Serial.println(httpResponseCode);   //Print return code
           Serial.println(response);           //Print request answer
           }
         else
           { 
           Serial.print("Error on sending POST: ");
           Serial.println(httpResponseCode); 
           }
         http.end();  //Free resources
       }
     else
       {
       Serial.println("Perte de la connexion WIFI. Reboot automatique."); 
       esp_restart();
       }
     }
    
    //Phase d'initialisation++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    void setup() {
      //initialize Serial Monitor
      Serial.begin(115200);
    
      delay(4000);   //Délais nécessaire avant d'appeler le wifi
    
      //Connexion au wifi
      WiFi.begin(ssid, password); 
      while (WiFi.status() != WL_CONNECTED) { //Check la connexion
        delay(1000);
        Serial.println("Tentative de connexion au WiFi...");
        cpt++;
        if (cpt>30)
          {
          Serial.println("Pas de connexion au wifi! Reboot automatique...");
          esp_restart();
          }
      }
      Serial.println("Connexion au réseau local wifi: OK!");
      
      while (!Serial);
      Serial.println("LoRa Receiver");
    
      //setup position
      position=Recup_position();
    
      //setup LoRa transceiver module
      LoRa.setPins(ss, rst, dio0);
      
      //replace the LoRa.begin(---E-) argument with your location's frequency 
      //433E6 for Asia
      //866E6 for Europe
      //915E6 for North America
      while (!LoRa.begin(866E6)) {
        Serial.println(".");
        delay(500);
      }
       // Change sync word (0xF3) to match the receiver
      // The sync word assures you don't get LoRa messages from other LoRa transceivers
      // ranges from 0-0xFF
      LoRa.setSyncWord(0xF3);
      Serial.println("LoRa Initializing OK!");
    }
    
    //Boucle d'attente de réception de la distance+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    void loop() {
      //delay(1000);
      cpt++;
      // try to parse packet
      int packetSize = LoRa.parsePacket();
      if (packetSize) {
        // received a packet
        Serial.print("Received packet '");
        cpt=0;
    
        // read packet
        while (LoRa.available()) {
          LoRaData = LoRa.readString();
          Serial.print(LoRaData); 
        }
    
        // print RSSI of packet
        Serial.print("' with RSSI ");
        Serial.println(LoRa.packetRssi());
        //traitement de la distance
        if (LoRaData.startsWith("dpg: ")){ //test si le paquet est valable
          LoRaData.replace("dpg: ",""); //Vire le protocol
          Serial.println(LoRaData);
          int distance=LoRaData.toInt();
          //Mise en mémoire des dernières positions  
          Mem[2]=Mem[1];
          Mem[1]=Mem[0];
          //Traitement de la distance
          if (distance>Longueur_Seuil)
            {
            Serial.println("Porte ouverte");
            Mem[0]=1;
            if ((position==0)&&(Mem[0]==1)&&(Mem[1]==1)&&(Mem[2]==1)) 
              {
              position=1;
              Serial.println("Chargement base: ouverte");
              //Met à jour la base et envoie un SMS
              Send_Data();
              }
            }
          else
            { 
            Serial.println("Porte fermée");
            Mem[0]=0;
            if ((position==1)&&(Mem[0]==0)&&(Mem[1]==0)&&(Mem[2]==0)) 
              {
              position=0;
              Serial.println("Chargement base: fermée");
              //Met à jour la base et envoie un SMS
              Send_Data();
              }
            }
          //Affiche la mémoire
          for (int i = 0; i <= 2; i++) {
            Serial.print(Mem[i]);
          }
          Serial.println(" ");
        }
      }
      if (cpt>1000000) esp_restart();
      }
    
    

    Extrait de monapi.php: Voici un extrait de code pour mettre à jour un changement de position de la porte ou plus simplement connaitre le dernier état connue de la porte pour initialiser la variable position.
    //******************************************************************************************
    //Fonction d'injection des données
    function api_set($var)
    {
    global $Connection;
    global $_GET;
    
    switch ($var)
      {
      case ("garage") : //insertion des logs d'ouverture et fermeture du garage	
    	  $data=$_GET["data"];
    	  if (($data=="0")||($data=="1"))
    	    {
            $requete="insert into mon_garage (date,position) values ('".date("Y-m-d H:i:s")."',".$data.")"; 
            $resultat=mysqli_query($Connection,$requete);
    		}
    	  break;	  
      default:
        print("La variable appelée n'existe pas.");
      } 
    }
    //******************************************************************************************
    //Fonction de récupération des données
    function api_get($var)
    {
    global $Connection;
    
    switch ($var)
      {
      case ("garage") : //Récupération de la position de la porte du garage	  
        $requete="select position from mon_garage ORDER BY num DESC LIMIT 1 "; 
        $resultat=mysqli_query($Connection,$requete);
        if ($resultat)  
    	  {
          $t2=mysqli_fetch_row($resultat);
    	  print($t2[0]);
    	  }    
        break; 	
      default:
        print("La variable appelée n'existe pas.");
      } 
    }
    

    Ce qui donne sur le moniteur du récepteur:

    Conclusion

    Après un mois de test, mon système fonctionne très bien. Aucun plantage, comme j'avais avant avec le Raspberry pi qui perdait la connexion wifi. Je connais désormais l'état d'ouverture de ma porte de garage.

    Pour le moment, le récepteur maison est assez sommaire mais je prévois de le transformer en tant que centrale domotique avec un petit écran LED (surement basée sur un écran Nextion), un buzzer pour avertir de l'ouverture du garage et quelques options pas encore clairement définies. On en recause.






    Pseudonyme (obligatoire) :
    Adresse mail (obligatoire) :
    Site web :




    © 2019 www.doritique.fr par Robert DORIGNY