Connecting Node-RED to Kaa

In this tutorial, we will look at how to send data to the Kaa platform by using Node-RED, which is a flow-based development tool for visual programming. You will learn how to create a digital twin of your Node-RED device, connect the device to Kaa, submit some telemetry and view it in the Kaa web interface.

Overview

We will be using Node-RED installed on a Raspberry Pi. However, in your case Node-RED can be installed anywhere - on a local machine, on some other device instead of Raspberry Pi, in the cloud, etc. Our device will represent an endpoint in the Kaa platform and report temperature, humidity, and air pressure. Also, we will interact with the Kaa Web Dashboard to create a digital twin of the Raspberry Pi and view telemetry data.

Prerequisites

  1. You have a Kaa Cloud account.
  2. You have a BME280 sensor (optional).

Playbook

Connect your device

Go to the “Device Management” dashboard in your account.

Menu device management

Then add a new device specifying a token that we will use later to identify the Raspberry Pi in the Kaa Cloud.

Add device

Also, go to the added device page from the “Device Management” dashboard and copy the application version name.

App version device page

We will need both the application version and the token to connect the Raspberry Pi to the Kaa Cloud.

Now that we have a digital twin of our device created in Kaa together with its token and application version, let’s work with the Raspberry Pi.

Connect a 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 the breadboard setup diagram.
BME280 module setup

If you don’t have a BME280 sensor, then skip the above step and move on.

You may use an Ethernet cable or connect the Raspberry Pi to WiFi using command line or graphical desktop user interface.

Once the Raspberry Pi is connected to the network, install Node-RED:

NOTE: Node-RED is pre-installed in the “Raspberry Pi OS (32-bit) with desktop and recommended software”, so you may skip installation step if you have this OS distribution.

$ sudo apt install nodejs
$ sudo apt install npm
$ sudo npm install -g --unsafe-perm node-red

After the installation is complete, start Node-RED:

$ node-red

Node-RED start

When Node-RED reports in a console that it is running, open a browser on the Raspberry Pi and go to 127.0.0.1:1880 or, if you are using the browser on another computer, you will need to use the Raspberry Pi IP address from your network: your_raspberry_ip_address:1880.

Node-RED blank

Sending data over MQTT

Install a BME280 node, for that click on a menu button in the right top corner and open Manage palette:

Node-RED manage palette

Then, click on Install tab and search for bme280.

Node-RED search BME280 node

Install node-red-contrib-bme280 node.

OK, now let’s import the Kaa Node-RED client flow description into Node-RED.

Click menu -> Import:

Node-RED import

If you have BME280, use this flow description:

