Mit MQTT einen Roboter steuern - [Teil 2] - AZ-Delivery

Dans la première partie de cette série de blogs, vous avez appris tout ce qu'il faut savoir sur le MQTT. En outre, un serveur, appelé "broker", a été installé sur un Raspberry Pi et des messages ont été envoyés au broker et reçus également avec des entrées en ligne de commande.

Dans cette partie, la configuration devient un peu plus complexe, les données étant envoyées et reçues par différents NodeMCU et un Arduino Uno avec Ethernet Shield, appelés clients.

Le contexte sera que vous pouvez faire fonctionner un client MQTT sur les microcontrôleurs les plus courants vendus par AZ-Delivery. Dans les exigences en matière de matériel, vous pouvez lire quelles sont les pièces obligatoires pour compléter la série. Certains composants ne sont nécessaires que pour cette partie du blog, mais sont utilisés dans divers autres articles de blog par AZ-Delivery.

Exigences en matière de matériel

Pour cet article de blog, vous n'avez besoin que de quelques éléments, voir le tableau 1.

nombre Composant
1 Raspberry Pi (obligatoire)
1 Alimentation adaptée
1 NodeMCU Lua Amica Module V2 ESP8266 ESP-12F(obligatoire)
1 Carte de développement WLAN WiFi du module ESP32 NodeMCU (obligatoire)
1 Carte micro contrôleur avec câble USB (optionnel)
1 Bouclier Ethernet W5100 (optionnel)
1 Gamme de résistances (optionnel)
2 Potentiomètre (obligatoire)
1 Ecran LCD 4x20 caractères bleu I2C (optionnel)
1 LED couleur unique (optionnel)

Tableau 1: Matériel requis

Pour le Pi, gardez à l'esprit que vous aurez besoin d'une carte MicroSD en plus du matériel mentionné ci-dessus. Pour cela, vous devez installer le système d'exploitation Raspberry Pi OS (anciennement appelé Raspbian) comme image sur la carte.

Exigences en matière de logiciels

