2022-ING2 Miroir Connecté

Un miroir intelligent sur écran tactile donnant accès à la météo, aux actualités avec l'aide des API mais aussi des données environnementales (CO2, Température, Humidité) transmises par des capteurs connectés

Slides & Videos

Members

NameContribution
Hamza RAHALPartie capteurs de données environnementales - ESP8266
Hicham GHANEM-Partie communication des données des capteurs via MQTT vers le miroir et affichage des données sur un dashboard avec Node Red
-Etat de l'art.
-Configuration de l'écran avec Raphael.
Raphaël GANDUSConfiguration de l'écran tactile avec la Raspberry Pi - Recherche et installation de l'application MagicMirror et des différents modules

State of the Art

Business Aspect

Miroir connecté : Guide comparatif des meilleurs miroirs intelligent 2022

Cet article nous fait rapidement la présentation des miroirs connectés, les différentes fonctionnalités qu’ils peuvent avoir et enfin il nous donne un petit classement des miroirs connectés qui sont présents sur le marché. Un miroir connecté coûte en moyenne 400 euros ce qui est assez cher.

 

Technical Aspect

Project Description

Problem Definition
Chez soi, comment accéder rapidement à des informations utiles sans perdre de temps (sur son téléphone par exemple), en faisant quelque chose d'important en même temps (comme se préparer en se regardant dans un miroir) ?
Challenges & Motivation
Lorsqu'on se lève le matin, se regarder dans un miroir est généralement l'une des premières choses que l'on fait. Nous avons donc eu l'idée d'allier ce besoin à celui d'accéder à des informations rapidement. Quand on se prépare à sortir, on a souvent besoin de connaître la météo du jour, les horaires de nos transports en commun ou les dernières actualités. Notre miroir connecté répond à ces problèmes.
Real and Complete Usecases

L’utilisateur se positionne devant le miroir. 

Il voit son reflet, ainsi qu’un bouton principal sur lequel il peut appuyer car l’écran est tactile.

Une fois le bouton appuyé, de nombreux widgets s’affichent sur le miroir : date, heure, escompte, compliments, actualités, données environnementales enregistrées par un capteur connecté, horaires de transports en commun.

Si l’utilisateur rappuie sur le bouton, les widgets disparaissent et le miroir n’affiche plus que son reflet.

Technical Description

1) Miroir

L’écran tactile est relié à un Raspberry Pi 3b qui contrôle toute l’interface.

D’abord, il fallait configurer la Raspberry pour qu’elle soit compatible avec cet écran. Nous avons pour cela suivi les instructions données par le manuel de l’écran. Il fallait faire plusieurs commandes Unix. Vous trouverez dans la partie logiciel un document regroupant toutes ces commandes.

Une fois l’écran bien configuré, nous avons installé l’application MagicMirror, qui est open source. Codée en node.js, elle requiert le paquet npm pour l’installation et la modification des fichiers. Nous avons copié sur la Raspberry le répertoire GitHub suivant : MagicMirror qui contient l’application. Nous avons suivi ces étapes fournies par l’auteur officiel de l’application pour l’installation.

Maintenant que l’application est installée, il faut modifier le fichier config.js pour pouvoir ajouter, modifier ou retirer des modules à notre interface graphique. Vous trouverez ce fichier dans le logiciel de partie. Pour ajouter un module, il faut :

  • Télécharger les fichiers du module (en général à partir de GitHub) et les mettre dans le dossier MagicMirror/modules. Nous les avons téléchargés à partir de ce répertoire GitHub .
  • Modifier le fichier config.js qui se trouve dans MagicMirror/config, puis ajouter un bloc dans la section modules avec le nom du module, sa position sur l’écran, ainsi que les options propres au module. Par exemple, pour le module météo, les options permettent de choisir le lieu, le nombre de jours de récupération à afficher, la clé API pour obtenir les données à partir d’un serveur web, …

 

