Station météo wifi – Partie 2

Après un premier essai de capteurs météo avec retransmission sans fil vers le web (Adafruit IO), voici un second projet dont l’objectif est de permettre le stockage en local des données. Trois avantages : vous êtes maître de vos données, pas de coût de stockage en ligne, et pas d’envoi en ligne en continu.

Passons à la description du projet…

Matériel utilisé

Carte NodeMCU ESP8266-12E

La carte utilisée (dénommée NodeMCU) s’appuie sur un module ESP-12E qui intègre un microcontrôleur ESP8266. L’ESP8266 est un circuit intégré à microcontrôleur avec connexion Wi-Fi développé par le fabricant chinois Espressif.

Cette carte regroupe donc :

  • un microcontrôleur ESP8266 notamment programmable avec l’IDE ARDUINO,
  • un module WIFI pour communiquer avec le vaste mnde,
  • une liaison série,
  • des entrées/sorties.

Capteur de température et d’hygrométrie DHT22

Ce capteur est utilisé pour capter température et hygrométrie à l’intérieur.

Capteur de température, humidité et pression atmosphérique bme280

Ce capteur est utilisé pour capter les données extérieures. Il est installé dans une petite capsule plastique. Il ne faut pas que la boîte soit étanche afin que le capteur puisse mesurer correctement la pression atmosphérique :

Le reste de l’électronique (carte ESP8266 + capteur DHT22) est positionné à l’intérieur :

Raspberry Pi 4

Ce mini-ordinateur (la framboise en français) va permettre de gérer le serveur de données (stockage, visualisation).

Programmation du ESP8266 sous ARDUINO

Le programme ci-dessous permet de :

  • récupérer les données des différents capteurs :
    • DHT22 (température, humidité) en direct sur une entrée de la carte NodeMCU
    • BME280 (pression, température, humidité) via un bus I2C géré par la carte NodeMCU
  • transmettre les données en wifi vers le serveur hébergé sur le Raspberry.

Les données sont transmises en utilisant le protocole MQTT (Message Queuing Telemetry Transport).

// ZapGillouMTO
// v2.0 - 05/2020
//
// DHT22  : capteur température et humidité
// BME280 : capteur température, humidité, et pression atmosphérique

// Bibliothèques
#include <Adafruit_Sensor.h>
#include <DHT.h>                // gestion DHT22
#include <DHT_U.h>              // gestion DHT22
#include <Adafruit_BME280.h>    // gestion BME280
#include <Wire.h>               // gestion bus I2C
#include "EspMQTTClient.h"      // gestion MQTT - Transfert de données

// MQTT/WIFI Config
EspMQTTClient client(
  "XXXXX",     // WIFI - Nom du réseau WIFI
  "XXXXX",     // WIFI - Mot de passe du réseau WIFI
  "XXXXX",     // MQTT - Serveur IP - Adresse Raspberry (192.168.1.XX)
  "XXXXX",     // MQTT - Login
  "XXXXX",     // MQTT - Psw
  "XXXXX",     // MQTT - Identifiant
  1883         // MQTT - port
  );

#define DHTPIN 14                   // Correspond à D5 sur la carte NodeMCU
#define DHTTYPE DHT22               // DHT 22 (AM2302)

DHT_Unified dht(DHTPIN, DHTTYPE);   // Variable capteur DHT22
Adafruit_BME280 bme;                // Variable capteur bmpe280

// Déclaration des variables globales utilisées pour l'aquisition des données
float ValeurTempin;
float ValeurTempout;
float ValeurHygroin;
float ValeurHygroout;
float ValeurPressout;

void setup()
  {
  
  // A des fins de test, transmission des infos sur la liaison série
  // Toute cette transmission peut être supprimée si non utilisée

  // Ouverture liaison série
  Serial.begin(115200);
  Serial.println("");
  Serial.println("_Station Meteo ZapGillou");
 
  // Initialisation liaison I2C
  Wire.begin(0, 2);   // Correspond aux broches D3/D4 sur la carte
 
  if (!bme.begin(0x76)) {
    Serial.println("_Capteur BME280 : NOK");
    while (1);
  }
  else
    {
    Serial.println("_Capteur BME280 : OK");
    }

  dht.begin();
  sensor_t sensor;
  dht.temperature().getSensor(&sensor);
  Serial.println("_Capteur DHT22");
  Serial.println("_ 1.DHT22 : Capteur temperature");
  Serial.print  ("_ 1.Type       : "); Serial.println(sensor.name);
  Serial.print  ("_ 1.Version    : "); Serial.println(sensor.version);
  Serial.print  ("_ 1.ID         : "); Serial.println(sensor.sensor_id);
  Serial.print  ("_ 1.Valeur min : "); Serial.print(sensor.min_value); Serial.println(F("°C"));
  Serial.print  ("_ 1.Valeur max : "); Serial.print(sensor.max_value); Serial.println(F("°C"));
  Serial.print  ("_ 1.Precision  : "); Serial.print(sensor.resolution); Serial.println(F("°C"));
  // Print humidity sensor details.
  dht.humidity().getSensor(&sensor);
  Serial.println("_ 2.DHT22 : Capteur humidite");
  Serial.print  ("_ 2.Type       : "); Serial.println(sensor.name);
  Serial.print  ("_ 2.Version    : "); Serial.println(sensor.version);
  Serial.print  ("_ 2.ID         : "); Serial.println(sensor.sensor_id);
  Serial.print  ("_ 2.Valeur min : "); Serial.print(sensor.min_value); Serial.println(F("%"));
  Serial.print  ("_ 2.Valeur max : "); Serial.print(sensor.max_value); Serial.println(F("%"));
  Serial.print  ("_ 2.Precision  : "); Serial.print(sensor.resolution); Serial.println(F("%"));
  }