[
  {
    "id": "7eddf7e8.3a94e8",
    "type": "tab",
    "label": "kaa_client",
    "disabled": false,
    "info": ""
  },
  {
    "id": "d80332d4.79c1b",
    "type": "inject",
    "z": "7eddf7e8.3a94e8",
    "name": "Metadata Update",
    "topic": "",
    "payload": "",
    "payloadType": "date",
    "repeat": "",
    "crontab": "",
    "once": true,
    "onceDelay": "1.0",
    "x": 150,
    "y": 180,
    "wires": [
      [
        "9b07caac.e1fce8"
      ]
    ]
  },
  {
    "id": "288fc079.cc7d4",
    "type": "mqtt out",
    "z": "7eddf7e8.3a94e8",
    "name": "Kaa KPC",
    "topic": "",
    "qos": "0",
    "retain": "",
    "broker": "ccf67e66.fe83c",
    "x": 700,
    "y": 260,
    "wires": []
  },
  {
    "id": "e5b6ee2a.8f12a",
    "type": "debug",
    "z": "7eddf7e8.3a94e8",
    "name": "",
    "active": true,
    "tosidebar": true,
    "console": false,
    "tostatus": false,
    "complete": "payload",
    "targetType": "msg",
    "x": 710,
    "y": 340,
    "wires": []
  },
  {
    "id": "9b07caac.e1fce8",
    "type": "function",
    "z": "7eddf7e8.3a94e8",
    "name": "Metadata",
    "func": "var deviceToken = global.get('DeviceToken');\nvar appVersion = global.get('AppVersion');\n\nmsg.topic = 'kp1/' + appVersion + '/epmx/' + deviceToken + '/update/keys';\n\nmsg.payload = {\n    \"model\": \"Node Red\",\n    \"works\": true,\n    \"model\": \"BME/BMP 280\",\n    \"fwVersion\": \"v0.0.1\",\n    \"latitude\": 40.71427,\n    \"longitude\": -74.00597,\n}\nreturn msg;",
    "outputs": 1,
    "noerr": 0,
    "x": 360,
    "y": 180,
    "wires": [
      [
        "288fc079.cc7d4",
        "bab967a5.742038"
      ]
    ]
  },
  {
    "id": "ac51a783.266c18",
    "type": "inject",
    "z": "7eddf7e8.3a94e8",
    "name": "Send Telemetry",
    "topic": "",
    "payload": "",
    "payloadType": "date",
    "repeat": "2",
    "crontab": "",
    "once": true,
    "onceDelay": "1.5",
    "x": 150,
    "y": 340,
    "wires": [
      [
        "1bd2fecf.dd9661"
      ]
    ]
  },
  {
    "id": "339bb634.0c571a",
    "type": "comment",
    "z": "7eddf7e8.3a94e8",
    "name": "Sending Metadata to Kaa",
    "info": "Example of sending metadata to kaa",
    "x": 150,
    "y": 140,
    "wires": []
  },
  {
    "id": "10fe191f.579067",
    "type": "comment",
    "z": "7eddf7e8.3a94e8",
    "name": "Sending Telemetry to Kaa",
    "info": "Example of sending telemtry data to kaa",
    "x": 150,
    "y": 300,
    "wires": []
  },
  {
    "id": "9d35257d.040298",
    "type": "inject",
    "z": "7eddf7e8.3a94e8",
    "name": "Init",
    "topic": "",
    "payload": "",
    "payloadType": "date",
    "repeat": "",
    "crontab": "",
    "once": true,
    "onceDelay": "0",
    "x": 110,
    "y": 80,
    "wires": [
      [
        "dd84eaa0.c19b18"
      ]
    ]
  },
  {
    "id": "dd84eaa0.c19b18",
    "type": "function",
    "z": "7eddf7e8.3a94e8",
    "name": "Init Global",
    "func": "global.set('DeviceToken', 'yourToken');\nglobal.set('AppVersion', 'yourAppVersion');\n\nconsole.log('kaa_client')\nconsole.log('DeviceToken:', global.get('DeviceToken'))\nconsole.log('AppVersion:', global.get('AppVersion'))\n\nvar deviceToken = global.get('DeviceToken');\nvar appVersion = global.get('AppVersion');\n\nmsg.payload = 'DeviceToken: ' + deviceToken + '; AppVersion: ' + appVersion;\nreturn msg;",
    "outputs": 1,
    "noerr": 0,
    "x": 360,
    "y": 80,
    "wires": [
      [
        "b5aad5ea.80c9d8"
      ]
    ]
  },
  {
    "id": "b5aad5ea.80c9d8",
    "type": "debug",
    "z": "7eddf7e8.3a94e8",
    "name": "Initial Variables",
    "active": true,
    "tosidebar": true,
    "console": false,
    "tostatus": false,
    "complete": "payload",
    "targetType": "msg",
    "x": 720,
    "y": 80,
    "wires": []
  },
  {
    "id": "bab967a5.742038",
    "type": "debug",
    "z": "7eddf7e8.3a94e8",
    "name": "",
    "active": true,
    "tosidebar": true,
    "console": false,
    "tostatus": false,
    "complete": "payload",
    "targetType": "msg",
    "x": 710,
    "y": 180,
    "wires": []
  },
  {
    "id": "cacc758b.f4b448",
    "type": "comment",
    "z": "7eddf7e8.3a94e8",
    "name": "Initialization",
    "info": "",
    "x": 110,
    "y": 40,
    "wires": []
  },
  {
    "id": "b662018c.77f55",
    "type": "function",
    "z": "7eddf7e8.3a94e8",
    "name": "Telemetry Data",
    "func": "var deviceToken = global.get('DeviceToken');\nvar appVersion = global.get('AppVersion');\n\nmsg.topic = 'kp1/' + appVersion + '/dcx/' + deviceToken + '/json/1';\n\nmsg.payload.timestamp = new Date().getTime()\ndelete msg.payload.model;\nmsg.payload = [msg.payload];\n\nreturn msg;",
    "outputs": 1,
    "noerr": 0,
    "x": 480,
    "y": 340,
    "wires": [
      [
        "e5b6ee2a.8f12a",
        "288fc079.cc7d4"
      ]
    ]
  },
  {
    "id": "1bd2fecf.dd9661",
    "type": "Bme280",
    "z": "7eddf7e8.3a94e8",
    "name": "",
    "bus": "1",
    "address": "0x76",
    "topic": "",
    "extra": false,
    "x": 320,
    "y": 340,
    "wires": [
      [
        "b662018c.77f55"
      ]
    ]
  },
  {
    "id": "ccf67e66.fe83c",
    "type": "mqtt-broker",
    "z": "",
    "name": "mqtt.cloud.kaaiot.com",
    "broker": "mqtt.cloud.kaaiot.com",
    "port": "1883",
    "clientid": "",
    "usetls": false,
    "compatmode": true,
    "keepalive": "60",
    "cleansession": true,
    "birthTopic": "",
    "birthQos": "0",
    "birthPayload": "",
    "closeTopic": "",
    "closeQos": "0",
    "closePayload": "",
    "willTopic": "",
    "willQos": "0",
    "willPayload": ""
  }
]