Voici les modules que nous avons décidé d’utiliser sur notre miroir connecté :

  • Module date qui affiche la date et l’heure en haut à gauche
  • Module météo qui affiche la température du jour, la direction du vent, l’heure de lever et coucher du soleil en haut à droite
  • Module météo sur 3 jours juste en dessous du module météo
  • Module RATP donnant l’état de fonctionnement de la ligne de métro 5 à Paris, sous les widgets météo
  • Module compliments qui affiche plusieurs phrases telles que “Salut toi !” ou “Tu es beau aujourd’hui” au milieu en haut de l’écran
  • Module Actualités relié au flux RSS du Monde et qui affiche en une phrase une actualité du jour, au milieu en bas de l’écran
  • Module Données environnementales qui affiche les données recueillies par nos capteurs et transmises au miroir via MQTT, au milieu à gauche de l’écran (voir partie suivante pour explication détaillée de ce module et de son fonctionnement)

 

2) Capteurs connectés – Recueil de données environnementales

Le capteur a été concu à l’aide d’un ESP8266 et de trois capteurs complémentaires pouvant fournir température, humidité, CO2, luminosité etc…

  • SGP30
  • VEML22
  • DHT11

Concernant la liaison physique des capteurs aux microcontrôleurs, il s’agit de liaison i2c et d’un pin GPIO en mode onewire pour le DHT11.

Le protocole utilisé est le MQTT, les données MQTT sont reçues par la framboise et un module miroir magique permet d’en afficher les données.

La plateforme utilisée pour programmer et flasher l’ESP est PlatformIO, une extension sur VS code.

L’alimentation de l’ESP peut se faire au choix : Par batterie branchée sur Vin, ou encore par câble micro usb branché sur du 5V 500mA min.

L’utilisation de ce capteur connecté permet de mettre en œuvre un miroir en utilisant des capteurs que l’on pourra trouver dans les jours à venir dans le cadre de la maison 3.0 (Smart Home).

Voir fichier toFlash.cpp pour le code des capteurs et de l’ESP.

 

 

 

 

 

Hardware

Materials
ImageNamePart NumberPriceCountLink
Raspberry Pi 3b V1.22ABCB-RPI32301🛒
RB-LCD10-2 Touchscreen DisplayD-47506149 €1🛒
ESP8266 + capteurs//15+3*101🛒
Schematic

Software

Arduino Code

/* ESP8266 BROCHAGE

                __________________
          ADC0 | A0 o         o D0|  GPIO16   --> a connecter à RST pour DeeplSleep
NC on some  5V | VU o         o D1|  GPIO5    --> SLK
NC on some GND |  G o         o D2|  GPIO4    --> SDA
        GPIO10 | S3 o         o D3|  GPIO0
         GPIO9 | S2 o         o D4|  GPIO2    --> DHT signal
          MOSI | S1 o         o 3V|  3.3V
            CS | SC o         o G |  GND
          MISO | SO o         o D5|  GPIO14
          SCLK | SK o         o D6|  GPIO12   --> DHT +vcc allumage
           GND |  G o         o D7|  GPIO13
          3.3V | 3V o         o D8|  GPIO15   
            EN | EN o         o RX|  GPIO3
         Reset |RST o         o TX|  GPIO1
           GND |  G o         o G |  GND      
           Vin |VIN o         o 3V|  3.3V     --> interrupteur sur + batterie
               |__________________|



*/

#include 
#include 

#include 
#include  // librairie pour configurer une liste de point d'accès (Router 1, router 2, etc.)
#include  //Librairie pour la gestion Mqtt 

#include 
#include 

#include 

#define TIME_TO_SLEEP           15 // en mn
#define DHT_PIN_VCC             12
#define DHT_PIN                 2
#define ESP32


#define DEBUGING                 1
#define MIROIR                   1

#if MIROIR == 1
  unsigned long tps=0;
#else
  float temp;
#endif

ADC_MODE(ADC_VCC); // pour renvoyer vcc sur ADC du coup on ne peut plus

// declaration des capteurs
DHTesp dht;
Adafruit_SGP30 sgp;
Adafruit_VEML7700 veml;

// variable d'erreur pour les capteurs
int8_t sgp_error = 1, veml_error = 1, dht_error = 1; 

#if MIROIR == 1

  const IPAddress remote_ip(192, 168, 137,67);//(192,168,137,161); // ip du broker a ping
  const IPAddress router_ip(192,168, 137,1); // adresse ip du ROUTEUR
  IPAddress subnet(255, 255, 255, 0);  //Subnet mask
  //IPAddress dns(8, 8, 8, 8);  //DNS
  //WIFI
  const char* ssid = "Pc Hamza";//"SFR_D400_EXT"; // "Pc Hamza";
  const char* password = "hamza123";//"dmjq3fvp87csqq7zys79"; // "hamza123";
  const IPAddress staticIP(192, 168, 137, 250); //ESP demande cette IP
  //MQTT
  const char* mqtt_server = "192.168.137.67 ";// "192.168.1.67";//Adresse IP du Broker Mqtt
  const int mqttPort = 1883; //port utilisé par le Broker 

