RuuviTag with C.H.I.P - BLE - Node-RED - InfluxDB and Grafana

A few weeks ago I received my RuuviTag. It is a small Bluetooth BLE device which contains a few sensors, including temperature, relative air humidity, air pressure and acceleration. The hardware and the software are both Open Source.

ruuvitag-enclosure
ruuvitag-pcb

It features a Bosch Sensortec BME280 environmental sensor which can also be found in several wearables.

bme280

Node-RED on C.H.I.P

To get the data from the Ruuvi Tag to my Dashboard, I've installed Node-RED on a C.H.I.P microcomputer. This microcomputer features WLAN and Bluetooth, so it's perfect for this use case.

chipAllSidesView

The Node-RED flow looks like this:

Screenshot_20171017_214102

(find the JSON code below)

An important node is the function which assigns names to the Ruuvi Tags so they can be easily identified in the database.

For accessing BLE from Node-RED I'm using the node-red-contrib-noble node. To allow access as non-root to bluetooth, run:

sudo setcap cap_net_raw+eip $(eval readlink -f `which node`)

source

The Ruuvi node can be found here: ojousima/node-red. The InfluxDB node here: node-red-contrib-influxdb.

More information can be found on RuuviLab.

InfluxDB and Grafana

The data is sent to InfluxDB and the visualized by Grafana which is displayed on a touchscreen powered by a RaspberryPI mounted to the wall on my home.

Screenshot_20171017_215357

IMG_20171017_221412

Node-RED Flow JSON export

[
    {
        "id": "5d49fc45.92f814",
        "type": "inject",
        "z": "3f454907.eaa58e",
        "name": "Start BLE scan",
        "topic": "startBLEScan",
        "payload": "{ \"scan\": true }",
        "payloadType": "json",
        "repeat": "120",
        "crontab": "",
        "once": true,
        "x": 252,
        "y": 91,
        "wires": [
            [
                "9f8d0dc1.46975"
            ]
        ]
    },
    {
        "id": "9f8d0dc1.46975",
        "type": "scan ble",
        "z": "3f454907.eaa58e",
        "uuids": "",
        "duplicates": false,
        "name": "",
        "x": 554,
        "y": 92,
        "wires": [
            [
                "5be2f753.9ef68"
            ]
        ]
    },
    {
        "id": "5be2f753.9ef68",
        "type": "ruuvitag",
        "z": "3f454907.eaa58e",
        "name": "",
        "x": 568,
        "y": 293,
        "wires": [
            [
                "cabc4ec8.42d408"
            ]
        ]
    },
    {
        "id": "9ffcb1ad.807238",
        "type": "inject",
        "z": "3f454907.eaa58e",
        "name": "Stop BLE scan",
        "topic": "stopBLEScan",
        "payload": "{ \"scan\": false }",
        "payloadType": "json",
        "repeat": "120",
        "crontab": "",
        "once": true,
        "x": 252,
        "y": 140,
        "wires": [
            [
                "ffc6e9b.2827198"
            ]
        ]
    },
    {
        "id": "ffc6e9b.2827198",
        "type": "delay",
        "z": "3f454907.eaa58e",
        "name": "",
        "pauseType": "delay",
        "timeout": "10",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "x": 430,
        "y": 222,
        "wires": [
            [
                "9f8d0dc1.46975"
            ]
        ]
    },
    {
        "id": "860f2dd9.03ea38",
        "type": "debug",
        "z": "3f454907.eaa58e",
        "name": "",
        "active": false,
        "console": "false",
        "complete": "true",
        "x": 858.5,
        "y": 315,
        "wires": [],
        "inputLabels": [
            "Input"
        ]
    },
    {
        "id": "cabc4ec8.42d408",
        "type": "function",
        "z": "3f454907.eaa58e",
        "name": "Device naming",
        "func": "var device = null;\nif (msg.peripheralUuid == \"f93fb3fdXXXX\") {\n    device = \"livingroom\";\n} else if (msg.peripheralUuid == \"da677de0YYY\") { \n    device = \"outside\";\n} else if (msg.peripheralUuid == \"d6b542a8ZZZZ\") { \n    device = \"bedroom\";\n}\n\nvar formated = {\n    payload: [ JSON.parse(msg.payload), { location: device } ]\n}\nreturn formated;",
        "outputs": 1,
        "noerr": 0,
        "x": 663.5,
        "y": 417,
        "wires": [
            [
                "860f2dd9.03ea38",
                "a8619034.a61de"
            ]
        ]
    },
    {
        "id": "a8619034.a61de",
        "type": "influxdb out",
        "z": "3f454907.eaa58e",
        "influxdb": "2ef027b9.6d1718",
        "name": "",
        "measurement": "ruuvi",
        "precision": "",
        "retentionPolicy": "",
        "x": 873.5,
        "y": 472,
        "wires": []
    },
    {
        "id": "2ef027b9.6d1718",
        "type": "influxdb",
        "z": "",
        "hostname": "influxdb.example.com",
        "port": "443",
        "protocol": "http",
        "database": "sensors",
        "name": "influxdb",
        "usetls": true,
        "tls": "9cad81c7.ce811"
    },
    {
        "id": "9cad81c7.ce811",
        "type": "tls-config",
        "z": "",
        "name": "",
        "cert": "",
        "key": "",
        "ca": "",
        "certname": "",
        "keyname": "",
        "caname": "",
        "verifyservercert": true
    }
]
You've successfully subscribed to Tobias Brunner aka tobru
Great! Next, complete checkout to get full access to all premium content.
Error! Could not sign up. invalid link.
Welcome back! You've successfully signed in.
Error! Could not sign in. Please try again.
Success! Your account is fully activated, you now have access to all content.
Error! Stripe checkout failed.
Success! Your billing info is updated.
Error! Billing info update failed.