Lab11 : Working with MQTT
In this lab, we will connect to a MQTT broker using various clients: our development machine, from Android and from Arduino.
1. Installing a MQTT Client (and Broker)
There are several implementations in different languages of the MQTT protocol, both for the server (broker) and client side. We will use mosquitto, an open-source implementation of MQTT. It includes both the broker and client.
- Download & Install mosquitto for your platform : https://mosquitto.org/download/
Let's try to use the MQTT client to connect to an existing MQTT broker
- Broker address :
test.mosquitto.org
- Subscribe to topic "lab11/<<yourname>>" by running
mosquitto_sub -t "lab11/<<yourname>>" -h <<BROKER_ADDRESS>>
- The program will keep running and print out any messages published to that topic.
- Publish a message to the topic by opening a second terminal, and running:
mosquitto_pub -h <<BROKER_ADDRESS>> -t "lab11/<<yourname>>" -m "Hello, MQTT!"
The message should appear in the 1st terminal. Try also subscribing to the topic "lab11/#", the "#" wildcard will subscribe to all subtopics under "hello", so if anybody else was publishing messages simultaneously, you'd see them too.
For more information about MQTT topics, this material has a good overview.
Note: By default, installing mosquitto will also start running the broker on your machine, on the default MQTT port 1883. You can try the broker running in your machine by substituting the IP with "localhost", for example.
2. MQTT Client on Android
2.1 Setting up library
- Create a new (Blank Empty Activity) Android Studio Project.
Let's add the Eclipse Paho MQTT Client Library to our project:
- in Project .gradle file:
allprojects { repositories { google() jcenter() maven { url "https://repo.eclipse.org/content/repositories/paho-snapshots/" } } }
- in Module .gradle file:
// MQTT client implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.1' implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1' implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0' // needed to resolve java.lang.NoClassDefFoundError: Failed resolution of: Landroidx/localbroadcastmanager/content/LocalBroadcastManager;
- In AndroidManifest.xml:
‹!-- Mqtt Service --› <service android:name="org.eclipse.paho.android.service.MqttService" />
- MQTT client also needs these permissions:
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
2.2 Logging MQTT messages
- Add the code from here to your Activity.
- Study the code, run it, verify that it can connect to the broker.
- Update the Kotlin code so that once the MQTT client is connected, it subscribes to topic "lab11/<<XXX>>" , where <<XXX>> is some unique topic name for you (e.g. your matricle number if you don't mind that somebody sees it)
Toast.makeText(applicationContext, "MQTT Connected, subscribing...", Toast.LENGTH_SHORT).show() mqttClient.subscribe("<<<topic>>>", 0) // 0 - is the QoS value
- Publish some message to the correct topic and verify you can see it being logged.
2.3 Drawing a plot with the data
Let's re-create the Arduino IDE plotter in Android, using a custom View. When creating a custom View, we can control the entire 2D drawing of the UI component down to pixel precision (if necessary), instead of using one of the existing View Widgets (e.g. Button, Spinner, etc) or Layouts.
We will use Android custom drawing capabilities to draw a line graph showing values sent to a MQTT topic.
- Create a new class that extends View, and overrides the constructor with signature (c: Context, atrs: AttributeSet)
class LinePlot(context: Context, attrs: AttributeSet) : View(context, attrs)
- Override onDraw(canvas: Canvas) method
- Using the Canvas object, we can actually draw something onto the screen
- Canvas provides various useful methods such as drawCircle, drawRectangle, drawText, drawLine, drawBitmap, etc.
- To draw something on the canvas, you must specify the coordinates and a Paint object which defines the drawing style (e.g. brush color, thickness).
Now, you place your custom View into a Layout XML, e.g. activity_main.xml:
<ee.ut.cs.lab11.LinePlot android:id="@+id/line_graph" android:layout_width="0dp" android:layout_height="0dp" ... ></ee.ut.cs.lab11.LinePlot>
Note, it's possible to define your XML attributes for your custom View (e.g. LinearLayout had a orientation attribute), see here for more info.
- Add the following to your custom View:
private val bluePaint = Paint().apply { color = Color.BLUE strokeWidth = 8f } // Sample dataset: var dataSet = mutableListOf(3.3f,4.0f,4.5f,6.6f,4.6f,2.6f,1.0f,1.5f,6.7f) var plotYMax: Float = Collections.max(dataSet) var plotYMin: Float = Collections.min(dataSet) /** Helper functions which scale the numeric values to screen dimensions */ private fun scaledX(value : Float) : Float { return (value / dataSet.size) * width } // Y- value is also reversed because 0,0 coordinate is in top-left corner in Canvas private fun scaledY(value : Float) : Float { return height - (((value - plotYMin) / (plotYMax-plotYMin)) * height) } override fun onDraw(canvas: Canvas?) { super.onDraw(canvas) dataSet.forEachIndexed { i, value -> canvas?.drawPoint(scaledX(i.toFloat()), scaledY(value), bluePaint ) } }
Try to run the application. The code creates some sample data and draws a point for each element in the dataSet collection. Note that the numeric values are adjusted based on screen height/width and dataset max value.
- Now, inside onDraw(), write code which would also draw lines connecting each pair of points in the dataset (using canvas.drawLine(x1,y1,x2,y2,paint) ). Try using a different colour paint.
- First try to get the line drawing to work by yourself, but if you need an example, see here
2.4 Joining the MQTT client and plotter
Add this function to your View:
fun addData(value : Float){ dataSet.add(value) if (dataSet.size > 20 ){ dataSet.removeAt(0) // remove oldest (FIFO) } // Update Max, Min plotYMax = if (value > plotYMax) value else plotYMax plotYMin = if (value < plotYMin) value else plotYMin // Force UI refresh: invalidate() }
- Update the MQTT client code so that it invokes the addData function of the custom View when a message arrives.
- You can refer to the View from Activity by its ID and thus access its methods.
- Test that it works by publishing some messages with mosquitto_pub
Arduino MQTT Client
Let's install the library PubSubClient
- Open Tools -> Manage Libraries and find PubSubClient by Nick O'Leary
Open the following Arduino code . This code:
- Establishes a connection to a pre-configured WiFi access point (in setup() )
- Using the WiFi library,
#include <WiFi.h>
- Using the WiFi library,
- Once the WiFi connection is established, it tries to connect to the MQTT broker (end of setup() )
- In the main loop(), every 100ms it reads an analog sensor value and publishes it as a MQTT message
- Every iteration of loop(), mqttClient.loop() is also called, this allows the client to process incoming messages and maintain its connection to the server.
Modify the code accordingly (update WiFi & MQTT Credentials and MQTT topic) and run it. Make sure all your devices are in the same network. Depending on your setup, a firewall may be blocking port 1883 (e.g. when creating a hotspot from Windows).
Other information
It is possible to configure authentication and SSL encryption for brokers, but we did not consider those options in this lab.