tutos:mqtt

MQTT (mosquitto)

Fait :

  1. [x] Comparatif
  2. [x] MQTT sur Raspberry Pi
  3. [x] MQTT sur Arduino
  4. [x] Relay + Capteur courant
  5. [x] MQTT et Node-Red
  6. [X] MQTT & InfluxDB
  7. [X] MQTT & Java

(Note : ces informations ont été compilées à partir de différentes sources anglaises/françaises)

Il existe deux types d’architectures les plus courantes :

  1. Request/Response (ReqRep) : Qui ressemble à la technologie client/serveur (comme HTTP)
  2. Publish/Subscribe (PubSub) : Des entités “publishers” publient un message (dans un “topic”) sur le réseau sans se soucier des destinataires du message. Les “subscribers” s’abonnent à un “topic” et reçoit tous les messages du “topic”. Le “broker” fait office de centralisateur des messages des “publishers” et se charge des envoyés aux “subscribers”. Le gros désavantage de cette architecture est la présence du “broker” qui est en fait un SPoF (Single point of Failure).

Avantages et Inconvénients : Request-response vs. publish-subscribe

CoAP (Constrained Application Protocol)

CoAP reprend les concepts du protocole HTTP en s’appuyant sur le protocole UDP et en allégeant les communications (paquet plus petit notamment)

Infos : The Constrained Application Protocol (CoAP)

Points clefs:

  1. Request/Response Architecture
  2. UDP/IP Based
  3. QoS Support
  4. Supports Unicast as well as Multicast
  5. Uses DTLS for Security
  6. Supports Resource Discovery
  7. CoAP Node plays Server Role too (NAT Issues)
  8. Decentralised (No Single Point of Failure)

REST/HTTP

REST (Representational State Transfer) est un concept, et non pas un protocole. Les API (application programming interface) REST utilisent des méthodes HTTP pour exécuter des actions. Il s’agit d’une interface pour permettre de communiquer avec l’objet.

Le gros désavantage du REST est qu’il s’agit d’une connexion à un seul sens.

Infos : MQTT Vs REST

Points clés:

  1. Request/Response Architecture
  2. TCP/IP Based
  3. No QoS Support
  4. Complex Implementation at Client Side
  5. Larger Header compared to other IoT Protocols (Higher Bandwidth Requirement)
  6. Uses SSL/TLS for Security

MQTT (Message Queuing Telemetry Transport)

MQTT est un protocole de messagerie spécifiquement conçu pour des applications M2M (Machine To Machine) et IoT (Internet of Things), à cause notamment de sa légèreté (peu de consommation de bande passante et d’énergie).

De plus, il permet la publication des nouvelles données en temps, sans nécessiter d’interroger un serveur, ce qui permet une réactivité et une économie de bande passante supplémentaire.

MQTT offre une Qualité de service (QoS) à 3 niveaux :

  1. QoS 0 : transmission de message sans accusé de réception
  2. QoS 1 : transmission de message avec accusé de réception
  3. QoS 2 : transmission de message avec accusé de réception et vérification du message transmis afin d’éviter qu’un même message ne soit pas transmis de nombreuses fois.

MQTT-SN (MQTT for Sensor Network) est une version optimisée pour les objets à très faibles ressources communiquant sans fil.

Points clés :

  1. Publish/Subscribe Architecture
  2. Light Weight (Min Header Size: 2 Bytes)
  3. TCP/IP Based
  4. QoS Support
  5. Payload Agnostic
  6. Uses SSL/TLS for Security
  7. Broker could be Single Point of Failure

XMPP (Extensible Messaging and Presence Protocol)

XMPP est à la base un protocole pour la messagerie instantanée (Jabber, iChat, ICQ, MSN, Google Talk, etc.), mais elle a été conçue de manière plus large et ouverte qu’une simple messagerie instantanée populaire et propriétaire. Cela lui permet de supporter aussi bien l’architecture request/response que publish/subscribe, via des flux de données au format XML.

  • Publish/Subscribe and Request/Response Architecture
  • Widely used for Instant Messaging, Presence, Voice/Video Calls etc.
  • No QoS Support
  • Text Based Communication (XML Payload)
  • Secure Authentication (SASL) and TLS Based Security
  • XML Payload creation and parsing may need extra compute and battery power.

AMQP (Advanced Message Queuing Protocol)

AMQP est similaire à MQTT, à la différence prêt qu’il gère une file d’attente pour la distribution des messages. La présence de cette file nous permet un contrôle fin de la gestion des messages. Cependant, elle est assez lente dans les environnements à bandes passantes faibles.

Points clés :

  1. Point to Point Message Exchange (Server to Server)
  2. Flexible Messaging Patterns
  3. TCP/IP Based
  4. Smallest Packet Size: 60 Bytes
  5. QoS Support
  6. Uses SSL/TLS for Security

DDS (Data Distribution Service): P2P, PubSup, ReqRep

DDS a l’avantage de ne pas utiliser de “broker”, ce qui nous donne une gestion décentralisée (pas de présence de SPoF). Cependant, il semblerait que DDS ne fonctionnerait pas sur Arduino…

Points clés :

  1. Publish/Subscribe and Request/Response Architecture
  2. Relational data modeling
  3. QoS Support
  4. Multicast support over plain UDP sockets
  5. Leverages both TCP/IP and UDP/IP Transport
  6. Uses TLS and DTLS for Security
  7. Decentralised (No Single Point of Failure)

Resume Source : https://istblog.adlinktech.com/tag/messaging-protocol/

Au vu des différentes options qui s’offrent et surtout par rapport aux projets existants dans le domaine, nous avons décidé de choisir le protocole MQTT.

MQTT est un standard OASIS (ization for the Advancement of Structured Information Standards). MQTT est très largement utilisé dans tout ce qui est IoT car il est léger et ne consomme que très peu de bande passante.

Pour rappel, dans un système MQTT complet nous retrouvons quatre notions importantes :

  1. Les publishers : L’origine des messages
  2. Les subscribers : La destination des messages
  3. Le broker : L’élément qui fait le lien entre les publishers et les subscribers
  4. Les topics : Canaux de communications.
Message Queue Telemetry Transport, MQTT pour abréger, est un protocole de messagerie utile dans la communication entre les dispositifs de l’Internet des objets. Fait intéressant, le protocole a été développé en 1999, alors que l’IdO, qui connaît une croissance rapide, en était à ses balbutiement.
Le MQTT utilise un paradigme de publication-abonnement. Nous comprenons tous les connexions point à point entre les pairs. C’est la base des conversations téléphoniques entre deux personnes. La connexion client-serveur est un autre liaison point à point bien connu. Pour afficher une page Web, un navigateur, qui est le client, demande la page à un serveur HTML qui peut envoyer la page ou qui peut refuser de le faire. Dans ces types de liens, chaque partie doit adresser ses messages à son interlocuteur intentionnellement. Avec MQTT, les dispositifs peuvent publier des données ou s’abonner pour recevoir des données après d’un «courtier». Certains appareils publient en même temps qu’ils sont abonnés. Lorsqu’un dispositif publie des données, il n’est pas nécessaire qu’il sache quels seront les dispositifs qui sont abonnés pour les recevoir. Un dispositif abonné, reçoit des données lorsqu’elles sont disponibles sans avoir à les demander d’un dispositif particulier. Au lieu de cela, le courtier agit comme un centre d’information qui reçoit toutes les données publiées organisées par «sujets», et envoie ces données à tous les abonnés de chaque sujet spécifique.
L’utilisation d’un courtier signifie que la publication de données d’un à plusieurs dispositifs est possible en même temps que la lecture de données de plusieurs vers un seul dispositif. Voici un arrangement typique.

 mqtt1

Le climatiseur est enregistré auprès du serveur MQTT pour recevoir toutes les messages au sujet de la «température» et de l’«humidité». Les capteurs de température et d’humidité ne sont pas enregistrés pour recevoir des messages, ils ne font que publier des messages vers le courtier.
Toutes les dix minutes, le capteur de température publie la température ambiante. Dans la pratique, cela signifie qu’il envoie un seul numéro, disons 23, sous le sujet «température» au courtier MQTT, pour qu’il fait actuellement 23°C. De même, toutes les quinze minutes, le capteur d’humidité publie des messages au sujet de l’«humidité» vers le courtier MQTT, pour signifier que le facteur d’humidité est de 62%. À son tour, le courtier MQTT transmet ces messages à l’unité de conditionnement d’air. Cette dernière décide si elle doit s’allumer ou s’éteindre sur la base de ces deux mesures. C’est Internet des objets au travail.
Un programme de domotique qui prend en charge le protocole MQTT peut être ajouté au système pour fournir “intelligence” et contrôle. L’architecture du système pourrait être quelque chose comme ci-dessous.

