==== U4: Receiving and handling MQTT messages ====
In this scenario, you will subscribe to the MQTT broker for MQTT messages and handle them. Most of the code yo will implement here is similar to the scenario U3, including LCD handling and connecting to the MQTT broker.
=== Target group ===
Undergraduate / Bachelor / Engineering Students
=== Prerequisites ===
We assume you already know how to:
* handle DHT sensor to read temperature and humidity,
* handle LCD screen to present information,
* connect to the existing WiFi network: ''internal.IOT'',
* additionally we will ask you to install and use an MQTT client of your choice. We suggest using [[https://github.com/eclipse/paho.mqtt-spy/releases|MQTT Spy]], but any MQTT client that will let you subscribe to the MQTT messages is OK. You connect it to the public IP of the MQTT broker (see below).
MQTT broker present in the ''internal.IOT'' network is also visible under public address. So whenever you subscribe to the MQTT message using VREL node that is connected to the ''internal.IOT'' network, you may publish to it using other devices connected to the ''internal.IOT'', i.e. other VREL node or if you're physically present at SUT in the IOT laboratory room 320, then you can connect your mobile and laptop to the ''internal.IOT'' network and use "internal" IP address. However, if you're in a remote location, you can access the same broker under public IP as stated in the node description. When you publish an MQTT message using public IP, it will be delivered to the subscribers in the private ''internal.IOT'' network as well. Mind, to access MQTT broker you need to use IP, user and password (applies to both public and private IPs of the MQTT Broker). Refer to the node documentation for the latest information.Note - information present in source code samples can be not up-to-date - remember to refer to the VREL node documentation for the latest information on IPs, users and passwords for both ''internal.IOT'' network access and for the MQTT Broker.
=== Scenario ===
In this scenario, you will connect to the infrastructure as a client (STA) and use the MQTT server to subscribe to the messages sent by some publisher. You will present received MQTT message on the LCD screen. We will implement a "remote display", where you handle remote requests to put contents on specific (one of four) LCD lines. MQTT payload should be a string then. We assume that the last character of the topic determines line number (between 0 and 3) so you need to construct MQTT topics as i.e. like ''/mydevice/someuniqueID/LCDcontent/0'' for the first line, ''/mydevice/someuniqueID/LCDcontent/1'' for the second one, and so on. Mind to update "someuniquieID" with your ID not to overlap with other students as you share single MQTT broker!
=== Result ===
You should be able to visualise payload of the subscribed messages on the LCD screen. Note, you handle only a limited number of messages that you subscribe to. When subscribing, do not go "too wide" using wildcards cause you won't be able to present all of them on the LCD display. Remember, you share MQTT broker with other users so tailor your subscriptions to those that apply to your device.
Subscribing to ''#'' is a really bad idea in production, live system. You will get a flood of messages!
=== Start ===
Define some identifiers to separate and update AP's SSID and passphrase easily. To format lines for the LCD, we suggest using a char buffer of 20 characters (one full line) and some 2-3 integers for iterators. Remember to declare the LCD control class in your code. You do not need to instantiate WiFi communication class - as you have only one interface here, it is singleton class you can refer directly using WiFi. Note - in this scenario, you connect only once. If your connection breaks, you will have no information about it here. To handle such situation, there are multiple solutions: you can check in the loop() if the connection is OK and in case it is down, you can call your re-connecting function, or you can use one of the asynchronous handlers, triggered automatically via the WiFi manager. To use the latter approach, refer to the ESP8266 WiFi implementation for Arduino documentation.
To handle incoming MQTT messages, you will need to implement a callback function that will be triggered, whenever a new message comes. You will receive only those messages that you've subscribed to. Note you need to check and decode message topics yourself.
=== Steps ===
Following steps do not present full code - you need to supply missing parts on your own! We do not present here how to connect to the WiFi AP. If you're in doubt, rever to the U1 scenario. Please refer to scenario B1, if you need a recall on how to handle LCD screen.
== Step 1 ==
Include all necessary libraries. We use ''PubSubClient'' library to contact MQTT broker. The minimum set here is:
#include
#include
#include
#include
#include
...
Declare some identifiers to let you easier handle necessary modifications and keep code clear:
#define wifi_ssid "internal.IOT"
#define wifi_password "IoTlab32768"
#define mqtt_server "192.168.90.5"
#define mqtt_user "vrel"
#define mqtt_password "vrel2018"
...
== Step 2 ==
Declare some identifiers, here MQTT messages' topic (one with wildcard, for four LCD lines) and MQTT client ID.
Use unique names for topics and for the MQTT client, do some random, use your MAC as part of it. It is important because MQTT broker identifies the client using its name. Also note, the set of MQTT topics that you use to send information to your LCD should be unique, otherwise some other users may "inject" information to your display.
Use topics that their last character is a line number (0 till 3) to easily suit ''lcd.setCursor(x,y)'' requirements, i.e. as below:
// MQTT messages
#define MQTTClientName "thisissomeuniqueclientname"
#define lcdTopicWithWildcard "/sut/mydevice/LCD/+"
Valid MQTT messages are:
* ''/sut/mydevice/LCD/0'' - delivers payload for line 1 of the LCD screen,
* ''/sut/mydevice/LCD/1'' - delivers payload for line 2 of the LCD screen, and so on.
== Step 3 ==
We will declare an MQTT handler callback function that is called whenever new MQTT message comes. Mind - only those messages that you've subscribed to are triggering this function, so you do not need to check the topic other than decoding its part to obtain, which LCD line number comes (this is the last character of the MQTT topic):
void mqttCallback(char* topic, byte* payload, unsigned int length) {
char nLine = topic[strlen(topic)-1];
n = atoi(&nLine);
if (n>=0 && n<=3)
{
lcd.setCursor(0,n);
lcd.print(" "); //clear one line
for (int i = 0; i < length; i++)
{
lcd.setCursor(i, n);
lcd.write((char)payload[i]);
}
}
}
Note - header of the function is interface declared by the mqtt PubSubClient library. ''topic'' is MQTT message topic, ''payload'' is a content and ''length'' is payload's length. As ''payload'' is binary, ''length'' is essential to know, how to handle ''payload'' contents.
== Step 4 ==
By the regular variables related to your WiFi ESP network client and so on, here you need to configure an object additionally to handle communication with MQTT broker. As you may use many brokers in your code, you need to instantiate it yourself, there is no singleton class as in case of the network interface. The constructor accepts ''WiFiClient'' as a parameter, so here you need explicitly declare one, to gain access to it:
// WiFi & MQTT
WiFiClient espClient;
PubSubClient client(espClient);
Configure your network client, remember to set ''ESP.mode(WIFI_SFA)''. Once you're done with starting, initialise MQTT broker client. Once you're done with connecting to the WiFi, configure your MQTT PubSubClient and give it a handler function to let it be called, whenever a new message comes (here ''mqttCallback'':
client.setServer(mqtt_server, 1883);
client.setCallback(mqttCallback);
You can call it i.e. in the ''setup()'' function, after a successful connection to the WiFi AP but before you execute step 4.
== Step 5 ==
If your WiFi client is working, your MQTT client is configured it is time to connect to the MQTT broker. It is a good idea to have this procedure separated to call as needed if your connection with MQTT broker goes down for any reason. Here we encapsulate it in the ''reconnect()'' function where we also subscribe to the number of topics delivering LCD content in the payload:
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
if (client.connect(MQTTClientName, mqtt_user, mqtt_password)) {
client.subscribe(lcdTopicWithWildcard);
} else {
// Wait 5 seconds before retrying
delay(5000);
}
}
lcd.setCursor(0,1);
lcd.print("MQTT Connected!");
}
== Step 6 ==
Finally your ''loop()'' may look like this:
void loop()
{
if (!client.connected()) {
reconnect();
}
client.loop();
}
The ''client.loop()'' is to handle incoming MQTT messages.
=== Result validation ===
Compile and run your code then wait till it connects to the network and MQTT broker. Once it is connected, use your favourite MQTT client to connect to the MQTT broker and issue a number of MQTT messages that their topics are:
* ''/sut/mydevice/LCD/0''
* ''/sut/mydevice/LCD/1''
* ''/sut/mydevice/LCD/2''
* ''/sut/mydevice/LCD/3''
and the payload is a text you want to display on your LCD. Note, your display is 20 characters each line only so keep your payload within those limits.