Le logiciel requis pour ce projet est gérable :

  • IDE Arduino (https://www.arduino.cc/en/Main/Software), il est préférable de télécharger la version actuelle ici
  • La bibliothèque PubSubClient avec toutes les dépendances
  • La bibliothèque LiquidCrystal_I2C avec toutes les dépendances

Comment installer des bibliothèques via la gestion des bibliothèques est sous https://www.az-delivery.de/blogs/azdelivery-blog-fur-arduino-und-raspberry-pi/arduino-ide-programmieren-fuer-einsteiger-teil-1 Section Gestion de la bibliothèque, décrite plus en détail.

Condition préalable

Pour que ce blog et les suivants fonctionnent, vous devez avoir un Raspberry Pi avec le broker MQTT installé. Vous pouvez lire exactement comment cela fonctionne et quelles commandes vous devez entrer dans la première partie de cette série de blogs. En outre, veuillez vérifier si le courtier a également été lancé au moment du démarrage du Raspberry Pi. Pour ce faire, entrez la commande du code 1 dans le terminal.

sudo service mosquitto status

Code 1 : Demande dans le terminal si le mosquitto est lancé

Si la sortie indique un actif (en cours), voir figure 1, aucune autre commande n'est nécessaire.

Figure 1: Statut du Mosquitto Broker dans le terminal

Si le résultat diffère, veuillez vérifier à nouveau si mosquitto a été installé correctement et si le service a été inclus correctement dans le démarrage automatique. Les instructions exactes se trouvent dans la partie 1.

Simples MQTT - Abonnez-vous avec NodeMCU Lua Amica Modul V2 ESP8266

Une remarque à ce stade : pour l'exemple décrit ici, le module V2 ESP8266 du NodeMCU Lua Amica est utilisé.

Dans le premier exemple, voir le code 2, le NodeMCU doit établir une connexion avec le courtier MQTT et recevoir toutes les données. Cela vous donne également un aperçu direct du fonctionnement de la communication générale avec la bibliothèque PubSubClient. Pour vous aider à comprendre le code source, chaque fonction a été décrite en détail.

Pour que le code source fonctionne pour vous, vous devez changer votre WLAN-ID et le mot de passe dans le code source.


//-----------------------------------------------------
// Example 1 NodeMCU with MQTT
// Autor: Joern Weise
// License: GNU GPl 3.0
// Created: 20. Oct 2020
// Update: 25. Oct 2020
//-----------------------------------------------------
#include <ESP8266WiFi.h> //Lib for Wifi
#include <PubSubClient.h> //Lib for MQTT Pub and Sub
//
#ifndef STASSID
#define STASSID "................"
#define STAPSK "................"
#endif

#define ADVANCEDIAG 1
const char* MQTT_BROKER = "raspberrypi"; //Name of the mqtt broker
const char* SUBTOPIC = "/#";
String clientID = "NodeMCU_1"; //Clientname for MQTT-Broker
WiFiClient espClient;
PubSubClient mqttClient(espClient);

void setup()
{
Serial.begin(115200); //Start Serial monitor baudrate 115200
delay(50);
writeAdvanceDiag("SerialMonitor enabled", true);
setupWifi();
writeAdvanceDiag("Set MQTT-Server", true);
mqttClient.setServer(MQTT_BROKER,1883);
writeAdvanceDiag("Set Callback-function", true);
mqttClient.setCallback(callback);
writeAdvanceDiag("Finish setup()-Function", true);
}

/*
* =================================================================
* Function: setupWifi
* Returns: void
* Description: Setup wifi to connect to network
* =================================================================
*/
void setupWifi()
{
Serial.println("Connection to: " + String(STASSID));
WiFi.mode(WIFI_STA);
WiFi.begin(STASSID, STAPSK);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}

void loop() {
// put your main code here, to run repeatedly:
if(!mqttClient.connected())
reconnectMQTT();

mqttClient.loop();
}

/*
* =================================================================
* Function: callback
* Returns: void
* Description: Will automatically be called, if a subscribed topic
* has a new message
* topic: Returns the topic, from where a new msg comes from
* payload: The message from the topic
* length: Length of the msg, important to get content
* =================================================================
*/
void callback(char* topic, byte* payload, unsigned int length)
{
String stMessage = "";
writeAdvanceDiag("Message arrived from topic: " + String(topic), true);
writeAdvanceDiag("Message length: " + String(length), true);
for (int i = 0; i < length; i++)
stMessage += String((char)payload[i]);
writeAdvanceDiag("Message is: " + stMessage, true);
}

/*
* =================================================================
* Function: reconnectMQTT
* Returns: void
* Description: If there is no connection to MQTT, this function is
* called. In addition, the desired topic is registered.
* =================================================================
*/
void reconnectMQTT()
{
while(!mqttClient.connected())
{
writeAdvanceDiag("Login to MQTT-Broker", true);
if(mqttClient.connect(clientID.c_str()))
{
Serial.println("Connected to MQTT-Broker " +String(MQTT_BROKER));
writeAdvanceDiag("Subscribe topic '" + String(SUBTOPIC)+ "'", true);
mqttClient.subscribe(SUBTOPIC,1); //Subscibe topic "SUBTOPIC"
}
else
{
writeAdvanceDiag("Failed with rc=" +String(mqttClient.state()), true);
Serial.println("Next MQTT-Connect in 3 sec");
delay(3000);
}
}
}

/*
* =================================================================
* Function: writeAdvanceDiag
* Returns: void
* Description: Writes advance msg to serial monitor, if
* ADVANCEDIAG >= 1
* msg: Message for the serial monitor
* newLine: Message with linebreak (true)
* =================================================================
*/
void writeAdvanceDiag(String msg, bool newLine)
{
if(bool(ADVANCEDIAG)) //Check if advance diag is enabled
{
if(newLine)
Serial.println(msg);
else
Serial.print(msg);
}
}


Code 2: Exemple de réception de données MQTT par NodeMCU

Au début du code, un objet espClient est créé et transmis à l'objet mqttClient également créé. Ainsi, une communication générale peut être établie avec une connexion WiFi active.

Si le NodeMCU démarre, le WiFi est d'abord configuré, ici dans la fonction setupWifi(), puis la connexion au courtier MQTT est établie dans la fonction setup().

Avec "mqttClient.setServer(MQTT_BROKER,1883)", l'adresse et le port du courtier MQTT sont configurés et avec "mqttClient.setCallback(callback)", la fonction de traitement des données reçues est déterminée, plus d'informations à ce sujet ci-dessous.

La fonction loop() fait exactement deux choses :

1. vérifier si le NodeMCU est connecté au courtier MQTT. Si ce n'est pas le cas, la fonction reconnectMQTT() est appelée.

2. la fonction mqttClient.loop() est appelée pour effectuer la communication avec MQTT.

La première étape consiste à s'inscrire auprès du courtier MQTT avec un nom de client unique. Vous pouvez le déterminer librement via la variable globale "clientID". Si l'enregistrement a réussi, le sujet souhaité est inscrit à l'étape suivante. Cela se fait via  

"MqttClient.subscribe (SUBTOPIC, 1)"

où dans cet exemple SUBTOPIC souscrit au sujet "/#" (tous les sujets) et le "1" représente une QoS supérieure à 0. Le NodeMCU est ainsi alimenté en messages du courtier dès qu'il y a des changements dans un sujet. 

En cas d'erreur, le NodeMCU tente de se reconnecter au courtier toutes les 3 secondes.  

Cela nous amène au point où le NodeMCU reçoit des données du courtier, à condition que le NodeMCU soit connecté. Au moyen de la fonction callback(), s'il y a de nouveaux messages, ceux-ci sont évalués. Trois paramètres sont passés à la fonction callback() :  

  1. topic : qui reflète le chemin de rubrique absolu du message reçu
  2. payload : Le message, au format octet
  3. length : la longueur du message

Grâce à ces trois informations, vous pouvez évaluer ce dont vous avez besoin pour le sujet choisi. Dans ce cas, nous diffusons le sujet via le moniteur en série. Immédiatement après, nous convertissons le message d'octet en une chaîne de caractères en utilisant la boucle pour indiquée dans le code 3. Cela vous donne un message normalement lisible.  

for (int i = 0; i < length; i++)
stMessage += String((char)payload[i]);

Code 3: fragment de code pour convertir le message MQTT en chaîne

Ce message au format chaîne est également émis via le moniteur série

Vous pouvez voir sur la figure 2 que cette esquisse fonctionne si bien.

Figure 2: Message reçu du courtier MQTT

Veuillez considérer à l'avance de vos messages que nos trémas allemands ne fonctionnent pas. Ceux-ci sont ensuite représentés par d'autres personnages.

Simples MQTT - S'abonner avec ESP32 NodeMCU

Notez à ce stade, pour l'exemple suivant, le module ESP32 NodeMCU WLAN WiFi Development Board est utilisé.  

Dans l'exemple du code 4, le NodeMCU ESP32 doit envoyer des données au courtier MQTT. Comme c'est très simple, nous commençons par envoyer le runtime du module ESP32 NodeMCU WLAN WiFi Development Board au broker toutes les deux secondes.   

Dans cet exemple, le code comporte également de nombreux commentaires pour faciliter votre compréhension. Pour que le code source fonctionne pour vous, vous devez changer votre ID et votre mot de passe WLAN dans le code source.  


//-----------------------------------------------------
// Example 2 ESP-NodeMCU with MQTT
// Autor: Joern Weise
// License: GNU GPl 3.0
// Created: 20. Oct 2020
// Update: 25. Oct 2020
//-----------------------------------------------------
#include <WiFi.h>
#include <PubSubClient.h> //Lib for MQTT Pub and Sub
//
#ifndef STASSID
#define STASSID "……………" //Enter Wfi-Name
#define STAPSK "……………" //Enter Passkey
#endif

#define ADVANCEDIAG 1
const char* MQTT_BROKER = "raspberrypi"; //Name of the mqtt broker
const char* PubTopic = "/Client/ESP32"; //Topic where to publish
String clientID = "ESP-DevKit_1"; //Clientname for MQTT-Broker
WiFiClient espClient;
PubSubClient mqttClient(espClient);

unsigned long lLastMsg = 0;
int iTimeDelay = 2000; //Set delay for next msg to 2 seconds
#define MSG_BUFFER_SIZE (50)
char msg[MSG_BUFFER_SIZE];

void setup()
{
Serial.begin(115200); //Start Serial monitor baudrate 115200
delay(50);
writeAdvanceDiag("SerialMonitor enabled", true);
setupWifi();
writeAdvanceDiag("Set MQTT-Server", true);
mqttClient.setServer(MQTT_BROKER,1883);
writeAdvanceDiag("Finish setup()-Function", true);
}

/*
* =================================================================
* Function: setupWifi
* Returns: void
* Description: Setup wifi to connect to network
* =================================================================
*/
void setupWifi()
{
Serial.println("Connection to: " + String(STASSID));
WiFi.mode(WIFI_STA);
WiFi.begin(STASSID, STAPSK);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}

void loop() {
// put your main code here, to run repeatedly:
if(!mqttClient.connected())
reconnectMQTT();

mqttClient.loop();

if(millis() - lLastMsg > iTimeDelay)
{
lLastMsg = millis();
snprintf(msg,MSG_BUFFER_SIZE, "%1d",millis()); //Convert message to char
mqttClient.publish(PubTopic,msg,true); //Send to broker
}
}

/*
* =================================================================
* Function: reconnectMQTT
* Returns: void
* Description: If there is no connection to MQTT, this function is
* called. In addition, the desired topic is registered.
* =================================================================
*/
void reconnectMQTT()
{
while(!mqttClient.connected())
{
writeAdvanceDiag("Login to MQTT-Broker", true);
if(mqttClient.connect(clientID.c_str()))
{
Serial.println("Connected to MQTT-Broker " +String(MQTT_BROKER));
}
else
{
writeAdvanceDiag("Failed with rc=" +String(mqttClient.state()), true);
Serial.println("Next MQTT-Connect in 3 sec");
delay(3000);
    }
  }
}

/*
* =================================================================
* Function: writeAdvanceDiag
* Returns: void
* Description: Writes advance msg to serial monitor, if
* ADVANCEDIAG >= 1
* msg: Message for the serial monitor
* newLine: Message with linebreak (true)
* =================================================================
*/
void writeAdvanceDiag(String msg, bool newLine)
{
if(bool(ADVANCEDIAG)) //Check if advance diag is enabled
{
if(newLine)
Serial.println(msg);
else
Serial.print(msg);
  }
}

Code 4: ESP32 MQTT-Daten empfangen lassen

Comme dans le premier exemple, un objet espClient est créé et transmis à l'objet mqttClient également créé. Lorsque le module ESP32 NodeMCU WLAN WiFi Development Board est lancé, le WiFi est configuré via la fonction setupWiFi(). Directement après la fonction setup(), le courtier MQTT est préconfiguré à l'aide de "mqttClient.setServer(MQTT_BROKER,1883) ;". Une fonction de rappel ("callback") n'est pas nécessaire à ce stade, car aucune donnée ne doit être reçue et traitée.

La fonction loop() est donc presque identique au premier exemple, à l'exception de la ligne qui montre le code 5.


if(millis() - lLastMsg > iTimeDelay)
{
lLastMsg = millis();
snprintf(msg,MSG_BUFFER_SIZE, "%1d",millis()); //Convert message to char
mqttClient.publish(PubTopic,msg,true); //Send to broker
}

 Code 5 : Codefragment pour l'envoi du temps actif

Cette partie envoie le runtime actuel au sujet "/Client/ESP32" toutes les 2 secondes. La figure 3 montre que cet exemple fonctionne. Dans ce cas, MQTT.fx affiche les messages du courtier MQTT.  

Figure 3 : MQTTfx montre les données envoyées par le NodeMCU ESP32

Exemple de MQTT avec plusieurs clients


Dans les derniers exemples, y compris le premier article de blog, nous avons toujours supposé qu'il s'agissait d'un courtier et d'un client. Cependant, la réalité de ces projets MQTT est souvent différente.  En pratique, il y a généralement des clients qui fournissent des données provenant de capteurs, des clients qui reçoivent et traitent les données transmises et, enfin, des clients qui exécutent.  

Pour l'exemple à venir, un module V2 de NodeMCU Lua Amica surveillera une valeur de potentiomètre et enverra la nouvelle valeur au courtier lorsqu'elle change. 

Un module ESP32 NodeMCU WLAN WiFi Development Board souscrit ces données et les cartographie de 0-1024 à 0-255, il publie également la température et la pression atmosphérique de deux capteurs BME/BMP280.  

Les données recueillies sont inscrites sur un Arduino Uno avec écran Ethernet, LED et écran LCD I2C et permettent de visualiser les données. La LED fournit le signal cartographié du potentiomètre et l'écran affiche la température et la pression de l'air des deux capteurs.  


Nous commençons par le module V2 du NodeMCU Lua Amica et le potentiomètre. Câblez-les tous les deux selon la figure 4. Cette partie est facile car il n'y a que trois fils à connecter.  

 

Figure 4: Câblage du module NodeMCU Lua Amica V2 avec potentiomètre

Le code du NodeMCU Lua Amica Module V2 est très fin et contient essentiellement l'initialisation du WLAN et la connexion au MQTT, ainsi que la transmission des données des valeurs du potentiomètre analogique, voir code 6. Pour que le code source fonctionne également pour vous, vous devez changer votre ID WLAN et le mot de passe dans le code source.

//-----------------------------------------------------
// Example 3 NodeMCU with Poti transfer to mqtt-broker
// Autor: Joern Weise
// License: GNU GPl 3.0
// Created: 20. Oct 2020
// Update: 25. Oct 2020
//-----------------------------------------------------
#include <ESP8266WiFi.h> //Lib for Wifi
#include <PubSubClient.h> //Lib for MQTT Pub and Sub

#ifndef STASSID
#define STASSID "ENTER-WIFI_HERE" //Enter Wfi-Name
#define STAPSK "ENTER-PASS-HERE" //Enter Wifi-Passkey
#endif
#define ADVANCEDIAG 1
#define MSG_BUFFER_SIZE (50)
#define UPDATETIME 200
#define MINVALUECHANGE 1

const char* MQTT_BROKER = "raspberrypi"; //Name of the mqtt broker
String clientID = "NodeMCU_1"; //Clientname for MQTT-Broker
const char* PubTopicPoti = "/Client/ESP32/Poti/Value"; //Topic where to publish
const int iAnalogPin = A0; //Set analog pin
int iSensorValue = 0;
int iLastValue = 0;

//Create objects for mqtt
WiFiClient espClient;
PubSubClient mqttClient(espClient);
unsigned int iLastTime = 0;
char msg[MSG_BUFFER_SIZE];
void setup()
{
Serial.begin(115200); //Start Serial monitor baudrate 115200
delay(50);
writeAdvanceDiag("SerialMonitor enabled", true);
setupWifi();
writeAdvanceDiag("Set MQTT-Server", true);
mqttClient.setServer(MQTT_BROKER, 1883);
writeAdvanceDiag("Set Callback-function", true);
writeAdvanceDiag("Finish setup()-Function", true);
}

/*
=================================================================
Function: setupWifi
Returns: void
Description: Setup wifi to connect to network
=================================================================
*/
void setupWifi()
{
Serial.println("Connection to: " + String(STASSID));
WiFi.mode(WIFI_STA);
WiFi.begin(STASSID, STAPSK);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}

void loop() {
// put your main code here, to run repeatedly:
if (!mqttClient.connected())
reconnectMQTT();

mqttClient.loop();

if (millis() - iLastTime > UPDATETIME)
{
iSensorValue = analogRead(iAnalogPin); //Read analog value
if (iSensorValue != iLastValue)
{
if (abs(iSensorValue - iLastValue) > MINVALUECHANGE) // Check if change is high enough
{
Serial.println("Sensorvalue: " + String(iSensorValue));
snprintf(msg, MSG_BUFFER_SIZE, "%d", iSensorValue); //Convert message to char
mqttClient.publish(PubTopicPoti, msg); //Send to broker
iLastValue = iSensorValue;
}
}
iLastTime = millis();
  }
}

/*
=================================================================
Function: reconnectMQTT
Returns: void
Description: If there is no connection to MQTT, this function is
called. In addition, the desired topic is registered.
=================================================================
*/
void reconnectMQTT()
{
while (!mqttClient.connected())
{
writeAdvanceDiag("Login to MQTT-Broker", true);
if (mqttClient.connect(clientID.c_str()))
{
Serial.println("Connected to MQTT-Broker " + String(MQTT_BROKER));
}
else
{
writeAdvanceDiag("Failed with rc=" + String(mqttClient.state()), true);
Serial.println("Next MQTT-Connect in 3 sec");
delay(3000);
    }
  }
}

/*
=================================================================
Function: writeAdvanceDiag
Returns: void
Description: Writes advance msg to serial monitor, if
ADVANCEDIAG >= 1
msg: Message for the serial monitor
newLine: Message with linebreak (true)
=================================================================
*/
void writeAdvanceDiag(String msg, bool newLine)
{
if (bool(ADVANCEDIAG)) //Check if advance diag is enabled
{
if (newLine)
Serial.println(msg);
else
Serial.print(msg);
  }
}

code 6: Module NodeMCU Lua Amica V2

Pour vous aider à comprendre plus rapidement le code source, de nombreux commentaires et notes sur les fonctions ont été notés.  

Le prochain microcontrôleur, qui doit être construit ici et obtenir sa fonction, est le module ESP32 NodeMCU WLAN WiFi Development Board. Comme il dispose de deux interfaces I2C, il reçoit deux capteurs BME/BMP280 et prend en charge la cartographie des valeurs des potentiomètres analogiques. Là encore, le câblage est simple, voir la figure 5. 

Figure 5: Câblage de la carte de développement WLAN WiFi du module ESP32 NodeMCU

Le code pour ces tâches, voir le code 7, est un peu plus étendu. Tout d'abord, la connexion WLAN, la connexion MQTT et les capteurs sont initialisés. Dans la fonction de rappel, la valeur du potentiomètre analogique nouvellement reçue est mise en correspondance avec une valeur comprise entre 0 et 255 et renvoyée au courtier. Si l'un ou les deux BME/BMP280 présentent de nouvelles données de mesure qui diffèrent des valeurs précédentes, celles-ci sont également transmises au courtier.

Pour que le code source fonctionne pour vous, vous devez changer votre WLAN-ID et le mot de passe dans le code source.

//-----------------------------------------------------
// Example 3 ESP-NodeMCU with two BME transfer to
// mqtt-broker and mapping analog input
// Autor: Joern Weise
// License: GNU GPl 3.0
// Created: 20. Oct 2020
// Update: 25. Oct 2020
//-----------------------------------------------------
#include <Adafruit_BME280.h>
#include <Wire.h>
#include <WiFi.h>
#include <PubSubClient.h> //Lib for MQTT Pub and Sub

//Define WiFi-Settings
#ifndef STASSID
#define STASSID "ENTER-WIFI-HERE" //Enter Wfi-Name
#define STAPSK "ENTER-PASS-HERE" //Enter Passkey
#endif

#define ADVANCEDIAG 1

#define I2C_SDA1 21
#define I2C_SCL1 22
#define I2C_SDA2 17
#define I2C_SCL2 16
#define NEXTUPDATE 2000

//Objects for I2C and BME
TwoWire I2Cone = TwoWire(0);
TwoWire I2Ctwo = TwoWire(1);
Adafruit_BME280 bmeOne;
Adafruit_BME280 bmeTwo;
unsigned long lastTime = 0;

const char* MQTT_BROKER = "raspberrypi"; //Name of the mqtt broker
const char* PubTopicTempOne = "/Client/ESP32/TempOne"; //Topic first temp
const char* PubTopicTempTwo = "/Client/ESP32/TempTwo"; //Topic second temp
const char* PubTopicPresOne = "/Client/ESP32/PressOne"; //Topic first pressure
const char* PubTopicPresTwo = "/Client/ESP32/PressTwo"; //Topic second pressure
const char* PubTopicPotiMap = "/Client/ESP32/PotiMapValue"; //Topic second pressure
const char* SUBTOPIC = "/Client/ESP32/Poti/Value"; //Topic subscribe poti value
String clientID = "ESP-DevKit_1"; //Clientname for MQTT-Broker

int iLastTempOne,iLastTempTwo,iLastPressOne,iLastPressTwo;

//Create objects for mqtt
WiFiClient espClient;
PubSubClient mqttClient(espClient);

#define MSG_BUFFER_SIZE (50)
char msg[MSG_BUFFER_SIZE];

void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
Serial.println("BME280 test");
Serial.println("Init both I2C-Connections");
I2Cone.begin(I2C_SDA1, I2C_SCL1, 400000);
I2Ctwo.begin(I2C_SDA2, I2C_SCL2, 400000);
Serial.println("Make first BME talking to us");
bool bStatus;
//Init first sensor
bStatus = bmeOne.begin(0x76, &I2Cone);
if (!bStatus)
{
Serial.println("Could not find a valid BME280 - 1 sensor, check wiring!");
while (1);
}
else
Serial.println("Valid BME280 - 1 sensor!");

//Init second sensor
bStatus = bmeTwo.begin(0x76, &I2Ctwo);
if (!bStatus)
{
Serial.println("Could not find a valid BME280 - 2 sensor, check wiring!");
while (1);
}
else
Serial.println("Valid BME280 - 2 sensor!");
writeAdvanceDiag("Init Wifi", true);
setupWifi();
writeAdvanceDiag("Init Wifi - DONE", true);
writeAdvanceDiag("Set MQTT-Server", true);
mqttClient.setServer(MQTT_BROKER,1883);
writeAdvanceDiag("Set Callback-function", true);
mqttClient.setCallback(callback);
writeAdvanceDiag("Finish setup()-Function", true);
}

