scriptr.io simple client for Mutitech Conduit device

Target audience

This flow is intended to developers using a Multitech® mDot-Box device in combination with a Multitech® Conduit gateway, and leveraging the scriptr.io IoT platform.

Purpose

Send sensor data to scriptr.io to be leveraged in IoT applications.

Details

The flow receives data sent by the mDot-Box device through the LoRa protocol. The received binary payload is parsed into a JSON structure then sent to the developer's scriptr.io account via HTTP.

A simple simulator is also available in the flow in case the developer does not have a Multitech mDot-Box device.

mDot-Box supported modes

  • GPS Survey (temperature, GPS location)
  • LoRa Demo (temperature, luminosity, pressure, GPS location)

How to install

  • Log in to the dashboard of your MultiConnect® ConduitTM
  • Click on Apps, in the menu on the left
  • Click on "Launch Node-Red". You will have to sign-in to the Node-Red editor
  • In the Node-Red editor, click on the menu (top-right corner), select Import > Clipboard
  • In the Import node section, paste the content of the flow below
  • Deploy the Node-Red flow

How to configure

  • From the scriptr.io web IDE, click on the username on the top-right corner, then click on Device directory.
  • Copy the token of the device that is listed
  • Log in to the dashboard of the MultiConnect® ConduitTM
  • Click on Apps, in the menu on the left
  • Click on "Launch Node-Red"
  • Double-click on the "config" node of the Node-Red flow. Set the value of the auh_token variable to the token obtained from scriptr.io