If not, use this flow description:

[
  {
    "id": "a120f4f.e30b208",
    "type": "tab",
    "label": "kaa_client",
    "disabled": false,
    "info": ""
  },
  {
    "id": "a12578ee.d4bef8",
    "type": "inject",
    "z": "a120f4f.e30b208",
    "name": "Metadata Update",
    "topic": "",
    "payload": "",
    "payloadType": "date",
    "repeat": "",
    "crontab": "",
    "once": true,
    "onceDelay": "1.0",
    "x": 150,
    "y": 180,
    "wires": [
      [
        "690e0efd.93f15"
      ]
    ]
  },
  {
    "id": "a54dbdb1.24dcc",
    "type": "mqtt out",
    "z": "a120f4f.e30b208",
    "name": "Kaa KPC",
    "topic": "",
    "qos": "0",
    "retain": "",
    "broker": "dc0deea1.ea5b2",
    "x": 700,
    "y": 260,
    "wires": []
  },
  {
    "id": "b23dc76c.f7c288",
    "type": "debug",
    "z": "a120f4f.e30b208",
    "name": "",
    "active": true,
    "tosidebar": true,
    "console": false,
    "tostatus": false,
    "complete": "payload",
    "targetType": "msg",
    "x": 710,
    "y": 340,
    "wires": []
  },
  {
    "id": "690e0efd.93f15",
    "type": "function",
    "z": "a120f4f.e30b208",
    "name": "Metadata",
    "func": "var deviceToken = global.get('DeviceToken');\nvar appVersion = global.get('AppVersion');\n\nmsg.topic = 'kp1/' + appVersion + '/epmx/' + deviceToken + '/update/keys';\n\nmsg.payload = {\n    \"model\": \"Node Red\",\n    \"works\": true,\n    \"model\": \"BME/BMP 280\",\n    \"fwVersion\": \"v0.0.1\",\n    \"latitude\": 40.71427,\n    \"longitude\": -74.00597,\n}\nreturn msg;",
    "outputs": 1,
    "noerr": 0,
    "x": 360,
    "y": 180,
    "wires": [
      [
        "a54dbdb1.24dcc",
        "8c84e0d7.92ae3"
      ]
    ]
  },
  {
    "id": "88765ea7.037f",
    "type": "inject",
    "z": "a120f4f.e30b208",
    "name": "Send Telemetry",
    "topic": "",
    "payload": "",
    "payloadType": "date",
    "repeat": "2",
    "crontab": "",
    "once": true,
    "onceDelay": "1.5",
    "x": 150,
    "y": 340,
    "wires": [
      [
        "a2dab299.37ae3"
      ]
    ]
  },
  {
    "id": "66c1df10.0d3db",
    "type": "comment",
    "z": "a120f4f.e30b208",
    "name": "Sending Metadata to Kaa",
    "info": "Example of sending metadata to kaa",
    "x": 150,
    "y": 140,
    "wires": []
  },
  {
    "id": "a2dab299.37ae3",
    "type": "function",
    "z": "a120f4f.e30b208",
    "name": "Telemetry Data",
    "func": "var deviceToken = global.get('DeviceToken');\nvar appVersion = global.get('AppVersion');\n\nmsg.topic = 'kp1/' + appVersion + '/dcx/' + deviceToken + '/json';\n\nmsg.payload = [{\n    temperature_C: Math.random() * (25 - 16) + 16,\n    humidity: Math.random() * (95 - 80) + 80,\n    pressure_hPa: Math.random() * (1000 - 990) + 990,\n    timestamp: new Date().getTime()\n}];\nreturn msg;",
    "outputs": 1,
    "noerr": 0,
    "x": 380,
    "y": 340,
    "wires": [
      [
        "a54dbdb1.24dcc",
        "b23dc76c.f7c288"
      ]
    ]
  },
  {
    "id": "bbea8f00.2718",
    "type": "comment",
    "z": "a120f4f.e30b208",
    "name": "Sending Telemetry to Kaa",
    "info": "Example of sending telemtry data to kaa",
    "x": 150,
    "y": 300,
    "wires": []
  },
  {
    "id": "3a42d047.ab7e5",
    "type": "inject",
    "z": "a120f4f.e30b208",
    "name": "Init",
    "topic": "",
    "payload": "",
    "payloadType": "date",
    "repeat": "",
    "crontab": "",
    "once": true,
    "onceDelay": "0",
    "x": 110,
    "y": 80,
    "wires": [
      [
        "2a00fd58.27c562"
      ]
    ]
  },
  {
    "id": "2a00fd58.27c562",
    "type": "function",
    "z": "a120f4f.e30b208",
    "name": "Init Global",
    "func": "global.set('DeviceToken', 'yourToken');\nglobal.set('AppVersion', 'yourAppVersion');\n\nconsole.log('kaa_client')\nconsole.log('DeviceToken:', global.get('DeviceToken'))\nconsole.log('AppVersion:', global.get('AppVersion'))\n\nvar deviceToken = global.get('DeviceToken');\nvar appVersion = global.get('AppVersion');\n\nmsg.payload = 'DeviceToken: ' + deviceToken + '; AppVersion: ' + appVersion;\nreturn msg;",
    "outputs": 1,
    "noerr": 0,
    "x": 360,
    "y": 80,
    "wires": [
      [
        "cd0bf7ba.c69908"
      ]
    ]
  },
  {
    "id": "cd0bf7ba.c69908",
    "type": "debug",
    "z": "a120f4f.e30b208",
    "name": "Initial Variables",
    "active": true,
    "tosidebar": true,
    "console": false,
    "tostatus": false,
    "complete": "payload",
    "targetType": "msg",
    "x": 720,
    "y": 80,
    "wires": []
  },
  {
    "id": "8c84e0d7.92ae3",
    "type": "debug",
    "z": "a120f4f.e30b208",
    "name": "",
    "active": true,
    "tosidebar": true,
    "console": false,
    "tostatus": false,
    "complete": "payload",
    "targetType": "msg",
    "x": 710,
    "y": 180,
    "wires": []
  },
  {
    "id": "a3cb4cfe.034f7",
    "type": "comment",
    "z": "a120f4f.e30b208",
    "name": "Initialization",
    "info": "",
    "x": 110,
    "y": 40,
    "wires": []
  },
  {
    "id": "dc0deea1.ea5b2",
    "type": "mqtt-broker",
    "z": "",
    "name": "mqtt.cloud.kaaiot.com",
    "broker": "mqtt.cloud.kaaiot.com",
    "port": "1883",
    "clientid": "",
    "usetls": false,
    "compatmode": true,
    "keepalive": "60",
    "cleansession": true,
    "birthTopic": "",
    "birthQos": "0",
    "birthPayload": "",
    "closeTopic": "",
    "closeQos": "0",
    "closePayload": "",
    "willTopic": "",
    "willQos": "0",
    "willPayload": ""
  }
]