void loop() {
// put your main code here, to run repeatedly:
int iTempOne,iTempTwo,iPressOne,iPressTwo;
if(!mqttClient.connected())
reconnectMQTT();

mqttClient.loop();
//Check after "NEXTUPDATE" if values has changed
if(millis() - lastTime > NEXTUPDATE)
{
iTempOne = int(bmeOne.readTemperature()); //Get temp one
iTempTwo = int(bmeTwo.readTemperature()); //Get temp two
iPressOne = int(bmeOne.readPressure() / 100.0F); //Get press one
iPressTwo = int(bmeTwo.readPressure() / 100.0F); //get press two
if(iTempOne != iLastTempOne) //Check temp one changed and send
{
snprintf(msg,MSG_BUFFER_SIZE, "%1d",iTempOne); //Convert message to char
mqttClient.publish(PubTopicTempOne,msg,true); //Send to broker
writeAdvanceDiag("Send Temp one: " + String(iTempOne), true);
iLastTempOne = iTempOne;
}
if(iTempTwo != iLastTempTwo) //Check temp two changed and send
{
snprintf(msg,MSG_BUFFER_SIZE, "%1d",iTempTwo); //Convert message to char
mqttClient.publish(PubTopicTempTwo,msg,true); //Send to broker
writeAdvanceDiag("Send Temp two: " + String(iTempTwo), true);
iLastTempTwo = iTempTwo;
}
if(iPressOne != iLastPressOne) //Check pressure one changed and send
{
snprintf(msg,MSG_BUFFER_SIZE, "%1d",iPressOne); //Convert message to char
mqttClient.publish(PubTopicPresOne,msg,true); //Send to broker
writeAdvanceDiag("Send Press one: " + String(iPressOne), true);
iLastPressOne = iPressOne;
}
if(iPressTwo!= iLastPressTwo) //Check pressure two changed and send
{
snprintf(msg,MSG_BUFFER_SIZE, "%1d",iPressTwo); //Convert message to char
mqttClient.publish(PubTopicPresTwo,msg,true); //Send to broker
writeAdvanceDiag("Send Press two: " + String(iPressTwo), true);
iLastPressTwo = iPressTwo;
}
lastTime = millis();
  }
}

