EnOceanのマルチセンサーで温湿度+照度をGrafanaに送って見える化する方法

EnOceanのマルチセンサー STM550Jから温湿度と照度をSakuraVPSで動かしているGrafanaで時系列データとして見える化する方法をご紹介します。

システムイメージ

弊社事務所に設置したSTM550JからEnOceanドングル(USB400J)を刺したReTerminalをゲートウエイにして、そこからさくらのVPSで構築した、Node-REDにMQTTで送信。受け取ったデータをinfluxDBに格納してGrafanaで表示するというイメージです。

今回、STM550Jは3つ用意しました。

ローカル側のNode-RED

ローカル側ではEnOceanセンサーからReTerminalでデータを受信します。

フローはこのとおり。

シリアルノードでUSB400Jのデータを受信。EnOcean data_chgでEnOceanの基本データをデコード。

Switchノードで利用するセンサーのIDだけをフィルタリングして、STM550Jデコードで、温度、湿度、照度、マグネットコンタクト状態を送ります。

MQTT Brokerに送るデータはこのようなJSONになります。

時系列データになるので、ここでタイムコードをしっかり入れておきましょう。

↓ フローはこちら

[{"id":"f4575ef8.576c9","type":"function","z":"73fa502e5317a230","name":"EnOcean data_chg","func":"// データそのもの\nvar buf = msg.payload;\n// データのコピー(検証用)\nvar rawData = buf.slice( 0 , buf.length  );\n// 取り出しやすいようにhex変換したもの\nvar rawByte = buf.toString( \"hex\" );\n// ESP3仕様のdataLengthを拾う\nvar dataLength = 255 * buf[ 1 ] + buf[ 2 ];\n// ESP3仕様のoptionalLengthを拾う\nvar optionalLength = buf[ 3 ];\n// packetType\nvar packetType = buf[ 4 ];\n// headerCRC\nvar headerCRC = buf[ 5 ];\n// dataLengthに基づいたデータ部分の切り出し\nvar rawDataByte = buf.slice( 7 , 7 + dataLength+2);\n\nvar id = rawDataByte.slice( 0 , 4 ).toString( \"hex\" );\n\n// payloadに格納\nmsg.payload = {\n    rawData:rawData,\n    rawByte:rawByte,\n    dataLength:dataLength,\n    optionalLength:optionalLength,\n    packetType:packetType,\n    headerCRC:headerCRC,\n    rawDataByte:rawDataByte,\n    ID:id\n} \n\n\n\nreturn msg;\n\n\n\n   ","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":430,"y":260,"wires":[["3e6ff4537e4f3c94"]]},{"id":"b457aedf2737e527","type":"serial in","z":"73fa502e5317a230","name":"USB400J","serial":"","x":300,"y":180,"wires":[["f4575ef8.576c9"]]},{"id":"3e6ff4537e4f3c94","type":"switch","z":"73fa502e5317a230","name":"","property":"payload.ID","propertyType":"msg","rules":[{"t":"else"}],"checkall":"true","repair":false,"outputs":1,"x":490,"y":380,"wires":[["adda7dcec6b45781"]]},{"id":"341622361af9c320","type":"mqtt out","z":"73fa502e5317a230","name":"Mosquitto","topic":"001/enocean","qos":"0","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"","x":860,"y":500,"wires":[]},{"id":"adda7dcec6b45781","type":"function","z":"73fa502e5317a230","name":"STM550J decode","func":"const rawData = msg.payload.rawDataByte;\n\n// Extract basic information\nmsg.id = rawData.slice(0, 4).toString('hex');\nmsg.dbm = rawData[15] * -1;\nmsg.stm550count = 1;\nmsg.type = 'STM550';\n\n// Extract data bytes\nconst d0 = 4;\nconst data = Array.from({ length: 9 }, (_, i) => rawData[d0 + i].toString(2).padStart(8, '0'));\n\nmsg.data = {\n    data_8: data[0],\n    data_7: data[1],\n    data_6: data[2],\n    data_5: data[3],\n    data_4: data[4],\n    data_3: data[5],\n    data_2: data[6],\n    data_1: data[7],\n    data_0: data[8]\n};\n\n// Calculate temperature\nconst temp = data[0] + data[1].slice(0, 2);\nmsg.temperature = Math.round((parseInt(temp, 2) / 10 - 40) * 100) / 100;\n\n// Calculate magnet\nmsg.magnet = parseInt(data[8].slice(3, 4), 2);\n\n// Calculate illumination\nconst illumination = data[2].slice(2) + data[3] + data[4].slice(0, 3);\nmsg.illumination = parseInt(illumination, 2);\n\nconst IllValue = msg.illumination;\nlet IllmaxValue = global.get('maxIll2') || 0;\nlet IllminValue = global.get('minIll2') || 1000;\n\nif (IllmaxValue < IllValue) {\n    IllmaxValue = IllValue;\n    global.set('maxIll2', IllmaxValue);\n}\nif (IllminValue > IllValue) {\n    IllminValue = IllValue;\n    global.set('minIll2', IllminValue);\n}\n\nglobal.set('Ill', IllValue);\n\n// Calculate humidity\nconst humidity = data[1].slice(2, 6) + data[2].slice(0, 2);\nmsg.humidity = Math.round(parseInt(humidity, 2) * 20) / 10;\n\n// Aggregate data\nmsg.payload = {\n    id: msg.id,\n    dbm: msg.dbm,\n    stm550count: msg.stm550count,\n    type: msg.type,\n    temperature: msg.temperature,\n    magnet: msg.magnet,\n    illumination: msg.illumination,\n    humidity: msg.humidity,\n    time:Date.now()\n};\n\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":790,"y":400,"wires":[["3767cfcb4e0c77b5","341622361af9c320"]]},{"id":"3767cfcb4e0c77b5","type":"debug","z":"73fa502e5317a230","name":"Output","active":false,"console":"false","complete":"payload","x":870,"y":320,"wires":[]}]

クラウド側 Node-RED

クラウド側のNode-REDは、MQTT OutからJSONで出力。それをそのままinfluxDBに格納するだけです。

クラウド側 Grafana

Grafanaでは、温度、湿度、照度のデータを、3つのセンサー分、折れ線グラフで表示させます。

Grafanaは7を使っています。

+マークを押してPanelを追加。

まずはデータソースの設定をします。

まずはセンサーAの温度を表示するようにします。

センサーBの値を表示するには、下にある+Queryをクリック

2つ目のセンサーの温度が表示されました。

同様に3つ目のセンサーも設定します。

このように3つのセンサーの温度が表示されました。

また、先ほどのパネルをDuplicateしてVisualizationでGaugeを選ぶとこのように表示されます。

FieldからThresholdsを開き、データごとの色を設定することもできます。

まずは、温度の折れ線グラフと現在の温度を表示することができました。