#else

  const IPAddress remote_ip(192, 168, 137,67);//(192,168,137,161); // ip du broker a ping
  const IPAddress router_ip(192,168, 137,1); // adresse ip du ROUTEUR
  IPAddress subnet(255, 255, 255, 0);  //Subnet mask
  //IPAddress dns(8, 8, 8, 8);  //DNS
  //WIFI
  const char* ssid = "Pc Hamza";//"SFR_D400_EXT"; // "Pc Hamza";
  const char* password = "hamza123";//"dmjq3fvp87csqq7zys79"; // "hamza123";
  const IPAddress staticIP(192, 168, 137, 250); //ESP demande cette IP
  //MQTT
  const char* mqtt_server = "192.168.137.67 ";// "192.168.1.67";//Adresse IP du Broker Mqtt
  const int mqttPort = 1883; //port utilisé par le Broker 

#endif

// objet pouvant contenir plusieurs ssid
ESP8266WiFiMulti WiFiMulti;

WiFiClient espClient;

// declaration du client MQTT
PubSubClient client(espClient);

//--------------------------------------------------------------------------------------------------------------------------------
void setup() {



  #if DEBUGING == 1

  {
    Serial.begin(9600); // pour le debugging
    Serial.println(); //saut de ligne
  }

  #endif
  sgp_test();

  veml_test();

  allumer_dht();
  dht.setup(DHT_PIN, DHTesp::DHT11);
  dht_test();

  //pinMode(4,INPUT); // port GPIO4 en mode Entrée
  
  setup_wifi();
  
  test_and_connexion_mqtt();

  delay(1000); // tempo

  
  
  //client.publish("test", "Hello from ESP8266");
}
//--------------------------------------------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------------------------------------
void loop() {

/*
 * si on est sur le projet miroir on évite de s'éteindre
 * 
 */

#if MIROIR == 1

    if(millis() - tps > 5)  
    {

      // si jamais le wifi se deconnecte, on reconnecte au wifi(utile que si on reste bloqué dans loop c'est a dire sans redemarré)
      if(WiFiMulti.run() != WL_CONNECTED) 
      {
        setup_wifi();
        test_and_connexion_mqtt();
      }

      if(sgp_error || veml_error || dht_error) ESP.restart();

      sgp.IAQmeasure();
      delay(2000);
      sgp.IAQmeasureRaw();
      delay(2000);
      sgp.IAQmeasure();
      delay(2000);
      sgp.IAQmeasureRaw();
      delay(2000);

      // Publication des valeurs sur les differents Topics MQTT
      mqtt_publish("esp/SGP30/CO2",sgp.eCO2);
      mqtt_publish("esp/SGP30/H2",sgp.rawH2);
      mqtt_publish("esp/SGP30/Ethanol",sgp.rawEthanol);
      
      mqtt_publish("esp/VEML/Lumiere",veml.readLux());
      mqtt_publish("esp/DHT11/Temperature", 20.5);// dht.getTemperature());
      
      mqtt_publish("esp/Battery",(float)ESP.getVcc()/1000.0); // car en mV
      
      tps = millis();
    }
#else
  
  
    delay(dht.getMinimumSamplingPeriod()); // on attend le temps necessaire pour avoir un echantillon
    connect_mqtt();
    client.loop(); 
    


    //On utilise pas un delay pour ne pas bloquer la réception des messages 
    //on envoit des donnes toutes les secondes DEBUG car après on passera au sleep mode
    //if (millis()-tps>10000)
    
      //tps=millis();
        
      mqtt_publish("esp/Battery",(float)ESP.getVcc()/1000.0); // car en mV

      // partie traitement des informations SGP30
      if(! sgp_error)
      {
          sgp.IAQmeasure();
          delay(2000);
          sgp.IAQmeasureRaw();
          delay(2000);
          sgp.IAQmeasure();
          delay(2000);
          sgp.IAQmeasureRaw();
          delay(2000);
          
          if(DEBUGING) Serial.println(" SGP values : Publication...");
          mqtt_publish("esp/SGP30/CO2",sgp.eCO2);
          mqtt_publish("esp/SGP30/H2",sgp.rawH2);
          mqtt_publish("esp/SGP30/Ethanol",sgp.rawEthanol);
          if(DEBUGING) Serial.println("Values Published !");
      }
      else
      {
        if(sgp_error == 1)
        {
          //client.loop(); 
          if(DEBUGING) Serial.println(" SGP Error : publication...");
          client.publish("esp/Erreur","FAIL : SGP30 Not Found");
          if(DEBUGING) Serial.println(" SGP Error : published !");

        }

        else
        {
          if(DEBUGING) Serial.println(" SGP Error : publication...");
          client.publish("esp/Erreur","FAIL : IAQ not initialized");
          if(DEBUGING) Serial.println(" SGP Error : published !");
        }
      }

      if(! veml_error)
      {
          delay(0.5*1000);
          if(DEBUGING) Serial.println(" VEML Values : publication...");
          mqtt_publish("esp/VEML/Lumiere",veml.readLux());
          if(DEBUGING) Serial.println(" VEML Values : published !");
      }
      else
      {
        client.loop();
        if(DEBUGING) Serial.println(" VEML Error : publication...");
        client.publish("esp/Erreur","FAIL : VEML Not Found");
        if(DEBUGING) Serial.println(" VEML Error : published !");
      }


      if (! dht_error)
      {
        Serial.println(dht.getTemperature());
        temp = dht.getTemperature();
        #if DEBUGING == 1 
          Serial.println(" DHT Values : publication...");
        #endif
        mqtt_publish("esp/DHT11/Temperature", temp);
        temp = dht.getHumidity();
        mqtt_publish("esp/DHT11/Humidite", temp);
        #if DEBUGING == 1 
          Serial.println(" DHT Values : published !");
        #endif
        
      }
      else
      {
        Serial.println(dht.getStatusString());
        client.loop(); 
        if(DEBUGING) Serial.println(" DHT Error : publication...");
        client.publish("esp/Erreur", dht.getStatusString());
        if(DEBUGING) Serial.println(" DHT Error : publication...");
      }

      sgp.softReset(); // on soft reset pour plonger le capteur dans le sleep mode
      veml.powerSaveEnable(true); // on disable pour plonger le capteur dans le sleep mode
      
      #if DEBUGING == 1
      
        Serial.print("sleeping for ");
        Serial.println(TIME_TO_SLEEP);
        delay(100);
      
      #endif
      
      client.disconnect();
      eteindre_dht();
      delay(250);
      ESP.deepSleep(TIME_TO_SLEEP*60*1000000);
#endif

     
}
//--------------------------------------------------------------------------------------------------------------------------------