/*
* =================================================================
* Function: callback
* Returns: void
* Description: Will automatical called, if a subscribed topic
* has a new message
* topic: Returns the topic, from where a new msg comes from
* payload: The message from the topic
* length: Length of the msg, important to get conntent
* =================================================================
*/
void callback(char* topic, byte* payload, unsigned int length)
{
String stMessage = "";
writeAdvanceDiag("Message arrived from topic: " + String(topic), true);
writeAdvanceDiag("Message length: " + String(length), true);
for (int i = 0; i < length; i++)
stMessage += String((char)payload[i]);
writeAdvanceDiag("Message is: " + stMessage, true);
//Map value and send the mapped value to mqtt broker
int iValue,iMapValue;
iValue = stMessage.toInt();
iMapValue = map(iValue,0,1024,0,255);
snprintf(msg,MSG_BUFFER_SIZE, "%1d",iMapValue); //Convert message to char
writeAdvanceDiag("Send mapped PotiValue: " + String(iMapValue), true);
mqttClient.publish(PubTopicPotiMap,msg,true); //Send to broker
}
/*
* =================================================================
* Function: setupWifi
* Returns: void
* Description: Setup wifi to connect to network
* =================================================================
*/
void setupWifi()
{
Serial.println("Connection to: " + String(STASSID));
WiFi.mode(WIFI_STA);
WiFi.begin(STASSID, STAPSK);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}