Copy the corresponding code, then paste it into the red field and click Import.

Node-RED import clipboard

Open the Init Global node and change the token and the app version:

Configure client

At this point, the nodes only exist in the editor and must be deployed to the server. Click Deploy. Node-Red will start executing the flow on the server (Raspberry Pi), pushing collected data from BME280 into the Kaa platform.

Visualize data from the device

Enable time series auto extract

Edit the application configuration for the Endpoint Time Series service (EPTS). EPTS is a Kaa platform component responsible for transforming raw data samples into well-structured time series. It also stores the time series data and provides access API for other services, including the Web Dashboard.

Edit EPTS configuration

Enable the time series auto-extraction from data samples.

Enable time series auto extract

With this function enabled, Kaa will automatically create a time series for each numeric field that it encounters at the root of data samples submitted by your endpoints. You will then be able to view these time series in the Kaa UI, with no extra configuration required.

Visualize data

Go to the device details page of the recently created endpoint (by clicking on the corresponding row in the device table on the “Devices” dashboard). See the data from your Raspberry Pi on the “Device telemetry” widget.

Device telemetry data

Congratulations, you have successfully send data from your Raspberry Pi with Node-RED and visualized it in the Kaa UI!

Resources

  • All tutorial resources are located on GitHub.

Next steps