mqtt2 > Le programme de domotique est abonné auprès du courtier MQTT pour recevoir toutes les messages aux sujets de la «température», de la «pression» et de l’«état du climatiseur». Le climatiseur est abonné auprès du serveur MQTT pour recevoir toutes les messages sur le thème «climatisation».

Comme auparavant, les capteurs de température et d’humidité publient des données vers le courtier. Mais maintenant, le programme de domotique qui est abonné pour recevoir cette information décide si le climatiseur doit être allumé ou éteint en tenant compte des données du capteur, de l’heure, du délai depuis la dernière fois que l’appareil est allumé ou éteint, la présence de personnes dans la maison, etc. Il ordonne au climatiseur de se mettre en marche ou de cesser de fonctionner en publiant un message sur le thème «climatisation». Le climatiseur publie des modifications sur son état sous le thème “état du climatiseur”. Parce que le programme de domotique est abonné à ce thème, il sait quand la climatisation a été activée ou désactivée par un individu.

Source : https://www.sigmdel.ca/michel/ha/domo/domo03fr.html// ===== Pratique ===== Dans cette partie, nous allons créer un broker sur un Raspberry Pi et les publishers seront des Arduino. ==== Raspberry Pi : Préparation de la carte SD ==== * Télécharger la dernière version de la distribution Raspbian * Télécharger Win32DiskImager (pour création depuis windows) * Flasher l’image sur la carte SD ==== Raspberry Pi : Installation et sécurisation ==== Pour une configuration “Headless”, c.-à-d. sans écran et sans clavier, pour que celle-ci puisse démarrer et se connecter en Wifi à votre réseau internet : - Activer la connexion SSH en créant un fichier ssh (sans extension) dans la partition boot - Créer un fichier wpa_supplicant.conf, situé à la racine de la partition boot de la carte avec : <code> country=fr update_config=1 ctrl_interface=/var/run/wpa_supplicant network={ scan_ssid=1 ssid=“LE2P” psk=“XXXXXX” } </code> Remarque : - [Optionnel] Fixer l’adresse IP de la machine si besoin pour le wifi - Faire sudo nano /etc/dhcpcd.conf et mettre ceci <code> interface wlan0 static ip_address=192.168.1.2/24 static routers=192.168.1.1 static domain_name_servers=192.168.1.1 </code> * * Au besoin désactiver le ipV6 en éditant le faisant sudo nano /etc/sysctl.d/local.conf et en mettant la ligne suivante net.ipv6.conf.all.disable_ipv6=1 * Faire ensuite sudo ifconfig wlan0 down && sudo ifconfig wlan0 up * Configurer raspberry avec sudo raspi-config * Changer les options de localisations (FR etc.) * sudo apt-get update && sudo apt-get upgrade * sudo apt-get install ntpdate * Changer le mot de passe par défaut : passwd * Mettre à jour la date du Raspberry : * Faire sudo apt-get install ntp * Editer le fichier sudo nano /etc/ntp.conf et dans les pools rajouter ntp.univ.run (uniquement dans le cas d’un serveur interne à L’université de la Réunion) * [Optionnel] Créer un username différent : * Faire sudo adduser house001 et entrer le mot de passante * Faire sudo adduser house001 sudo pour l’ajouter au groupe sudo * Au besoin, supprimer l’utilisateur pi en faisant sudo deluser -remove-home pi ou encore le désactiver en faisant sudo passwd --lock pi * [Optionnel] Pour plus de sécurité, il est possible ensuite (tuto sur ce site) : * Interdire l’accès root en SSH * Obliger le mot de passe pour les sudo * Forcer l’utilisation de clés SSH * Installer un firewall (ufw ou iptables) * Installer fail2ban ==== Raspberry Pi : Installation du broker ==== L’installation se fait comme suit : <HTML><ul></HTML> <HTML><li></HTML>sudo apt-get install mosquitto<HTML></li></HTML> <HTML><li></HTML>Vérifier la bonne installation en faisant systemctl status mosquitto, éventuellement après un reboot<HTML></li></HTML> <HTML><li></HTML>Dans le cas où cela ne fonctionne pas après le reboot, faire sudo systemctl enable mosquitto.service<HTML></li></HTML> <HTML><li></HTML>[Optionnel] Créer un accès par mot de passe<HTML></li></HTML> <HTML><li></HTML>sudo mosquitto_passwd -c /etc/mosquitto/passwd user<HTML></li></HTML> <HTML><li></HTML><HTML><p></HTML>Dans /etc/mosquitto/mosquitto.conf, rajouter les lignes<HTML></p></HTML> <code> allow_anonymous false password_file /etc/mosquitto/passwd </code> Ensuite il faut faire : <HTML></li></HTML> <HTML><li></HTML> Faire systemctl restart mosquitto <HTML></li></HTML><HTML></ul></HTML> Nous interdisons l’accès en anonyme et nous allons autoriser les connexions uniquement avec un identifiant mot de passe (dont la liste se trouve le fichier passwd que nous avons créé et est de la forme user:motdepasse). Il est possible aussi pour plus de sécurité de changer le port par défaut (1883), mais dans ce cas, on s’éloigne du standard. Remarque : la commande -c permet de créer le fichier orignal (ou d’écraser l’ancien). Si nous voulons rajouter d’autres utilisateurs, il est faut utiliser la commande mosquitto_passwd -b passwordfile user password * Pour tester la connexion depuis un autre appareil, il est possible d’utiliser le client Java (fichier jar) mqtt-spy à fois comme publisher et subscribers (Infos sur l'appli) ou encore MyMQTT sur Android ==== Rappel : Fonctionnement des flux ==== Source : https://www.sigmdel.ca/michel/ha/domo/domo03fr.html//

Les sujets/flux/themes MQTT sont hiérarchiques et représentés d’une manière très similaire aux chemins de fichiers complets (en utilisant le séparateur Unix /). Et, un peu comme les noms de fichiers, il existe des caractères génériques :

  • # qui représente tous les sujets de niveau inférieur. Par conséquent, s’abonner à la maison/# signifie que le courtier enverra tous les messages avec des sujets tels que maison, maison/humidité, maison/température, maison/température/centigrade, maison/température/fahrenheit, et ainsi de suite.
  • + qui représente un niveau unique dans la hiérarchie des sujets. Par conséquent, l’abonnement à la maison/+/commutateurs signifie que les messages avec des sujets tels que maison/chambre/commutateurs et maison/cuisine/commutateurs seront reçus, mais pas ceux dont le thème est maison/interrupteurs ni maison ni interrupteurs.

Pour des raisons évidentes, vous ne pouvez pas avoir un des caractères /, #, + dans le sujet. Il n’est pas judicieux d’inclure ces caractères dans un nom d’utilisateur ou un identifiant de client (voir le conseil de sécurité CVE-2017-7650 de mosquitto.org.) Vous pouvez utiliser le texte codé UTF-8 dans des sujets tels que /saison/été.

Lors du débogage de la communication avec un serveur MQTT, il peut s’avérer utile de s’abonner à toutes les rubriques en faisant mosquitto_sub -h 192.168.0.22 -v -t "#"

Dans notre cas, nous avons utilisé :

Le Grove - LCD RGB Backlight est branché sur le 1er port I2C du Grove Base Shield. Ce dernier (mis sous 5V) a été placé entre le WiFi Shield et l’Arduino.

L’Arduino est alimenté par une source externe par connecteur Jack, à 7V, en respectant la polarité (le + est à l’intérieur et le

  1. à l’extérieur).

Les Adafruit ATWINC1500 WiFi Shield ont été mis à jour avec le dernier firmware (Voir Site officiel).

Un réseau wifi (avec le ssid opale) a été créé avec un routeur wifi pour permettre au Raspberry Pi et à l’Arduino de communiquer.

Il est important de bien configurer l’IDE et d’importer les bibliothèques nécessaires :

  1. Librairies :
  2. WiFi101
  3. Adafruit IO Arduino
  4. Grove
  5. LCD RGB Backlight
  6. Pubsubclient ← non utilisé
  7. Configuration :
  8. Type de carte : Arduino/Genuino Uno
  9. Serial : 9600 baud