/*
* =================================================================
* Function: writeAdvanceDiag
* Returns: void
* Description: Writes advance msg to serial monitor, if
* ADVANCEDIAG >= 1
* msg: Message for the serial monitor
* newLine: Message with linebreak (true)
* =================================================================
*/
void writeAdvanceDiag(String msg, bool newLine)
{
if(bool(ADVANCEDIAG)) //Check if advance diag is enabled
{
if(newLine)
Serial.println(msg);
else
Serial.print(msg);
}
}

/*
* =================================================================
* Function: reconnectMQTT
* Returns: void
* Description: If there is no connection to MQTT, this function is
* called. In addition, the desired topic is registered.
* =================================================================
*/
void reconnectMQTT()
{
while(!mqttClient.connected())
{
writeAdvanceDiag("Login to MQTT-Broker", true);
if(mqttClient.connect(clientID.c_str()))
{
Serial.println("Connected to MQTT-Broker " +String(MQTT_BROKER));
writeAdvanceDiag("Subscribe topic '" + String(SUBTOPIC)+ "'", true);
mqttClient.subscribe(SUBTOPIC,1); //Subscibe topic "SUBTOPIC"
}
else
{
writeAdvanceDiag("Failed with rc=" +String(mqttClient.state()), true);
Serial.println("Next MQTT-Connect in 3 sec");
delay(3000);
    }
  }
}

 Code 7: Carte de développement WLAN WiFi du module ESP32 NodeMCU