/*!
*
* @brief configure la connexion wifi de l'ESP
* 
* ssid et password sont définis en variable globale
*/

void setup_wifi()
{
  //connexion au wifi
  WiFi.config(staticIP, subnet, router_ip);
  WiFiMulti.addAP(ssid, password);
  #if  DEBUGING == 1
  
    Serial.print("\n\nconnection au wifi : ");
    Serial.print(ssid);
  
  #endif
  while ( WiFiMulti.run() != WL_CONNECTED ) {
    delay ( 500 );

    #if DEBUGING == 1
      Serial.print ( "." );
    #endif
  }

  #if DEBUGING == 1
  
    Serial.println("");
    Serial.println("WiFi connecté");
    Serial.print("MAC : ");
    Serial.println(WiFi.macAddress());
    Serial.print("Adresse IP : ");
    Serial.println(WiFi.localIP());
  #endif

}

/*!
*
* @brief configure la connexion au serveur mqtt.
* 
*   Utilise variable global description donc pas besoin de parametre, mais il faut declarer et définir mqtt_server et mqttPort.
*
*/

void test_and_connexion_mqtt()
{
  /*
  * Parfois ca ne marche pas car le router bloque les pings entre devices par sécurité c'est le cas avec le partage de connexion wifi
  */
 #if DEBUGING == 1
 
    Serial.print("ping ");
    Serial.print(router_ip);
 
 #endif


  while(!Ping.ping(router_ip)) {
    if(DEBUGING) Serial.print(".");
    //delay(500);
  }
  #if DEBUGING == 1
  {
    Serial.println();
    Serial.print(router_ip);
    Serial.println(" has responded Successfully!");
    Serial.print("ping Mqtt server...");
  }
  #endif
  while(!Ping.ping(remote_ip)) {
    
    Serial.print('.');
  }
  #if DEBUGING == 1
  
    Serial.println("Success!");
  #endif
  client.setServer(mqtt_server, mqttPort);
  //client.setCallback(callback);//Déclaration de la fonction de souscription
  connect_mqtt();

}


