Bijeenkomst 2#
Programma
Gateway
programmeren van de gateway (meerdere modules)
volgen van de logging-berichten op de radio-logger
NodeRed
elementaire flow(s), oefeningen
kopiëren en importeren van een flow (in JSON-formaat)
mqtt input, configureren van de broker-security (wordt niet geëxporteerd!)
speciale functies voor onze toepasssing - sensoren en actuatoren
NodeRed dashboard
toevoegen van een extra sensor: lightlevel
Programmeren van de IoT-gateway#
De hardware van de IoT-gateway bestaat uit een microbit (V2!), een Elecfreaks iot:bit bordje, en een USB-voeding voor het iot:bit bordje. (De iot:bit heeft een aparte aansluiting voor de USB-voeding; je kunt het bordje niet voeden via de microbit!)
De IoT-gateway verbindt het lokale IoT-netwerk met het internet, via het lokale WiFi netwerk.
Zie voor het programmeren van de IoT-gateway: Programmeren van de micro:bit als gateway
NodeRed#
Eerste flow#
Webserver-flow#
Code voor html-template: <h1>Hello World!</h1>
Test met de URL van je NodeRed editor: het deel tot het ?
. Bijvoorbeeld: https://elegant-goosander-2583.infvoflows.nl/hello?test=123
Importeren van een flow: via hamburger-menu rechtsboven in NodeRed.
Kopieer de onderstaande flow-code (via de “Copy” kno rechtsboven in het uitgeklapte code-blok)
Open het import-venster in NodeRed (via het hamburger-menu)
Plak de gekopieerde flow in het venster, en klik op Import.
flow-code
[
{
"id": "cb507012ba185a11",
"type": "tab",
"label": "Flow 6",
"disabled": false,
"info": "",
"env": []
},
{
"id": "028e671dc5be25f5",
"type": "template",
"z": "cb507012ba185a11",
"name": "",
"field": "payload",
"fieldType": "msg",
"format": "handlebars",
"syntax": "mustache",
"template": "<h1>Hello World!</h1>",
"output": "str",
"x": 380,
"y": 620,
"wires": [
[
"25c2ead9cff0d34f"
]
]
},
{
"id": "1c6c14290c16be21",
"type": "http in",
"z": "cb507012ba185a11",
"name": "",
"url": "/hello",
"method": "get",
"upload": false,
"swaggerDoc": "",
"x": 180,
"y": 620,
"wires": [
[
"028e671dc5be25f5",
"9ab80f67a0d3e61b"
]
]
},
{
"id": "25c2ead9cff0d34f",
"type": "http response",
"z": "cb507012ba185a11",
"name": "",
"statusCode": "",
"headers": {},
"x": 570,
"y": 620,
"wires": []
},
{
"id": "9ab80f67a0d3e61b",
"type": "debug",
"z": "cb507012ba185a11",
"name": "debug http",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 390,
"y": 560,
"wires": []
}
]
MQTT flow#
MQTT input: sensor payload
MQTT node parameters (via flow-code hieronder):
bekijk de instellingen (parameters) van de MQTT-input-node
klik op een node om de instellingen te bekijken en aan te passen
Server:
infvopedia.nl:1883
Topic:
node/8c8a/sensors
Output:
A parsed JSON object
datatype:
json
MQTT server parameters (via de MQTT-node: potloodje naast Server-selectie)
Security (zelf invullen!):
username:
mqtttest
password:
abc?
flow-code
[
{
"id": "9dc0d8f5a7556d1e",
"type": "tab",
"label": "Flow 10",
"disabled": false,
"info": "",
"env": []
},
{
"id": "53ab0ab2e93ae083",
"type": "mqtt in",
"z": "9dc0d8f5a7556d1e",
"name": "",
"topic": "node/8c8a/sensors",
"qos": "2",
"datatype": "json",
"broker": "35520d39dbc586cb",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 170,
"y": 180,
"wires": [
[
"ab167874870a989f"
]
]
},
{
"id": "ab167874870a989f",
"type": "debug",
"z": "9dc0d8f5a7556d1e",
"name": "debug sensor payload",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 400,
"y": 180,
"wires": []
},
{
"id": "35520d39dbc586cb",
"type": "mqtt-broker",
"name": "",
"broker": "infvopedia.nl",
"port": "1883",
"clientid": "",
"autoConnect": true,
"usetls": false,
"protocolVersion": "4",
"keepalive": "60",
"cleansession": true,
"birthTopic": "",
"birthQos": "0",
"birthPayload": "",
"birthMsg": {},
"closeTopic": "",
"closeQos": "0",
"closePayload": "",
"closeMsg": {},
"willTopic": "",
"willQos": "0",
"willPayload": "",
"willMsg": {},
"userProps": "",
"sessionExpiry": ""
}
]
Sensor-functies#
We gebruiken voor alle IoT-nodes dezelfe JSON-layout voor sensoren en actuatoren-payload:
"payload": {
"0": {"dOut": 0},
"1": {"dOut": 0},
"2": {"dIn": 0},
"3": {"dIn": 0},
"4": {"temperature": 260},
"5": {"barometer": 1019},
"6": {"humidity": 140}
"7": {"presence": 0},
"8": {"aIn": 15}}
Een JSON object, of het bijbehorende JavaScript, bevat de gegevens van meerdere sensoren. In de NodeRed-flows hebben we voor het dashboard en voor allerlei besturingen de gegevens van de afzonderlijke sensoren nodig. Per sensor hebben we een functie(-node) gedefinieerd die de gegevens van die sensor selecteert, zie de figuur hieronder.
De tekst van de temperatuur-functie ziet er als volgt uit:
if (msg.payload.payload[4]) {
msg.payload = msg.payload.payload[4].temperature / 10;
return msg;
} else {
return null;
}
Deze functie-node kun je dan bijvoorbeeld gebruiken in de dashboard-flow.
Sensor function nodes code
[
{
"id": "6a34f441bef14c04",
"type": "tab",
"label": "Flow 11",
"disabled": false,
"info": "",
"env": []
},
{
"id": "0459bb20f395f8cb",
"type": "function",
"z": "6a34f441bef14c04",
"name": "4-temperature",
"func": "if (msg.payload.payload[4]) {\n msg.payload = msg.payload.payload[4].temperature / 10;\n return msg;\n} else {\n return null;\n}",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 260,
"y": 280,
"wires": [
[]
]
},
{
"id": "a7731a2decf0d455",
"type": "function",
"z": "6a34f441bef14c04",
"name": "5-barometer",
"func": "if (msg.payload.payload[5]) {\n msg.payload = msg.payload.payload[5].barometer / 10;\n return msg;\n} else {\n return null;\n}",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 250,
"y": 340,
"wires": [
[]
]
},
{
"id": "719ec1b59c77283b",
"type": "function",
"z": "6a34f441bef14c04",
"name": "8-lightlevel",
"func": "if (msg.payload.payload[8]) {\n msg.payload = msg.payload.payload[8].aIn;\n return msg;\n} else {\n return null;\n}",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 250,
"y": 520,
"wires": [
[]
]
},
{
"id": "c30fec99cf6c8179",
"type": "function",
"z": "6a34f441bef14c04",
"name": "2-dIn",
"func": "if (msg.payload.payload[2]) {\n msg.payload = msg.payload.payload[2].dIn;\n return msg;\n} else {\n return null;\n}\n",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 230,
"y": 160,
"wires": [
[]
]
},
{
"id": "72f4538b210a6294",
"type": "function",
"z": "6a34f441bef14c04",
"name": "3-dIn",
"func": "if (msg.payload.payload[3]) {\n msg.payload = msg.payload.payload[3].dIn;\n return msg;\n} else {\n return null;\n}",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 230,
"y": 220,
"wires": [
[]
]
},
{
"id": "ac0afb77869a978f",
"type": "function",
"z": "6a34f441bef14c04",
"name": "7-presence",
"func": "if (msg.payload.payload[7]) {\n msg.payload = msg.payload.payload[7].presence;\n return msg;\n} else {\n return null;\n}",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 250,
"y": 460,
"wires": [
[]
]
},
{
"id": "411288406cc12a5a",
"type": "function",
"z": "6a34f441bef14c04",
"name": "6-humidity",
"func": "if (msg.payload.payload[6]) {\n msg.payload = msg.payload.payload[6].humidity / 2;\n return msg;\n} else {\n return null;\n}",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 250,
"y": 400,
"wires": [
[]
]
},
{
"id": "c62c75f51c5bb8bc",
"type": "template",
"z": "6a34f441bef14c04",
"name": "0-dOut-on",
"field": "payload",
"fieldType": "msg",
"format": "json",
"syntax": "mustache",
"template": "{\"0\": {\"dOut\": 1}}",
"output": "str",
"x": 610,
"y": 160,
"wires": [
[]
]
},
{
"id": "a0220e3959e84953",
"type": "template",
"z": "6a34f441bef14c04",
"name": "0-dOut-off",
"field": "payload",
"fieldType": "msg",
"format": "json",
"syntax": "mustache",
"template": "{\"0\": {\"dOut\": 0}}",
"output": "str",
"x": 610,
"y": 220,
"wires": [
[]
]
},
{
"id": "3eead327923f4e02",
"type": "template",
"z": "6a34f441bef14c04",
"name": "1-dOut-on",
"field": "payload",
"fieldType": "msg",
"format": "json",
"syntax": "mustache",
"template": "{\"1\": {\"dOut\": 1}}",
"output": "str",
"x": 610,
"y": 280,
"wires": [
[]
]
},
{
"id": "1bfbb39c8fc92766",
"type": "template",
"z": "6a34f441bef14c04",
"name": "1-dOut-off",
"field": "payload",
"fieldType": "msg",
"format": "json",
"syntax": "mustache",
"template": "{\"1\": {\"dOut\": 0}}",
"output": "str",
"x": 610,
"y": 340,
"wires": [
[]
]
}
]
Dashboard-flow#
Installeren van dashboard-nodes#
De bovenstaande flow maakt gebruik van de dashboard-nodes. Met de onderstaande aanwijzingen kun je deze installeren.
selecteer in het hamburger-menu rechts: Manage Palette
selecteer de tab Install
type in het zoekveld:
dashboard
klik bij
node-red-dashboard
: Install.
temperatuur dashboard flow
[
{
"id": "886e157d7b6324df",
"type": "tab",
"label": "Flow 12",
"disabled": false,
"info": "",
"env": []
},
{
"id": "752cc73ce4821834",
"type": "mqtt in",
"z": "886e157d7b6324df",
"name": "",
"topic": "node/99f6/sensors",
"qos": "2",
"datatype": "json",
"broker": "35520d39dbc586cb",
"nl": false,
"rap": false,
"inputs": 0,
"x": 190,
"y": 180,
"wires": [
[
"dee02a99ffd9d808"
]
]
},
{
"id": "d453e9b737b7bbba",
"type": "ui_gauge",
"z": "886e157d7b6324df",
"name": "",
"group": "a4643fc8.e80d68",
"order": 0,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "Temperatuur",
"label": "'C",
"format": "{{payload}}",
"min": 0,
"max": "50",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"diff": false,
"className": "",
"x": 650,
"y": 180,
"wires": []
},
{
"id": "cf57f073a2389bbb",
"type": "ui_chart",
"z": "886e157d7b6324df",
"name": "",
"group": "b7537500.9e9de",
"order": 0,
"width": 0,
"height": 0,
"label": "Temperatuur",
"chartType": "line",
"legend": "false",
"xformat": "HH:mm:ss",
"interpolate": "linear",
"nodata": "",
"dot": false,
"ymin": "0",
"ymax": "50",
"removeOlder": 1,
"removeOlderPoints": "",
"removeOlderUnit": "86400",
"cutout": 0,
"useOneColor": false,
"colors": [
"#1f77b4",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
"outputs": 1,
"x": 650,
"y": 220,
"wires": [
[]
]
},
{
"id": "dee02a99ffd9d808",
"type": "function",
"z": "886e157d7b6324df",
"name": "4-temperature",
"func": "if (msg.payload.payload[4]) {\n msg.payload = msg.payload.payload[4].temperature / 10;\n return msg;\n} else {\n return null;\n}",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 440,
"y": 180,
"wires": [
[
"7a07079123f92475",
"d453e9b737b7bbba",
"cf57f073a2389bbb"
]
]
},
{
"id": "7a07079123f92475",
"type": "debug",
"z": "886e157d7b6324df",
"name": "debug temp",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "{\"temperature\": payload}",
"targetType": "jsonata",
"statusVal": "",
"statusType": "auto",
"x": 650,
"y": 120,
"wires": []
},
{
"id": "35520d39dbc586cb",
"type": "mqtt-broker",
"name": "",
"broker": "infvopedia.nl",
"port": "1883",
"clientid": "",
"autoConnect": true,
"usetls": false,
"protocolVersion": "4",
"keepalive": "60",
"cleansession": true,
"birthTopic": "",
"birthQos": "0",
"birthPayload": "",
"birthMsg": {},
"closeTopic": "",
"closeQos": "0",
"closePayload": "",
"closeMsg": {},
"willTopic": "",
"willQos": "0",
"willPayload": "",
"willMsg": {},
"userProps": "",
"sessionExpiry": ""
},
{
"id": "a4643fc8.e80d68",
"type": "ui_group",
"name": "My-meters",
"tab": "4e75c8d2.40f86",
"order": 1,
"disp": true,
"width": "6",
"collapse": false
},
{
"id": "b7537500.9e9de",
"type": "ui_group",
"name": "My-graphs",
"tab": "4e75c8d2.40f86",
"order": 2,
"disp": true,
"width": "6",
"collapse": false
},
{
"id": "4e75c8d2.40f86",
"type": "ui_tab",
"name": "My-node 99f6",
"icon": "dashboard",
"disabled": false,
"hidden": false
}
]
Bekijken van het dashboard#
open de dashboard-tab in het rechter zij-venster in NodeRed
klik op het “uitgaande pijltje” (rechts) voor het openen van het dashboard in de browser.
Opdracht: lichtniveau in het dashboard#
Opdracht
bekijk de instellingen van de verschillende dashboard-nodes
klik op een node om de instellingen te bekijken en aan te passen
voeg een extra dashboard-flow toe, voor het lichtniveau (sensor/kanaal 8)
Dashboard-flow code
[
{
"id": "886e157d7b6324df",
"type": "tab",
"label": "Flow 12",
"disabled": false,
"info": "",
"env": []
},
{
"id": "752cc73ce4821834",
"type": "mqtt in",
"z": "886e157d7b6324df",
"name": "",
"topic": "node/99f6/sensors",
"qos": "2",
"datatype": "json",
"broker": "35520d39dbc586cb",
"nl": false,
"rap": false,
"inputs": 0,
"x": 190,
"y": 180,
"wires": [
[
"dee02a99ffd9d808"
]
]
},
{
"id": "d453e9b737b7bbba",
"type": "ui_gauge",
"z": "886e157d7b6324df",
"name": "",
"group": "a4643fc8.e80d68",
"order": 0,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "Temperatuur",
"label": "'C",
"format": "{{payload}}",
"min": 0,
"max": "50",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"diff": false,
"className": "",
"x": 650,
"y": 180,
"wires": []
},
{
"id": "cf57f073a2389bbb",
"type": "ui_chart",
"z": "886e157d7b6324df",
"name": "",
"group": "b7537500.9e9de",
"order": 0,
"width": 0,
"height": 0,
"label": "Temperatuur",
"chartType": "line",
"legend": "false",
"xformat": "HH:mm:ss",
"interpolate": "linear",
"nodata": "",
"dot": false,
"ymin": "0",
"ymax": "50",
"removeOlder": 1,
"removeOlderPoints": "",
"removeOlderUnit": "86400",
"cutout": 0,
"useOneColor": false,
"colors": [
"#1f77b4",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
"outputs": 1,
"x": 650,
"y": 220,
"wires": [
[]
]
},
{
"id": "dee02a99ffd9d808",
"type": "function",
"z": "886e157d7b6324df",
"name": "4-temperature",
"func": "if (msg.payload.payload[4]) {\n msg.payload = msg.payload.payload[4].temperature / 10;\n return msg;\n} else {\n return null;\n}",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 440,
"y": 180,
"wires": [
[
"7a07079123f92475",
"d453e9b737b7bbba",
"cf57f073a2389bbb"
]
]
},
{
"id": "7a07079123f92475",
"type": "debug",
"z": "886e157d7b6324df",
"name": "debug temp",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "{\"temperature\": payload}",
"targetType": "jsonata",
"statusVal": "",
"statusType": "auto",
"x": 650,
"y": 120,
"wires": []
},
{
"id": "7de65936a99884a8",
"type": "ui_gauge",
"z": "886e157d7b6324df",
"name": "",
"group": "a4643fc8.e80d68",
"order": 0,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "Licht",
"label": "units",
"format": "{{payload}}",
"min": "0",
"max": "1023",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"x": 630,
"y": 300,
"wires": []
},
{
"id": "a0fed3c53cf4f8bb",
"type": "ui_chart",
"z": "886e157d7b6324df",
"name": "",
"group": "b7537500.9e9de",
"order": 0,
"width": 0,
"height": 0,
"label": "Licht",
"chartType": "line",
"legend": "false",
"xformat": "HH:mm:ss",
"interpolate": "linear",
"nodata": "",
"dot": false,
"ymin": "0",
"ymax": "1023",
"removeOlder": 1,
"removeOlderPoints": "",
"removeOlderUnit": "86400",
"cutout": 0,
"useOneColor": false,
"colors": [
"#1f77b4",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
"outputs": 2,
"x": 630,
"y": 340,
"wires": [
[],
[]
]
},
{
"id": "35520d39dbc586cb",
"type": "mqtt-broker",
"name": "",
"broker": "infvopedia.nl",
"port": "1883",
"clientid": "",
"autoConnect": true,
"usetls": false,
"protocolVersion": "4",
"keepalive": "60",
"cleansession": true,
"birthTopic": "",
"birthQos": "0",
"birthPayload": "",
"birthMsg": {},
"closeTopic": "",
"closeQos": "0",
"closePayload": "",
"closeMsg": {},
"willTopic": "",
"willQos": "0",
"willPayload": "",
"willMsg": {},
"userProps": "",
"sessionExpiry": ""
},
{
"id": "a4643fc8.e80d68",
"type": "ui_group",
"name": "My-meters",
"tab": "4e75c8d2.40f86",
"order": 1,
"disp": true,
"width": "6",
"collapse": false
},
{
"id": "b7537500.9e9de",
"type": "ui_group",
"name": "My-graphs",
"tab": "4e75c8d2.40f86",
"order": 2,
"disp": true,
"width": "6",
"collapse": false
},
{
"id": "4e75c8d2.40f86",
"type": "ui_tab",
"name": "My-node 99f6",
"icon": "dashboard",
"disabled": false,
"hidden": false
}
]
Actuator-flows#
We hebben een aantal nodes gedefinieerd voor het aansturen van de LEDs (display van de micro:bit).
De tekst van de “0-dOut-on” node is:
{"0": {"dOut": 1}}
De tekst van de andere nodes is analoog.
Deze nodes kun je in een actuator-flow gebruiken, met een MQTT-output-node als bestemming
Opdracht
maak deze flow voor je eigen node(s)
breid deze flow uit met een “off” flow