Node-REDのメモ 応用編 BME280+Websocket+Dashboard
Node-REDでBME280で測定したデータをWebsocketで飛ばして、Dashboardでグラフ表示するときの設定手順のメモ。
鶏頭で忘れっぽいのでメモ。
それぞれの基本は、Node-REDのHowTo(その1) ~ (その4)参照
その3 の Websocketでデータ送信(クライアント)の手順で作成したノードに接続
このとき、送信されるデータはmsg.payloadなので、文字列ではなく、object。
(WebsocketのパケットにはobjectをJSON文字列化したものが入る)
[
{
"id": "411d9021.cb1948",
"type": "tab",
"label": "BME280+Websocket",
"disabled": false,
"info": ""
},
{
"id": "72021e21.cce078",
"type": "Bme280",
"z": "411d9021.cb1948",
"name": "",
"bus": "1",
"address": "0x76",
"topic": "bme280",
"extra": false,
"x": 300,
"y": 60,
"wires": [
[
"571951b6.480168",
"4ce01eb0.f1d438"
]
]
},
{
"id": "571951b6.480168",
"type": "websocket out",
"z": "411d9021.cb1948",
"name": "",
"server": "",
"client": "f24a6df3.ed0608",
"x": 660,
"y": 60,
"wires": []
},
{
"id": "4ce01eb0.f1d438",
"type": "debug",
"z": "411d9021.cb1948",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"x": 570,
"y": 140,
"wires": []
},
{
"id": "480f869f.968e18",
"type": "function",
"z": "411d9021.cb1948",
"name": "DummyData",
"func": "msg.payload = {\n\t\t\"temperature_C\": Math.floor((Math.random() * ( 40 - (-30)) * 100) / 100) + (-30),\n\t\t\"humidity\": Math.floor((Math.random() * ( 100 - 0 ) * 100) / 100) + 0,\n\t\t\"pressure_hPa\": Math.floor((Math.random() * (1100 - 800 ) * 100) / 100) + 800,\n\t\t\"model\":\"DUMMY\"\n}\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
"x": 310,
"y": 140,
"wires": [
[
"571951b6.480168",
"4ce01eb0.f1d438"
]
]
},
{
"id": "c6ba67a3.1c69c",
"type": "inject",
"z": "411d9021.cb1948",
"name": "",
"topic": "",
"payload": "true",
"payloadType": "bool",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 130,
"y": 60,
"wires": [
[
"72021e21.cce078"
]
]
},
{
"id": "ab623444.9c5148",
"type": "inject",
"z": "411d9021.cb1948",
"name": "",
"topic": "",
"payload": "true",
"payloadType": "bool",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 130,
"y": 140,
"wires": [
[
"480f869f.968e18"
]
]
},
{
"id": "f24a6df3.ed0608",
"type": "websocket-client",
"z": "",
"path": "ws://PiDev25.local:1880/ws/bme280",
"tls": "",
"wholemsg": "false"
}
]
これだけでWebsocketからデータは受信できる。
このとき、Websocketの受信ノードのmag.payloadはJSON文字列なので、objectに変換してやらないと後段で使用できない。
そのため、Websocketのノードの出力をjsonノードで変換してやる必要がある。
出力側に受信データを処理するノードを接続
この状態でRaspberryPi側でBME280のデータ送信をトリガすれば、受信したデータで処理ノードが実行される
上記、WebsocketからBME280データを受信(サーバ)の処理ノードとして
[!WARNING] これで理屈的には大丈夫なハズなんだけど、
{{msg.payload.pressure_hPa}}
を指定すると、値は正常に表示されるけど、グラフが正常に表示されないことがある。。。
どうやら、この形で指定すると、値が1000を超えるとグラフ表示がおかしくなるようだ。バグか?
下の(その2)の方法で回避可能。
[
{
"id": "61238c01.1e1fdc",
"type": "tab",
"label": "Websocket_recv_BME280",
"disabled": false,
"info": ""
},
{
"id": "2163c454.8e4654",
"type": "json",
"z": "61238c01.1e1fdc",
"name": "",
"property": "payload",
"action": "obj",
"pretty": false,
"x": 310,
"y": 120,
"wires": [
[
"8a687382.d3a38",
"54ecaf20.30611",
"1e9e6439.3de04c",
"54f5a1ee.e4fb5"
]
]
},
{
"id": "de53f105.be0bb",
"type": "websocket in",
"z": "61238c01.1e1fdc",
"name": "",
"server": "107d62cf.5a6d8d",
"client": "",
"x": 134,
"y": 121,
"wires": [
[
"2163c454.8e4654",
"8a687382.d3a38"
]
]
},
{
"id": "8a687382.d3a38",
"type": "debug",
"z": "61238c01.1e1fdc",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"x": 530,
"y": 40,
"wires": []
},
{
"id": "54ecaf20.30611",
"type": "ui_gauge",
"z": "61238c01.1e1fdc",
"name": "",
"group": "9912b800.6921d8",
"order": 0,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "温度",
"label": "℃",
"format": "{{msg.payload.temperature_C | number:1}}℃",
"min": "-10",
"max": "40",
"colors": [
"#0000ff",
"#e6e600",
"#ca3838"
],
"seg1": "0",
"seg2": "30",
"x": 510,
"y": 120,
"wires": []
},
{
"id": "1e9e6439.3de04c",
"type": "ui_gauge",
"z": "61238c01.1e1fdc",
"name": "",
"group": "9912b800.6921d8",
"order": 0,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "湿度",
"label": "%",
"format": "{{msg.payload.humidity| number:1}}%",
"min": "0",
"max": "100",
"colors": [
"#ff8080",
"#00ff00",
"#00ffff"
],
"seg1": "50",
"seg2": "60",
"x": 510,
"y": 160,
"wires": []
},
{
"id": "54f5a1ee.e4fb5",
"type": "ui_gauge",
"z": "61238c01.1e1fdc",
"name": "",
"group": "9912b800.6921d8",
"order": 0,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "気圧",
"label": "hPa",
"format": "{{msg.payload.pressure_hPa| number:0}}hPa",
"min": "900",
"max": "1050",
"colors": [
"#00ff00",
"#00ff00",
"#00ff00"
],
"seg1": "",
"seg2": "",
"x": 510,
"y": 200,
"wires": []
},
{
"id": "107d62cf.5a6d8d",
"type": "websocket-listener",
"z": "",
"path": "/ws/bme280",
"wholemsg": "false"
},
{
"id": "9912b800.6921d8",
"type": "ui_group",
"z": "",
"name": "グループ1",
"tab": "9c00695d.f96b1",
"disp": true,
"width": "6",
"collapse": false
},
{
"id": "9c00695d.f96b1",
"type": "ui_tab",
"z": "",
"name": "タブ2",
"icon": "dashboard",
"disabled": false,
"hidden": false
}
]
(その1)での不具合を回避するため、msg.payloadのオブジェクト内のそれぞれの変数をバラすfunctionノードを追加する
// make deep copy
var msg_temp = JSON.parse(JSON.stringify(msg));
var msg_hum = JSON.parse(JSON.stringify(msg));
var msg_press = JSON.parse(JSON.stringify(msg));
msg_temp.topic = "temperature_C";
msg_temp.payload = msg.payload.temperature_C;
msg_hum.topic = "humidity";
msg_hum.payload = msg.payload.humidity;
msg_press.topic = "pressure_hPa";
msg_press.payload = msg.payload.pressure_hPa;
return [msg_temp, msg_hum, msg_press];
[!NOTE]
var msg_temp = msg;
などとしてはいけない。
この場合、msg_temp
はmsg
の浅いコピーとなってしまうため、その下でmsg_temp.payload
を変更すると、msg.payload
も変更されてしまうことになる。
これを防ぐため、深いコピーを作成している。これにはmsg
をJSON文字列化して、再度パースすることで対応している。
フローエディタ上で、どの端子がどの信号か分からなくなるのを防ぐため、端子に名前を付けることができる。
(付けなくても動作上は問題ない)
[
{
"id": "9c09bf08.8c36a8",
"type": "tab",
"label": "Websocket_recv_BME280_2",
"disabled": false,
"info": ""
},
{
"id": "1e53a14c.4133a7",
"type": "json",
"z": "9c09bf08.8c36a8",
"name": "",
"property": "payload",
"action": "obj",
"pretty": false,
"x": 310,
"y": 120,
"wires": [
[
"855491ee.723a88",
"3cee4c1c.c6861c"
]
]
},
{
"id": "ada05d07.d2d8b",
"type": "websocket in",
"z": "9c09bf08.8c36a8",
"name": "",
"server": "107d62cf.5a6d8d",
"client": "",
"x": 134,
"y": 121,
"wires": [
[
"1e53a14c.4133a7",
"855491ee.723a88"
]
]
},
{
"id": "855491ee.723a88",
"type": "debug",
"z": "9c09bf08.8c36a8",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"x": 690,
"y": 40,
"wires": []
},
{
"id": "51b57ef4.80809",
"type": "ui_gauge",
"z": "9c09bf08.8c36a8",
"name": "",
"group": "34e8ddba.6c67fa",
"order": 0,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "温度2",
"label": "℃",
"format": "{{value | number:1}}℃",
"min": "-10",
"max": "40",
"colors": [
"#0000ff",
"#e6e600",
"#ca3838"
],
"seg1": "0",
"seg2": "30",
"x": 670,
"y": 80,
"wires": []
},
{
"id": "be7cac56.7bcc4",
"type": "ui_gauge",
"z": "9c09bf08.8c36a8",
"name": "",
"group": "34e8ddba.6c67fa",
"order": 0,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "湿度2",
"label": "%",
"format": "{{value | number:1}}%",
"min": "0",
"max": "100",
"colors": [
"#ff8080",
"#00ff00",
"#00ffff"
],
"seg1": "50",
"seg2": "60",
"x": 670,
"y": 120,
"wires": []
},
{
"id": "f36e338d.e77e6",
"type": "ui_gauge",
"z": "9c09bf08.8c36a8",
"name": "",
"group": "34e8ddba.6c67fa",
"order": 0,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "気圧2",
"label": "hPa",
"format": "{{value | number:0}}hPa",
"min": "900",
"max": "1050",
"colors": [
"#00ff00",
"#00ff00",
"#00ff00"
],
"seg1": "",
"seg2": "",
"x": 670,
"y": 160,
"wires": []
},
{
"id": "3cee4c1c.c6861c",
"type": "function",
"z": "9c09bf08.8c36a8",
"name": "BME280",
"func": "// make deep copy\nvar msg_temp = JSON.parse(JSON.stringify(msg));\nvar msg_hum = JSON.parse(JSON.stringify(msg));\nvar msg_press = JSON.parse(JSON.stringify(msg));\n\nmsg_temp.topic = \"temperature_C\";\nmsg_temp.payload = msg.payload.temperature_C;\nmsg_hum.topic = \"humidity\";\nmsg_hum.payload = msg.payload.humidity;\nmsg_press.topic = \"pressure_hPa\";\nmsg_press.payload = msg.payload.pressure_hPa;\n\nreturn [msg_temp, msg_hum, msg_press];",
"outputs": 3,
"noerr": 0,
"x": 460,
"y": 120,
"wires": [
[
"51b57ef4.80809"
],
[
"be7cac56.7bcc4"
],
[
"f36e338d.e77e6"
]
],
"inputLabels": [
"BME280データ"
],
"outputLabels": [
"温度",
"湿度",
"気圧"
]
},
{
"id": "107d62cf.5a6d8d",
"type": "websocket-listener",
"z": "",
"path": "/ws/bme280",
"wholemsg": "false"
},
{
"id": "34e8ddba.6c67fa",
"type": "ui_group",
"z": "",
"name": "グループ2",
"tab": "9c00695d.f96b1",
"disp": true,
"width": "6",
"collapse": false
},
{
"id": "9c00695d.f96b1",
"type": "ui_tab",
"z": "",
"name": "タブ2",
"icon": "dashboard",
"disabled": false,
"hidden": false
}
]