[{"id":"8cf2ef48.66f56","type":"tab","label":"mDot Box > Scriptr.io"},{"id":"d2d46502.7233d8","type":"inject","z":"8cf2ef48.66f56","name":"cron trigger","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":133,"y":112.00000190734863,"wires":[["942bf633.4e6b18"]]},{"id":"942bf633.4e6b18","type":"function","z":"8cf2ef48.66f56","name":"data simulator","func":"// Generate simulated data\nvar temperature = Math.round(Math.random() * 5) + 20;\nvar luminosity = Math.round(Math.random() * 10) + 80;\nvar loc = {\n    lat: getRandomInRange(39, 40, 5),\n    lon: getRandomInRange(69, 70, 5)\n};\n\nvar pressure = Math.round(Math.random() * 4) + 1;\nvar timestamp = msg.payload;\n\n// Prepare request payload\nmsg.payload = {};\nmsg.payload.timestamp = timestamp;\nmsg.payload.temperature = temperature;\nmsg.payload.luminosity = luminosity;\nmsg.payload.location = loc;\nmsg.payload.pressure = pressure;\n\nreturn msg;\n\nfunction getRandomInRange(from, to, fixed) {\n    return (Math.random() * (to - from) + from).toFixed(fixed) * 1;\n    // .toFixed() returns string, so ' * 1' is a trick to convert to number\n}","outputs":1,"noerr":0,"x":287.00000381469727,"y":189.00000190734863,"wires":[["17050156.d4fbef"]]},{"id":"54c8f058.4c395","type":"comment","z":"8cf2ef48.66f56","name":"click for simulation","info":"You can use this simulator in case your Conduit device is not connected to any sensor.\nWhen using a real mdot box device, connect the lora node","x":120.00001525878906,"y":69.00000762939453,"wires":[]},{"id":"17050156.d4fbef","type":"function","z":"8cf2ef48.66f56","name":"config","func":"var auth_token = \"\";\nvar url = \"https://api.scriptrapps.io/app/api/subscription/subscriber\";\n\nmsg.auth_token = auth_token,\nmsg.url = url;\n\n// fill your default device location\n// it will be used if your device cannot lock\n// a GPS location\nmsg.default_location = {\n    lat: 40.6976701, // replace with your lat\n    lon: -74.2598681 // replace with your lon\n}\nreturn msg;","outputs":1,"noerr":0,"x":339.00000762939453,"y":298.0000171661377,"wires":[["c8c96d7c.bb4de"]]},{"id":"6219a9e7.b21748","type":"http request","z":"8cf2ef48.66f56","name":"Send data to scriptr.io","method":"POST","ret":"txt","url":"","x":640.0000610351562,"y":480.00006675720215,"wires":[["313e257d.37059a"]]},{"id":"313e257d.37059a","type":"debug","z":"8cf2ef48.66f56","name":"Response received","active":true,"console":"false","complete":"true","x":736.0000190734863,"y":577.0000400543213,"wires":[]},{"id":"c86eddca.6664","type":"comment","z":"8cf2ef48.66f56","name":"Customize me","info":"This config file has to be customized with the adequest auth token and URL.\n","x":330.00000762939453,"y":337.0000190734863,"wires":[]},{"id":"51b9e56f.1d274c","type":"lora in","z":"8cf2ef48.66f56","name":"mdotbox","datatype":"bytes","x":87,"y":345.0000057220459,"wires":[["1a61a53e.99b99b"]]},{"id":"c8c96d7c.bb4de","type":"function","z":"8cf2ef48.66f56","name":"Configure request","func":"var newMsg = {};\nnewMsg.payload = {\n    temperature: msg.temperature,\n    luminosity: msg.lux,\n    pressure: msg.baro_pressure,\n    location: {\n        lat: msg.lat_deg ? msg.lat_deg : msg.default_location.lat,\n        lon: msg.long_deg ? msg.long_deg : msg.default_location.lon\n    },\n    gps_status: msg.gps_status\n};\n\n// Prepare request headers\nnewMsg.headers = {\n    \"Content-Type\": \"application/json\",\n    \"Authorization\": \"Bearer \" + msg.auth_token \n};\n\n// Prepare request url and method\nnewMsg.url = msg.url;\nnewMsg.method = \"POST\";\n\nreturn newMsg;","outputs":1,"noerr":0,"x":542.0000610351562,"y":374.0000648498535,"wires":[["6219a9e7.b21748","cc3bcdc6.4cf49"]]},{"id":"1a61a53e.99b99b","type":"function","z":"8cf2ef48.66f56","name":"Lora Payload Process","func":"var data_type = {\n\tnone : 0x00,\n\tled1 : 0x01,\n\tled2 : 0x02,\n\tlux_max : 0x03,\n\tlux_min : 0x04,\n\tlux_curr : 0x05,\n\tbaro_max : 0x06,\n\tbaro_min : 0x07,\n\tbaro_curr : 0x08,\n\ttemp_max : 0x09,\n\ttemp__min : 0x0A,\n\ttemp_curr : 0x0B,\n\taccel_max : 0x0C,\n\taccel_min : 0x0D,\n\taccel_curr : 0x0E,\n\tconfiguration : 0x0F,\n\tgpio_in : 0x10,\n\tgpio_out : 0x11,\n\tcurrent_max : 0x12,\n\tcurrent_min : 0x13,\n\tcurrent_curr : 0x14,\n\tgps_latitude : 0x15,\n\tgps_longitude : 0x16,\n\tgps_time : 0x17,\n\tgps_date : 0x18,\n\tgps_lock : 0x19,\n\tqos_up : 0x1A,\n\tqos_dwn : 0x1B,\n\trf_out : 0x1C,\n\tdata_mark : 0x1D,\n};\n\nvar data_struc = {\n\tdata_valid : 0,\n\tblock_start :0,\n\ttemperature : 0,\n\tx_pos : 0,\n\ty_pos : 0,\n\tz_pos : 0,\n\tbaro_pressure : 0,\n\tlux : 0,\n\tpkt_timer :0,\n\trf_pwr : 0,\n\tsf_val : 0,\n\tgateways : 0,\n\tmargin_up : 0,\n\trssi_dwn : 0,\n\tsnr_dwn :0 ,\n\tlat_deg : 0 ,\n\tlat_min : 0,\n\tlong_deg : 0,\n\tlong_min : 0,\n\tnum_sats : 0 ,\n\tgps_status : 0,\n};\n\ncontext.global.data_out = context.global.data_out || data_struc;\nvar pData = context.global.data_out;\n\nvar msg_pntr = 0;\nvar temp = 0;\n\nwhile (msg_pntr < msg.payload.length){\n\tswitch (msg.payload[msg_pntr]){\n\tcase data_type.lux_max:\n\tcase data_type.lux_min:\n\tcase data_type.lux_curr:\n\t\tpData.lux = msg.payload[++msg_pntr] << 8 \n\t\tpData.lux |= msg.payload[++msg_pntr];\n\t\tpData.lux = pData.lux * 0.24;\n\t\tmsg_pntr++;\n\t\tbreak;\n\tcase data_type.baro_max:\n\tcase data_type.baro_min:\n\tcase data_type.baro_curr:\n\t\tpData.baro_pressure = msg.payload[++msg_pntr]<<16;\n\t\tpData.baro_pressure |= msg.payload[++msg_pntr]<<8;\n\t\tpData.baro_pressure |= msg.payload[++msg_pntr];\n\t\tpData.baro_pressure = pData.baro_pressure * 0.25;\n\t\tmsg_pntr++;\n\t\tbreak;\n\tcase data_type.accel_max:\n\tcase data_type.accel_min:\n\tcase data_type.accel_curr:\n\t\tpData.x_pos = ((msg.payload[++msg_pntr] << 24) >> 24) * 0.0625;\n\t\tpData.y_pos = ((msg.payload[++msg_pntr] << 24) >> 24) * 0.0625;\n\t\tpData.z_pos = ((msg.payload[++msg_pntr] << 24) >> 24) * 0.0625;\n\t\tmsg_pntr++;\n\t\tbreak;\n\tcase data_type.temp_min:\n\tcase data_type.temp_max:\n\tcase data_type.temp_curr:\n\t\tpData.temperature = msg.payload[++msg_pntr] << 24;\n\t\tpData.temperature |= msg.payload[++msg_pntr] << 16;\n\t\tpData.temperature = (pData.temperature >> 16) * .0625;\n\t\tmsg_pntr++;\n\t\tbreak;\n\tcase data_type.configuration:\n\t\tpData.pkt_timer = msg.payload[++msg_pntr];\n\t\tmsg_pntr++\n\t\tbreak;\n\tcase data_type.current_max:\n\tcase data_type.current_min:\n\tcase data_type.current_curr:\n\t\tmsg_pntr++;\n\t\tmsg_pntr++;\n\t\tmsg_pntr++;\n\t\tbreak;\n\tcase data_type.gps_latitude:\n\t\tpData.lat_deg = (msg.payload[++msg_pntr] << 24) >> 24;\n\t\tpData.lat_min = msg.payload[++msg_pntr];\n\t\ttemp = msg.payload[++msg_pntr] << 8 \n\t\ttemp |= msg.payload[++msg_pntr];\n\t\tpData.lat_min = pData.lat_min + (temp * 0.0001);\n\t\tmsg_pntr++;\n\t\tbreak;\n\tcase data_type.gps_longitude:\n\t\tpData.long_deg = (msg.payload[++msg_pntr] << 24);\n\t\tpData.long_deg |= (msg.payload[++msg_pntr] << 16);\n\t\tpData.long_deg = pData.long_deg >> 16;\n\t\tpData.long_min = msg.payload[++msg_pntr];\n\t\ttemp = msg.payload[++msg_pntr] << 8 \n\t\ttemp |= msg.payload[++msg_pntr];\n\t\tpData.long_min = pData.long_min + (temp * 0.0001);\n\t\tmsg_pntr++;\n\t\tbreak;\n\tcase data_type.gps_time:\n\t\tmsg_pntr++;\n\t\tmsg_pntr++;\n\t\tmsg_pntr++;\n\t\tmsg_pntr++;\n\t\tbreak;\n\tcase data_type.gps_date:\n\t\tmsg_pntr++;\n\t\tmsg_pntr++;\n\t\tmsg_pntr++;\n\t\tmsg_pntr++;\n\t\tbreak;\n\tcase data_type.gps_lock:\n\t\tmsg_pntr++;\n\t\tpData.gps_status = msg.payload[msg_pntr] & 0x0F;\n\t\tpData.num_sats = msg.payload[msg_pntr++] >> 4;\n\t\tbreak;\n\tcase data_type.qos_up:\n\t\tpData.gateways = msg.payload[++msg_pntr] << 24;\n\t\tpData.gateways |= msg.payload[++msg_pntr] << 16;\n\t\tpData.gateways = pData.gateways >> 16;\n\t\tpData.margin_up = (msg.payload[++msg_pntr] << 24) >> 24;\n\t\tpData.rf_pwr = (msg.payload[++msg_pntr] << 24) >> 24;\n\t\tmsg_pntr++;\n\t\tbreak;\n\tcase data_type.qos_dwn:\n\t\tpData.rssi_dwn = msg.payload[++msg_pntr] << 24;\n\t\tpData.rssi_dwn |= msg.payload[++msg_pntr] << 16;\n\t\tpData.rssi_dwn = pData.rssi_dwn >> 16;\n\t\tpData.snr_dwn = ((msg.payload[++msg_pntr] << 24) >> 24);\n\t\tmsg_pntr++;\n\t\tbreak;\n\tcase data_type.rf_out:\n\t\tpData.rf_pwr = (msg.payload[++msg_pntr] << 24) >> 24;\n\t\tmsg_pntr++;\n\t\tbreak;\n\tcase data_type.data_mark:\n\t\tif (msg_pntr == 0) {\n\t\t\tpData = data_struc;\n\t\t\tpData.block_start = 1;\n\t\t\tmsg_pntr++;\n\t\t}\n\t\telse if (msg_pntr == (msg.payload.length - 1) && (pData.block_start === 1)) {\n\t\t\t\tpData.data_valid = 1;\n\t\t\t\tmsg_pntr++;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tpData = data_struc;\n\t\t\t\tmsg_pntr = msg.payload.length;\n\t\t\t\t}\n\t\tbreak;\n\tdefault:\n\t\tpData = data_struc;\n\t\tmsg_pntr = msg.payload.length;\n\t}\n}\n\npData.sf_val = parseInt(msg.datr.replace(\"SF\",\"  \"),10);\n\ncontext.global.data_out = pData;\n\nreturn pData;","outputs":1,"noerr":0,"x":132,"y":466.0000228881836,"wires":[["17050156.d4fbef"]]},{"id":"cc3bcdc6.4cf49","type":"debug","z":"8cf2ef48.66f56","name":"","active":true,"console":"false","complete":"true","x":716.0000152587891,"y":240.2727289199829,"wires":[]}]
scriptrdotio

Flow Info

created 11 months ago
updated 3 weeks, 5 days ago

Node Types

Core
  • comment (x2)
  • debug (x2)
  • function (x4)
  • http request (x1)
  • inject (x1)
Other
  • lora in (x1)
  • tab (x1)

Tags

  • scriptr.io
  • multitech
  • conduit
  • mDot-Box
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option