Mit MQTT einen Roboter steuern - [Teil 2]

In the first part of this blog series you have learned everything you need to know about MQTT. In addition, a server, the so-called broker, was set up on a Raspberry Pi and with command line input messages were sent to the broker and also received. 

In this part, the structure becomes somewhat more complex, whereby the data is sent and received by various NodeMCUs and an Arduino Uno with Ethernet shield, the so-called clients. 

The background will be that you can operate an MQTT client on the most common micro controllers distributed by AZ-Delivery. The hardware requirements show which parts are required to complete the series. Some components are only needed for this blog part, but are used in various other blog posts by AZ-Delivery.

Hardware requirement 

For this blog post you only need a few components, see Table 1.

Number Component
1 Raspberry Pi (required)
1 Suitable Power Supply
1 NodeMCU Lua Amica Module V2 ESP8266 ESP-12F(necessary)
1 ESP32 NodeMCU Module WLAN WiFi Development Board (necessary)
1 Micro Controller Board with USB cable (optional)
1 Ethernet Shield W5100 (optional)
1 Resistors Assortment (optional)
2 Potentiometer (necessary)
1 LCD Display 4x20 Characters Blue I2C (optional)
1 LED monochrome (optional)

Table 1: Required hardware

Remember with the Pi that you need a microSD card in addition to the above hardware. To do this, you should install the Raspberry Pi OS (formerly known as Raspbian) as an image on the map.

Software requirement

The required Software for this project is manageable:

How to install libraries via library management is described in https://www.az-delivery.de/blogs/azdelivery-blog-fur-arduino-und-raspberry-pi/arduino-ide-programmieren-fuer-einsteiger-teil-1 Library Management section, described in more detail.

Basic requirement

For this and the following blog posts to work, you need a Raspberry Pi with an installed MQTT broker. You can read exactly how this works and which commands you have to enter for it in part 1 of this blog series. Please also check whether the broker was started from the Raspberry Pi when booting up. To do this, issue the command Code 1 enter the terminal.

sudo service mosquitto status

Code 1: Query in the terminal whether mosquitto has started

Should the output be a active (running) show, see Figure 1, no further commands are necessary.

Figure 1: Status of mosquitto Broker in the Terminal

If the output differs, please check again whether mosquitto has been installed correctly and whether the service has been correctly integrated into the autostart. The detailed instructions can be found in Part 1.

Simple MQTT-Subscribe with NodeMCU Lua Amica Module V2 ESP8266

A note at this point: For the example described here, the NodeMCU Lua Amica module V2 ESP8266 is used.

In the first example, see Code 2, the NodeMCU should establish a connection with the MQTT broker and receive all data. This also gives you a direct insight into how general communication with the PubSubClient library works. To help you better understand the source code, each function has been described in detail.

In order for the source code to work for you, you need to change your Wi-Fi ID and password in the source code.


//-----------------------------------------------------
// Example 1 NodeMCU with MQTT
// Author: Joern Weise
// License: GNU GPl 3.0
// Created: 20. Oct 2020
// Update: 25. Oct 2020
//-----------------------------------------------------
#include //Lib for Wifi
#include //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 baud rate 115200
delay(50);
writeAdvanceDiag ("SerialMonitor enabled", true);
setup WiFi();
writeAdvanceDiag("Set MQTT Server", true);
mqttClient.setServer(MQTT_BROKER,1883);
writeAdvanceDiag("Set Callback function", true);
mqttClient.setCallback(callback);
writeAdvanceDiag("Finish setup () Function", true);
}