Le déroulement du programme est le suivant :

  1. Imports & Initialisations
  2. Connexion au Wifi
  3. Inscription au flux user/feeds/onoff
  4. Boucle :
  5. Connexion au serveur MQTT (si non connecté ou si déconnexion)
  6. Ecoute au flux user/feeds/onoff et affiche sur le LCD s’il y ON ou OFF reçus.
  7. Publication d’un entier (croissant au fur et à mesure) sur le flux user/feeds/numbers

Le code du programme MQTT_Basic est :

/***************************************************
  Adafruit MQTT Library WINC1500 Modified by Yassine
 
  Demo written by Limor Fried/Ladyada for Adafruit Industries.
  Modified by Yassine Gangat for LE²P.
 
  MIT license, all text above must be included in any redistribution
 ****************************************************/
#include <SPI.h>
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"
#include <WiFi101.h>
 
//
  ---------------------------- GROVE LCD Lib
  -------------------------
//#include <Wire.h>           // i2c LIB to use LCD
#include "rgb_lcd.h"        // lcd LIB
 
#include "arduino_secrets.h"
///////please enter your sensitive data in the Secret tab/arduino_secrets.h
const char ssid[] = SECRET_SSID;        // your network SSID (name)
const char pass[] = SECRET_PASS;       // your network password (use for WPA, or use as key for WEP)
int status = WL_IDLE_STATUS;     // the WiFi radio's status
// int keyIndex = 0;                // your network key Index number (needed only for WEP)
 
/************************* Broker Setup **********************************/
 
#define SERVER      "192.168.2.2"
#define SERVERPORT  1883
#define USERNAME    "user"
#define PWD         "apqm"
 
/******************************* Global State  ****************************/
 
//Set up the wifi client & Adafruit MQTT Client
WiFiClient client;
Adafruit_MQTT_Client mqtt(&client, SERVER, SERVERPORT, USERNAME, PWD);
 
#define halt(s) { Serial.println(F( s )); while(1);  }
 
/****************************** Feeds ***************************************/
 
// Setup a feed called 'photocell' for publishing.
// Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname>
Adafruit_MQTT_Publish numbers = Adafruit_MQTT_Publish(&mqtt, USERNAME "/feeds/numbers");
 
// Setup a feed called 'onoff' for subscribing to changes.
Adafruit_MQTT_Subscribe onoffmsg = Adafruit_MQTT_Subscribe(&mqtt, USERNAME "/feeds/onoff");
 
 
 
/*************************** Sketch Code ************************************/
 
//
  --------------------------- Variables & others
  --------------------------------
rgb_lcd lcd;
byte heart[8] = {
    0b00000,
    0b01010,
    0b11111,
    0b11111,
    0b11111,
    0b01110,
    0b00100,
    0b00000
};
 
void setup() {
    //
  ------------------------- Initialization Serial
  ------------------------------
    while (!Serial);  // wait for serial port to connect. Needed for native USB port only
    Serial.begin(9600);
 
    Serial.println(F("Adafruit MQTT Library WINC1500 Modified by Yassine"));
 
    //
  ---------------------- Initialization Wifi WINC1500
  --------------------------
    // Initialise the Client
    Serial.print(F("\nInit the WiFi module..."));
    // check for the presence of the breakout
    if (WiFi.status() == WL_NO_SHIELD) {
        Serial.println(F("WINC1500 not present"));
        // don't continue:
        while (true);
    }
    Serial.println(F("ATWINC OK!"));
 
    //
  ------------------------------ Physical part
  ----------------------------------
 
    Serial.println(F("LCD Init"));
    // set up the LCD's number of columns and rows:
    lcd.begin(16, 2);
 
    // Print a message to the LCD.
#if 1
    lcd.createChar(0, heart);
#endif
    lcd.setCursor(0, 0);
    lcd.print(F("Made with "));
    lcd.write((unsigned char)0);
    lcd.setCursor(0, 1);
    lcd.print(F("     by Yassine"));
 
    //
  --------------------------- Variables & others
  --------------------------------
    mqtt.subscribe(&onoffmsg);
}
 
uint32_t x = 0;
 
void loop() {
    //
  ------------------------------ MQTT Connection
  --------------------------------
    // Ensure the connection to the MQTT server is alive (this will make the first
    // connection and automatically reconnect when disconnected).
    MQTT_connect();
    lcd.setRGB(255,165,0);
    //
  ------------------------------ Subscription
  -----------------------------------
    Adafruit_MQTT_Subscribe *subscription;
    while ((subscription = mqtt.readSubscription(5000))) {
        if (subscription == &onoffmsg) {
            Serial.print(F("Got: "));
            Serial.println((char *)onoffmsg.lastread);
 
            if (0 == strcmp((char *)onoffmsg.lastread, "OFF")) {
                lcd.setRGB(255,0,0);
                lcd.setCursor(0, 1);
                lcd.print(F("Received OFF    "));
            }
            if (0 == strcmp((char *)onoffmsg.lastread, "ON")) {
                lcd.setRGB(0,255,0);
                lcd.setCursor(0, 1);
                lcd.print(F("Received ON     "));
            }
        }
    }
 
    //
  ------------------------------ Publication
  ----------------------------------
    Serial.print(F("\nSending "));
    Serial.print(x);
    lcd.setCursor(0, 1);
    lcd.print(F("Sending         "));
    lcd.setCursor(8, 1);
    lcd.print(x);
 
    if (! numbers.publish(x++)) {
        Serial.println(F("...Failed"));
    } else {
        Serial.println(F("...OK!"));
    }
 
}
 
// Function to connect and reconnect as necessary to the MQTT server.
// Should be called in the loop function and it will take care if connecting.
void MQTT_connect() {
    int8_t ret;
 
    // attempt to connect to Wifi network:
    while (WiFi.status() != WL_CONNECTED) {
        Serial.print(F("Attempting to connect to SSID: "));
        Serial.println(ssid);
        // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
        status = WiFi.begin(ssid, pass);
 
        // wait 10 seconds for connection:
        uint8_t timeout = 10;
        while (timeout && (WiFi.status() != WL_CONNECTED)) {
            timeout--;
            delay(1000);
        }
    }
 
    // Stop if already connected.
    if (mqtt.connected()) {
        return;
    }
 
    Serial.print(F("Connecting to MQTT... "));
 
    while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
        Serial.println(mqtt.connectErrorString(ret));
        Serial.println(ret);
        Serial.println(F("Retrying MQTT connection in 5 seconds..."));
        mqtt.disconnect();
        delay(5000);  // wait 5 seconds
    }
    Serial.println(F("MQTT Connected!"));
}

Remarques sur ce code :

  1. Le USERNAME et PWD sont ceux qui ont été définis dans les mosquitto_passwd (voir précédemment).
  2. Le mot de passe du wifi est stocké dans arduino_secrets.h (mais celui du broker, volontairement)
  3. Dès que la mémoire dynamique (vive) est utilisée à plus de 80%, il y a des soucis de connexion au wifi comme au MQTT.
  4. La solution la plus rapide pour diminuer ce nombre a été de remplacer Serial.println("Text") par Serial.println(F("Text")). Cela fonctionne aussi pour les lcd.print.
  5. Une autre solution est de mettre const partout où cela est possible !
  6. Une solution plus complexe, mais envisageable à la fin est d’utiliser PROGMEM.
  7. Sachant que les Serial.println utilisent beaucoup cette SRAM, il est possible :
  8. de le supprimer lorsque tout fonctionne
  9. d’utiliser ce genre d’astuce
#define DEBUG    //que l'on commente avant de téléverser

puis dans le code…

#ifdef DEBUG
Serial.prinlln(F("Affichage\n"));
#endif

Plus d’informations sur la fonction F() et sur la gestion de mémoire sont disponibles :

  1. En français sur ce site et ce site.
  2. En anglais sur ce site et ce forum.

Le fichier arduino_secrets.h contient le code suivant :

#define SECRET_SSID "NOM-POINT-D-ACCES"
#define SECRET_PASS "MDP-WIFI"

Pour la suite du test, nous avons fait le montage suivant sur le Arduino :

  1. Nous avons branché un Octopus Current Sensor en plaçant le signal reçu sur le PIN A0. Sur ce relai, nous avons branché une LED (et une résistance ainsi qu’un potentiomètre).

 Octopus Current Sensor