/*!
* @brief 
*
*
*
*
*
*/
void sgp_test()
{
  if (!sgp.begin())
  {// test de l'existence du capteur sur la liaision i2c
    sgp_error = 1;
    if(DEBUGING) Serial.println("fail : sgp not found");

  }
  else
  {
    if(DEBUGING) Serial.println("Success : sgp found");
    //sgp.setHumidity(getAbsoluteHumidity(dht.getTemperature(),dht.getHumidity()));
    delay(1000);
    sgp.IAQinit();
    delay(15*1000);
        
    if(! sgp.IAQinit())// commande le capteur pour commencer l'algorithme Init_Air_Quality
    {
      if(DEBUGING) Serial.println("fail : IAQ not initialized");
      sgp_error = 2;
    }
    else
    {
      if(DEBUGING) Serial.println("Success : sgp IAQ initialized");
      sgp.IAQmeasure();
      delay(2000);
      sgp.IAQmeasureRaw();
      delay(2000);
      sgp_error = 0;
          
    }

  }
}

/*!
* @brief 
*
*
*
*
*
*/

void veml_test()
{
  if(veml.begin())
  {
    if(DEBUGING) Serial.println("Success : VEML found");
    veml.powerSaveEnable(false);
    veml.setLowThreshold(10000);
    veml.setHighThreshold(20000);
    veml_error = 0;
  }
}

/*!
* @brief 
*
*
*
*
*
*/

void allumer_dht()
{
  pinMode(DHT_PIN_VCC, OUTPUT);
  digitalWrite(DHT_PIN_VCC, HIGH);
  delay(2000);
}

/*!
* @brief 
*
*
*
*
*
*/

void eteindre_dht()
{
  digitalWrite(DHT_PIN_VCC, LOW);
}

/*!
* @brief 
*
*
*
*
*
*/

void dht_test()
{
   if(dht.getStatus() == 0)
  {
    if(DEBUGING)
    {
    Serial.print("SUCCESS : DHT ");
    Serial.println(dht.getStatusString());

    }

    dht_error = 0;
  }
}


/*!
* @brief 
*
*
*
*
*
*/
void connect_mqtt(){
  while (!client.connected()) {
    if(DEBUGING)Serial.println("Connection au serveur MQTT ...");
    if (client.connect("ESP32Client")) {
      if(DEBUGING)Serial.println("MQTT connecté");
    }
    else {
      if(DEBUGING)
      {
        Serial.print("echec, code erreur= ");
        Serial.println(client.state());
        Serial.println("nouvel essai dans 2s");
      }
    delay(2000);
    }
  }
  //client.subscribe("led");//souscription au topic led pour commander une led
}

//Fonction pour publier un float sur un topic 










/*!
*
* @brief publie un flottant en mqtt sur le topic
*
* @param  topic
* topic mqtt
* @param  value
* vakuer flottante a envoyer
* 
*/
void mqtt_publish(String topic, float value){
  client.loop(); 

  char top[topic.length()+1];

  topic.toCharArray(top,topic.length()+1);
  char val[100];

  String t_str = String(value);

  t_str.toCharArray(val, t_str.length() + 1);
  if(DEBUGING)Serial.println(topic);
  client.publish(top,val);
  
}

/*! 
* @brief return absolute humidity [mg/m^3] with approximation formula
* @param temperature [°C]
* @param humidity [%RH]
*/
uint32_t getAbsoluteHumidity(float temperature, float humidity) {
    // approximation formula from Sensirion SGP30 Driver Integration chapter 3.15
    const float absoluteHumidity = 216.7f * ((humidity / 100.0f) * 6.112f * exp((17.62f * temperature) / (243.12f + temperature)) / (273.15f + temperature)); // [g/m^3]
    const uint32_t absoluteHumidityScaled = static_cast(1000.0f * absoluteHumidity); // [mg/m^3]
    return absoluteHumidityScaled;
}

External Services