/*
* =================================================================
* Function: setup WiFi
* Returns: void
* Description: Setup wifi to connect to network
* =================================================================
*/
void setup WiFi()
{
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: Example NodeMCU Receive MQTT-Data


At the beginning of the code, an object espClient is created and passed to the also created object MqttClient. This allows a general communication to be established with an active WiFi connection.

If the NodeMCU starts, the WiFi is first set up, here in the function setupWifi (), and then the connection to the MQTT broker is established in the setup () function.

Using " MqttClient.setServer(MQTT_BROKER,1883);" the address and port is configured by the MQTT broker and via "MqttClient.setCallback (callback);“ determines the function for processing the received data, more on this below.

The loop () function does exactly two things:

1. Check whether the NodeMCU is connected to the MQTT broker. If this is not the case, the function reconnectMQTT() is called.

2. The function mqttClient.loop () is called to communicate with MQTT.

The first step is logging in to the MQTT broker with a unique client name. You can freely determine this via the global variable "ClientID". If the registration was successful, the desired topic will be subscribed in the next step. This is done by

"MqttClient.subscribe (SUBTOPIC, 1)”

where in this example SUBTOPIC subscribes to the topic "/#" (all topics) and the "1" stands for a QoS greater than 0. This provides the NodeMCU with messages from the broker as soon as there are changes to a topic.

In the event of an error, the NodeMCU tries to reconnect with the broker every 3 seconds.

This brings us to the point where the NodeMCU receives data from the broker if the NodeMCU is connected. If there are new messages, they are evaluated using the callback() function. Three parameters are passed to the callback () function:

  1. topic: Which displays the absolute topic path of the received message
  2. payload: The message, which is in byte format
  3. length: The length of the message

With these three information you can evaluate what you need the selected topic for. In this case, we output the topic via the serial monitor. Immediately afterwards, we convert the byte message into a string using the for loop shown in Code 3. This gives you a normally readable message.

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

Code 3: Code fragment to convert MQTT message to String

This message in string format is also output via the serial monitor

You can see in Figure 2 that this sketch also works so smoothly.

Figure 2: Message received from MQTT broker

Please bear in mind in advance of your messages that our German umlauts do not work. These are then represented by other characters.

Simples MQTT-Subscribe with ESP32 NodeMCU

Note at this point, for the following example, the ESP32 NodeMCU Module WLAN WiFi Development Board is used.

In the example of Code 4, the ESP32 NodeMCU should send data to the MQTT broker. Since it is very simple, we start by sending the runtime of the ESP32 NodeMCU Module WLAN WiFi Development Board to the broker every two seconds.

Also in this example, there are many comments in the code to make it easier for you to understand. In order for the source code to work for you, you need to change your Wi-Fi ID and password in the source code.


//-----------------------------------------------------
// Example 2 ESP-NodeMCU with MQTT
// Author: Joern Weise
// License: GNU GPl 3.0
// Created: 20. Oct 2020
// Update: 25. Oct 2020
//-----------------------------------------------------
# include
#include //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 baud rate 115200
delay(50);
writeAdvanceDiag ("SerialMonitor enabled", true);
setup WiFi();
writeAdvanceDiag("Set MQTT Server", true);
mqttClient.setServer(MQTT_BROKER,1883);
writeAdvanceDiag("Finish setup () Function", true);
}

/*
* =================================================================
* Function: setup WiFi
* Returns: void
* Description: Setup wifi to connect to network
* =================================================================
*/
void setup WiFi()
{
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: Receive ESP32 MQTT data

As in the first example, an object espClient is created and passed to the also created object MqttClient. When starting the ESP32 NodeMCU Module WLAN WiFi Development Board, the WiFi is set up via the setupWiFi() function. Directly after the setup () function, the MQTT broker is called "MqttClient.setServer (MQTT_BROKER, 1883); " preconfigured. A callback () function is not needed at this point, because no data needs to be received and processed.

The loop () function is therefore almost identical to the first example, with the exception of the line Code 5 show.


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: Code Fragment for Sending the active time

This part sends the current runtime to the topic "/Client/ESP32 " every 2 seconds. Figure 3 shows that this example works. In this case, MQTT shows.fx the news from the MQTT broker.

Figure 3: MQTTfx shows sent data from ESP32 NodeMCU

MQTT example with multiple clients


In the last examples, also in the first blog post, we have always assumed a broker and a client. However, the reality of such MQTT projects often looks different.  In practice, there are usually clients that supply data from sensors, clients that receive and process the pushed data, and last but not least, clients that execute.


For the example to come, a NodeMCU Lua Amica module V2 will monitor a potentiometer value and send the new value to the broker if it changes.

An ESP32 NodeMCU Module WLAN WiFi Development Board subscribes to this data and maps it from 0-1024 to 0-255, it also publishes the temperature and air pressure from two BME/BMP280 sensors.

This collected data subscribes an Arduino Uno with Ethernet shield, LED and I2C LCD display and visualizes the data. The LED provides the mapped signal from the potentiometer and the display shows the temperature and air pressure of the two sensors.


We start with the NodeMCU Lua Amica module V2 and the potentiometer. Wire both according to Figure 4. This part is simple as there are only three wires to be connected.

 

Figure 4: Wiring NodeMCU Lua Amica Module V2 with Poti

The code for the NodeMCU Lua Amica module V2 is kept slim and basically includes the initialization of the WLAN and the connection to MQTT, as well as the data transmission of the analog potentiometer values, see Code 6. In order for the source code to run with you, you have to change your WLAN ID and password in the source code.

//-----------------------------------------------------
// Example 3 NodeMCU with Poti transfer to mqtt-broker
// Author: Joern Weise
// License: GNU GPl 3.0
// Created: 20. Oct 2020
// Update: 25. Oct 2020
//-----------------------------------------------------
#include //Lib for Wifi
#include //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 MinValue change 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 baud rate 115200
delay(50);
writeAdvanceDiag ("SerialMonitor enabled", true);
setup WiFi();
writeAdvanceDiag("Set MQTT Server", true);
mqttClient.setServer(MQTT_BROKER, 1883);
writeAdvanceDiag("Set Callback function", true);
writeAdvanceDiag("Finish setup () Function", true);
}

