Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

The example above shows us, that the KNOT sees two Bluetooth tags within its operating range with MAC addresses "DC:2C:6E:0F:C0:3D" and "2C:C8:1B:4B:BB:0A", their respective payloads ("DATA" field) and the signal strength ("RSSI" field).

Info
When testing locally to see how many payloads can the KNOT handle, we've achieved the following results →  with 300 tags (in factory settings), scattered around the KNOT and using a Bluetooth filter "keep-newest" (which overwrites previously received payloads for each unique MAC address with the newest one, so that the Bluetooth list would display 1 payload per 1 unique tag's MAC address at all times), all 300 MAC addresses appeared in the KNOT's range after 30-40 seconds. This is where you need to keep in mind that all 300 tags broadcasting over the same channels at the same time will cause interference (delay in the reception). When we "cleared" the Bluetooth payload list, each second the list got 20 new entries and after about ~15 seconds, the list had 250-290 payloads. Then after ~15 seconds more, the list displayed all 300 unique tag payloads. The actual number of tags your KNOT is able to handle is heavily dependent on the environment, so it is better to test it on sight.

With the help of RouterOS scripting and scheduling, we can make the KNOT automatically-periodically scan the payload list and, in case, a specific payload or a specific tag's MAC address is found on the list, we can make the KNOT structure an MQTT message (out of the printed information shown in the example above) and send it to the configured server via MQTT, e-mail or HTTP post. Script examples will be shown later on in the guide.

...

In the second example, we will showcase another topology:

Image Added

We have 3 a few warehouses, 3 a few company delivery vehicles, and 3 assets that we are interested in tracking. Our assets are pallets that hold cargo and our goals are to know:

  • in which specific warehouse (equipped with the KNOT) the asset (equipped with the tag) is currently in and how much time it spent inside the specific warehouse;
  • whether the asset (equipped with the tag) is on the road, traveling between warehouses, and how much time it spent inside the vehicle (equipped with the KNOT);
  • (optional-additionallyoptionally) if TG-BT5-OUT tags are used, what was the temperature during all this time? You can also/instead monitor other parameters that you can get out of the advertised payload, like for example acceleration;
  • (optionally) find out the KNOT's GPS location.

To achieve Bluetooth asset-tracking, just install x1 KNOT per warehouse, x1 KNOT per vehicle, and x1 tag per asset.

...

Code Block
languageros
/system script run tracking
Script

...

that includes temperature data (optional)

In case you wish to add temperature reports to the structured message, use the script below:

Code Block
languageros
/system script add dont-require-permissions=no name=tracking+temp owner=admin policy=\
    ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="# Required package\
    s: iot\r\
    \n\r\
    \n################################ Configuration ################################\r\
    \n# Name of an existing MQTT broker that should be used for publishing\r\
    \n:local broker \"tb\"\r\
    \n\r\
    \n# MQTT topic where the message should be published\r\
    \n:local topic \"v1/gateway/telemetry\"\r\
    \n\r\
    \n# POSIX regex for filtering advertisement Bluetooth addresses. E.g. \"^BC:33:AC\"\r\
    \n# would only include addresses which start with those 3 octets.\r\
    \n# To disable this filter, set it to \"\"\r\
    \n:local addressRegex \"\"\r\
    \n\r\
    \n# POSIX regex for filtering Bluetooth advertisements based on their data. Same\r\
    \n# usage as with 'addressRegex'.\r\
    \n:local advertisingDataRegex \"\"\r\
    \n\r\
    \n# Signal strength filter. E.g. -40 would only include Bluetooth advertisements\r\
    \n# whose signal strength is stronger than -40dBm.\r\
    \n# To disable this filter, set it to \"\"\r\
    \n:local rssiThreshold \"\"\r\
    \n\r\
    \n#Name the KNOT. Identity of the unit that will be senting the message. This name will be \
    reported to the MQTT broker.\r\
    \n:local gwName \"1\"\r\
    \n\r\
    \n################################## Bluetooth ##################################\r\
    \n:put (\"[*] Gathering Bluetooth info...\")\global invertU16 do={\r\
    \n    :local inverted 0\r\
    \n:global makeRecord    :for idx from=0 to=15 step=1 do={\r\
    \n        :local mask (1 jsonStr<< \"{$idx)\r\\"ts\\\":\$ts,\\\"values\\\":{\\\"KNOT_\$gwName\\\":\\\"\$gwName
    \n        :if (\$1 & \$mask = 0) do={\r\
    \n            :set \$inverted (\$inverted | \$mask)\r\
    \n        }\\",\\\"rssi\\\":\$rssi}}\"r\
    \n    }\r\
    \n    :return \$jsonStr$inverted\r\
    \n}   \r\
    \n\r\
    \n#n:global array of record strings collected for each advertising MAC addressle16ToHost do={\r\
    \n    :local lsb [:pick \$1 0 2]\r\
    \n    :globallocal macRecordsmsb [:toarraypick \"\"$1 2 4]\r\
    \n\r\
    \n#n  process advertisements and update macRecords:return [:tonum \"0x\$msb\$lsb\"]\r\
    \n:local advertisements [/iot bluetooth scanners advertisements print detail as-value where\
    \_\\}\r\
    \n:local from88 do={\r\
    \n    :global invertU16\r\
    \naddressn  ~ \$addressRegex and:global \\le16ToHost\r\
    \ndatan ~ \$advertisingDataRegex and \\\r\
    \nrssi > \$rssiThreshold :local num [\$le16ToHost \$1]\r\
    \n\r\
    \n/iot/bluetooth/scanners/advertisements clear    # Handle negative numbers\r\
    \n\r\
    :if (\n:foreach$num adv in=\$advertisements& 0x8000) do={\r\
    \n:local address (\$adv->\"address\"        :set num (-1 * ([\$invertU16 \$num] + 1))\r\
    \n:local   rssi (\$adv->\"rssi\") }\r\
    \n:local epoch (\$adv->\"epoch\")\r\
    \n    # Convert from 8.8. Scale by 1000 since floating point is not supported\r\
    \n    :local recordStr [\$makeRecord ts=\$epoch gwName=\$gwName rssi=\$rssi]return ((\$num * 125) / 32)\r\
    \n}\r\
    \n:ifput ([:len (\$macRecords->\$address)] > 0) do={\"[*] Gathering Bluetooth info...\")\r\
    \n\r\
    \n:localglobal str (\$macRecords->\$address)makeRecord do={\r\
    \n    :local newStrjsonStr \"{\$str,\$recordStr\"ts\r\
    \n:set (\$macRecords->\$address) \$newStr} else={:set (\$macRecords->\$address) \$recordStr":\$ts,\\\"values\\\":{\\\"KNOT_\$gwName\\\":\\\"\$gwName\
    \\\",\\\"temp\\\":\$temp}}\"\r\
    \n\r\
    \n# TODO: add some logic to decide when we want to send datareturn \$jsonStr\r\
    \n:local}  sendData true\r\
    \n\r\
    \n:if (\$sendData) do={n# array of record strings collected for each advertising MAC address\r\
    \n:local jsonStrglobal macRecords [:toarray \"{\"]\r\
    \n\r\
    \n:foreach addr,advRec in=\$macRecords do={n# process advertisements and update macRecords\r\
    \n:set jsonStr \"\$jsonStrlocal advertisements [/iot bluetooth scanners advertisements print detail as-value where\
    \_\\\"\$addr\\\":[\$advRec],\"}\r\
    \nr\
    \naddress ~ \$addressRegex and \\\r\
    \ndata ~ \$advertisingDataRegex and \\\r\
    \n:local payloadlengthnrssi > \$rssiThreshold]\r\
    \n:set payloadlength [:len (\$jsonStr)]\r\
    \n/iot/bluetooth/scanners/advertisements clear\r\
    \n:local remcom\r\
    \n:setforeach remcom [:pick \$jsonStr 0 (\$payloadlength-1)]\adv in=\$advertisements do={\r\
    \n:setlocal jsonStraddress (\$adv->\"address\$remcom}\")\r\
    \n:local message\r\
    \n:set message \"\$jsonStr\"ad (\$adv->\"data\")\r\
    \n:loglocal inforssi (\$adv->\"rssi\$message\";)\r\
    \n:putlocal epoch (\$adv->\"[*] Message structured: \$messageepoch\")\r\
    \n:putlocal temp ([\"[*] Total message size: \$[:len \$message] bytes\")$from88 [:pick \$ad 28 32]]\r\
    \n:put (\"[*] Sending message to MQTT broker...\")                \r\
    \n/iot:local mqttrecordStr publish[\$makeRecord brokerts=\"\$broker\" topic=\"\$topic\" message=\$message}"
