Back to Blog

Connect Raspberry Pi to Kaa IoT Cloud

Tech

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:

Connect Raspberry Pi to Kaa Cloud use case

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.

Get your Node-RED instance up and running in minutes for $15/months

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.


Breadboard setup for BME280

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

order node-red

Get your NODE-Red instance up and running in minutes

for $15/month

Related Stories

KaaIoT and ELSYS Join Forces for Smart Building Solutions

KaaIoT and ELSYS join forces to bring robust smart building solutions with real-time climate monitoring.

Promoting Smart Building Solutions: Roomsys & Kaa Partnership

Exciting news! KaaIoT has teamed up with Roomsys to promote smart building management IoT solutions!

Simple Industrial IoT Solutions: KaaIoT & Sensative Partnership

The KaaIoT and Sensative partnership tackles a common challenge: water & oil leaks.

Remote Asset Tracking Solutions: KaaIoT & Seeed Partnership

Seeed offers a wide range of IoT hardware products. This article explores how KaaIoT and Seeed are joining...