La dernière étape consiste à visualiser les données collectées. Pour ce faire, on utilise l'Arduino Uno avec son écran Ethernet, l'écran LCD I2C 20x04 et une LED monochrome.

Vous pouvez voir le câblage exact à la figure 6. N'oubliez pas que l'écran LCD I2C 20x04 nécessite une alimentation électrique de 5V.

Figure 6: Arduino Uno Ethernetshield pour la sortie

La programmation de l'Arduino Uno ne réserve pas non plus de grandes surprises. Tout d'abord, le réseau est initialisé et la connexion est vérifiée. Dans l'étape suivante, l'affichage et la connexion au courtier MQTT sont établis. Pour la sortie des données, la fonction callback() est pertinente, qui reçoit toutes les données du courtier et, en cas de modification d'une ou plusieurs valeurs, les affiche sur l'écran ou via la LED.

//-----------------------------------------------------
// Example 3 Arduino Uno Ehternetshiled to receive
// data from broker and show on LCD and LED
// Autor: Joern Weise
// License: GNU GPl 3.0
// Created: 25. Oct 2020
// Update: 26. Oct 2020
//-----------------------------------------------------
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <PubSubClient.h> //Lib for MQTT Pub and Sub
#include <SPI.h>
#include <Ethernet.h>

#define ADVANCEDIAG 1

LiquidCrystal_I2C lcd(0x27,20,4); // set the LCD address to 0x27 for a 16 chars and 2 line display
const int iAnalogOut = 6;
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //MAC-Adress for shield
IPAddress ip(192, 168, 178, 177); //Static IP-Adress for arduino
IPAddress myDns(192, 168, 178, 1); //IP-Adress router
char server[] = "www.google.com"; //Check if Arduino is online

String clientID = "Arduino_Uno"; //Clientname for MQTT-Broker
//Topics for subscribe
const char* MQTT_BROKER = "raspberrypi"; //Name of the mqtt broker
const char* SubTopicTempOne = "/Client/ESP32/TempOne"; //Topic first temp
const char* SubTopicTempTwo = "/Client/ESP32/TempTwo"; //Topic second temp
const char* SubTopicPresOne = "/Client/ESP32/PressOne"; //Topic first pressure
const char* SubTopicPresTwo = "/Client/ESP32/PressTwo"; //Topic second pressure
const char* SubTopicPotiMap = "/Client/ESP32/PotiMapValue"; //Topic mapped Poti