Scheduler

Apply a scheduler to the script, so that RouterOS periodically initiates the script by itself:

Code Block
languageros
/system/scheduler/add name=bluetoothscheduler interval=30s on-event="/system/script/run tracking"

You can set up shorter and longer intervals. If you want to send data more often, so that the data is "fresher" →  set up shorter time intervals (10-15 seconds). If you want to send fewer messages, less often → you can set up longer time intervals (30min+).

The JSON message structured using the script has a "ts" value (timestamp) assigned for each payload received. Meaning that when the script is run, for example, every minute, 1 tag is used and the tag broadcasts 1 payload every 10 seconds (that is 6 payloads per minute) → ThingsBoard data (GUI) will be updated every minute, and every minute, 6 new entries will appear (each entry will indicate that it was received 10 seconds after the previous one). And if you send the message every 15 minutes when using 1 tag that is broadcasting a payload every 10 seconds (that is 6*15=90 payloads per 15 minutes) → ThingsBoard data (GUI) will be updated every 15 minutes but 90 entries will appear.

ThingsBoard data visualization and result verification

After you run the script with /system script run tracking or via a scheduler and refresh the GUI portal → all MAC addresses (tags) that are found in the JSON message, will be made into new devices under the ThingsBoard GUI:

Image Removed

To help you visualize the data, you can use the built-in widgets or create your own one.

Select the tag's MAC address from the list of devices, go to the "Latest telemetry" section, checkbox the "reporter" parameter, and click on the "Show on widget" button:

Image Removed

Select a widget that you wish to use, for example under the "Cards" bundle, "Timeseries table" and click on "Add to dashboard":

Image Removed

Create a new dashboard and name it, however, you like. Click on "Add":

Image Removed

Do the same steps for your other tags that appeared under the "Devices" tab. Create a new widget for each unique tag under the same dashboard.

Change the widget's "Timewindow" from "Realtime-last minute" (which is used by default) to "Realtime-current day":

Image Removed

As a result, if both tags are inside the KNOT A range, the dashboard would show:

Image Removed

If they move to the KNOT B range, it would show:

Image Removed

If the tags move to the overlapped area, inside both ranges, both reporters (KNOT_A and KNOT_B) should show up within a few seconds after each other, depending on the interval used in the scheduler:

...