4 Channel 12vdc Relay Module Board

// Initialise the Arduino data pins for INPUT/OUTPUT
const int analogInPin = A0;
const int RELAY1 = 7;
 
void setup() {
  pinMode(RELAY1, OUTPUT);
  Serial.begin(9600);
  Serial.println("Setup");
}
 
void loop() {
  digitalWrite(RELAY1, LOW); // Turns ON Relays 1
  Serial.println("---------------------- OFF
  ----------------------");
  delay(5000);
  mesure(analogInPin);
  delay(5000);
 
  digitalWrite(RELAY1, HIGH); // Turns Relay Off
  Serial.println("---------------------- ON
  -----------------------");
  delay(2000);
  mesure(analogInPin);
  delay(2000);
  Serial.println("=================================================");
 
}
 
void mesure(int analogPin) {
 
  float sensorValue = 0; // value read from the pot
  // read the analog in value:
  sensorValue = analogRead(analogPin);
  // map it to the range of the analog out:
 
  Serial.print("sensor = ");
  Serial.println(sensorValue);
  sensorValue = ((sensorValue
  - 506) * 5 / 1024 / 0.066);
  // print the results to the serial monitor:
  Serial.print("Converted = ");
  Serial.println(sensorValue);
  Serial.print("Mapped = ");
  Serial.println(map(sensorValue, 0, 1023, 0, 255));
 
}

Ensuite nous avons modifié le code original de notre Arduino (voir Arduino : Code) pour que :

  1. Le relai s’actionne lorsque la valeur lut sur le flux user/feeds/onoff est ON et s’éteint lorsque la valeur est OFF.
  2. Le capteur permet de mesurer le courant que nous ferons varier avec un potentiomètre. L’Arduino renverra cette valeur sur le broker MQTT.

Nous avons donc le code suivant :

/***************************************************
  Adafruit MQTT Library WINC1500 Modified by Yassine
 
  Demo written by Limor Fried/Ladyada for Adafruit Industries.
  Modified by Yassine Gangat for LE²P.
 
  MIT license, all text above must be included in any redistribution
 ****************************************************/
#include <SPI.h>
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"
#include <WiFi101.h>
 
//
  ---------------------------- GROVE LCD Lib
  -------------------------
//#include <Wire.h>           // i2c LIB to use LCD
#include "rgb_lcd.h"        // lcd LIB
 
#include "arduino_secrets.h"
///////please enter your sensitive data in the Secret tab/arduino_secrets.h
const char ssid[] = SECRET_SSID;        // your network SSID (name)
const char pass[] = SECRET_PASS;       // your network password (use for WPA, or use as key for WEP)
int status = WL_IDLE_STATUS;     // the WiFi radio's status
// int keyIndex = 0;                // your network key Index number (needed only for WEP)
 
// Initialise the Arduino data pins for INPUT/OUTPUT
const int analogInPin = A0;
const int RELAY2 = 8;
const int mVperAmp = 185; // use 100 for 20A Module and 66 for 30A Module
 
 
/************************* Broker Setup **********************************/
 
#define SERVER      "192.168.2.2"
#define SERVERPORT  1883
#define USERNAME    "user"
#define PWD         "apqm"
 
/******************************* Global State  ****************************/
 
//Set up the wifi client & Adafruit MQTT Client
WiFiClient client;
Adafruit_MQTT_Client mqtt(&client, SERVER, SERVERPORT, USERNAME, PWD);
 
#define halt(s) { Serial.println(F( s )); while(1);  }
 
/****************************** Feeds ***************************************/
 
// Setup a feed called 'photocell' for publishing.
// Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname>
Adafruit_MQTT_Publish numbers = Adafruit_MQTT_Publish(&mqtt, USERNAME "/feeds/numbers");
 
// Setup a feed called 'onoff' for subscribing to changes.
Adafruit_MQTT_Subscribe onoffmsg = Adafruit_MQTT_Subscribe(&mqtt, USERNAME "/feeds/onoff");
 
 
 
/*************************** Sketch Code ************************************/
 
//
  --------------------------- Variables & others
  --------------------------------
rgb_lcd lcd;
byte heart[8] = {
    0b00000,
    0b01010,
    0b11111,
    0b11111,
    0b11111,
    0b01110,
    0b00100,
    0b00000
};
 
void setup() {
    //
  ------------------------- Initialization Serial
  ------------------------------
    while (!Serial);  // wait for serial port to connect. Needed for native USB port only
    Serial.begin(9600);
 
    Serial.println(F("Adafruit MQTT Library WINC1500 Modified by Yassine"));
 
    //
  ---------------------- Initialization Wifi WINC1500
  --------------------------
    // Initialise the Client
    Serial.print(F("\nInit the WiFi module..."));
    // check for the presence of the breakout
    if (WiFi.status() == WL_NO_SHIELD) {
        Serial.println(F("WINC1500 not present"));
        // don't continue:
        while (true);
    }
    Serial.println(F("ATWINC OK!"));
 
    //
  ------------------------------ Physical part
  ----------------------------------
 
    Serial.println(F("LCD Init"));
    // set up the LCD's number of columns and rows:
    lcd.begin(16, 2);
 
    // Print a message to the LCD.
#if 1
    lcd.createChar(0, heart);
#endif
    lcd.setCursor(0, 0);
    lcd.print(F("Made with "));
    lcd.write((unsigned char)0);
    lcd.setCursor(0, 1);
    lcd.print(F("     by Yassine"));
 
    pinMode(RELAY2, OUTPUT);
 
    //
  --------------------------- Variables & others
  --------------------------------
    mqtt.subscribe(&onoffmsg);
}
 
// uint32_t x = 0;
 
void loop() {
    //
  ------------------------------ MQTT Connection
  --------------------------------
    // Ensure the connection to the MQTT server is alive (this will make the first
    // connection and automatically reconnect when disconnected).
    MQTT_connect();
    lcd.setRGB(255,165,0);
    //
  ------------------------------ Subscription
  -----------------------------------
    Adafruit_MQTT_Subscribe *subscription;
    while ((subscription = mqtt.readSubscription(5000))) {
        if (subscription == &onoffmsg) {
            Serial.print(F("Got: "));
            Serial.println((char *)onoffmsg.lastread);
 
            if (0 == strcmp((char *)onoffmsg.lastread, "OFF")) {
                lcd.setRGB(255,0,0);
                lcd.setCursor(0, 1);
                lcd.print(F("Received OFF    "));
                digitalWrite(RELAY2, LOW); // Turns OFF Relays 1
            }
            if (0 == strcmp((char *)onoffmsg.lastread, "ON")) {
                lcd.setRGB(0,255,0);
                lcd.setCursor(0, 1);
                lcd.print(F("Received ON     "));
                digitalWrite(RELAY2, HIGH); // Turns ON Relays 1
            }
        }
    }
 
    //
  ------------------------------ Publication
  ----------------------------------
 
    Serial.print(F("\nSending "));
    Serial.print(mesure(analogInPin));
    lcd.setCursor(0, 1);
    lcd.print(F("Sending         "));
    lcd.setCursor(8, 1);
    lcd.print(mesure(analogInPin));
 
    if (! numbers.publish((uint32_t) mesure(analogInPin))) {
        Serial.println(F("...Failed"));
    } else {
        Serial.println(F("...OK!"));
    }
 
}
 
// Function to connect and reconnect as necessary to the MQTT server.
// Should be called in the loop function and it will take care if connecting.
void MQTT_connect() {
    int8_t ret;
 
    // attempt to connect to Wifi network:
    while (WiFi.status() != WL_CONNECTED) {
        Serial.print(F("Attempting to connect to SSID: "));
        Serial.println(ssid);
        // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
        status = WiFi.begin(ssid, pass);
 
        // wait 10 seconds for connection:
        uint8_t timeout = 10;
        while (timeout && (WiFi.status() != WL_CONNECTED)) {
            timeout--;
            delay(1000);
        }
    }
 
    // Stop if already connected.
    if (mqtt.connected()) {
        return;
    }
 
    Serial.print(F("Connecting to MQTT... "));
 
    while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
        Serial.println(mqtt.connectErrorString(ret));
        Serial.println(ret);
        Serial.println(F("Retrying MQTT connection in 5 seconds..."));
        mqtt.disconnect();
        delay(5000);  // wait 5 seconds
    }
    Serial.println(F("MQTT Connected!"));
}
double mesure(int analogPin) {
  int RawValue= 0;
  int ACSoffset = 2500;
  double Voltage = 0;
  double Amps = 0;
  RawValue = analogRead(analogPin);
  Voltage = (RawValue / 1024.0) * 5000; // Gets you mV
  Amps = ((Voltage
  - ACSoffset) / mVperAmp);
  /*Serial.print("Raw Value = " ); // shows pre-scaled value
  Serial.print(RawValue);
  Serial.print("\t mV = "); // shows the voltage measured
  Serial.print(Voltage,3);
  // the '3' after voltage allows you to display 3 digits after decimal point
  Serial.print("\t Amps = "); // shows the voltage measured
  Serial.println(Amps,3);
  // the '3' after voltage allows you to display 3 digits after decimal point
  delay(2500); */
  return(Voltage);
}

Remarque :

  1. on est toujours confronté au problème de mémoire dynamique. Dans les logs de téléversement de Arduino, si dans la phrase Les variables globales utilisent 1614 octets (78%) de mémoire dynamique, la mémoire utilisée arrive ou dépasse 80% la connexion au wifi devient impossible…

Bonus(es)

Il est possible d’accéder au broker depuis l’extérieur en suivant une partie du tuto suivant : Tuto IpDynamique

Node-RED est un outil puissant pour construire des applications de l’Internet des Objets (IoT) en mettant l’accent sur la simplification de la programmation qui se fait grâce à des blocs de code prédéfinis, appelés « nodes » pour effectuer des tâches. Il utilise une approche de programmation visuelle qui permet aux développeurs de connecter les blocs de code ensemble. Les nœuds connectés, généralement une combinaison de nœuds d’entrée, de nœuds de traitement et de nœuds de sortie, lorsqu’ils sont câblés ensemble, constituent un « flow ». Tuto Node-RED

La vocation de cette solution est de permettre de lier aisément des sources de données à des composants de traitement, locaux ou distants, et de créer des chaines de valeurs en quelques clics.

Node-RED est compatible avec MQTT : Tuto MQTT Node-RED

Sur le raspberry-pi, allez dans le menu : “Menu → Preferences → Recommended Software”. Node-RED en fait partie depuis 2018.

Si déjà installé, il est possible (voire préférable) de faire un sudo apt-get upgrade. D’autres options d’installation sont disponibles sur le site officiel.

Node-RED peut fonctionner tel quel, cependant, pour pouvoir rajouter des modules, il faut avoir npm en faisant sudo apt-get install npm et en mettant à jour sudo npm i npm -g. (sinon, l’option “Manage Palette” sera absente)

Une autre possibilité est de lancer la commande suivante, qui permet de faire tout ce qu’il y a à faire : bash <(curl -sL https://raw.githubusercontent.com/node-red/raspbian-deb-package/master/resources/update-nodejs-and-nodered)

 script

Pour démarrer Node-RED dans “Menu → Programmation → Node-RED”. On peut également démarrer depuis le Terminal avec la commande sudo node-red-start.

Remarque :

  1. Pour permettre d’auto-start de node-red, il faut faire sudo systemctl enable nodered.service
  2. On accéde à Node-RED avec http://localhost:1880/ ou encore http://adresse-Ip-Du-Rpi:1880/

 Ecran de démarrage

Installation du module

Pour contrôler nos objets MQTT, nous allons utiliser le module Dashboard. Pour cela:

  1. Aller dans le menu de Node-RED en haut à droite
  2. Cliquer sur “Manage Palette”
  3. Dans l’onglet install, chercher et installer node-red-dashboard

Configuration du module

  • Aller dans le menu de Node-RED en haut à droite
  • Cliquer sur “View → Dashboard”
  • Cliquer sur +tab pour rajouter un écran, dans lequel on mette ensuite 2 groupes avec +group Note : On accéde à Node-RED avec http://localhost:1880/ui (après avoir déployé le flux)

Création de Nodes

Une fois les nodes créés et liés, en cliquant sur “deploy” en haut à droite, on permet la mise en fonction de ces nodes.

Nodes MQTT
  • Faire un drag & drop d’un node MQTT A en input
  • Le configurer :
  • Configurer le serveur la première fois
  • Configurer le topic //“user/feeds/numbers”//
  • Faire un drag & drop d’un node MQTT B en output
  • Le configurer :
  • Résutiliser le même serveur
  • Configurer le topic //“user/feeds/onff”//
Nodes Dashboard
  • Faire un drag & drop d’un node switch (input du dashboard)
  • Le connecter avec l’entrée du node B
  • Le configurer
  • Mettre ON pour //“On Payload”// (sous forme de string)
  • Mettre OFF pour //“Off Payload”// (sous forme de string)
  • Faire un drag & drop d’un node text (output du dashboard)
  • Le connecter avec la sortie du node A
  • Le configurer (on peut laisser ce qu’il y a par défaut)
  • Faire un drag & drop d’un node chart (output du dashboard)
  • Le connecter avec la sortie du node A
  • Le configurer (on peut laisser ce qu’il y a par défaut)
Pour test:

Pour aller plus rapidement, il est possible de faire directement un import du code json du flow au lieu de créer les nodes précédents :

[
    {
        "id": "798f513a.e8a04",
        "type": "tab",
        "label": "Mon 1er flow",
        "disabled": false,
        "info": "Flow de test pour :\n- Récupérer une donnée MQTT et afficher\n- Envoyer une valeur par MQTT"
    },
    {
        "id": "57a9ca2c.283154",
        "type": "ui_base",
        "theme": {
            "name": "theme-dark",
            "lightTheme": {
                "default": "#0094CE",
                "baseColor": "#0094CE",
                "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif",
                "edited": true,
                "reset": false
            },
            "darkTheme": {
                "default": "#097479",
                "baseColor": "#097479",
                "baseFont": "Verdana,Verdana,Geneva,sans-serif",
                "edited": true,
                "reset": false
            },
            "customTheme": {
                "name": "Untitled Theme 1",
                "default": "#4B7930",
                "baseColor": "#4B7930",
                "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif",
                "reset": false
            },
            "themeState": {
                "base-color": {
                    "default": "#097479",
                    "value": "#097479",
                    "edited": false
                },
                "page-titlebar-backgroundColor": {
                    "value": "#097479",
                    "edited": false
                },
                "page-backgroundColor": {
                    "value": "#111111",
                    "edited": false
                },
                "page-sidebar-backgroundColor": {
                    "value": "#000000",
                    "edited": false
                },
                "group-textColor": {
                    "value": "#0eb8c0",
                    "edited": false
                },
                "group-borderColor": {
                    "value": "#555555",
                    "edited": false
                },
                "group-backgroundColor": {
                    "value": "#333333",
                    "edited": false
                },
                "widget-textColor": {
                    "value": "#eeeeee",
                    "edited": false
                },
                "widget-backgroundColor": {
                    "value": "#097479",
                    "edited": false
                },
                "widget-borderColor": {
                    "value": "#333333",
                    "edited": false
                },
                "base-font": {
                    "value": "Verdana,Verdana,Geneva,sans-serif"
                }
            }
        },
        "site": {
            "name": "Node-RED Dashboard",
            "hideToolbar": "false",
            "allowSwipe": "false",
            "allowTempTheme": "true",
            "dateFormat": "DD/MM/YYYY",
            "sizes": {
                "sx": 48,
                "sy": 48,
                "gx": 6,
                "gy": 6,
                "cx": 6,
                "cy": 6,
                "px": 0,
                "py": 0
            }
        }
    },
    {
        "id": "106a0892.4df7b7",
        "type": "ui_tab",
        "z": "",
        "name": "Ecran principale",
        "icon": "dashboard",
        "order": 1
    },
    {
        "id": "8e831ec7.d81eb",
        "type": "ui_group",
        "z": "",
        "name": "Flux reçus",
        "tab": "106a0892.4df7b7",
        "order": 1,
        "disp": true,
        "width": "6",
        "collapse": false
    },
    {
        "id": "279e1ad8.ebeae6",
        "type": "ui_group",
        "z": "",
        "name": "Envoie infos",
        "tab": "106a0892.4df7b7",
        "order": 2,
        "disp": true,
        "width": "6",
        "collapse": false
    },
    {
        "id": "f460a0.8fc35f6",
        "type": "mqtt-broker",
        "z": "",
        "name": "Mon Broker",
        "broker": "localhost",
        "port": "1883",
        "clientid": "",
        "usetls": false,
        "compatmode": true,
        "keepalive": "60",
        "cleansession": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": "",
        "closeTopic": "",
        "closeQos": "0",
        "closePayload": "",
        "willTopic": "",
        "willQos": "0",
        "willPayload": ""
    },
    {
        "id": "503ea954.5b97c8",
        "type": "ui_switch",
        "z": "798f513a.e8a04",
        "name": "ON/OFF switch",
        "label": "ON/OFF switch",
        "group": "279e1ad8.ebeae6",
        "order": 0,
        "width": 0,
        "height": 0,
        "passthru": true,
        "decouple": "false",
        "topic": "switch",
        "style": "",
        "onvalue": "ON",
        "onvalueType": "str",
        "onicon": "",
        "oncolor": "",
        "offvalue": "OFF",
        "offvalueType": "str",
        "officon": "",
        "offcolor": "",
        "x": 225,
        "y": 265,
        "wires": [
            [
                "8495340b.8c7758"
            ]
        ]
    },
    {
        "id": "e98fe402.458458",
        "type": "ui_text",
        "z": "798f513a.e8a04",
        "group": "8e831ec7.d81eb",
        "order": 0,
        "width": 0,
        "height": 0,
        "name": "Valeur actuelle",
        "label": "Valeur Actuelle",
        "format": "{{msg.payload}}",
        "layout": "row-spread",
        "x": 220,
        "y": 323,
        "wires": []
    },
    {
        "id": "9878823e.bbfaa",
        "type": "mqtt in",
        "z": "798f513a.e8a04",
        "name": "Lecture user/feeds/numbers",
        "topic": "user/feeds/numbers",
        "qos": "2",
        "broker": "f460a0.8fc35f6",
        "x": 144,
        "y": 455,
        "wires": [
            [
                "e98fe402.458458",
                "a63e7897.d1d608"
            ]
        ]
    },
    {
        "id": "8495340b.8c7758",
        "type": "mqtt out",
        "z": "798f513a.e8a04",
        "name": "Envoie vers user/feeds/onoff",
        "topic": "user/feeds/onoff",
        "qos": "",
        "retain": "",
        "broker": "f460a0.8fc35f6",
        "x": 392,
        "y": 175,
        "wires": []
    },
    {
        "id": "a63e7897.d1d608",
        "type": "ui_chart",
        "z": "798f513a.e8a04",
        "name": "Valeur du courant",
        "group": "8e831ec7.d81eb",
        "order": 0,
        "width": 0,
        "height": 0,
        "label": "Valeur du courant",
        "chartType": "line",
        "legend": "true",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": true,
        "ymin": "",
        "ymax": "",
        "removeOlder": 1,
        "removeOlderPoints": "",
        "removeOlderUnit": "3600",
        "cutout": 0,
        "useOneColor": false,
        "colors": [
            "#1f77b4",
            "#aec7e8",
            "#ff7f0e",
            "#2ca02c",
            "#98df8a",
            "#d62728",
            "#ff9896",
            "#9467bd",
            "#c5b0d5"
        ],
        "useOldStyle": false,
        "x": 339,
        "y": 548,
        "wires": [
            [],
            []
        ]
    }
]

En partant du principe que InfluxDB a été installé (voir mon tuto), la configuration de telegraf se fait comme suit :

[[inputs.mqtt_consumer]]
   ## MQTT broker URLs to be used. The format should be scheme://host:port,
   ## schema can be tcp, ssl, or ws.
   servers = ["tcp://10.243.4.250:1883"]
 
   ## MQTT QoS, must be 0, 1, or 2
   qos = 0
   ## Connection timeout for initial connection in seconds
   connection_timeout = "30s"
 
   ## Topics to subscribe to
   topics = [
     "CtrlLoad/#",
     "NetwEmul/#",
     "SolarPan/#",
   ]
 
   # if true, messages that can't be delivered while the subscriber is offline
   # will be delivered when it comes back (such as on service restart).
   # NOTE: if true, client_id MUST be set
   persistent_session = false
   # If empty, a random client ID will be generated.
   client_id = ""
 
   ## username and password to connect MQTT server.
   username = "Admin"
   password = "apqm"
 
   ## Optional SSL Config
   # ssl_ca = "/etc/telegraf/ca.pem"
   # ssl_cert = "/etc/telegraf/cert.pem"
   # ssl_key = "/etc/telegraf/key.pem"
   ## Use SSL but skip chain & host verification
   # insecure_skip_verify = false
 
   ## Data format to consume.
   ## Each data format has its own unique set of configuration options, read
   ## more about them here:
   ## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
   data_format = "value"
   data_type= "integer"

Les valeurs sont ensuite stocké avec les tag appropriés (par exemple CtrlLoad/feeds/numbers) dans telegraf.mqtt_consumer.topic.

Exemple local de dashboard : http://10.82.64.147/chronograf/sources/1/dashboards/3

L’implémentation de MQTT sous java est simple. Il suffit d’utiliser la libraire org.eclipse.paho avec GRADLE compile("org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.0") ou MAVEN

<dependency>
  <groupId>org.eclipse.paho</groupId>
  <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
  <version>1.1.0</version>
</dependency>

On utilisera les variables suivantes :

           String topic        = "MQTT Examples";
           String content      = "Message from MqttPublishSample";
           int qos             = 2;
           String broker       = "tcp://10.243.4.250:1883";

La création de la connexion au broker se fait comme suit :

           MqttClient client = new MqttClient(broker, MqttClient.generateClientId());

On peut rajouter des options de connexions comme suit :

           MqttConnectOptions options = new MqttConnectOptions();
           options.setAutomaticReconnect(true);
           options.setCleanSession(true);
           options.setConnectionTimeout(10);

puis on se connecte avec :

           client.connect(options); // enlever options si aucune option configurée

Pour publier un message, il suffit de faire :

           System.out.println("Publishing message: "+content);
           MqttMessage message = new MqttMessage(content.getBytes());
           message.setQos(qos);
           client.publish(topic, message);

Pour souscrire à un topic, il faut créer une classe qui reçoit et traite les messages :

           // Latch used for synchronizing b/w threads
           final CountDownLatch latch = new CountDownLatch(1);
 
           // Topic filter the client will subscribe to
           final String subTopic = "T/GettingStarted/pubsub";
 
           // Callback
  - Anonymous inner-class for receiving messages
           mqttClient.setCallback(new MqttCallback() {
 
               public void messageArrived(String topic, MqttMessage message) throws Exception {
                   // Called when a message arrives from the server that
                   // matches any subscription made by the client
                   String time = new Timestamp(System.currentTimeMillis()).toString();
                   System.out.println("\nReceived a Message!" +
                           "\n\tTime:    " + time +
                           "\n\tTopic:   " + topic +
                           "\n\tMessage: " + new String(message.getPayload()) +
                           "\n\tQoS:     " + message.getQos() + "\n");
                   latch.countDown(); // unblock main thread
               }
 
               public void connectionLost(Throwable cause) {
                   System.out.println("Connection to Solace messaging lost!" + cause.getMessage());
                   latch.countDown();
               }
 
               public void deliveryComplete(IMqttDeliveryToken token) {
               }
 
           });
 
           // Subscribe client to the topic filter and a QoS level of 0
           System.out.println("Subscribing client to topic: " + subTopic);
           mqttClient.subscribe(subTopic, 0);
           System.out.println("Subscribed");
 
           // Wait for the message to be received
           try {
               latch.await(); // block here until message received, and latch will flip
           } catch (InterruptedException e) {
               System.out.println("I was awoken while waiting");
           }

Un publisher et subscriber complet est disponible sur GitHub.

Source : https://dev.solace.com/samples/solace-samples-mqtt/publish-subscribe/

Code final

/***************************************************
  Adafruit MQTT Library WINC1500 Modified by Yassine
 
  Demo written by Limor Fried/Ladyada for Adafruit Industries.
  Modified by Yassine Gangat for LE²P.
 
  MIT license, all text above must be included in any redistribution
 ****************************************************/
#include <SPI.h>
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"
#include <WiFi101.h>
 
//
  ---------------------------- GROVE LCD Lib
  -------------------------
//#include <Wire.h>           // i2c LIB to use LCD
#include "rgb_lcd.h"        // lcd LIB
 
#include "arduino_secrets.h"
///////please enter your sensitive data in the Secret tab/arduino_secrets.h
const char ssid[] = SECRET_SSID;        // your network SSID (name)
const char pass[] = SECRET_PASS;       // your network password (use for WPA, or use as key for WEP)
int status = WL_IDLE_STATUS;     // the WiFi radio's status
// int keyIndex = 0;                // your network key Index number (needed only for WEP)
 
// Initialise the Arduino data pins for INPUT/OUTPUT
const int analogInPin = A0;
const int RELAY2 = 8;
const int mVperAmp = 185; // use 100 for 20A Module and 66 for 30A Module
 
 
/************************* Broker Setup **********************************/
 
#define SERVER      "192.168.2.2"
#define SERVERPORT  1883
#define USERNAME    "SolarPan"   // NetwEmul
  - CtrlLoad
  - SolarPan
#define PWD         "apqm"
 
/******************************* Global State  ****************************/
 
//Set up the wifi client & Adafruit MQTT Client
WiFiClient client;
Adafruit_MQTT_Client mqtt(&client, SERVER, SERVERPORT, USERNAME, PWD);
 
#define halt(s) { Serial.println(F( s )); while(1);  }
 
/****************************** Feeds ***************************************/
 
// Setup a feed called 'photocell' for publishing.
// Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname>
Adafruit_MQTT_Publish numbers = Adafruit_MQTT_Publish(&mqtt, USERNAME "/feeds/numbers");
 
// Setup a feed called 'onoff' for subscribing to changes.
Adafruit_MQTT_Subscribe onoffmsg = Adafruit_MQTT_Subscribe(&mqtt, USERNAME "/feeds/onoff");
 
 
 
/*************************** Sketch Code ************************************/
 
//
  --------------------------- Variables & others
  --------------------------------
rgb_lcd lcd;
byte heart[8] = {
  0b00000,
  0b01010,
  0b11111,
  0b11111,
  0b11111,
  0b01110,
  0b00100,
  0b00000
};
 
void setup() {
  //
  ------------------------- Initialization Serial
  ------------------------------
  while (!Serial);  // wait for serial port to connect. Needed for native USB port only
  Serial.begin(9600);
 
  //    Serial.println(F("Adafruit MQTT Library WINC1500 Modified by Yassine"));
 
  //
  ---------------------- Initialization Wifi WINC1500
  --------------------------
  // Initialise the Client
  //    Serial.print(F("\nInit the WiFi module..."));
  // check for the presence of the breakout
  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println(F("WINC1500 not present"));
    // don't continue:
    while (true);
  }
  //    Serial.println(F("ATWINC OK!"));
 
  //
  ------------------------------ Physical part
  ----------------------------------
 
  //    Serial.println(F("LCD Init"));
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
 
  // Print a message to the LCD.
#if 1
  lcd.createChar(0, heart);
#endif
  lcd.setCursor(0, 0);
  lcd.print(F("Made with "));
  lcd.write((unsigned char)0);
  lcd.setCursor(0, 1);
  lcd.print(F("     by Yassine"));
 
  pinMode(RELAY2, OUTPUT);
 
  //
  --------------------------- Variables & others
  --------------------------------
  mqtt.subscribe(&onoffmsg);
}
 
// uint32_t x = 0;
 
void loop() {
  //
  ------------------------------ MQTT Connection
  --------------------------------
  // Ensure the connection to the MQTT server is alive (this will make the first
  // connection and automatically reconnect when disconnected).
  MQTT_connect();
  // lcd.setRGB(255,165,0);
  //
  ------------------------------ Subscription
  -----------------------------------
  Adafruit_MQTT_Subscribe *subscription;
  while ((subscription = mqtt.readSubscription(5000))) {
    if (subscription == &onoffmsg) {
      Serial.print(F("Got: "));
      Serial.println((char *)onoffmsg.lastread);
 
      if (0 == strcmp((char *)onoffmsg.lastread, "OFF")) {
        lcd.setRGB(255, 0, 0);
        lcd.setCursor(0, 1);
        lcd.print(F("Closing "));
        lcd.print(F(USERNAME));
        digitalWrite(RELAY2, LOW); // Turns OFF Relays 1
      }
      if (0 == strcmp((char *)onoffmsg.lastread, "ON")) {
        lcd.setRGB(0, 255, 0);
        lcd.setCursor(0, 1);
        lcd.print(F("Opening "));
        lcd.print(F(USERNAME));
        digitalWrite(RELAY2, HIGH); // Turns ON Relays 1
      }
    }
  }
 
  //
  ------------------------------ Publication
  ----------------------------------
  int32_t val = mesureSimple(analogInPin);
  if (val < 0) {
    lcd.setRGB(245, 130, 49);
  } else {
    lcd.setRGB(0, 0, 117);
  }
 
 
  // int32_t val = mesure(analogInPin) * 100;
 
  Serial.print(F("\nSending "));
  Serial.print(val);
  lcd.setCursor(0, 1);
  lcd.print(F("                "));
  lcd.setCursor(0, 1);
  lcd.print(F(USERNAME));
  lcd.setCursor(9, 1);
  lcd.print(val);
 
  // Si on met double au lieu de int32_t, plantage !!!
  if (! numbers.publish(val)) {
    Serial.println(F("...Failed"));
  } else {
    Serial.println(F("...OK!"));
  }
 
}
 
// Function to connect and reconnect as necessary to the MQTT server.
// Should be called in the loop function and it will take care if connecting.
void MQTT_connect() {
  int8_t ret;
 
  // attempt to connect to Wifi network:
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(F("Attempting to connect to SSID: "));
    Serial.println(ssid);
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    status = WiFi.begin(ssid, pass);
 
    // wait 10 seconds for connection:
    uint8_t timeout = 10;
    while (timeout && (WiFi.status() != WL_CONNECTED)) {
      timeout--;
      delay(1000);
    }
  }
 
  // Stop if already connected.
  if (mqtt.connected()) {
    return;
  }
 
  Serial.print(F("Connecting to MQTT... "));
 
  while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
    Serial.println(mqtt.connectErrorString(ret));
    Serial.println(ret);
    Serial.println(F("Retrying MQTT connection in 5 seconds..."));
    mqtt.disconnect();
    delay(5000);  // wait 5 seconds
  }
  Serial.println(F("MQTT Connected!"));
}
double mesure(int analogPin) {
 
  const int ACSoffset = 2500;
  double Amps = 0;
  /*double Voltage = 0;
    int RawValue= 0;
    RawValue = analogRead(analogPin);
    /*Voltage = (RawValue / 1024.0) * 5000; // Gets you mV
    Amps = ((Voltage
  - ACSoffset) / mVperAmp);*/
 
  Amps = (((analogRead(analogPin) / 1024.0) * 5000
  - ACSoffset) / mVperAmp);
 
  /*Serial.print("Raw Value = " ); // shows pre-scaled value
    Serial.print(RawValue);
    Serial.print("\t mV = "); // shows the voltage measured
    Serial.print(Voltage,3);
    // the '3' after voltage allows you to display 3 digits after decimal point
    Serial.print("\t Amps = "); // shows the voltage measured
    Serial.println(Amps,3);
    // the '3' after voltage allows you to display 3 digits after decimal point
    delay(2500); */
  return (Amps);
}
int mesureSimple(int analogPin) {
 
  int Valeur = analogRead(analogPin);
 
  return (Valeur);
}
[
    {
        "id": "798f513a.e8a04",
        "type": "tab",
        "label": "Mon 1er flow",
        "disabled": false,
        "info": "Flow de test pour :\n- Récupérer une donnée MQTT et afficher\n- Envoyer une valeur par MQTT"
    },
    {
        "id": "57a9ca2c.283154",
        "type": "ui_base",
        "theme": {
            "name": "theme-dark",
            "lightTheme": {
                "default": "#0094CE",
                "baseColor": "#0094CE",
                "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif",
                "edited": true,
                "reset": false
            },
            "darkTheme": {
                "default": "#097479",
                "baseColor": "#097479",
                "baseFont": "Verdana,Verdana,Geneva,sans-serif",
                "edited": true,
                "reset": false
            },
            "customTheme": {
                "name": "Untitled Theme 1",
                "default": "#4B7930",
                "baseColor": "#4B7930",
                "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif",
                "reset": false
            },
            "themeState": {
                "base-color": {
                    "default": "#097479",
                    "value": "#097479",
                    "edited": false
                },
                "page-titlebar-backgroundColor": {
                    "value": "#097479",
                    "edited": false
                },
                "page-backgroundColor": {
                    "value": "#111111",
                    "edited": false
                },
                "page-sidebar-backgroundColor": {
                    "value": "#000000",
                    "edited": false
                },
                "group-textColor": {
                    "value": "#0eb8c0",
                    "edited": false
                },
                "group-borderColor": {
                    "value": "#555555",
                    "edited": false
                },
                "group-backgroundColor": {
                    "value": "#333333",
                    "edited": false
                },
                "widget-textColor": {
                    "value": "#eeeeee",
                    "edited": false
                },
                "widget-backgroundColor": {
                    "value": "#097479",
                    "edited": false
                },
                "widget-borderColor": {
                    "value": "#333333",
                    "edited": false
                },
                "base-font": {
                    "value": "Verdana,Verdana,Geneva,sans-serif"
                }
            }
        },
        "site": {
            "name": "Node-RED Dashboard",
            "hideToolbar": "false",
            "allowSwipe": "false",
            "allowTempTheme": "true",
            "dateFormat": "DD/MM/YYYY",
            "sizes": {
                "sx": 48,
                "sy": 48,
                "gx": 6,
                "gy": 6,
                "cx": 6,
                "cy": 6,
                "px": 0,
                "py": 0
            }
        }
    },
    {
        "id": "106a0892.4df7b7",
        "type": "ui_tab",
        "z": "",
        "name": "Ecran principale",
        "icon": "dashboard",
        "order": 1
    },
    {
        "id": "8e831ec7.d81eb",
        "type": "ui_group",
        "z": "",
        "name": "Flux reçus",
        "tab": "106a0892.4df7b7",
        "order": 1,
        "disp": true,
        "width": "17",
        "collapse": false
    },
    {
        "id": "279e1ad8.ebeae6",
        "type": "ui_group",
        "z": "",
        "name": "Envoie infos",
        "tab": "106a0892.4df7b7",
        "order": 2,
        "disp": true,
        "width": "6",
        "collapse": false
    },
    {
        "id": "f460a0.8fc35f6",
        "type": "mqtt-broker",
        "z": "",
        "name": "Mon Broker",
        "broker": "localhost",
        "port": "1883",
        "clientid": "",
        "usetls": false,
        "compatmode": true,
        "keepalive": "60",
        "cleansession": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": "",
        "closeTopic": "",
        "closeQos": "0",
        "closePayload": "",
        "willTopic": "",
        "willQos": "0",
        "willPayload": ""
    },
    {
        "id": "503ea954.5b97c8",
        "type": "ui_switch",
        "z": "798f513a.e8a04",
        "name": "NetwEmul ON/OFF switch",
        "label": "NetwEmul ON/OFF switch",
        "group": "279e1ad8.ebeae6",
        "order": 0,
        "width": 0,
        "height": 0,
        "passthru": true,
        "decouple": "false",
        "topic": "switch",
        "style": "",
        "onvalue": "ON",
        "onvalueType": "str",
        "onicon": "",
        "oncolor": "",
        "offvalue": "OFF",
        "offvalueType": "str",
        "officon": "",
        "offcolor": "",
        "x": 188,
        "y": 176,
        "wires": [
            [
                "8495340b.8c7758"
            ]
        ]
    },
    {
        "id": "e98fe402.458458",
        "type": "ui_text",
        "z": "798f513a.e8a04",
        "group": "8e831ec7.d81eb",
        "order": 0,
        "width": 0,
        "height": 0,
        "name": "Valeur actuelle",
        "label": "Valeur Actuelle",
        "format": "{{msg.payload}}",
        "layout": "row-spread",
        "x": 480,
        "y": 443,
        "wires": []
    },
    {
        "id": "9878823e.bbfaa",
        "type": "mqtt in",
        "z": "798f513a.e8a04",
        "name": "Lecture de SolarPan",
        "topic": "SolarPan/feeds/numbers",
        "qos": "2",
        "broker": "f460a0.8fc35f6",
        "x": 151,
        "y": 479,
        "wires": [
            [
                "e98fe402.458458",
                "a63e7897.d1d608"
            ]
        ]
    },
    {
        "id": "8495340b.8c7758",
        "type": "mqtt out",
        "z": "798f513a.e8a04",
        "name": "Envoie vers NetwEmul/feeds/onoff",
        "topic": "NetwEmul/feeds/onoff",
        "qos": "",
        "retain": "",
        "broker": "f460a0.8fc35f6",
        "x": 502,
        "y": 178,
        "wires": []
    },
    {
        "id": "a63e7897.d1d608",
        "type": "ui_chart",
        "z": "798f513a.e8a04",
        "name": "Ensoleillement",
        "group": "8e831ec7.d81eb",
        "order": 0,
        "width": 0,
        "height": 0,
        "label": "Ensoleillement",
        "chartType": "line",
        "legend": "true",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "",
        "ymax": "",
        "removeOlder": "1",
        "removeOlderPoints": "",
        "removeOlderUnit": "60",
        "cutout": 0,
        "useOneColor": false,
        "colors": [
            "#1f77b4",
            "#aec7e8",
            "#ff7f0e",
            "#2ca02c",
            "#98df8a",
            "#d62728",
            "#ff9896",
            "#9467bd",
            "#c5b0d5"
        ],
        "useOldStyle": false,
        "x": 472,
        "y": 506,
        "wires": [
            [],
            []
        ]
    },
    {
        "id": "c447fb5f.ce5208",
        "type": "ui_switch",
        "z": "798f513a.e8a04",
        "name": "CtrlLoad ON/OFF switch",
        "label": "CtrlLoad ON/OFF switch",
        "group": "279e1ad8.ebeae6",
        "order": 0,
        "width": 0,
        "height": 0,
        "passthru": true,
        "decouple": "false",
        "topic": "switch",
        "style": "",
        "onvalue": "ON",
        "onvalueType": "str",
        "onicon": "",
        "oncolor": "",
        "offvalue": "OFF",
        "offvalueType": "str",
        "officon": "",
        "offcolor": "",
        "x": 178,
        "y": 243,
        "wires": [
            [
                "4096fa94.514d14"
            ]
        ]
    },
    {
        "id": "4096fa94.514d14",
        "type": "mqtt out",
        "z": "798f513a.e8a04",
        "name": "Envoie vers CtrlLoad/feeds/onoff",
        "topic": "CtrlLoad/feeds/onoff",
        "qos": "",
        "retain": "",
        "broker": "f460a0.8fc35f6",
        "x": 502,
        "y": 245,
        "wires": []
    },
    {
        "id": "33ce16a5.0352ca",
        "type": "mqtt in",
        "z": "798f513a.e8a04",
        "name": "Lecture de NetwEmul",
        "topic": "NetwEmul/feeds/numbers",
        "qos": "2",
        "broker": "f460a0.8fc35f6",
        "x": 159,
        "y": 552,
        "wires": [
            [
                "f185a9c5.e05d98"
            ]
        ]
    },
    {
        "id": "6020fc0e.f19df4",
        "type": "mqtt in",
        "z": "798f513a.e8a04",
        "name": "Lecture de CtrlLoad",
        "topic": "CtrlLoad/feeds/numbers",
        "qos": "2",
        "broker": "f460a0.8fc35f6",
        "x": 148,
        "y": 624,
        "wires": [
            [
                "f185a9c5.e05d98"
            ]
        ]
    },
    {
        "id": "f185a9c5.e05d98",
        "type": "ui_chart",
        "z": "798f513a.e8a04",
        "name": "Valeur du courant",
        "group": "8e831ec7.d81eb",
        "order": 0,
        "width": 0,
        "height": 0,
        "label": "Valeur du courant",
        "chartType": "line",
        "legend": "true",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "",
        "ymax": "",
        "removeOlder": "1",
        "removeOlderPoints": "",
        "removeOlderUnit": "60",
        "cutout": 0,
        "useOneColor": false,
        "colors": [
            "#1f77b4",
            "#aec7e8",
            "#ff7f0e",
            "#2ca02c",
            "#98df8a",
            "#d62728",
            "#ff9896",
            "#9467bd",
            "#c5b0d5"
        ],
        "useOldStyle": false,
        "x": 486,
        "y": 599,
        "wires": [
            [],
            []
        ]
    }
]
  • tutos/mqtt.txt
  • Dernière modification : 2020/11/02 11:45
  • de mdelsaut