//Objects for ethernet-com
EthernetClient client;
PubSubClient mqttClient(client);

//Some vars for update
bool bUpdateDisplay = false;
bool bUpdatePWM = false;
int iLastTempOne,iLastTempTwo,iLastPressOne,iLastPressTwo,iLastPotiMap;

void setup() {
Serial.begin(115200);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.println("Arduino Uno - Monitor");
pinMode(iAnalogOut, OUTPUT);
Ethernet.init(10); // Most Arduino shields use digital Pin 10
lcd.init(); //Init LCD
lcd.backlight(); //Backlight on
lcd.clear(); //Clear old content
bUpdateDisplay = true;
Ethernet.begin(mac, ip); //Init ethernet-shild
// Check for Ethernet hardware present
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. :(");
while (true) {
delay(1); // do nothing, no point running without Ethernet hardware
}
}
//Check if there is com to router
while (Ethernet.linkStatus() == LinkOFF) {
Serial.println("Ethernet cable is not connected.");
delay(500);
}

// give the Ethernet shield a second to initialize:
delay(1000);
Serial.println("connecting...");

//Check if system is able to communicate
if (client.connect(server, 80)) {
Serial.print("connected to ");
Serial.println(client.remoteIP());
// Make a HTTP request:
client.println("GET /search?q=arduino HTTP/1.1");
client.println("Host: www.google.com");
client.println("Connection: close");
client.println();
} else {
// if you didn't get a connection to the server:
Serial.println("connection failed");
}
//Init MQTT
writeAdvanceDiag("Set MQTT-Server", true);
mqttClient.setServer(MQTT_BROKER,1883);
writeAdvanceDiag("Set Callback-function", true);
mqttClient.setCallback(callback);
writeAdvanceDiag("Finish setup()-Function", true);
}

void loop() {
// put your main code here, to run repeatedly:
if(!mqttClient.connected())
reconnectMQTT();

mqttClient.loop();

if(bUpdateDisplay)
{
UpdateDisplay();
bUpdateDisplay = false;
}
if(bUpdatePWM)
{
analogWrite(iAnalogOut, iLastPotiMap); //Write new analog value to LED-Pin
bUpdatePWM = false;
  }
}

/*
* =================================================================
* Function: writeAdvanceDiag
* Returns: void
* Description: Writes advance msg to serial monitor, if
* ADVANCEDIAG >= 1
* msg: Message for the serial monitor
* newLine: Message with linebreak (true)
* =================================================================
*/
void writeAdvanceDiag(String msg, bool newLine)
{
if(bool(ADVANCEDIAG)) //Check if advance diag is enabled
{
if(newLine)
Serial.println(msg);
else
Serial.print(msg);
  }
}

/*
* =================================================================
* Function: reconnectMQTT
* Returns: void
* Description: If there is no connection to MQTT, this function is
* called. In addition, the desired topic is registered.
* =================================================================
*/
void reconnectMQTT()
{
while(!mqttClient.connected())
{
writeAdvanceDiag("Login to MQTT-Broker", true);
if(mqttClient.connect(clientID.c_str()))
{
Serial.println("Connected to MQTT-Broker " +String(MQTT_BROKER));
writeAdvanceDiag("Subscribe topic '" + String(SubTopicTempOne)+ "'", true);
mqttClient.subscribe(SubTopicTempOne,1); //Subscibe topic "SubTopicTempOne"
writeAdvanceDiag("Subscribe topic '" + String(SubTopicTempTwo)+ "'", true);
mqttClient.subscribe(SubTopicTempTwo,1); //Subscibe topic "SubTopicTempTwo"
writeAdvanceDiag("Subscribe topic '" + String(SubTopicPresOne)+ "'", true);
mqttClient.subscribe(SubTopicPresOne,1); //Subscibe topic "SubTopicPresOne"
writeAdvanceDiag("Subscribe topic '" + String(SubTopicPresTwo)+ "'", true);
mqttClient.subscribe(SubTopicPresTwo,1); //Subscibe topic "SubTopicPresTwo"
writeAdvanceDiag("Subscribe topic '" + String(SubTopicPotiMap)+ "'", true);
mqttClient.subscribe(SubTopicPotiMap,1); //Subscibe topic "SubTopicPotiMap"
}
else
{
writeAdvanceDiag("Failed with rc=" +String(mqttClient.state()), true);
Serial.println("Next MQTT-Connect in 3 sec");
delay(3000);
    }
  }
}

/*
* =================================================================
* Function: callback
* Returns: void
* Description: Will automatical called, if a subscribed topic
* has a new message
* topic: Returns the topic, from where a new msg comes from
* payload: The message from the topic
* length: Length of the msg, important to get conntent
* =================================================================
*/
void callback(char* topic, byte* payload, unsigned int length)
{
String stMessage = "";
for (int i = 0; i < length; i++)
stMessage += String((char)payload[i]);

//Check if temp one has changed
if(String(topic) == "/Client/ESP32/TempOne")
{
if(iLastTempOne != stMessage.toInt())
{
iLastTempOne = stMessage.toInt();
bUpdateDisplay = true;
}
}
//Check if temp two has changed
if(String(topic) == "/Client/ESP32/TempTwo")
{
if(iLastTempTwo != stMessage.toInt())
{
iLastTempTwo = stMessage.toInt();
bUpdateDisplay = true;
}
}
//Check if pressure one has changed
if(String(topic) == "/Client/ESP32/PressOne")
{
if(iLastPressOne != stMessage.toInt())
{
iLastPressOne = stMessage.toInt();
bUpdateDisplay = true;
}
}
//Check is pressure two has changed
if(String(topic) == "/Client/ESP32/PressTwo")
{
if(iLastPressTwo != stMessage.toInt())
{
iLastPressTwo = stMessage.toInt();
bUpdateDisplay = true;
}
}
//Check if mapped poti value has changed
if(String(topic) == "/Client/ESP32/PotiMapValue")
{
if(iLastPotiMap != stMessage.toInt())
{
iLastPotiMap = stMessage.toInt();
bUpdatePWM = true;
    }
  }
}