$epoch gwName=\$gwName temp=\$temp]\r\
    \n\r\
    \n:if ([:len (\$macRecords->\$address)] > 0) do={\r\
    \n:local str (\$macRecords->\$address)\r\
    \n:local newStr \"\$str,\$recordStr\"\r\
    \n:set (\$macRecords->\$address) \$newStr} else={:set (\$macRecords->\$address) \$recordStr\
    }}\r\
    \n\r\
    \n# TODO: add some logic to decide when we want to send data\r\
    \n:local sendData true\r\
    \n\r\
    \n:if (\$sendData) do={\r\
    \n:local jsonStr \"{\"\r\
    \n\r\
    \n:foreach addr,advRec in=\$macRecords do={\r\
    \n:set jsonStr \"\$jsonStr\\\"\$addr\\\":[\$advRec],\"}\r\
    \n\r\
    \n:local payloadlength\r\
    \n:set payloadlength [:len (\$jsonStr)]\r\
    \n:local remcom\r\
    \n:set remcom [:pick \$jsonStr 0 (\$payloadlength-1)]\r\
    \n:set jsonStr \"\$remcom}\"\r\
    \n:local message\r\
    \n:set message \"\$jsonStr\"\r\
    \n:log info \"\$message\";\r\
    \n:put (\"[*] Message structured: \$message\")\r\
    \n:put (\"[*] Total message size: \$[:len \$message] bytes\")\r\
    \n:put (\"[*] Sending message to MQTT broker...\")\r\
    \n/iot mqtt publish broker=\"\$broker\" topic=\"\$topic\" message=\$message}"

In this case, the JSON message would look like this:

{
  "2C:C8:1B:4B:BB:0A": [
    {
      "ts": 1680527467840,
      "values": {
        "KNOT_1": "1",
        "temp": 26460
      }
    }
  ],
  "DC:2C:6E:0F:C0:3D": [
    {
      "ts": 1680527464996,
      "values": {
        "KNOT_1": "1",
        "temp": 24750
      }
    },
    {
      "ts": 1680527474996,
      "values": {
        "KNOT_1": "1",
        "temp": 24750
      }
    }
  ]
}
Info

Because of the fact that floating point is not supported → every calculation behind a decimal point will be "rounded up" to a whole number. This is why the script will calculate the temperature and acceleration values scaled by 1000 (multiplied by 1000).
So, if you see the temperature as temp=24750, the real temperature is 24.750 C. To add a decimal point, you will need to do an additional result "translation" on the server side or with additional scripting on the RouterOS side, which will increase the CPU usage of the device.

Script that includes GPS data (optional)

In case you also wish to include GPS data (longitude and latitude values from the KNOTs), use the script shown below:

Code Block
languageros
/system script add dont-require-permissions=no name=tracking+gps+temp owner=admin policy=\
    ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="# Required package\
    s: iot\r\
    \n\r\
    \n################################ Configuration ################################\r\
    \n# Name of an existing MQTT broker that should be used for publishing\r\
    \n:local broker \"tb\"\r\
    \n\r\
    \n# MQTT topic where the message should be published\r\
    \n:local topic \"v1/gateway/telemetry\"\r\
    \n:local gwtopic \"v1/devices/me/telemetry\"\r\
    \n\r\
    \n# POSIX regex for filtering advertisement Bluetooth addresses. E.g. \"^BC:33:AC\"\r\
    \n# would only include addresses which start with those 3 octets.\r\
    \n# To disable this filter, set it to \"\"\r\
    \n:local addressRegex \"\"\r\
    \n\r\
    \n# POSIX regex for filtering Bluetooth advertisements based on their data. Same\r\
    \n# usage as with 'addressRegex'.\r\
    \n:local advertisingDataRegex \"15ff\"\r\
    \n\r\
    \n# Signal strength filter. E.g. -40 would only include Bluetooth advertisements\r\
    \n# whose signal strength is stronger than -40dBm.\r\
    \n# To disable this filter, set it to \"\"\r\
    \n:local rssiThreshold \"\"\r\
    \n\r\
    \n#Name the KNOT. Identity of the unit that will be senting the message. This name will be \
    reported to the MQTT broker.\r\
    \n:local gwName \"2\"\r\
    \n\r\
    \n###########GPS#############\r\
    \n:global lat\r\
    \n:global lon\r\
    \n\r\
    \n/interface ppp-client set ppp-out1 disabled=yes\r\
    \n:log info (\"disabling WWAN to get GPS coordinates\")\r\
    \n\r\
    \n/interface ppp-client at-chat ppp-out1 input=\"AT+QGPSCFG=\\\"priority\\\",0\"\r\
    \n:log info (\"enabling priority for GPS\")\r\
    \n\r\
    \n###the time in the delay below is the time that the device will wait for to get the coord\
    inate fix\r\
    \n:delay 32000ms\r\
    \n:log info (\"reading GPS coordinates\")\r\
    \n/system gps monitor once do={\r\
    \n:set \$lat \$(\"latitude\")\r\
    \n:set \$lon \$(\"longitude\")\r\
    \n}\r\
    \n:if (\$lat != \"none\") do={\\\r\
    \n:log info (\"enabling priority back to WWAN\")\r\
    \n/interface ppp-client at-chat ppp-out1 input=\"AT+QGPSCFG=\\\"priority\\\",1\"\r\
    \n:log info (\"enabling WWAN\")\r\
    \n/interface ppp-client set ppp-out1 disabled=no\r\
    \n:delay 1000ms\r\
    \n###if dial on demand is enabled\r\
    \n/ping 1.1.1.1 count=1\r\
    \n\r\
    \n#the delay below waits for 5 seconds for the ppp connection to get established - this tim\
    e can differ based on the signal strength\r\
    \n:delay 5000ms\r\
    \n:log info (\"posting coordinates via mqtt\")\r\
    \n:local gpsmessage \\\r\
    \n    \"{\\\"latitude\\\":\$lat,\\\r\
    \n    \\\"longitude\\\":\$lon}\"\r\
    \n/iot mqtt publish broker=\$broker topic=\$gwtopic message=\$gpsmessage} else={\\\r\
    \n:log info (\"could not read GPS coordinates...enabling back WWAN\")\r\
    \n/interface ppp-client at-chat ppp-out1 input=\"AT+QGPSCFG=\\\"priority\\\",1\"\r\
    \n/interface ppp-client set ppp-out1 disabled=no\r\
    \n:delay 1000ms\r\
    \n###if dial on demand is enabled\r\
    \n/ping 1.1.1.1 count=1\r\
    \n:delay 5000ms\r\
    \n}\r\
    \n\r\
    \n##################################Bluetooth##################################\r\
    \n:global invertU16 do={\r\
    \n    :local inverted 0\r\
    \n    :for idx from=0 to=15 step=1 do={\r\
    \n        :local mask (1 << \$idx)\r\
    \n        :if (\$1 & \$mask = 0) do={\r\
    \n            :set \$inverted (\$inverted | \$mask)\r\
    \n        }\r\
    \n    }\r\
    \n    return \$inverted\r\
    \n}\r\
    \n\r\
    \n:global le16ToHost do={\r\
    \n    :local lsb [:pick \$1 0 2]\r\
    \n    :local msb [:pick \$1 2 4]\r\
    \n\r\
    \n    :return [:tonum \"0x\$msb\$lsb\"]\r\
    \n}\r\
    \n:local from88 do={\r\
    \n    :global invertU16\r\
    \n    :global le16ToHost\r\
    \n    :local num [\$le16ToHost \$1]\r\
    \n\r\
    \n    # Handle negative numbers\r\
    \n    :if (\$num & 0x8000) do={\r\
    \n        :set num (-1 * ([\$invertU16 \$num] + 1))\r\
    \n    }\r\
    \n\r\
    \n    # Convert from 8.8. Scale by 1000 since floating point is not supported\r\
    \n    :return ((\$num * 125) / 32)\r\
    \n}\r\
    \n:put (\"[*] Gathering Bluetooth info...\")\r\
    \n\r\
    \n:global makeRecord do={\r\
    \n    :local jsonStr \"{\\\"ts\\\":\$ts,\\\"values\\\":{\\\"KNOT_\$gwName\\\":\\\"\$gwName\
    \\\",\\\"temp\\\":\$temp}}\"\r\
    \n    :return \$jsonStr\r\
    \n}   \r\
    \n\r\
    \n# array of record strings collected for each advertising MAC address\r\
    \n:global macRecords [:toarray \"\"]\r\
    \n\r\
    \n# process advertisements and update macRecords\r\
    \n:local advertisements [/iot bluetooth scanners advertisements print detail as-value where\
    \_\\\r\
    \naddress ~ \$addressRegex and \\\r\
    \ndata ~ \$advertisingDataRegex and \\\r\
    \nrssi > \$rssiThreshold]\r\
    \n\r\
    \n/iot/bluetooth/scanners/advertisements clear\r\
    \n\r\
    \n:foreach adv in=\$advertisements do={\r\
    \n:local address (\$adv->\"address\")\r\
    \n:local ad (\$adv->\"data\")\r\
    \n:local rssi (\$adv->\"rssi\")\r\
    \n:local epoch (\$adv->\"epoch\")\r\
    \n:local temp [\$from88 [:pick \$ad 28 32]]\r\
    \n                \r\
    \n:local recordStr [\$makeRecord ts=\$epoch gwName=\$gwName temp=\$temp]\r\
    \n\r\
    \n:if ([:len (\$macRecords->\$address)] > 0) do={\r\
    \n:local str (\$macRecords->\$address)\r\
    \n:local newStr \"\$str,\$recordStr\"\r\
    \n:set (\$macRecords->\$address) \$newStr} else={:set (\$macRecords->\$address) \$recordStr\
    }}\r\
    \n\r\
    \n# TODO: add some logic to decide when we want to send data\r\
    \n:local sendData true\r\
    \n\r\
    \n:if (\$sendData) do={\r\
    \n:local jsonStr \"{\"\r\
    \n\r\
    \n:foreach addr,advRec in=\$macRecords do={\r\
    \n:set jsonStr \"\$jsonStr\\\"\$addr\\\":[\$advRec],\"}\r\
    \n\r\
    \n:local payloadlength\r\
    \n:set payloadlength [:len (\$jsonStr)]\r\
    \n:local remcom\r\
    \n:set remcom [:pick \$jsonStr 0 (\$payloadlength-1)]\r\
    \n:set jsonStr \"\$remcom}\"\r\
    \n:local message\r\
    \n:set message \"\$jsonStr\"\r\
    \n:log info \"\$message\";\r\
    \n:put (\"[*] Message structured: \$message\")\r\
    \n:put (\"[*] Total message size: \$[:len \$message] bytes\")\r\
    \n:put (\"[*] Sending message to MQTT broker...\")\r\
    \n/iot mqtt publish broker=\"\$broker\" topic=\"\$topic\" message=\$message}"

This is where you need to keep in mind the BG77 modem behavior → BG77 cellular modem (used in the KNOT) can not be used to have a cellular CAT-M/NB-IoT ongoing connection and to obtain GPS coordinates at the same time.

When the script is run:

  • PPP interface is disabled and the highest priority is set for GPS reception (while the cellular PPP interface is down) for 32 seconds (this time can be altered in the script);
  • (a) If the device managed to obtain GPS latitude value during the configured 32 seconds (if the value obtained equals anything other than "none"), the script will structure a JSON message with captured latitude and longitude values, the script will set the highest priority for WWAN (change the priority from GPS to WWAN) and enable PPP interface back (for internet access). After that, the script will send GPS data JSON message via the first MQTT publish and, the script will run the Bluetooth part, where the Bluetooth data JSON message is structured and sent via the second MQTT publish (x2 MQTT messages are sent → GPS data MQTT message and Bluetooth data MQTT message);
  • (b) If the device fails to obtain GPS latitude value during the 32-second interval (if the value obtained equals "none"), the script sets the highest priority for WWAN (changes the priority from GPS to WWAN), enables PPP interface back (for internet access) and just processes the Bluetooth part, where the Bluetooth data JSON message is structured and sent via MQTT publish (basically, if GPS data could not be obtained, x1 MQTT message with Bluetooth data is sent).
Scheduler

Apply a scheduler to the script, so that RouterOS periodically initiates the script by itself:

Code Block
languageros
/system/scheduler/add name=bluetoothscheduler interval=50s on-event="/system/script/run tracking"

You can set up shorter and longer intervals. If you want to send data more often, so that the data is "fresher" →  set up shorter time intervals (10-15 seconds). If you want to send fewer messages, less often → you can set up longer time intervals (30min+).

The JSON message structured using the script has a "ts" value (timestamp) assigned for each payload received. Meaning that when the script is run, for example, every minute, 1 tag is used and the tag broadcasts 1 payload every 10 seconds (that is 6 payloads per minute) → ThingsBoard data (GUI) will be updated every minute, and every minute, 6 new entries will appear (each entry will indicate that it was received 10 seconds after the previous one). And if you send the message every 15 minutes when using 1 tag that is broadcasting a payload every 10 seconds (that is 6*15=90 payloads per 15 minutes) → ThingsBoard data (GUI) will be updated every 15 minutes but 90 entries will appear.

ThingsBoard data visualization and result verification

After you run the script with /system script run tracking or via a scheduler and refresh the GUI portal → all MAC addresses (tags) that are found in the JSON message, will be made into new devices under the ThingsBoard GUI:

Image Added

To help you visualize the data, you can use the built-in widgets or create your own one.

Select the tag's MAC address from the list of devices, go to the "Latest telemetry" section, checkbox KNOT IDs that you wish to monitor, and click on the "Show on widget" button:

Image Added

Select a widget that you wish to use, for example under the "Charts" bundle, "Timeseries Bar Chart" and click on "Add to dashboard":

Image Added

Create a new dashboard and name it, however, you like. Click on "Add":

Image Added

Change the widget's "Timewindow" from "Realtime-last minute" (which is used by default) to "Realtime-last 5 hours" and disable "data aggregation function" (select "none"):

Image Added

To help you better visualize the result, edit the widget and then edit each "KNOT_X" parameter/key. Enable the "Show points" checkbox for each key:

Image Added

Check the ThingsBoard widget guide for more options that you have.

The end result would look like this:

Image Added

Per the dashboard, we can tell that:

  • from ~11:00 to ~11:30, our asset was inside KNOT_1 Bluetooth range (inside warehouse #1);
  • from ~11:30 to ~11:35, our asset was relocated to the vehicle (KNOT_2) that was parked near the warehouse (the tag was inside both KNOT's ranges);
  • from ~11:35 to ~12:00, the tag was inside the truck (KNOT_2) - traveling to another warehouse;
  • from ~12:00 to ~12:05, the asset was parked outside of warehouse #2, and it was inside both KNOT_2 and KNOT_3 ranges at the same time;
  • from ~12:05 to 12:30, our asset was stored inside warehouse #2 (KNOT_3);
  • from ~12.:30 onwards, the tag was on the road again, inside the truck (KNOT_2).

Temperature visualization (optional)

Select the tag's MAC address from the list of devices, go to the "Latest telemetry" section, checkbox "temp" parameter, and click on the "Show on widget" button:

Image Added

Select a widget that you wish to use, for example under the "Charts" bundle, "Timeseries Line Chart". Click on "Add to dashboard", and choose the dashboard where you want to add the widget.

The result would look like this:

Image Added

Now you have an additional graph that indicates how the tag's temperature changes during different time intervals.

GPS coordinate visualization (optional)

Per the script in the Script that includes GPS data section, the script sends x2 MQTT messages. Each message is sent to a different MQTT topic. GPS coordinate message will be posted to a topic named "1/devices/me/telemetry", while Bluetooth data will be posted to a topic named "v1/gateway/telemetry". Coordinates will be available to you under the ThingsBoard device list, under the specific gateway:

Image Added

Checkbox both "latitude" and "longitude" parameters, click on the "Show on widget button", select "Current bundle" to "Maps", and choose the "Route Map - OpenStreetMap" widget:

Image Added

To finish things up, click on the "Add to dashboard" button and choose the dashboard where you want the widget to be shown.

After adding 3 widgets into 1 dashboard (temperature line chart, Bluetooth reporter bar chart, and GPS coordinates map), you would get something similar to this:

Image Added

  • You will have a graph showing temperature changes (the tag's surrounding temperature);
  • You will have the chart that indicates which specific KNOT has sent the report, that tells you in which KNOT's Bluetooth range the tag is currently in;
  • You will have a map that shows the GPS position of the KNOT.