Ces cartes font partie de la famille MKR déjà étudiée dans le chapitre Arduino.
MKR Wifi 1010
Cette carte MKR possède en plus des interfaces Wifi et bluetooth.
MKR ENV
Cette carte fille possède 3 capteurs : température+humidité, pression et UV accessible via le bus I2C et un capteur de luminosité connecté sur l'entrée analogique A2.
Notez que je décline toutes responsabilités quant aux conséquences que pourraient avoir l'application des méthodes et conseils suivants ainsi que l'utilisation des programmes présentés. Ceux-ci pourraient être erronés ou obsolètes.
Le Wifi est géré par la librairie WININA. Les principales méthodes de cette classe sont :
WiFi.begin(ssid, motpasse);
avec ssid qui est le ssid du réseau Wifi et motpasse qui est la passphrase, établit la connexion Wifi.WiFi.status()
retourne le statut de l'interface wifi
WL_NO_MODULE
: module Wifi non trouvéWL_CONNECTED
: connexion Wifi réussieWL_DISCONNECTED
: plus de connexion WifiWL_IDLE_STATUS
: Wifi en état d'attenteWiFi.firmwareVersion()
: retourne la version du firmware du Wifi sous la forme d'une chaîne (String).WiFi.macAddress(adrmac)
: avec adrmac qui est un tableau de 6 octets. L'adresse MAC est fournie dans ce tableau.WiFi.localIP()
retourne l'adresse IP de la carte sous la forme d'un objet IPAddress
WiFi.subnetMask()
retourne le masque de sous-réseau de la carte sous la forme d'un objet IPAddress
WiFi.gatewayIP()
retourne l'adresse IP de la passerelle sous la forme d'un objet IPAddress
#include <SPI.h>
#include <WiFiNINA.h>
#include "wifi_connexion.h"
int statut = WL_IDLE_STATUS ;
bool CheckWifi() {
if (WiFi.status() == WL_NO_MODULE) {
Serial.println("Problème module wifi");
return false;
}
String fv = WiFi.firmwareVersion();
if ( fv < "1.0.0" ) {
Serial.println("Problème version wifi");
return false;
}
return true;
}
void connexionWifi(char *ssid,char *passphrase) {
statut = WL_IDLE_STATUS ;
Serial.print("Connexion wifi ");
statut = WiFi.begin(ssid,passphrase);
while ( statut != WL_CONNECTED ) {
Serial.print(".");
statut = WiFi.status();
delay(1000);
}
Serial.println("OK");
}
void printInfosConnexion() {
byte adrmac[6];
WiFi.macAddress(adrmac);
Serial.print("MAC : ");
for(int i=0;i<5;i+=1) {
Serial.print(adrmac[i],HEX);
Serial.print(":");
}
Serial.println(adrmac[5],HEX);
IPAddress ip = WiFi.localIP();
Serial.print("IP : ");
Serial.println(ip);
IPAddress mask = WiFi.subnetMask();
Serial.print("MASK : ");
Serial.println(mask);
IPAddress passerelle = WiFi.gatewayIP();
Serial.print("Passerelle : ");
Serial.println(passerelle);
}
void setup() {
Serial.begin(115200);
while (!Serial) ;
if (!CheckWifi()) {
Serial.println("Problème connexion wifi");
while (true);
}
Serial.println("Wifi disponible");
connexionWifi(BORNE_SSID,BORNE_PASSPHRASE);
printInfosConnexion();
}
void loop() {}
Le fichier wifi_connexion.h contient les valeurs de BORNE_SSID et BORNE_PASSPHRASE.
Le code de test est composé de deux fonctions qui seront utilisées dans tous les programmes qui utilisent le Wifi.
CheckWifi()
qui retourne vrai si le module Wifi est présent et si la version du firmware est compatible avec la librairie.connexionWifi(ssid,motpasse)
qui effectue la connexion Wifi avec le ssid et le motpasse transmis. Cette fonction boucle tant que la connexion n'est pas effective.Après, si la connexion est réussie, le programme affiche l'adresse MAC de l'interface WIfi, L'adresse IP obtenue par DHCP, le masque de sous-réseau ainsi que l'adresse IP de la passerelle du réseau.
La ligne de code while (!Serial) ;
attend la connexion du terminal série. Cela permet d'afficher les messages contenus dans la fonction setup(). Pour une utilisation sans moniteur, il faut commenter cette ligne, sinon le programme ne démarre jamais. Cela sera présent dans tous les codes.
La boucle loop() ne fait rien, c'est seulement un programme de test de la connexion Wifi.
ENV.begin()
initialise les capteurs de la carte, retourne VRAI si tout est OKENV.readTemperature(unite)
retourne la température, unite précise l'unité de conversion qui est le degré Celsius par défaut.ENV.readHumidity()
retourne le taux d'humidité en %RHENV.readPressure(unite)
retourne la pression, unite précise l'unité de la valeur retournée : MILLIBAR pour une valeur en mBar ou hPa, KPa par défaut.ENV.readIlluminance(unite)
retourne la luminosité, unite précise l'unité de la valeur retournée, la valeur par défaut est le Lux.ENV.readUVIndex()
retourne l'indice d'UV.
#include <Arduino_MKRENV.h>
typedef struct s_capteurs {
float temperature;
float humidite;
float pression;
float luminosite;
unsigned int indiceuv;
} T_capteurs;
typedef T_capteurs *ptr_capteurs ;
T_capteurs capteurs;
void litcapteurs(ptr_capteurs pcapteurs) {
pcapteurs->temperature = ENV.readTemperature();
pcapteurs->humidite = ENV.readHumidity();
pcapteurs->pression = ENV.readPressure(MILLIBAR);
pcapteurs->luminosite = ENV.readIlluminance();
pcapteurs->indiceuv = ENV.readUVIndex();
}
void printCapteurs(T_capteurs capteurs) {
Serial.print("Temperature = ");
Serial.print(capteurs.temperature,1);
Serial.print(" °C");
Serial.print(" , Humidite = ");
Serial.print(capteurs.humidite,0);
Serial.print(" %");
Serial.print(" , Pression = ");
Serial.print(capteurs.pression,0);
Serial.print(" hPa");
Serial.print(" , luminosite = ");
Serial.print(capteurs.luminosite,0);
Serial.print(" lx");
Serial.print(" , Indice UV = ");
Serial.println(capteurs.indiceuv);
}
void setup() {
Serial.begin(9600);
while(!Serial);
if (!ENV.begin()) {
Serial.println("Erreur initialisation capteurs!");
while (true);
}
}
void loop() {
litcapteurs(&capteurs);
printCapteurs(capteurs);
delay(1000);
}
Afin de regrouper tous les capteurs on utilise une structure de type T_capteurs et afin de simplifier l'écriture on définit également un type pointeur vers cette structure ptr_capteurs.
La valeur de la température est en degré Celcius, l'humidité en %RH, la pression en hPa, la luminosité en lux.
Comme cela a déjà été écrit dans les exemples I2C et SPI des systèmes embarqués, il faut bien évidement vérifier les valeurs obtenues. Une première évaluation rapide montre que la température est supérieure d'environ 2 degrés à la valeur réelle, l'humidité 10%RH en dessous, la pression 10hPa en dessous, la luminosité environ 50 lux en dessous de la valeur mesurée (≈ 500 lux) avec un smartphone. L'indice UV n'ayant pas pu être vérifié.
Le capteur de luminosité est un circuit TEMT6000X01. La documentation nous donne les principales caractéristiques de ce composant, notamment le courant en fonction de la luminosité en lux. De cette courbe, on peut déduire une fonction de transfert approximative qui est L=2×I avec I en µA et L en Lux. Ensuite d'après le schéma de la carte MKR ENV, on déduit la tension à l'entrée analogique du microcontrôleur qui est V=10×I avec R en kΩ et I en µA. Le capteur est un phototransistor, qui, comme le transistor possède une tension de saturation (non documentée), ceci implique que l'on aura jamais la tension maximale de l'alimentation 3.3v. Le convertisseur analogique-numérique fonctionne sur 10 bits avec une tension de référence de 3.3V ce qui donne la fonction de transfert :
Le circuit utilisé est un LPS22HB. La notice d'application AN4672 décrit l'implémentation de ce composant sur un circuit imprimé. La notice d'application AN4833 explique comment compenser le problème de décalage du à l'implémentation en modifiant le contenu d'un registre (RPDS). Il convient de vérifier que ce décalage est constant dans toute la plage de mesures.
Le circuit utilisé est un HTS221. Comme pour le précédent la notice d'application AN4722 explique comment implémenter le composant sur un circuit imprimé.
Mais cette fois-ci il n'existe pas de registre pour corriger le décalage. Les registres de calcul de la température et de l'humidité sont intégrés dans le composant et accessibles en lecture seule. On propose de suivre cette discussion pour comprendre la difficulté de mise en oeuvre de ce composant. En conclusion, il faut recalculer la fonction de conversion et ne plus utiliser la fonction de conversion interne au capteur.
Avant d'utiliser les capteurs, il convient de vérifier les valeurs retournées et ainsi vérifier que les erreurs sont bien inférieures à celles imposées par le cahier des charges. Sinon il faut effectuer des mesures avec du matériel adapté afin de modifier ou ajouter les fonctions de transfert. Cela implique donc de modifier les codes sources des librairies ou bien de réécrire les librairies.
Pour effectuer le transfert des données au serveur web, on utilise une librairie ArduinoHttpClient qui, elle-même, utilise la librairie WiFiClient. Les principales méthodes utilisées sont :
HttpClient(client,hostname,port)
qui est le constructeur utilisé lors de la construction de l'objet avec client objet de la classe WiFiClient, hostname nom de la machine ou adresse IP, port port TCP (80 pour HTTP).objethttpclient.get(uri)
méthode POST avec uri qui est le contenu de l'URL avec la transmission des données.objethttpclient.post(url,contenttype,donnees)
méthode POST avec url qui est le nom du fichier (programme CGI), contenttype qui est le type d'encodage qui vaut généralement application/x-www-form-urlencoded et enfin les données encodées comme les paramètres de la méthode get.objethttpclient.responseStatusCode
qui retourne le statut de la réponse, comme la valeur 200 quand la requête est valide et la réponse OKobjethttpclient.skipResponseHeaders()
pour passer l'entête de la réponse et ne garder que le contenu.objethttpclient.connected()
retourne VRAI si la connexion est toujours activeobjethttpclient.available()
retourne VRAI si des données sont disponiblesobjethttpclient.read()
retourne un octet si celui ci est disponibleobjethttpclient.stop()
termine la connexion HTTPOn transmet les données au serveur web de la raspberry pi qui utilise le script CGI sauvecapteurs.cgi
#include <Arduino_MKRENV.h>
#include <WiFiNINA.h>
#include <ArduinoHttpClient.h>
#include "wifi_connexion.h"
#define PGMCGI "/cgi-bin/savecapteurs.cgi"
typedef struct s_capteurs {
float temperature;
float humidite;
float pression;
float luminosite;
float indiceuv;
} T_capteurs;
typedef T_capteurs *ptr_capteurs ;
T_capteurs capteurs;
void litcapteurs(ptr_capteurs pcapteurs) {
pcapteurs->temperature = ENV.readTemperature();
pcapteurs->humidite = ENV.readHumidity();
pcapteurs->pression = ENV.readPressure(MILLIBAR);
pcapteurs->luminosite = ENV.readIlluminance();
pcapteurs->indiceuv = ENV.readUVIndex();
}
void printCapteurs(T_capteurs capteurs) {
Serial.print("Temperature = ");
Serial.print(capteurs.temperature);
Serial.println(" °C");
Serial.print("Humidite = ");
Serial.print(capteurs.humidite);
Serial.println(" %");
Serial.print("Pressure = ");
Serial.print(capteurs.pression);
Serial.println(" hPa");
Serial.print("Illuminance = ");
Serial.print(capteurs.luminosite);
Serial.println(" lx");
Serial.print("UV Index = ");
Serial.println(capteurs.indiceuv);
}
int statut = WL_IDLE_STATUS;
WiFiClient client;
HttpClient http(client,HOSTNAME,80);
bool CheckWifi() {
if (WiFi.status() == WL_NO_MODULE) {
Serial.println("Problème module wifi");
return false;
}
String fv = WiFi . firmwareVersion ();
if ( fv < "1.0.0" ) {
Serial.println("Problème version wifi");
return false;
}
return true;
}
void connexionWifi(char *ssid,char *passphrase) {
statut = WL_IDLE_STATUS ;
Serial.print("connexion wifi ");
statut = WiFi.begin(ssid,passphrase);
while ( statut != WL_CONNECTED ) {
Serial.print(".");
statut = WiFi.status();
delay(1000);
}
Serial.println("");
}
int ConnexionHTTPGET(String parametres) {
int err = 0;
String sReponse ;
int coderetour;
err = http.get(parametres.c_str());
if (err == 0) {
int statutcode = http.responseStatusCode();
if (statutcode == 200) {
err = http.skipResponseHeaders();
if (err >= 0) {
sReponse = "";
while (http.connected() || http.available() ) {
if (http.available()) {
sReponse += http.read();
}
}
if (sReponse.startsWith("code=")) {
String scode = sReponse.substring(5);
coderetour = scode.toInt();
}
}
else {
Serial.println("erreur skip reponseheader");
coderetour = err ;
}
}
else {
Serial.println("erreur code retour ");
coderetour = statutcode ;
}
}
else {
Serial.println("erreur get");
coderetour = err ;
}
http.stop();
return coderetour ;
}
int ConnexionHTTPPOST(const char *url,String parametres) {
int err = 0;
String sReponse ;
int coderetour;
String contentType = "application/x-www-form-urlencoded";
err = http.post(url,contentType,parametres.c_str());
if (err == 0) {
int statutcode = http.responseStatusCode();
if (statutcode == 200) {
err = http.skipResponseHeaders();
if (err >= 0) {
sReponse = "";
while (http.connected() || http.available() ) {
if (http.available()) {
sReponse += http.read();
}
}
if (sReponse.startsWith("code=")) {
String scode = sReponse.substring(5);
coderetour = scode.toInt();
}
}
else {
Serial.println("erreur skip reponseheader");
coderetour = err ;
}
}
else {
Serial.println("erreur code retour ");
coderetour = statutcode ;
}
}
else {
Serial.println("erreur get");
coderetour = err ;
}
http.stop();
return coderetour ;
}
void setup() {
Serial.begin(115200);
while (!Serial);
if (!ENV.begin()) {
Serial.println("Problème avec la carte MKRenv");
while (true);
}
if (!CheckWifi()) {
Serial.println("Problème connexion wifi");
while (true);
}
Serial.println("Wifi Ok");
connexionWifi(BORNE_SSID,BORNE_PASSPHRASE);
Serial.println("Wifi Connecte");
}
String chainevaleurs;
String requete ;
void loop() {
litcapteurs(&capteurs);
printCapteurs(capteurs);
chainevaleurs = "temperature=" + String(capteurs.temperature);
chainevaleurs += "&humidite=" + String(capteurs.humidite);
chainevaleurs += "&pression=" + String(capteurs.pression);
chainevaleurs += "&luminosite=" + String(capteurs.luminosite);
chainevaleurs += "&uv=" + String(capteurs.indiceuv) ;
requete = String(PGMCGI) + "?" + chainevaleurs ;
int coderetour = ConnexionHTTPGET(requete);
if (coderetour == 0) {
Serial.println("Transmission OK");
}
else {
Serial.print("Transmission ERREUR ");
Serial.print(coderetour);
}
delay(10000);
}
Le fichier wifi_connexion.h contient les valeurs de BORNE_SSID, BORNE_PASSPHRASE et HOSTNAME.
Ce code définit les deux fonctions d'envoi des données au serveur :
ConnexionHTTPGET()
qui effectue la connexion HTTP, envoie les données avec la méthode GET, vérifie l'état de la transmission puis ferme la connexion HTTP.ConnexionHTTPPOST()
qui effectue la connexion HTTP, envoie les données avec la méthode POST, vérifie l'état de la transmission puis ferme la connexion HTTP. L'algorithme reste identique à celui de la méthode ConnexionHTTPGET().La fonction loop construit la chaîne des paramètres à partir des valeurs des capteurs, puis appel la fonction ConnexionHTTPGET pour transmettre des données au serveur. Il est tout à fait possible de modifier ce programme pour utiliser la méthode POST.
On utilise toujours la librairie HTTP, la différence réside dans le contenu de l'URL envoyée, à raison d'une connexion HTTP par capteur.
La connexion avec Domoticz nécessite une authentification avec utilisateur et mot de passse.
objethttpclient.beginRequest()
débute la requête HTTP avec authentificationobjethttpclient..sendBasicAuth
envoie les login et mot de passe codés en base 64. Cette fonction est précédée de get("/") et suivie de get(url) où url est la requête de transmission de données.objethttpclient.endRequest()
termine la requête HTTP avec authentificationLe serveur domoticz retourne une réponse au format JSON, ce traitement nécessite la librairie ArduinoJson, les principales fonctions sont :
JsonDocument()
constructeur de l'objet jsondeserializeJson()
fonction qui extrait les différentes valeurs de la donnée JSON sous la forme d'un objet JsonDocument qui est utilisé sous la forme d'un tableau associatif : objet['cle'] contient la valeur associée à cle.On transmet les données au serveur domoticz de la raspberry pi
#include <Arduino_MKRENV.h>
#include <WiFiNINA.h>
#include <ArduinoHttpClient.h>
#include <ArduinoJson.h>
#include "wifi_connexion.h"
#define DOMOTICZ_URL "/json.htm?type=command¶m=udevice&idx"
#define DOMOTICZ_UTILISATEUR_64 "dXNlcgo="
#define DOMOTICZ_MOTPASSE_64 "YWNjZXNzCg=="
#define IDXTEMPERATURE 1
#define IDXHUMIDITE 2
#define IDXPRESSION 3
#define IDXLUMINOSITE 4
#define IDXUV 5
typedef struct s_capteurs {
float temperature;
float humidite;
float pression;
float luminosite;
float indiceuv;
} T_capteurs;
typedef T_capteurs *ptr_capteurs ;
T_capteurs capteurs;
JsonDocument JsReponse;
void litcapteurs(ptr_capteurs pcapteurs) {
pcapteurs->temperature = ENV.readTemperature();
pcapteurs->humidite = ENV.readHumidity();
pcapteurs->pression = ENV.readPressure(MILLIBAR);
pcapteurs->luminosite = ENV.readIlluminance();
pcapteurs->indiceuv = ENV.readUVIndex();
}
void printCapteurs(T_capteurs capteurs) {
Serial.print("Temperature = ");
Serial.print(capteurs.temperature);
Serial.println(" °C");
Serial.print("Humidite = ");
Serial.print(capteurs.humidite);
Serial.println(" %");
Serial.print("Pressure = ");
Serial.print(capteurs.pression);
Serial.println(" hPa");
Serial.print("Illuminance = ");
Serial.print(capteurs.luminosite);
Serial.println(" lx");
Serial.print("UV Index = ");
Serial.println(capteurs.indiceuv);
}
#define HUMIDITE_NORMAL 0
#define HUMIDITE_CONFORT 1
#define HUMIDITE_SEC 2
#define HUMIDITE_HUMIDE 3
int statutHumidite(float humidite) {
int statut=0;
if (humidite < 20) {
statut = HUMIDITE_SEC;
}
else if (humidite < 50) {
statut = HUMIDITE_NORMAL;
}
else if (humidite < 70) {
statut = HUMIDITE_CONFORT;
}
else {
statut = HUMIDITE_HUMIDE;
}
return statut;
}
#define PRESSION_STABLE 0
#define PRESSION_ENSOLEILLE 1
#define PRESSION_NUAGEUX 2
#define PRESSION_INSTABLE 3
#define PRESSION_ORAGEUX 4
#define PRESSION_INCONNU 5
#define PRESSION_PLUIE 6
int statutPression(float pression) {
int statut=0;
if (pression < 980) {
statut = PRESSION_INSTABLE;
}
if (pression < 1000) {
statut = PRESSION_PLUIE;
}
else if (pression < 1010) {
statut = PRESSION_NUAGEUX;
}
else if (pression < 1020) {
statut = PRESSION_STABLE;
}
else if (pression < 1040) {
statut = PRESSION_ENSOLEILLE;
}
else {
statut = PRESSION_ORAGEUX;
}
return statut;
}
int statut = WL_IDLE_STATUS;
WiFiClient client;
HttpClient http(client,HOSTNAME,DOMOTICZ_PORT);
bool CheckWifi() {
if (WiFi.status() == WL_NO_MODULE) {
Serial.println("Problème module wifi");
return false;
}
String fv = WiFi . firmwareVersion ();
if ( fv < "1.0.0" ) {
Serial.println("Problème version wifi");
return false;
}
return true;
}
void connexionWifi(char *ssid,char *passphrase) {
statut = WL_IDLE_STATUS ;
Serial.print("connexion wifi ");
statut = WiFi.begin(ssid,passphrase);
while ( statut != WL_CONNECTED ) {
Serial.print(".");
statut = WiFi.status();
delay(1000);
}
Serial.println("");
}
int ConnexionHTTPGET(String parametres) {
int err = 0;
String sReponse ;
int coderetour;
int statutcode;
http.beginRequest();
http.get("/");
http.sendBasicAuth(DOMOTICZ_UTILISATEUR_64, DOMOTICZ_MOTPASSE_64);
http.get(parametres.c_str());
http.endRequest();
statutcode = http.responseStatusCode();
if (statutcode == 200) {
err = http.skipResponseHeaders();
if (err >= 0) {
sReponse = "";
while (http.connected() || http.available() ) {
if (http.available()) {
sReponse += (char)http.read();
}
}
deserializeJson(JsReponse, sReponse.c_str());
const char *statut = JsReponse["status"];
Serial.print("statut : ");
Serial.println(statut);
coderetour = 0 ;
}
else {
Serial.println("erreur skip reponseheader");
coderetour = err ;
}
}
else {
int taille = http.contentLength();
Serial.print(taille);
Serial.println(" erreur code retour ");
coderetour = statutcode ;
}
http.stop();
return coderetour ;
}
void setup() {
Serial.begin(115200);
while (!Serial);
if (!ENV.begin()) {
Serial.println("Problème avec la carte MKRenv");
while (true);
}
if (!CheckWifi()) {
Serial.println("Problème connexion wifi");
while (true);
}
Serial.println("Wifi Ok");
connexionWifi(BORNE_SSID,BORNE_PASSPHRASE);
Serial.println("Wifi Connecte");
}
String requete ;
void loop() {
litcapteurs(&capteurs);
printCapteurs(capteurs);
int statuthumidite = statutHumidite(capteurs.humidite);
int statutpression = statutPression(capteurs.pression);
requete = String(DOMOTICZ_URL) + "=" + String(IDXTEMPERATURE) + "&nvalue=0&svalue=" + String(capteurs.temperature) ;
ConnexionHTTPGET(requete);
requete = String(DOMOTICZ_URL) + "=" + String(IDXHUMIDITE) + "&nvalue=" + String(capteurs.humidite) + "&svalue=" + String(statuthumidite) ;
ConnexionHTTPGET(requete);
requete = String(DOMOTICZ_URL) + "=" + String(IDXPRESSION) + "&nvalue=0&svalue=" + String(capteurs.pression) + ";" + String(statutpression) ;
ConnexionHTTPGET(requete);
requete = String(DOMOTICZ_URL) + "=" + String(IDXLUMINOSITE) + "&nvalue=0&svalue=" + String(capteurs.luminosite) + ";0" ;
ConnexionHTTPGET(requete);
requete = String(DOMOTICZ_URL) + "=" + String(IDXUV) + "&nvalue=0&svalue=" + String(capteurs.indiceuv) + ";0" ;
ConnexionHTTPGET(requete);
delay(1000);
}
Le codage du login et du mot de passe en base 64 a été effectué sur l'ordinateur de développement avec la commande base64 (linux)
Par rapport à l'exemple précédent, on a ajouté deux fonctions de calculs pour du statut l'humidité et la prévision barométrique avec la pression
statutHumidite(humidite)
qui détermine le statut entre 0 et 3, le choix des seuils est totalement arbitraire et ne correspond à aucune réalité.statutPression(pression)
qui détermine la prévision entre 0 et 6, comme précédemment, le choix des seuils est totalement arbitraire et ne correspond à aucune réalité.La fonction ConnexionHTTPGET
a été modifiée pour effectuer une connexion authentifiée et traiter une réponse de type JSON.
La fonction loop , après avoir lu les valeurs des capteurs, envoie successivement les valeurs au serveur domoticz à raison d'une connexion par capteur.
La librairie MQTT utilisée est PubSubClient, les principales fonctions sont :
PubSubClient(client)
: constructeur avec client qui est un objet WiFiClientobjetpubsubclient.setServer(HOSTNAME,MQTT_PORT)
initisalise l'objet avec HOSTNAME qui est le nom ou l'adresse IP du serveur et MQTT_PORT qui est le port du serveur mqtt (en général 1883).objetpubsubclient.connected()
qui retourne VRAI si la carte est toujours connectée au serveur.objetpubsubclient.connect()
établit la connexion au serveur.objetpubsubclient.publish(TOPIC,message)
qui publie un message correspondant à l'arborescence TOPIC.objetpubsubclient.setCallback(fonction)
initialise la fonction de callback pour traiter les résultats des abonnements.objetpubsubclient.setBufferSize(taille)
modifie la taille du buffer de réception qui est par défaut de 256 octets.objetpubsubclient.suscribe(TOPIC)
réalise la demande d'abonnement auprès du serveur mqtt pour l'arborescence TOPIC.objetpubsubclient.loop()
démarre la boucle de surveillance de l'abonnement.La syntaxe des données mqtt de domoticz est au format JSON :
{ "idx" : valeur , "nvalue" : valeur , "svalue" : "valeur" }
#include <Arduino_MKRENV.h>
#include <WiFiNINA.h>
#include <PubSubClient.h>
#include "wifi_connexion.h"
#define TOPIC "domoticz/in"
#define IDXTEMPERATURE 1
#define IDXHUMIDITE 2
#define IDXPRESSION 3
#define IDXLUMINOSITE 4
#define IDXUV 5
typedef struct s_capteurs {
float temperature;
float humidite;
float pression;
float luminosite;
float indiceuv;
} T_capteurs;
typedef T_capteurs *ptr_capteurs ;
T_capteurs capteurs;
void litcapteurs(ptr_capteurs pcapteurs) {
pcapteurs->temperature = ENV.readTemperature();
pcapteurs->humidite = ENV.readHumidity();
pcapteurs->pression = ENV.readPressure(MILLIBAR);
pcapteurs->luminosite = ENV.readIlluminance();
pcapteurs->indiceuv = ENV.readUVIndex();
}
void printCapteurs(T_capteurs capteurs) {
Serial.print("Temperature = ");
Serial.print(capteurs.temperature);
Serial.println(" °C");
Serial.print("Humidite = ");
Serial.print(capteurs.humidite);
Serial.println(" %");
Serial.print("Pressure = ");
Serial.print(capteurs.pression);
Serial.println(" hPa");
Serial.print("Illuminance = ");
Serial.print(capteurs.luminosite);
Serial.println(" lx");
Serial.print("UV Index = ");
Serial.println(capteurs.indiceuv);
}
#define HUMIDITE_NORMAL 0
#define HUMIDITE_CONFORT 1
#define HUMIDITE_SEC 2
#define HUMIDITE_HUMIDE 3
int statutHumidite(float humidite) {
int statut=0;
if (humidite < 20) {
statut = HUMIDITE_SEC;
}
else if (humidite < 50) {
statut = HUMIDITE_NORMAL;
}
else if (humidite < 70) {
statut = HUMIDITE_CONFORT;
}
else {
statut = HUMIDITE_HUMIDE;
}
return statut;
}
#define PRESSION_STABLE 0
#define PRESSION_ENSOLEILLE 1
#define PRESSION_NUAGEUX 2
#define PRESSION_INSTABLE 3
#define PRESSION_ORAGEUX 4
#define PRESSION_INCONNU 5
#define PRESSION_PLUIE 6
int statutPression(float pression) {
int statut=0;
if (pression < 980) {
statut = PRESSION_INSTABLE;
}
if (pression < 1000) {
statut = PRESSION_PLUIE;
}
else if (pression < 1010) {
statut = PRESSION_NUAGEUX;
}
else if (pression < 1020) {
statut = PRESSION_STABLE;
}
else if (pression < 1040) {
statut = PRESSION_ENSOLEILLE;
}
else {
statut = PRESSION_ORAGEUX;
}
return statut;
}
int statut = WL_IDLE_STATUS;
WiFiClient client;
PubSubClient mqtt(client);
bool CheckWifi() {
if (WiFi.status() == WL_NO_MODULE) {
Serial.println("Problème module wifi");
return false;
}
String fv = WiFi . firmwareVersion ();
if ( fv < "1.0.0" ) {
Serial.println("Problème version wifi");
return false;
}
return true;
}
void connexionWifi(char *ssid,char *passphrase) {
statut = WL_IDLE_STATUS ;
Serial.print("Connexion wifi ");
statut = WiFi.begin(ssid,passphrase);
while ( statut != WL_CONNECTED ) {
Serial.print(".");
statut = WiFi.status();
delay(1000);
}
Serial.println("OK");
}
void reconnect() {
while (!mqtt.connected()) {
Serial.print("MQTT connexion... ");
if (mqtt.connect(HOSTNAME)) {
Serial.println("OK");
}
else {
Serial.print("ERREUR, statut=");
Serial.println(mqtt.state());
delay(5000);
}
}
}
void publiecapteur(const char *chainevaleurs) {
boolean ok=mqtt.publish(TOPIC,chainevaleurs);
if (ok) {
Serial.println("publish OK");
}
else {
Serial.println("publish ERREUR");
}
}
void setup() {
Serial.begin(115200);
while (!Serial);
if (!ENV.begin()) {
Serial.println("Problème avec la carte MKRenv");
while (true);
}
if (!CheckWifi()) {
Serial.println("Problème connexion wifi");
while (true);
}
Serial.println("Wifi disponible");
connexionWifi(BORNE_SSID,BORNE_PASSPHRASE);
mqtt.setServer(HOSTNAME,MQTT_PORT);
}
String chainevaleurs;
void loop() {
litcapteurs(&capteurs);
printCapteurs(capteurs);
int statuthumidite = statutHumidite(capteurs.humidite);
int statutpression = statutPression(capteurs.pression);
if (!mqtt.connected()) {
reconnect();
}
chainevaleurs = "{ \"idx\" : " + String(IDXTEMPERATURE) + " , \"nvalue\" : 0 , \"svalue\" : \"" + String(capteurs.temperature) + "\" }";
publiecapteur(chainevaleurs.c_str());
chainevaleurs = "{ \"idx\" : " + String(IDXHUMIDITE) + " , \"nvalue\" : " + String((int)capteurs.humidite) + " , \"svalue\" : \"" + String(statuthumidite) + "\" }";
publiecapteur(chainevaleurs.c_str());
chainevaleurs = "{ \"idx\" : " + String(IDXPRESSION) + " , \"nvalue\" : 0 , \"svalue\" : \"" + String((int)capteurs.pression) + ";" + String(statutpression) + " \" }";
publiecapteur(chainevaleurs.c_str());
chainevaleurs = "{ \"idx\" : " + String(IDXLUMINOSITE) + " , \"nvalue\" : 0 , \"svalue\" : \"" + String((int)capteurs.luminosite) + ";0\" }";
publiecapteur(chainevaleurs.c_str());
chainevaleurs = "{ \"idx\" : " + String(IDXUV) + " , \"nvalue\" : 0 , \"svalue\" : \"" + String(capteurs.indiceuv) + ";0\" }";
publiecapteur(chainevaleurs.c_str());
delay(2000);
}
La fonction reconnect
reconnecte le client au serveur en cas de déconnexion, la connexion est surveillée dans la boucle loop.
La fonction publiecapteur
publie les valeurs d'un capteur en respectant la syntaxe JSON de Domoticz.
La led RGB est connectée à la puce Wifi, il faut utiliser librairie Wifi pour commander cette LED.
WiFiDrv::analogWrite(ID_LED,niveau)
commande la led numéro ID_LED avec l'intensité niveau entre 0 et 255.
ID_LED vaut 25 pour le vert, 26 pour le rouge et 27 pour le bleu.
#include <WiFiNINA.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include "wifi_connexion.h"
#define TOPIC "domoticz/out/6"
# define RGB_GREEN 25
# define RGB_RED 26
# define RGB_BLUE 27
const uint8_t portRGB[3] = { RGB_RED , RGB_GREEN , RGB_BLUE };
int statut = WL_IDLE_STATUS;
WiFiClient client;
PubSubClient mqtt(client);
JsonDocument JsReponse;
bool CheckWifi() {
if (WiFi.status() == WL_NO_MODULE) {
Serial.println("Problème module wifi");
return false;
}
String fv = WiFi . firmwareVersion ();
if ( fv < "1.0.0" ) {
Serial.println("Problème version wifi");
return false;
}
return true;
}
void connexionWifi(char *ssid,char *passphrase) {
statut = WL_IDLE_STATUS ;
Serial.print("Connexion wifi ");
statut = WiFi.begin(ssid,passphrase);
while ( statut != WL_CONNECTED ) {
Serial.print(".");
statut = WiFi.status();
delay(1000);
}
Serial.println("OK");
}
uint32_t jsontorvb(String sjson) {
uint32_t rvb=0;
deserializeJson(JsReponse, sjson.c_str());
const char *sniveau = JsReponse["svalue1"];
int niveau = strtoul(sniveau,NULL,10);
Serial.println(niveau);
switch (niveau) {
case 0 :
rvb = 0;
break;
case 10 :
rvb = 0xFF0000;
break;
case 20 :
rvb = 0x00FF00;
break;
case 30 :
rvb = 0x0000FF;
break;
case 40 :
rvb = 0xFFFFFF;
break;
}
return rvb;
}
void setLEDRGB(uint32_t rgb) {
WiFiDrv::analogWrite(RGB_RED,(rgb >> 16) & 0xff);
WiFiDrv::analogWrite(RGB_GREEN, (rgb >> 8) & 0xff);
WiFiDrv::analogWrite(RGB_BLUE,rgb & 0xff);
}
void callback(char* topic, byte* payload, unsigned int longueur) {
Serial.print("Message recu [");
Serial.print(topic);
Serial.print("] : ");
String sjson= "";
for (int i=0,j=0;i<longueur;i++) {
sjson += (char)payload[i];
}
uint32_t rvb = jsontorvb(sjson);
Serial.print(sjson);
setLEDRGB(rvb);
Serial.println();
}
void reconnect() {
while (!mqtt.connected()) {
Serial.print("MQTT connexion... ");
if (mqtt.connect(HOSTNAME)) {
Serial.println("OK");
}
else {
Serial.print("ERREUR, statut=");
Serial.println(mqtt.state());
delay(5000);
}
}
}
void setup() {
Serial.begin(115200);
while (!Serial);
if (!CheckWifi()) {
Serial.println("Problème connexion wifi");
while (true);
}
Serial.println("Wifi disponible");
connexionWifi(BORNE_SSID,BORNE_PASSPHRASE);
mqtt.setServer(HOSTNAME,MQTT_PORT);
mqtt.setCallback(callback);
mqtt.setBufferSize (512);
setLEDRGB(0x00101010);
}
String chainevaleurs;
void loop() {
if (!mqtt.connected()) {
reconnect();
mqtt.subscribe(TOPIC);
}
mqtt.loop();
}
le TOPIC correspond à l'interrupteur utilisé sous Domoticz.
Le message JSON est au format précisé sur le chapitre précédent, la valeur qui nous intéresse pour piloter la led RGB est celle qui correspond à la clé "svalue1".
La fonction callback
stocke le message JSON reçu et le transmet à la fonction jsontorvb
pour convertir la valeur reçue en RVB, valeur qui sera transmise à la fonction affichage de la LED setLEDRGB(rvb)
La fonction jsontorvb
extrait du message la valeur de la clé "svalue1", puis code la valeur rvb en fonction du code reçu