/*
* =================================================================
* Function: UpdateDisplay
* Returns: void
* Description: Display new values on I2C-Display
* =================================================================
*/
void UpdateDisplay()
{
lcd.clear();
lcd.home();
lcd.print("Temp one[C]: ");
lcd.print(iLastTempOne);
lcd.setCursor(0,1);
lcd.print("Temp two[C]: ");
lcd.print(iLastTempTwo);
lcd.setCursor(0,2);
lcd.print("Press one[hPa]: ");
lcd.print(iLastPressOne);
lcd.setCursor(0,3);
lcd.print("Press two[hPa]: ");
lcd.print(iLastPressTwo);
}

Code 8: Arduino Uno zur Anzeige der MQTT-Daten

Si tout est correctement câblé et que le code est transféré à tous les microcontrôleurs, vous devriez rapidement voir un résultat sur l'écran LCD et la LED connectée. En tournant le potentiomètre, la luminosité de la LED change et la température et la pression actuelles des deux capteurs doivent être visualisées. La figure 7 montre à quoi cela peut ressembler.


Figure 7: Sortie des données des capteurs et luminosité des LED

Si vous voulez voir toutes les données de votre courtier, je peux vous recommander l'outil MQTT.fx. Avec elle, vous pouvez vous abonner et publier les données du courtier. Vous pouvez également essayer de connecter du matériel supplémentaire au microcontrôleur et envoyer les données au courtier. L'Arduino Uno peut, via une boucle, afficher les données en alternance.

Avec l'achèvement de ce poste, vous avez reçu à la fois les bases et un exemple pratique plus complexe. Dans la prochaine partie, les bases et l'exemple pratique seront utilisés pour contrôler un robot roulant.

Ce projet et d'autres sont disponibles sur GitHub à l'adresse https://github.com/M3taKn1ght/Blog-Repo

Projekte für anfängerRaspberry pi

3 commentaires

Jörn Weise

Jörn Weise

Hello jm1704,

thank you for the nice compliment. Of course, as bloggers, we are always interested in writing interesting and knowledgeable posts with az-delivery. Of course, the variety must not be missing and the reference to the products. Especially IoT is an interesting topic, but also holds great dangers if the knowledge is missing.
However, it must not be forgotten at this point that the topic is only dealt with on the surface! The broker is not secured and the Pi itself has no significant protection against manipulation. This makes it easy for criminal bodies to do damage when sensitive systems are controlled and regulated.
As my professor in college used to say, “You’ve now learned the basics, do something with it!”

Greetings Weise
-—-IN GERMAN———

Hallo jm1704,

danke für das nette Kompliment. Natürlich sind wir als Blogger immer daran interessiert mit az-delivery interessante und wissenswerte Beiträge zu schreiben. Natürlich darf die Abwechslung nicht fehlen und der Bezug den den Produkten nicht fehlen. Gerade IoT ist ein interessantes Thema, birgt aber auch große Gefahren, wenn das Wissen fehlt.
Man darf an dieser Stelle aber auch nicht vergessen, dass das Thema nur an der Oberfläche behandelt wird! Der Broker ist nicht gesichert und auch der Pi an sich hat keinen nennenswerten Schutz gegen Manipulation. Das macht es für kriminelle Organe einfach, Schaden anzurichten, wenn sensible Systeme kontrolliert und geregelt werden.
Wie mein Professor an der Uni immer sagte, “Sie haben nun die Grundlagen gelernt, machen Sie was drauß!”

Gruß Weise

Dieter Behm

Dieter Behm

Hallo und einen schönen guten Morgen.
Ich habe den ersten Blog glaube ich verstanden und mein Broker läuft auf dem Raspi. Jetzt mein Problem :
Der Node MCU (allerdings V3) lauft , bekommt nur die Verbindung zur FritzBox , gibt im seriellen Monitor keine Ip aus und folgendes erscheint:
-————-

ets Jan 8 2013,rst cause:2, boot mode:(3,6)

load 0×4010f000, len 3584, room 16
tail 0
chksum 0xb0
csum 0xb0
v2843a5ac
~ld
SerialMonitor enabled
Connection to: FRITZ!Box 7590 XQ

CUT HERE FOR EXCEPTION DECODER -——————

Exception (3):
epc1=0×40100718 epc2=0×00000000 epc3=0×00000000 excvaddr=0×4006eb39 depc=0×00000000

>>>stack>>>

Warum verbindet er sich nicht mit dem Broker
Ich hoffe Sie können einem Anfänger aufs Fahrrad helfen
Gruß
Dieter

jm1704

jm1704

Jörn,
Very good article on MQTT with explanatory notes and examples.
By this article you show the know how of AZ-Delivery and its team to use products offered for sale and their integrations.
Thanks

Laisser un commentaire

Tous les commentaires sont modérés avant d'être publiés

Articles de blog recommandés

  1. ESP32 jetzt über den Boardverwalter installieren - AZ-Delivery
  2. Internet-Radio mit dem ESP32 - UPDATE - AZ-Delivery
  3. Arduino IDE - Programmieren für Einsteiger - Teil 1 - AZ-Delivery
  4. ESP32 - das Multitalent - AZ-Delivery