on
2020-11-25
12:35 AM
- edited on
2024-06-18
03:01 AM
by
Laurids_PETERSE
This is the second part of a custom LoRaWAN server tutorial.
For the first part which describes up the Network server, please refer to:
How to create a LoRaWAN server on local PC(1/2) - Network server
Now that the network server is up and running, we can move to creating an Application server. In this case it is a Node-Red server running again on localhost with a simple application. It receives frame and based on the value, sends answer back to the Nwk server.
https://nodejs.org/en/download/
npm install -g --unsafe-perm node-red
node-red
The Node Red server works with flows, so that it is easy to use, because it is almost like an application flow chart. The included flow is ready to handle our application, just click top right, Import and link the json file. You will get a pop up with non-recognized package, which is the MQTT broker used in the application. Install it from Manage palette in the top right corner menu, Install tab, find node-red-contrib-mqtt-broker. Once the installation is done, it is needed to fill out the authentication for each http request with username admin, psw admin, otherwise it will return Unauthorized status. Lastly just click the red button Deploy to run the server.
Now we have an Application server and a Network server running, but they do not know about each other. In the App server there is also a MQTT broker which provides connection to the App server, so that it can read the coming data – subscribe and post new data coming back to the Nwk server. So now we must define a MQTT connection for the Nwk server to the MQTT broker.
The flow used in this tutorial is quite simple.
There are two mqtt brokers to receive and send data from and to the server.
The received data is in JSON format, so a converter is used to get the actual data to a JavaScript object.
Then, in the payload.text attribute there is the data sent from our node, so it is compared as a number with a threshold.
The comparison decides which path is taken (increase, decrease, equal) and each path inserts I,D,E character in hexadecimal to the payload.
The original text is deleted, the object converted again to JSON and sent over the mqtt broker.
This was the main part of the application, but because the network server does not have any option to accept all devices, it is either necessary to register the end devices manually (which for a larger amount of devices would be quite tiresome to achieve), or with predefined information (which is in case of larger amount of devices again more complicated to maintain and not human error proof). So, the only easy solution is that the application server will also take care of the registration. Unfortunately, there is no API to react on the event of connection of a new device, but there is at least REST API, which can give out a list of unregistered devices. This list is checked each minute to obtain new devices.
First, and to keep it simple, we need a storage for our devices. A simple function, that will set a global variable to store the devEUI.
To register a node, we need the mentioned list of unregistered devices, which is accessible using GET on the following url: http://localhost:8080/api/events?_filters={"text":"unknown_deveui"}
Then the payload is processed by JSON converter and then a function follows to store the information into the storage variable created before and to prepare the data for the registration request, again using REST API.
The information is converted to JSON, http headers are added and a POST is generated to the following url: http://localhost:8080/api/devices
Just to note, this process does not clear the unknown_deveui event list, so that must be done manually in the server dashboard.
It is possible to filter by the specific event and clear just the registered devices. This will prevent the JSON list of devices to be too long in case of many devices joining. If a device not registered yet has been cleared from the list, it is not an issue, as the device will try to join again, or one can push the reset button on the device to make the device appear again in the list.
You can simply import the flow used in this tutorial by clicking the top right corner -> Import and paste the JSON encoded flow below.
[{"id":"d9da327e.4ef12","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"c16ffb2a.e37088","type":"debug","z":"d9da327e.4ef12","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":870,"y":760,"wires":[]},{"id":"8924618b.cf3b9","type":"mqtt in","z":"d9da327e.4ef12","name":"","topic":"data","qos":"2","datatype":"auto","broker":"8de7932d.d610d","x":690,"y":680,"wires":[["c16ffb2a.e37088","167d6db.1a71892"]]},{"id":"9215b618.66a9f8","type":"switch","z":"d9da327e.4ef12","name":"COMPARE","property":"payload.text","propertyType":"msg","rules":[{"t":"lt","v":"7","vt":"num"},{"t":"gt","v":"7","vt":"num"},{"t":"eq","v":"7","vt":"num"}],"checkall":"true","repair":false,"outputs":3,"x":870,"y":600,"wires":[["3c8f1b7d.71c204"],["8b05244b.b4b798"],["ef29c2b1.5823"]]},{"id":"3c8f1b7d.71c204","type":"change","z":"d9da327e.4ef12","name":"INCREASE","rules":[{"t":"set","p":"payload.data","pt":"msg","to":"49","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1110,"y":520,"wires":[["d1a23cc6.0c9b3"]]},{"id":"8b05244b.b4b798","type":"change","z":"d9da327e.4ef12","name":"DECREASE","rules":[{"t":"set","p":"payload.data","pt":"msg","to":"44","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1110,"y":600,"wires":[["d1a23cc6.0c9b3"]]},{"id":"ef29c2b1.5823","type":"change","z":"d9da327e.4ef12","name":"EQUAL","rules":[{"t":"set","p":"payload.data","pt":"msg","to":"45","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1100,"y":680,"wires":[["d1a23cc6.0c9b3"]]},{"id":"de40a174.faaaa","type":"debug","z":"d9da327e.4ef12","name":"","active":false,"console":"false","complete":"false","x":1530,"y":680,"wires":[]},{"id":"b22aad06.d581d","type":"mqtt out","z":"d9da327e.4ef12","name":"","topic":"tx","qos":"","retain":"","broker":"8de7932d.d610d","x":1510,"y":760,"wires":[]},{"id":"167d6db.1a71892","type":"json","z":"d9da327e.4ef12","name":"","property":"payload","action":"","pretty":false,"x":850,"y":680,"wires":[["9215b618.66a9f8"]]},{"id":"d1a23cc6.0c9b3","type":"change","z":"d9da327e.4ef12","name":"","rules":[{"t":"delete","p":"payload.text","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1370,"y":600,"wires":[["821d13af.e85fc"]]},{"id":"821d13af.e85fc","type":"json","z":"d9da327e.4ef12","name":"","property":"payload","action":"","pretty":false,"x":1310,"y":700,"wires":[["b22aad06.d581d","de40a174.faaaa"]]},{"id":"9627abbf.0d1678","type":"http request","z":"d9da327e.4ef12","name":"","method":"GET","ret":"txt","paytoqs":false,"url":"http://localhost:8080/api/events?_filters={\"text\":\"unknown_deveui\"}","tls":"","persist":false,"proxy":"","authType":"basic","x":670,"y":940,"wires":[["7717d48d.2dd22c"]]},{"id":"2673ac65.6bc8e4","type":"debug","z":"d9da327e.4ef12","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":1230,"y":1060,"wires":[]},{"id":"1736154f.ad5f4b","type":"inject","z":"d9da327e.4ef12","name":"GET new devices","topic":"","payload":"","payloadType":"date","repeat":"60","crontab":"","once":true,"onceDelay":0.1,"x":630,"y":860,"wires":[["9627abbf.0d1678"]]},{"id":"7717d48d.2dd22c","type":"json","z":"d9da327e.4ef12","name":"","property":"payload","action":"","pretty":false,"x":850,"y":940,"wires":[["ffbc473f.3bb198"]]},{"id":"ffbc473f.3bb198","type":"function","z":"d9da327e.4ef12","name":"Register node","func":"var msgIn = msg.payload;\nvar devEUIs = global.get(\"devEUIs\");\nmsg.payload = [];\nfor(var item of msgIn){\n console.log(item.eid);\n if(devEUIs.indexOf(item.eid)==-1){\n msg.payload.push({\n \"appeui\":\"0101010101010101\",\n \"appkey\":\"2B7E151628AED2A6ABF7158809CF4F3C\",\n \"deveui\":item.eid,\n \"profile\":\"TestProfile\"\n \n });\n devEUIs.push(item.eid);\n }\n}\nglobal.set(\"devEUIs\",devEUIs);\n\nreturn msg;","outputs":1,"noerr":0,"x":1060,"y":940,"wires":[["f0f5b0f2.dbc27"]]},{"id":"dc3e2aa9.4fbcb8","type":"inject","z":"d9da327e.4ef12","name":"List registered devices","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":640,"y":1060,"wires":[["7d4eaabe.1c5f04"]]},{"id":"7d4eaabe.1c5f04","type":"http request","z":"d9da327e.4ef12","name":"","method":"GET","ret":"txt","paytoqs":false,"url":"http://localhost:8080/api/devices","tls":"","persist":false,"proxy":"","authType":"basic","x":870,"y":1060,"wires":[["2673ac65.6bc8e4"]]},{"id":"f4dd02ab.66c73","type":"debug","z":"d9da327e.4ef12","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":1290,"y":920,"wires":[]},{"id":"f0f5b0f2.dbc27","type":"json","z":"d9da327e.4ef12","name":"","property":"payload","action":"","pretty":false,"x":1090,"y":860,"wires":[["26aaa1c0.a5493e","f4dd02ab.66c73"]]},{"id":"9ba941c.1a8e3c","type":"http request","z":"d9da327e.4ef12","name":"","method":"POST","ret":"txt","paytoqs":false,"url":"http://localhost:8080/api/devices","tls":"","persist":false,"proxy":"","authType":"basic","x":1510,"y":860,"wires":[[]]},{"id":"26aaa1c0.a5493e","type":"change","z":"d9da327e.4ef12","name":"","rules":[{"t":"set","p":"headers","pt":"msg","to":"{\"Content-Type\": \"application/json\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":1300,"y":860,"wires":[["9ba941c.1a8e3c"]]},{"id":"405d62df.5a248c","type":"comment","z":"d9da327e.4ef12","name":"The AppKey is inside this function","info":"","x":1050,"y":980,"wires":[]},{"id":"9d9327f7.2c1cc8","type":"inject","z":"d9da327e.4ef12","name":"Initiate storage of devEUIs","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":640,"y":1180,"wires":[["d12d6219.f5668"]]},{"id":"d12d6219.f5668","type":"function","z":"d9da327e.4ef12","name":"Init devEUIs","func":"var devEUIs = [];\nglobal.set(\"devEUIs\",devEUIs);\nreturn msg;","outputs":1,"noerr":0,"x":890,"y":1180,"wires":[[]]},{"id":"10cdc4b1.fc265b","type":"mosca in","z":"d9da327e.4ef12","mqtt_port":1883,"mqtt_ws_port":"8081","name":"","username":"","password":"","dburl":"","x":650,"y":440,"wires":[[]]},{"id":"8de7932d.d610d","type":"mqtt-broker","z":"","name":"LoraMQTT","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":false,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""}]
Dear Laura, I could not understand well about the "Adding a flow part" section and how to import json file. When I select the import from top right, I can not find a json file to import. Can you explain how to do it ?