Connect Raspberry Pi to Kaa IoT Cloud
This tutorial provides a step-by-step instruction on how to connect a Raspberry Pi to your free Kaa IoT Cloud account. Kaa Cloud account is essentially your personal isolated sandbox space in Kaa IoT platform where you can connect smart things, visualize data, build automation, manage devices and more.
The following diagram illustrates what we will have in our final solution:
We will be using a BME280 sensor for measuring temperature, humidity, and pressure values. But even if you don't have a sensor like that, you will still be able to randomly generate measurement values.
Kaa Cloud Account setup
Create a free Kaa Cloud account if you don’t have it yet.
Then go to the "Device Management" dashboard in your account and add a new device specifying a token that we will use later to identify a Raspberry Pi in the Kaa Cloud. Also, go to the added device page from the "Device Management" dashboard and copy the application version name. It looks like this:
871c9b3a-38f5-4f4c-a2a6-ce5792b3906b-v1
We will need both the application version and token to connect a Raspberry Pi to the Kaa Cloud.
Now that we have a device’s digital twin created in Kaa as well as obtained its token and application version, let's work with the Raspberry Pi.
BME280 Sensor and WiFi Setup
Connect the BME280 sensor to the Raspberry Pi by referring to the following table:
Module PCB | Desc | GPIO Header Pins |
---|---|---|
VCC | 3.3V | P1-01 |
GND | Ground | P1-06 |
SCL | I2C SCL | P1-05 |
SDA | I2C SDA | P1-03 |
Here’s the diagram of a breadboard setup.
If you don't have the BME280 sensor, then skip the above step and move on.
Connect the Raspberry Pi to the WiFi.
Once the Raspberry Pi is connected to the WiFi, install the BME Python library:
$ pip install bme280
Sending Data Over MQTT
Install Eclipse Paho MQTT Python client library:
$ pip install paho-mqtt
Create somewhere on the Raspberry Pi filesystem a file called "client.py" and open it.
Copy the following Python code that reads temperature, humidity, and pressure from a BME280 sensor and sends it into the Kaa Cloud. Then paste the code into the "client.py" file. If you don't have a BME280 sensor, jump to the next code snippet.
import argparse import json import logging import os import random import signal import string import sys import time import paho.mqtt.client as mqtt import bme280 DEFAULT_KPC_HOST = os.getenv('DEFAULT_KPC_HOST', 'mqtt.cloud.kaaiot.com') DEFAULT_KPC_PORT = os.getenv('DEFAULT_KPC_PORT', 1883) EPMX_INSTANCE_NAME = os.getenv('EPMX_INSTANCE_NAME', 'epmx') DCX_INSTANCE_NAME = os.getenv('DCX_INSTANCE_NAME', 'dcx') def killhandle(signum, frame): logger.info("SIGTERM detected, shutting down") disconnectFromServer(client=client, host=host, port=port) sys.exit(0) signal.signal(signal.SIGINT, killhandle) signal.signal(signal.SIGTERM, killhandle) # Configure logging logger = logging.getLogger('mqtt-client') logger.setLevel(logging.DEBUG) hdl = logging.StreamHandler() hdl.setLevel(logging.DEBUG) hdl.setFormatter(logging.Formatter('%(levelname)s: %(message)s')) logger.addHandler(hdl) # Parse command line arguments and get device name parser = argparse.ArgumentParser(description="MQTT client for demo application") parser.add_argument("-d", "--deviceName", action="store", dest="deviceName", default="BME/BMP 280", required=False, help="Name of connected device") parser.add_argument("-a", "--appversion", action="store", dest="appversion", required=True, help="Application version") parser.add_argument("-t", "--token", action="store", dest="token", required=True, help="Device token") parser.add_argument("-s", "--host", action="store", dest="host", default=DEFAULT_KPC_HOST, help="Server host to connect to") parser.add_argument("-p", "--port", action="store", dest="port", default=DEFAULT_KPC_PORT, help="Server port to connect to") args = parser.parse_args() appversion = args.appversion token = args.token client_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(6)) host = args.host port = args.port logger.info("Using EP token {0}, server at {1}:{2}".format(token, host, port)) def connectToServer(client, host, port): logger.info("Connecting to KPC instance at {0}:{1}...".format(host, port)) client.connect(host, port, 60) logger.info("Successfully connected") def disconnectFromServer(client, host, port): logger.info("Disconnecting from server at {0}:{1}.".format(host, port)) time.sleep(4) # wait client.loop_stop() # stop the loop client.disconnect() logger.info("Successfully disconnected") # METADATA section ------------------------------------ # Compose KP1 topic for metadata metadata_request_id = random.randint(1, 99) topic_metadata = "kp1/{application_version}/{service_instance}/{resource_path}".format( application_version=appversion, service_instance=EPMX_INSTANCE_NAME, resource_path="{token}/update/keys/{metadata_request_id}".format(token=token, metadata_request_id=metadata_request_id) ) logger.debug("Composed metadata topic: {}".format(topic_metadata)) def composeMetadata(version): return json.dumps( { "model": "BME/BMP 280", "fwVersion": version, "customer": "Andrew", "latitude": 40.71427, "longitude": -74.00597, } ) # TELEMETRY section -------------------------------------- # Compose KP1 topic for data collection data_request_id = random.randint(1, 99) topic_data_collection = "kp1/{application_version}/{service_instance}/{resource_path}".format( application_version=appversion, service_instance=DCX_INSTANCE_NAME, resource_path="{token}/json/{data_request_id}".format(token=token, data_request_id=data_request_id) ) logger.debug("Composed data collection topic: {}".format(topic_data_collection)) def composeDataSample(): # BME/BMP 280 temperature, pressure, humidity = bme280.readBME280All() payload = [ { "timestamp": int(round(time.time() * 1000)), "temperature": round(temperature,1), "humidity": int(humidity), "pressure": round(pressure,2) } ] return json.dumps(payload) def on_connect(client, userdata, flags, rc): if rc == 0: client.connected_flag = True # set flag logger.info("Successfully connected to MQTT server") else: logger.info("Failed to connect to MQTT code. Returned code=", rc) def on_message(client, userdata, message): logger.info("Message received: topic [{}]\nbody [{}]".format(message.topic, str(message.payload.decode("utf-8")))) # Initiate server connection client = mqtt.Client(client_id=client_id) client.connected_flag = False # create flag in class client.on_connect = on_connect # bind call back function client.on_message = on_message # Start the loop client.loop_start() connectToServer(client=client, host=host, port=int(port)) while not client.connected_flag: # wait in loop logger.info("Waiting for connection with MQTT server") time.sleep(1) # Send metadata once on the first connection metadata = composeMetadata(version="v0.0.1") client.publish(topic=topic_metadata, payload=metadata) logger.info("Sent metadata: {0}\n".format(metadata)) # Send data sample in loop while 1: payload = composeDataSample() result = client.publish(topic=topic_data_collection, payload=payload) if result.rc != 0: logger.info("Server connection lost, attempting to reconnect") connectToServer(client=client, host=host, port=port) else: logger.debug("{0}: Sent next data: {1}".format(token, payload)) time.sleep(8)
If you don't have a BME280 sensor then use the following code with randomly generated temperature, humidity, and pressure values.
import argparse import json import logging import os import random import signal import string import sys import time import paho.mqtt.client as mqtt DEFAULT_KPC_HOST = os.getenv('DEFAULT_KPC_HOST', 'mqtt.cloud.kaaiot.com') DEFAULT_KPC_PORT = os.getenv('DEFAULT_KPC_PORT', 1883) EPMX_INSTANCE_NAME = os.getenv('EPMX_INSTANCE_NAME', 'epmx') DCX_INSTANCE_NAME = os.getenv('DCX_INSTANCE_NAME', 'dcx') def killhandle(signum, frame): logger.info("SIGTERM detected, shutting down") disconnectFromServer(client=client, host=host, port=port) sys.exit(0) signal.signal(signal.SIGINT, killhandle) signal.signal(signal.SIGTERM, killhandle) # Configure logging logger = logging.getLogger('mqtt-client') logger.setLevel(logging.DEBUG) hdl = logging.StreamHandler() hdl.setLevel(logging.DEBUG) hdl.setFormatter(logging.Formatter('%(levelname)s: %(message)s')) logger.addHandler(hdl) # Parse command line arguments and get device name parser = argparse.ArgumentParser(description="MQTT client for demo application") parser.add_argument("-d", "--deviceName", action="store", dest="deviceName", default="BME/BMP 280", required=False, help="Name of connected device") parser.add_argument("-a", "--appversion", action="store", dest="appversion", required=True, help="Application version") parser.add_argument("-t", "--token", action="store", dest="token", required=True, help="Device token") parser.add_argument("-s", "--host", action="store", dest="host", default=DEFAULT_KPC_HOST, help="Server host to connect to") parser.add_argument("-p", "--port", action="store", dest="port", default=DEFAULT_KPC_PORT, help="Server port to connect to") args = parser.parse_args() appversion = args.appversion token = args.token client_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(6)) host = args.host port = args.port logger.info("Using EP token {0}, server at {1}:{2}".format(token, host, port)) def connectToServer(client, host, port): logger.info("Connecting to KPC instance at {0}:{1}...".format(host, port)) client.connect(host, port, 60) logger.info("Successfully connected") def disconnectFromServer(client, host, port): logger.info("Disconnecting from server at {0}:{1}.".format(host, port)) time.sleep(4) # wait client.loop_stop() # stop the loop client.disconnect() logger.info("Successfully disconnected") # METADATA section ------------------------------------ # Compose KP1 topic for metadata metadata_request_id = random.randint(1, 99) topic_metadata = "kp1/{application_version}/{service_instance}/{resource_path}".format( application_version=appversion, service_instance=EPMX_INSTANCE_NAME, resource_path="{token}/update/keys/{metadata_request_id}".format(token=token, metadata_request_id=metadata_request_id) ) logger.debug("Composed metadata topic: {}".format(topic_metadata)) def composeMetadata(version): return json.dumps( { "model": "BME/BMP 280", "fwVersion": version, "customer": "Andrew", "latitude": 40.71427, "longitude": -74.00597, } ) # TELEMETRY section -------------------------------------- # Compose KP1 topic for data collection data_request_id = random.randint(1, 99) topic_data_collection = "kp1/{application_version}/{service_instance}/{resource_path}".format( application_version=appversion, service_instance=DCX_INSTANCE_NAME, resource_path="{token}/json/{data_request_id}".format(token=token, data_request_id=data_request_id) ) logger.debug("Composed data collection topic: {}".format(topic_data_collection)) def composeDataSample(): payload = [ { "timestamp": int(round(time.time() * 1000)), "temperature": random.randint(18, 23), "humidity": random.randint(40, 60), "pressure": random.randint(980, 1000) } ] return json.dumps(payload) def on_connect(client, userdata, flags, rc): if rc == 0: client.connected_flag = True # set flag logger.info("Successfully connected to MQTT server") else: logger.info("Failed to connect to MQTT code. Returned code=", rc) def on_message(client, userdata, message): logger.info("Message received: topic [{}]\nbody [{}]".format(message.topic, str(message.payload.decode("utf-8")))) # Initiate server connection client = mqtt.Client(client_id=client_id) client.connected_flag = False # create flag in class client.on_connect = on_connect # bind call back function client.on_message = on_message # Start the loop client.loop_start() connectToServer(client=client, host=host, port=int(port)) while not client.connected_flag: # wait in loop logger.info("Waiting for connection with MQTT server") time.sleep(1) # Send metadata once on the first connection metadata = composeMetadata(version="v0.0.1") client.publish(topic=topic_metadata, payload=metadata) logger.info("Sent metadata: {0}\n".format(metadata)) # Send data sample in loop while 1: payload = composeDataSample() result = client.publish(topic=topic_data_collection, payload=payload) if result.rc != 0: logger.info("Server connection lost, attempting to reconnect") connectToServer(client=client, host=host, port=port) else: logger.debug("{0}: Sent next data: {1}".format(token, payload)) time.sleep(8)
Close the "client.py" file and run it:
$ python client.py -a {copied application version} -t {copied token}
Go to the device page in the "Device Management" dashboard on the Kaa Cloud and observe live data from the connected Raspberry Pi.
Next Steps
Now that you have successfully connected a Raspberry Pi to the Kaa Cloud, expand your solution by adding new dashboards and widgets, sending new types of data, and exploring analytics.
If you have any questions, feel free to ask them in Gitter.
Also, here is the link to our webinar where we explained how to connect a Raspberry Pi with BME280 and Senseair S08 CO2 sensors to the Kaa Cloud in more details: Kaa 1.1 webinar.
Andrew Pasika
Software Engineer