This is a project made for the Assignment2 of Internet of Things class at the Sapienza University of Rome.
This work is an extension of the one already presented in:
Google Cloud-based IoT system with MQTT
For this reason, I strongly recommend you to see first the work that has been done in the previous tutorial and then navigate through these new topics.
IntroductionIn this assignment, I've created a cloud-based IoT system that collects information from a set of virtual environmental sensors based in RIOT-OS using MQTT-SN and MQTT protocols.
MQTT-SN is managed using Mosquitto RSMB while MQTT with the cloud-based backend of Google IoT Core.
The following sections are a hands-on tutorial on how to setup and run the system.
TECHNOLOGY USED: Python, C, RIOT-OS, MQTT, MQTT-SN.
As you can see in the picture above our purpose is to make a connection between the RIOT based devices and an MQTT-SN broker and then between the MQTT-SN broker and the Google broker. Unfortunately, Google doesn't support a direct connection with RSMB, for this reason, I've created a Python Bridge that receives the data from RSMB and sends them to the Google MQTT broker.
Set Up Google Cloud PlatformWe have to do some changes in the setting of the Google platform made in the previous tutorial. Simply we have to add a new device to our existing registry.
Remember you must generate also a certificate and a private key, and insert them in the device folder, to authenticate it when the connection starts. To do this step in the proper way you can see this very simple guide provided by Google: Quickstart - Google guide. In particular, pay attention at the section Generate a device key pair and Add a device to the registry.
Set up RSMB (mqtt-sn) brokerFirst of all, we have to setup the Mosquitto Real Simple Message Broker (RSMB):
1. Get the RSMB here: https://github.com/eclipse/mosquitto.rsmb
git clone https://github.com/eclipse/mosquitto.rsmb.git
2. Go into the source folder and build the RSMB
cd mosquitto.rsmb/rsmb/src
make
3. Create a config.conf file. In this case, we run the RSMB using port 1885 for MQTT-SN and 1886 for MQTT and enabling IPv6, so save the following into config.conf:
# add some debug output
trace_output protocol
# listen for MQTT-SN traffic on UDP port 1885
listener 1885 INADDR_ANY mqtts
ipv6 true
# listen to MQTT connections on tcp port 1886
listener 1886 INADDR_ANY
ipv6 true
4. Start the broker:
./broker_mqtts config.conf
You should have this view:
Before to start with the main steps of the device's configuration, I recommend you to install all the dependencies for RIOT-OS following this guide:
https://github.com/RIOT-OS/Tutorials/blob/master/README.md
Devices setup
Since the RIOT application is written in ANSI-C this time I built the devices in C language and I created an application based on the emCute module (MQTT-SN). Unlike the previous tutorial I merged all the virtual devices within the same C file, however, for brevity, I'll only show you how to generate the values for the temperature sensor. Let's start by seeing how to retrieve the current date in C.
#include <time.h>
/* Return current seconds since January 1, 1970 */
static double getTime(void){
time_t timer;
struct tm y2k = {0};
double seconds;
y2k.tm_hour = 0; y2k.tm_min = 0; y2k.tm_sec = 0;
y2k.tm_year = 70; y2k.tm_mon = 0; y2k.tm_mday = 1;
time(&timer); /* get current time; same as: timer = time(NULL) */
seconds = difftime(timer,mktime(&y2k));
return seconds;
}
As for NodeJs, the date that will refer to our device will be the seconds elapsed from January 1, 1970 until today. Now we have to build our payload:
deviceID ; temperature ; date
#include <string.h>
static char* TemperaturePayload(char* id){
int upper = 50;
int lower = -50;
srand(time(NULL));
int num = (rand() % (upper - lower + 1)) + lower;
char temperature[8];
sprintf(temperature, "%d °C", num);
char time[10];
sprintf(time, "%.f", getTime());
char* payload = malloc(sizeof(char)*80);
strcpy(payload, id);
strcat(payload, ";");
strcat(payload, temperature);
strcat(payload, ";");
strcat(payload, time);
return payload;
}
I recommend you to use the function srand(time(NULL)) to generate different seeds at every run. This allows us to have always random values.
/* Publish random values periodically (5 sec) */
static int cmd_pub(int argc, char **argv)
{
emcute_topic_t t;
unsigned flags = EMCUTE_QOS_0;
char* id1 = "riot_device1";
char* id2 = "riot_device2";
char* id3 = "riot_device3";
char* id4 = "riot_device4";
if (argc < 2) {
printf("usage: %s <topic name>\n", argv[0]);
return 1;
}
printf("pub with topic: %s and flags 0x%02x\n", argv[1], (int)flags);
/* step 1: get topic id */
t.name = argv[1];
if (emcute_reg(&t) != EMCUTE_OK) {
puts("error: unable to obtain topic ID");
return 1;
}
/* step 2: publish random data periodically */
while(1){
char* temperature = TemperaturePayload(id1);
char* humidity = humidityPayload(id2);
char* wdirection = windDirectionPayload(id3);
char* rheight = rainHeightPayload(id4);
/* TEMPERATURE */
if (emcute_pub(&t,temperature,strlen(temperature), flags) != EMCUTE_OK){
printf("err: unable to publish data to '%s [%i]'\n",t.name,(int)t.id);
return 1;
}
xtimer_sleep(2);
/* HUMIDITY */
if (emcute_pub(&t,humidity,strlen(humidity),flags) != EMCUTE_OK) {
printf("err: unable to publish data to '%s [%i]'\n",t.name,(int)t.id);
return 1;
}
xtimer_sleep(2);
/* WIND DIRECTION */
if (emcute_pub(&t,wdirection,strlen(wdirection),flags) != EMCUTE_OK) {
printf("err: unable to publish data to '%s [%i]'\n",t.name,(int)t.id);
return 1;
}
xtimer_sleep(2);
/* RAIN HEIGHT */
if (emcute_pub(&t,rheight,strlen(rheight),flags) != EMCUTE_OK) {
printf("err: unable to publish data to '%s [%i]'\n",t.name,(int)t.id);
return 1;
}
xtimer_sleep(5);
}
return 0;
}
This is the function that handles the command periodic_pub (we'll see it in the next section). It set the topic inserted from the shell and then starts the process of publishing periodic random data on the RSMB broker with the module emcute. You get this code in my GitHub repository (at the end of the page) at the folder /RIOT_devices
. As you we'll see there are many other functions inside the file main.c, this because I've, simply, extended the emcute_mqttsn example that you can find in the main RIOT repository at this link:
https://github.com/RIOT-OS/RIOT/tree/master/examples/emcute_mqttsn
Run the devices
Before launching our sensors we must necessarily carry out some steps to make everything work.
1. Download RIOT-OS:
git clone https://github.com/RIOT-OS/RIOT.git
2. Access RIOT folder and setup tap and tapbr network devices using RIOT's tapsetup script:
sudo ./dist/tools/tapsetup/tapsetup
3. Assign a site-global prefix to the tapbr0 interface (the name could be different on OSX etc):
sudo ip a a fec0:affe::1/64 dev tapbr0
4. Insert our application inside the RIOT folder. You should be able to access this path:
cd /RIOT/RIOT_devices
5. Open a native instance:
make all term BOARD=native
6. Assign a site-global address with the same prefix within the RIOT:
ifconfig 5 add fec0:affe::99
Now we are ready to use our application and sends data to the RSMB broker (you can type help to see the available commands).
- To connect to the broker, use the con command:
con fec0:affe::1 1885
- To subscribe to the topic, use the sub command with the topic name as a parameter:
sub riot_device
- Now to start the periodic sending process, use the periodic_pub command followed by the topic name:
periodic_pub riot_device
If everything goes well you have a situation like this:
At this point we've realized the first part of our system:
To send the data at the MQTT Google broker we have to establish a connection. I've tried several attempts to connect directly RSMB and Google but unfortunately, I discovered that Google doesn't support the direct bridge from Mosquitto broker. The main reason is that it requires a lot of parameters, certificates and authentication codes that RSMB is unable to send.
Ok, so what we can do is to build from scratch an application that acts as a bridge. To do this I took an RSMB client and an MQTT client and joining them I've created a device that receives data via MQTT-SN and sends it to Google via MQTT. We will see in this section this experiment.
First of all, goes to the main folder of my project and open the MQTTSNbridge. Inside there are five python files:
MQTTSNclient.py
MQTTSNregister.py
MQTTSN.py MQTTSNinternal.py
GoogleMQTTClient.py
The first four are the same files of the folder MQTTSNclient
that you can find in mosquitto.rsmb/rsmb/src
while the last file is a simple python Class that implements the main methods to interact with Google MQTT bridge. I took the latter directly from the google guide and I simplify for our purpose:
Publishing over MQTT bridge - Google guide
But let's see the code:
import MQTTSN, socket, time, MQTTSNinternal, thread, types, sys, struct
import GoogleMQTTClient
import jwt
import paho.mqtt.client as mqtt
import datetime
import ssl
# Parameters to authenticate the device on google
project_id = 'your-project_id'
cloud_region = 'your-cloud_region'
registry_id = 'your-registry_id'
device_id = 'your-device_id'
private_key_file = 'your-private_key_file'
algorithm = 'RS256'
ca_certs = './roots.pem'
mqtt_bridge_hostname = 'mqtt.googleapis.com'
mqtt_bridge_port = '8883'
message_type = 'event'
sub_topic = 'events' if message_type == 'event' else 'state'
mqtt_topic = '/devices/{}/{}'.format(device_id, sub_topic)
print mqtt_topic
# Create the Google client with parameters */
client = GoogleMQTTClient.get_client(
project_id, cloud_region, registry_id,
device_id, private_key_file, algorithm,
ca_certs, mqtt_bridge_hostname, mqtt_bridge_port)
After importing the required libraries, I fill in in the fields the main parameters needed to establish a Google connection. Finally I Create a Google MQTT client with the method provided by the GoogleMQTTClient.py
class.
def get_client(
project_id, cloud_region, registry_id, device_id, private_key_file,
algorithm, ca_certs, mqtt_bridge_hostname, mqtt_bridge_port):
/*Create our MQTT client. The client_id is a unique string that identifies
this device. For Google Cloud IoT Core, it must be in the format below.*/
client_id = 'projects/{}/locations/{}/registries/{}/devices/{}'.format(
project_id, cloud_region, registry_id, device_id)
print('Device client_id is \'{}\''.format(client_id))
client = mqtt.Client(client_id=client_id)
/* With Google Cloud IoT Core, the username field is ignored, and the
password field is used to transmit a JWT to authorize the device.*/
client.username_pw_set(
username='unused',
password=create_jwt(
project_id, private_key_file, algorithm))
/* Enable SSL/TLS support.*/
client.tls_set(ca_certs=ca_certs, tls_version=ssl.PROTOCOL_TLSv1_2)
/* Register message callbacks. In this example, the callbacks
just print to standard out. */
client.on_connect = on_connect
client.on_publish = on_publish
client.on_disconnect = on_disconnect
client.on_message = on_message
/* Connect to the Google MQTT bridge. */
client.connect(mqtt_bridge_hostname, mqtt_bridge_port)
/* This is the topic that the device will receive configuration updates */
mqtt_config_topic = '/devices/{}/config'.format(device_id)
/* Subscribe to the config topic. */
client.subscribe(mqtt_config_topic, qos=0)
/* The topic that the device will receive commands on */
mqtt_command_topic = '/devices/{}/commands/#'.format(device_id)
/*Subscribe to the commands topic.*/
print('Subscribing to {}'.format(mqtt_command_topic))
client.subscribe(mqtt_command_topic, qos=0)
return client
This function does the same steps that we have just seen with the NodeJS client in the previous tutorial but requires a secure connection with SSL/TLS because the python MQTT module is managed by Paho MQTT and not directly by Google. For this reason, you have to download and add in the bridge folder the certificates provided by google. For further details see at this guide: Downloading server certificates - Google guide.
Now that we have set our client, we just have to listen to our RSMB broker and make sure that every message that arrives is forwarded to Google.
def messageArrived(self, topicName, payload, qos, retained, msgid):
/* Publish "payload" to the MQTT topic. */
client.publish(mqtt_topic, payload, qos=0)
client.loop()
-----------------------------------------------------------------------------
if __name__ == "__main__":
aclient = Client("linh", port=1885)
aclient.registerCallback(Callback())
aclient.connect()
rc, topic1 = aclient.subscribe("riot_device")
while(True):{} /*listening for messages*/
You find all this code in my GitHub repository (at the end of the page) in the directory /MQTTSNbridge
Run the Bridge
1. Open the Bride folder:
cd MQTTSNbridge/
2. Install all the modules needed:
pip install
3. Start the bridge (This procedure has to be done before to launch the RIOT devices):
python MQTTSNclient.py
Note: this implementation uses Python 2.0
System in actionWell, we are ready to run. We have completed the building of the system represented in the introduction picture. We have already seen in the previous tutorial the part regarding the dashboard. In the back-end, I only added the management of the new values received from RIOT, which is the same for the NodeJS, while in the front-end, I divided the received values according to their origin platform.
Action:
- Run the RIOT devices, the RSMB broker, and the python bridge:
- Open the dashboard and see the new results:
Comments
Please log in or sign up to comment.