...
0 | 15 | ManufacturerData | company identifier |
1 | FF | ManufacturerData | company identifier |
2 | 4F | ManufacturerData | company identifier |
3 | 09 | ManufacturerData | company identifier |
4 | 01 | Version | the version of this advertisement structure |
5 | 00 | UserData | user-configured part of the payload |
6 | xx* | Secret | secret: salt |
7 | xx* | Secret | secret: salt |
8 | xx* | Secret | secret: acceleration on the X-axis |
9 | xx* | Secret | secret: acceleration on the X-axis |
10 | xx* | Secret | secret: acceleration on the Y-axis |
11 | xx* | Secret | secret: acceleration on the Y-axis |
12 | xx* | Secret | secret: acceleration on the Z-axis |
13 | xx* | Secret | secret: acceleration on the Z-axis |
14 | xx* | Secret | secret: temperature |
15 | xx* | Secret | secret: temperature |
16 | xx* | Secret | secret: uptime |
17 | xx* | Secret | secret: uptime |
18 | xx* | Secret | secret: uptime |
19 | xx* | Secret | secret: uptime |
20 | 00 | Secret | secret: flags |
21 | 64 | Secret | secret: batteryPercentage |
* - can vary
...
Example
An example of the payload configured in MikroTik's format (non-encrypted) would be:
15ff4f090100cea6000000000200a01c91085700005f |
---|
...
5f (22nd octet) → battery percentage of the tag = 95 %. 0x5f from hex to dec is95.
...
Script for decoding
Add a new script under the "System>Scripts" tab and import the script there (for non-encrypted payloads).
# POSIX regex for filtering advertisement Bluetooth addresses. E.g. "^BC:33:AC"
# would only include addresses which start with those 3 octets.
# To disable this filter, set it to ""
:local addressRegex "2C:C8:1B:4B:BB:0A"# POSIX regex for filtering Bluetooth advertisements based on their data. Same
# usage as with 'addressRegex'.
:local advertisingDataRegex ""# Signal strength filter. E.g. -40 would only include Bluetooth advertisements
# whose signal strength is stronger than -40dBm.
# To disable this filter, set it to ""
:local rssiThreshold ""################################## Bluetooth ##################################
:global invertU16 do={
:local inverted 0
:for idx from=0 to=15 step=1 do={
:local mask (1 << $idx)
:if ($1 & $mask = 0) do={
:set $inverted ($inverted | $mask)
}
}
return $inverted
}:global le16ToHost do={
:local lsb [:pick $1 0 2]
:local msb [:pick $1 2 4]:return [:tonum "0x$msb$lsb"]
}:local le32ToHost do={
:local lsb [:pick $1 0 2]
:local midL [:pick $1 2 4]
:local midH [:pick $1 4 6]
:local msb [:pick $1 6 8]:return [:tonum "0x$msb$midH$midL$lsb"]
}:local from88 do={
:global invertU16
:global le16ToHost
:local num [$le16ToHost $1]# Handle negative numbers
:if ($num & 0x8000) do={
:set num (-1 * ([$invertU16 $num] + 1))
}# Convert from 8.8. Scale by 1000 since floating point is not supported
:return (($num * 125) / 32)
}:local flagStr do={
:local str "":if ($1 & 0x01) do={ :set $str " switch" }
:if ($1 & 0x02) do={ :set $str "$str tilt" }
:if ($1 & 0x04) do={ :set $str "$str free_fall" }
:if ($1 & 0x08) do={ :set $str "$str impact_x" }
:if ($1 & 0x10) do={ :set $str "$str impact_y" }
:if ($1 & 0x20) do={ :set $str "$str impact_z" }:if ([:len $str] = 0) do={ :return "" }
:return [:pick $str 1 [:len $str]]
}# Find fresh Bluetooth advertisements
:global btOldestAdvertisementTimestamp
:if ([:typeof $btOldestAdvertisementTimestamp] = "nothing") do={
# First time this script has been run since booting, need to initialize
# persistent variables
:set $btOldestAdvertisementTimestamp 0
}
:local advertisements [/iot bluetooth scanners advertisements print detail \
as-value where \
epoch > $btOldestAdvertisementTimestamp and \
address ~ $addressRegex and \
data ~ $advertisingDataRegex and \
rssi > $rssiThreshold
]
:local advCount 0
:local lastAdvTimestamp 0
:local advJson ""
:local advSeparator ""# Remove semicolons from MAC/Bluetooth addresses
:local minimizeMac do={
:local minimized
:local lastIdx ([:len $address] - 1)
:for idx from=0 to=$lastIdx step=1 do={
:local char [:pick $address $idx]
:if ($char != ":") do={
:set $minimized "$minimized$char"
}
}
:return $minimized
}:foreach adv in=$advertisements do={
:local address ($adv->"address")
:local rssi ($adv->"rssi")
:local epoch ($adv->"epoch")
:local ad ($adv->"data")
:local version [:tonum "0x$[:pick $ad 8 10]"]
:local encrypted [:tonum "0x$[:pick $ad 10 12]"]
:local salt [$le16ToHost [:pick $ad 12 16]]
:local accelX [$from88 [:pick $ad 16 20]]
:local accelY [$from88 [:pick $ad 20 24]]
:local accelZ [$from88 [:pick $ad 24 28]]
:local temp [$from88 [:pick $ad 28 32]]
:local uptime [$le32ToHost [:pick $ad 32 40]]
:local flags [:tonum "0x$[:pick $ad 40 42]"]
:local bat [:tonum "0x$[:pick $ad 42 44]"]:put ("$advCount: \
address=$address \
ts=$epoch \
rssi=$rssi \
version=$version \
encrypted=$encrypted \
salt=$salt \
accelX=$accelX \
accelY=$accelY \
accelZ=$accelZ \
temp=$temp \
uptime=$uptime \
flags=\"$[$flagStr $flags]\" \
bat=$bat" \
)
:set $advCount ($advCount + 1)
:set $lastAdvTimestamp $epoch
}
:if ($advCount > 0) do={
:set $btOldestAdvertisementTimestamp $lastAdvTimestamp
}
...
Code Block |
---|
[admin@MikroTik] > system script run decode 0: address=2C:C8:1B:4B:BB:0A ts=1662553431348 rssi=-45 version=1 encrypted=0 salt=57919 accelX=3 accelY=-35 accelZ=-70 temp=25535 uptime=1046174 flags="" bat=99 1: address=2C:C8:1B:4B:BB:0A ts=1662553436349 rssi=-40 version=1 encrypted=0 salt=24154 accelX=-19 accelY=-23 accelZ=0 temp=25546 uptime=1046179 flags="" bat=99 2: address=2C:C8:1B:4B:BB:0A ts=1662553446351 rssi=-37 version=1 encrypted=0 salt=37822 accelX=-15 accelY=35 accelZ=15 temp=25550 uptime=1046189 flags="" bat=99 [admin@MikroTik] > |
As you can see from the example above, the script will "translate" all payloads from a hexadecimal format to a decimal format.
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. |
iBeacon packet structure
iBeacon is one of the supported advertising packet types. You can find more information about the protocol following the link.
...