/*
=================================================================
Function: setup WiFi
Returns: void
Description: Setup wifi to connect to network
=================================================================
*/
void setup WiFi()
{
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) > MinValue change) // 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: NodeMCU Lua Amica Module V2

To help you understand the source code faster, many comments and hints about the functions have been noted.

The next MicroController to be built up here and get its function is the ESP32 NodeMCU Module WLAN WiFi Development Board. Since it has two I2C interfaces, it receives two BME/BMP280 sensors and takes over the mapping of the analog potentiometer values. Here, too, the wiring is simple, see Figure 5.

Figure 5: Wiring ESP32 NodeMCU Module WLAN WiFi Development Board

The code for these tasks, see Code 7, is somewhat more extensive. First, the WLAN connection, the MQTT connection and the sensors are initialized. In the callback function, the newly received analog potentiometer value is mapped to a value between 0 and 255 and sent back to the broker. If one or both BME/BMP280 has new measurement data that deviate from the previous values, these will also be transmitted to the broker.

In order for the source code to work for you, you need to change your Wi-Fi ID and password in the source code.

//-----------------------------------------------------
// Example 3 ESP-NodeMCU with two BME transfer to
// mqtt-broker and mapping analog input
// Author: Joern Weise
// License: GNU GPl 3.0
// Created: 20. Oct 2020
// Update: 25. Oct 2020
//-----------------------------------------------------
#include
#include
# include
#include //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);
setup WiFi();
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.0 F); / / Get press one
iPressTwo = int(bmeTwo.readPressure() / 100.0 F); / / 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 (itemTwo != 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 automatic 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 potentiometer value:" + String(iMapValue), true);
mqttClient.publish(PubTopicPotiMap,msg,true); //Send to broker
}

/*
* =================================================================
* Function: setup WiFi
* Returns: void
* Description: Setup wifi to connect to network
* =================================================================
*/
void setup WiFi()
{
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: ESP32 NodeMCU Module WLAN WiFi Development Board

In the last step, the collected data should be visualized. For this purpose, the Arduino Uno with attached Ethernet shield, the 20x04 LCD I2C display, and a monochrome LED are used.

The exact wiring can be found in Figure 6 see. Keep in mind that the 20x04 LCD I2C display requires a power supply of 5V.

Figure 6: Uno Microcontroller Ethernet Shield for output

There are no big surprises in the programming of the Arduino Uno either. Initially, the network is initialized, and the connection is checked. In the next step, the display and the connection to the MQTT broker are established. The callback () function is relevant for the output of the data, which receives all data from the broker and displays it on the display or via the LED if one or more values are changed.

//-----------------------------------------------------
// Example 3 Arduino Uno Ehternetshiled to receive
// data from broker and show on LCD and LED
// Author: Joern Weise
// License: GNU GPl 3.0
// Created: 25. Oct 2020
// Update: 26. Oct 2020
//-----------------------------------------------------
#include
#include
#include //Lib for MQTT Pub and Sub
#include
# include

#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-Address for shield
IPAddress ip(192, 168, 178, 177); //Static IP address for arduino
IPAddress myDns(192, 168, 178, 1); //IP Address 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 shield
// Check for Ethernet hardware present
if (Ethernet.hardware status() == Ethernet ohardware) {
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 automatic 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 to display the MQTT data

If everything is wired correctly and the code is transferred to all microcontrollers, you should quickly see a result on the LCD display and the connected LED. When turning the potentiometer, the brightness of the LED is changed and the current temperature and pressure of both sensors should be visualized. How this can look, shows Figure 7.

Figure 7: Output of sensor data and LED brightness

Do you want to see all the data of your broker, I can recommend the tool MQTT.fx. This allows you to subscribe and publish data from the broker. Try to connect more hardware to the microcontrollers and send the data to the broker. The Arduino Uno can display the data alternately via a loop.

By completing this post, you have received both the basics and a more complex practical example. In the next part, the basics and the practical example are used to control a rolling robot

This and other projects can be found on GitHub at https://github.com/M3taKn1ght/Blog-Repo

Projects for beginnersRaspberry pi

3 comments

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

Leave a comment

All comments are moderated before being published

Recommended blog posts

  1. Install ESP32 now from the board manager
  2. Lüftersteuerung Raspberry Pi
  3. Arduino IDE - Programmieren für Einsteiger - Teil 1
  4. ESP32 - das Multitalent
  5. OTA - Over the Air - ESP programming via WLAN