void onConnectionEstablished()
{
  // Envoi MQTT des données sur les différents flux (pressout, tempout, hygroout, tempin, hygroin)
  client.publish("pressout", String(ValeurPressout));
  client.publish("tempout", String(ValeurTempout));
  client.publish("hygroout", String(ValeurHygroout));
  client.publish("tempin", String(ValeurTempin));
  client.publish("hygroin", String(ValeurHygroin));
}

void loop()
  {
 
  ValeurPressout = bme.readPressure() / 100.0F;
  Serial.print("_BME280 - Pression (hPA)  = ");     Serial.println(ValeurPressout);
 
  ValeurTempout = bme.readTemperature();
  Serial.print("_BME280 - Temperature (°C) = ");    Serial.println(ValeurTempout);
 
  ValeurHygroout = bme.readHumidity();
  Serial.print("_BME280 - Humidite (%) = ");        Serial.println(ValeurHygroout);
   
  sensors_event_t event;
  dht.temperature().getEvent(&event);
  ValeurTempin = event.temperature;
  Serial.print("_DHT22  - Temperature (°C) = ");    Serial.println(ValeurTempin);
     
  dht.humidity().getEvent(&event);
  ValeurHygroin = event.relative_humidity;
  Serial.print("_DHT22  - Humidite (%)     = ");    Serial.println(ValeurHygroin);
 
  client.loop();   // Publication MQTT
 
  // Tempo
  Serial.println("_Tempo");
  delay(60000);    // 1 acquisition par minute
  }

Et côté Raspberry

Ça a été le plus délicat car je découvrais tout de ce côté là…

On trouve donc sur le Raspberry :

  • Mosquitto : Gestion de l’interface MQTT sur le Raspberry en réceptionnant les flux arrivant de la carte NodeMCU.
  • InfluxDB : Système de gestion de base de données dédié au stockage de séries temporelles (time series), c’est à dire des données horodatées.
  • Node-RED : Permet de faire le lien entre Mosquitto et InfluxDB. Node-RED

Node-RED permet ici de récupérer chaque flux MQTT (ex: tempin) et de le renvoyer vers la base de données ZGMTO gérée par InfluxDB, dans la table nommée DATAMTO. Cette table contient l’ensemble des valeurs des capteurs, la fonction intermédiaire permet d’affecter l’étiquette ad hoc à chaque flux (ici tempin) :

Le bloc vert « msg.payload » permet simplement de visualiser les données dans l’onglet DEBUG de Node-RED :

  • Grafana : Visualisation des données sous une forme sympathique (graphes, valeurs instantanée, etc…).

Accès distant

Pour permettre l’accès distant, il faut une liaison sécurisée (https). De bonnes explications sur le sujet ici.

Je me suis basé sur ce mode opératoire. Le principe consiste à configurer grafana afin qu’il soit accessible via un adresse sécurisée du type https://grafana.mondomaine.com:443.

DuckDNS – pour avoir un nom de domaine

Vous ne pourrez pas attribuer un certificat SSL à l’adresse IP de votre Raspberry. Il faut donc lui attribuer un nom de domaine. Pour ce faire, vous pouvez utiliser DuckDNS qui permet de lier l’adresse IP de votre Raspberry à un nom de domaine du type monnomdomaine.duckdns.org

Let’s encrypt – pour générer le certificat SSL

Let’s encrypt propose un service gratuit. Pour pouvoir ensuite générer le certificat en utilisant certbot, veillez à ce que le port 80 soit ouvert sur votre routeur, et veillez à ce qu’il ne soit pas utiliser par apache sur le Raspberry (au besoin, stopper le serveur apache (sudo systemctl stop apache2) et configurez le pour qu’il ne redémarre pas à chaque redémarrage du Raspberry (sudo systemctl disable apache2)).

Le certificat est généré est stocké sous /etc/letsencrypt/live/monnomdedomaine.duckdns.org

Configurer Grafana en mode sécurisé

Tout se passe dans le fichier Grafana.ini sous \etc\grafana :

30 [server]
31 # Protocol (http, https, socket)
32 protocol = https

37 # The http port to use
38 http_port = 443

40 # The public facing domain name used to access grafana from a browser
41 domain = monnomdedomaine.duckdns.org

47 # The full public facing url you use in browser, used for redirects and emails
48 # If you use reverse proxy and sub path specify full url (with sub path)
49 root_url = https://monnomdedomaine.duckdns.org

60 # https certs & key file
61 cert_file = /etc/letsencrypt/live/monnomdedomaine.duckdns.org/fullchain.pem
62 cert_key = /etc/letsencrypt/live/monnomdedomaine.duckdns.org/privkey.pem

Reste ensuite à redémarrer le service Grafana :

sudo systemctl restart grafana-server.service

Si ça ne fonctionne pas, ayez un regard sur le fichier log de Grafana pour comprendre d’où vient le problème :

sudo tail -f /var/log/grafana/grafana.log

1 commentaire pour “Station météo wifi – Partie 2”

Laisser un commentaire

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