简介
TG-BT5-XX标签可以在4种不同的模式下工作:
- 工厂休眠模式
- 配置模式
- 广告模式
- 升级模式
在广告模式下,标签将在蓝牙广告包中广播关于自身的信息。该信息取决于广告报文的类型。
目前,这些是所有支持的类型,可以使用MikroTik Beacon Manager应用程序配置:雷竞技网站
Eddystone-TLM,Eddystone-UID、M雷竞技网站ikroTik和iBeacon.
蓝牙技术在数据交换过程中使用2种类型的通道(每种通道使用不同的频率)。
- 专用于数据传输的数据通道
- 专门做广告的渠道
有40个独特的波段(通道),每个波段有2兆赫的分离。37、38、39通道用于广告,0-36通道用于数据传输。
在发布过程中,会发布BLE广告报文。该报文包含了Preamble、Access Address、PDU和CRS字段。
Preamble和Access Address字段帮助接收端检测帧。CRS字段用于检查错误。PDU定义数据包本身。
雷竞技网站MikroTik标签支持传统的不可连接不可扫描无定向广告(ADV_NOCONN_IND
).在这种情况下,有效载荷由“AdvA”(包含关于广告客户地址的信息的字段)和“AdvData”(包含数据信息的字段)字段组成。
1 octet = 1 byte = 8位
序言 | 1八隅体 |
存取地址 | 四个八位字节 |
PDU |
|
CRS | 三个八位字节 |
雷竞技网站MikroTik包结构
"AdvData"字段结构(最多31字节):
ManufacturerData | 公司标识符 | 四字节(15 ff4f09 ) |
版本 | 这种广告的版本结构 | 1字节(单位) |
用户数据 | 有效载荷的用户配置部分 | 1字节(单位) |
秘密 | 有效载荷的可选加密(AES-ECB)部分 |
|
请注意,所有多字节值都是小端排序的。这意味着,例如,如果您想获得温度值,并且#14和#15字节表示温度为“a1 19”(“加上”温度)→实际温度值将是(0x19a1)/256 = 25.6 C。
关于如何进行转换的详细示例十六进制有效载荷的值小数的后面将显示PDU负载结构部分。
的帮助下配置UserData和Secret字段旗帜
.在“UserData”部分中,调用控制“Secret”是否加密的参数FLAG_ENCRYPTED
.当FLAG_ENCRYPTED = 0
,则表示秘密未加密(第6字节的第1位将设置为0),当FLAG_ENCRYPTED = 1
,这意味着秘密是加密的(第6字节的第1位将被设置为1)。
在“秘密”部分,有6个旗帜
(21八隅体):
FLAG_REED_SWITCH
(第1位-如果设置为1,显示广告播的时候簧片开关是关着的)FLAG_ACCEL_TILT
(第2位-如果设置为1,表示通过倾斜设备发送广告)FLAG_ACCEL_FREE_FALL
(3d位-如果设置为1,表示广告是通过丢弃设备发送的)FLAG_IMPACT_X
(第4位-如果设置为1,表示在广告发布的时刻x轴受到了影响)FLAG_IMPACT_Y
(第5位-如果设为1,表示在投放广告的时刻对y轴有影响)FLAG_IMPACT_Z
(第6位-如果设置为1,表示在发布广告的时刻z轴受到了影响)
例如,如果您看到十六进制消息的第21字节是“02”(当将值“02”从十六进制来二进制“0010”→第2位设置为1)→表示设备倾斜。如果你看到“04”(十六进制“04”箱子是“0100”→3d位设置为1)→表示设备被丢弃(触发自由落体)。如果你看到“38”(十六进制来箱子它是“00111000”→第4、5和6位设置为1)→这意味着当广告发送时,加速度计在所有3个x/y/z轴上检测到一个冲击/唤醒。
更多的例子(对于21 octet的值):
- “08”是对x轴的冲击;
- “18”是对x轴和y轴的影响;
- “28”是对x轴和z轴的影响;
- “10”是对y轴的影响;
- “30”是对y轴和z轴的影响;
- “20”是对z轴的冲击。
雷竞技网站MikroTik PDU负载结构
0 | 15 |
ManufacturerData | 公司标识符 |
1 | FF | ManufacturerData | 公司标识符 |
2 | 4 f | ManufacturerData | 公司标识符 |
3. | 09 | ManufacturerData | 公司标识符 |
4 | 01 | 版本 | 这种广告的版本结构 |
5 | 00 | 用户数据 | 有效载荷的用户配置部分 |
6 | xx * | 秘密 | 秘密:盐 |
7 | xx * | 秘密 | 秘密:盐 |
8 | xx * | 秘密 | 秘密:x轴上的加速度 |
9 | xx * | 秘密 | 秘密:x轴上的加速度 |
10 | xx * | 秘密 | 秘密:y轴上的加速度 |
11 | xx * | 秘密 | 秘密:y轴上的加速度 |
12 | xx * | 秘密 | 秘密:z轴上的加速度 |
13 | xx * | 秘密 | 秘密:z轴上的加速度 |
14 | xx * | 秘密 | 秘密:温度 |
15 | xx * | 秘密 | 秘密:温度 |
16 | xx * | 秘密 | 秘密:正常运行时间 |
17 | xx * | 秘密 | 秘密:正常运行时间 |
18 | xx * | 秘密 | 秘密:正常运行时间 |
19 | xx * | 秘密 | 秘密:正常运行时间 |
20. | 00 | 秘密 | 秘密:国旗 |
21 | 64 | 秘密 | 秘密:batteryPercentage |
* -可以变化
例子
以MikroTik格式(非加密)配置的有效载荷示例如下:雷竞技网站
15 ff4f090100cea6000000000200a01c91085700005 f |
---|
15 ff4f09(前四段)→ManufacturerData。对于每个MikroTik格式的有效负载都是相同的(常量数据)。雷竞技网站
01(4日八隅体)→有效载荷结构的当前版本。对于每个有效载荷(常量数据)应该是相同的。
00(5日八隅体)→负载未加密。“01”表示加密了。
cea6(第6和第7八位)→盐。每个新的有效载荷都应该生成不同的盐值。您可以使用此值检查相同的有效负载是否以不同的方式加密。该值本身不包含任何有用的信息.如果您看到在不同时间间隔内接收到的两个有效载荷的盐值是相同的,这将意味着接收到的两个有效载荷完全相同。您可以使用应用于正常运行时间计算(第17到20字节)的相同原则来计算盐值——参见下面。
0000(第8和第9字节)→广播时刻x轴上的加速度=0米/秒2.检查以下z轴的加速度计算。
0000(第10和第11字节)→广播时刻y轴上的加速度=0米/秒2.检查以下z轴的加速度计算。
0200(第12和第13字节)→广播时刻z轴上的加速度=0.0078米/秒2.要从十六进制格式中获取十进制值,您需要遵循以下步骤:
- 如前所述,多字节值是小端序的,这意味着,要计算域值,您需要切换字节的位置(切换字节顺序)。第一步是把0x的值交换位置02000 x0002.0 x0002转换从十六进制来小数是02.
- 请记住,加速是有符号的8.8不动点格式(2的补数),这意味着你基本上需要将结果除以“256”。第二步是将该值除以256→(0x)0002十六进制或0212月) / 256 =0.0078米/秒2.
- 同样的计算原理也适用于X轴和y轴的加速度。在我们的例子中,它们恰好是0→0x000 /256=0。
a01c(第14和第15字节)→标签检测到的温度,以摄氏度为单位=28.625摄氏度.温度是小端序(因为它是一个多字节值),它是in有符号16位整数[twos补]8.8定点格式,s同样的“公式”也适用于这里:
- 0 x1 ca0/ 256=28.625摄氏度。
91085700(16日至19日)Octets)→标记的正常运行时间,单位为秒=5703825年代.0 x91085700是小端序的,所以只要把八位字节换成0x00570891结果是十进制的5703825。也就是1584.395833小时66天的正常运行时间。
00(第20 octet)→发送有效载荷的触发器(标志)。如果是的话”00这意味着没有检测到触发器,它只是一个定期广播的有效负载(基于为标记配置的发布间隔)。如果值为"04“这意味着设备掉落了(触发了自由落体)。你可以在上面的“标志”和“秘密”部分找到更多的信息包结构部分。
5 f(第21 octet)→标签的电池百分比=95%.0 x5 f从十六进制到dec是95.
解码脚本
在“系统>脚本选项卡,并在那里导入脚本(对于非加密的有效负载)。
# POSIX正则表达式用于过滤广告蓝牙地址。如。“公元前^:33:交流”
#将只包含以这3个字节开头的地址。
#禁用该过滤器,设置为""
:本地地址regex "2C:C8:1B:4B:BB:0A"# POSIX正则表达式用于根据蓝牙广告的数据过滤。相同
#使用与'addressRegex'。
:本地广告数据gex ""#信号强度滤波器。例:-40只包括蓝牙广告
#信号强度大于-40dBm。
#禁用该过滤器,设置为""
:local rssiThreshold ""################################## 蓝牙 ##################################
:global invertU16 do={
:本地倒0
:for idx from=0 to=15 step=1 do={
:本地掩码(1 << $idx)
if ($1 & $mask = 0) do={
:设置$ reversed ($ reversed | $mask)
}
}
返回$倒
}:global le16ToHost do={
:本地LSB [:pick $ 10 0 2]
:本地MSB [:pick $1 2 4]:return [:tonum "0x$msb$lsb"]
}:local le32ToHost do={
:本地LSB [:pick $ 10 0 2]
:local midL [:pick $1 2 4]
:local midH [:pick $1 4 6]
:本地MSB [:pick $1 6 8]:return [:tonum "0x$msb$midH$midL$lsb"]
}:local from88 do={
:全球invertU16
:全球le16ToHost
:local num [$le16ToHost $1]#处理负数
if ($num & 0x8000) do={
设置num (-1 * ([$invertU16 $num] + 1))
}#从8.8转换。缩放1000,因为不支持浮点数
:return (($num * 125) / 32)
}:本地标志str do={
:本地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 ""}
:返回[:select $str 1 [:len $str]]
}#寻找新的蓝牙广告
:全球btOldestAdvertisementTimestamp
:if ([:typeof $btOldestAdvertisementTimestamp] = "nothing") do={
#启动后第一次运行这个脚本,需要初始化
#持久变量
:设置“$btOldestAdvertisementTimestamp 0”
}
:本地广告[/iot蓝牙扫描仪广告打印细节\
As-value where \
纪元> $btOldestAdvertisementTimestamp和\
地址~ $addressRegex和\
数据~ $advertisingDataRegex和\
rssi > $rssiThreshold
]
:本地advCount 0
:local lastAdvTimestamp 0
:本地advJson ""
:本地advSeparator ""#删除MAC/蓝牙地址中的分号
:local minimizeMac do={
:本地最小化
:local lastIdx ([:len $address] - 1)
:for idx from=0 to=$lastIdx step=1 do={
:local char [:pick $address $idx]
:if ($char != ":") do={
:设置$ minimal "$ minimal $char"
}
}
:返回$最小化
}:foreach adv in=$ ads do={
:本地地址($adv->"address")
: $adv->"rssi")
:本地epoch ($adv->"epoch")
:本地AD ($adv->"data")
:本地版本[:tonum "0x$[:pick $ad 8 10]"]
:本地加密[:tonum "0x$[:pick $ad 10 12]"]
:本地盐[$le16ToHost [:pick $ad 12 16]]
:本地加速器[$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]]
:本地运行时间[$le32ToHost [:pick $ad 32 40]]
:本地旗帜[:tonum "0x$[:pick $ad 40 42]"]
:local bat [:tonum "0x$[:pick $ad 42 44]"]:put ("$advCount: \
地址= $地址\
ts = $ \时代
rssi = $ rssi \
版本= $ \
加密的加密\ = $
盐= $ \盐
accelX = $ accelX \
访问= $访问\
accelZ = $ accelZ \
temp = temp \美元
正常运行时间= $正常运行时间\
flags=\"$[$flagStr $flags]\" \
蝙蝠= $ \蝙蝠”
)
设置$advCount ($advCount + 1)
:设置$lastAdvTimestamp $epoch
}
:if ($advCount > 0) do={
:设置$btOldestAdvertisementTimestamp $lastAdvTimestamp
}
唯一需要修改的行是:
:本地地址regex "2C:C8:1B:4B:BB:0A"
行中,您需要输入标签的MAC地址。
用你喜欢的名字保存脚本,例如,解码.
通过命令行接口("新航站楼”按钮在Winbox/Webfig):
[admin@雷竞技网站MikroTik] >系统脚本运行decode 0: address=2C:C8:1B:4B:BB:0A ts=1662553431348 rssi=-45 version=1 encrypted=0 salt=57919 accel= 3 accelY=-35 accz =-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 accelY=-23 accz =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 accelY= -15 accelY=35 accz =15 temp=25550 uptime=1046189 flags="" bat=99
正如您从上面的例子中看到的,脚本将“翻译”来自a的所有有效负载十六进制格式为小数格式化并打印到终端。
您还可以进一步修改脚本,以从“已解码”的值构建消息,并将其发送到您选择的EMAIL、MQTT或HTTP服务器但是!请记住,它可能会加载更多的设备。因此,您需要在运行脚本时测试性能。当解码在服务器端完成时,RouterOS资l雷竞技源会更容易使用。
由于不支持浮点数的事实→小数点后的每一次计算都将被“四舍五入”到一个整数。这就是为什么脚本将计算温度和加速度值按1000缩放(乘以1000).
所以,如果你把温度看成temp = 25546,实际温度为25.546摄氏度(25546/1000)如果你看到accelZ = 15,对z轴的实际加速度为0.015米/秒2(15/1000)。
iBeacon包结构
iBeacon是一种支持的广告报文类型。您可以在后面找到关于协议的更多信息链接.
在本例中,PDU有效负载由“AdvA”(长6字节)和“AdvData”(包含数据信息的字段)字段组成。传统蓝牙设备只能支持31字节长的信标消息。UUID为16字节(MikroTik默认UI雷竞技网站D=b2b98de4-c81c-47c2-b14e-791b3e5587ec)。
“AdvData”字段结构:
ManufacturerData | 公司标识符 | 四字节(1 aff4c00 ) |
BeaconType | 辅助标识符 | 1 octet (const) |
RemainingDataLength | 以字节为单位定义有效负载的剩余长度 | 1 octet (const) |
用户数据 | 有效载荷的用户配置部分 |
|
TxPower | 表示距离设备一米处的信号强度 | 1字节(int) |
iBeacon PDU负载结构
0 | 1 |
ManufacturerData | 公司标识符 |
1 | ff | ManufacturerData | 公司标识符 |
2 | 4摄氏度 | ManufacturerData | 公司标识符 |
3. | 00 | ManufacturerData | 公司标识符 |
4 | 02 | BeaconType | 辅助标识符 |
5 | 15 | RemainingDataLength | 以字节为单位定义有效负载的剩余长度 |
6 | b2 | 用户数据 | 接近UUID |
7 | b9 | 用户数据 | 接近UUID |
8 | 8 d | 用户数据 | 接近UUID |
9 | e4 | 用户数据 | 接近UUID |
10 | c8 | 用户数据 | 接近UUID |
11 | 1 c | 用户数据 | 接近UUID |
12 | 47 | 用户数据 | 接近UUID |
13 | c2 | 用户数据 | 接近UUID |
14 | b1 | 用户数据 | 接近UUID |
15 | 4 e | 用户数据 | 接近UUID |
16 | 79 | 用户数据 | 接近UUID |
17 | 1 b | 用户数据 | 接近UUID |
18 | 3 e | 用户数据 | 接近UUID |
19 | 55 | 用户数据 | 接近UUID |
20. | 87 | 用户数据 | 接近UUID |
21 | 电子商务 | 用户数据 | 接近UUID |
22 | xx * | 用户数据 | 主设备号 |
23 | xx * | 用户数据 | 主设备号 |
24 | xx * | 用户数据 | 次要版本号 |
25 | xx * | 用户数据 | 次要版本号 |
26 | xx * | TxPower | 表示距离设备一米处的信号强度 |
* -可以变化
Eddystone-TLM包结构
Eddystone-TLM是一种支持的广告包类型。您可以在后面找到关于协议的更多信息链接.
在本例中,PDU有效负载由“AdvA”(长6字节)和“AdvData”(包含数据信息的字段)字段组成。雷竞技网站MikroTik default CompleteUUID=03 03 aa fe;ServiceData=11 16 aa fe。
“AdvData”字段结构:
CommonPayload | 广告有效载荷的一部分,对于所有Eddystone的帧类型都是通用的 |
|
TlmPayload | Eddystone-TLM帧有效载荷 |
|
Eddystone-TLM PDU负载结构
0 | 03 |
CommonPayload | CompleteUUID |
1 | 03 | CommonPayload | CompleteUUID |
2 | aa | CommonPayload | CompleteUUID |
3. | 菲 | CommonPayload | CompleteUUID |
4 | 11 | CommonPayload | ServiceData |
5 | 16 | CommonPayload | ServiceData |
6 | aa | CommonPayload | ServiceData |
7 | 菲 | CommonPayload | ServiceData |
8 | 20. | CommonPayload | FrameType |
9 | 00 | TlmPayload | 版本 |
10 | xx * | TlmPayload | BatteryVoltageMv |
11 | xx * | TlmPayload | BatteryVoltageMv |
12 | xx * | TlmPayload | TemperatureC |
13 | xx * | TlmPayload | TemperatureC |
14 | xx * | TlmPayload | AdvertisementCount |
15 | xx * | TlmPayload | AdvertisementCount |
16 | xx * | TlmPayload | AdvertisementCount |
17 | xx * | TlmPayload | AdvertisementCount |
18 | xx * | TlmPayload | UptimeCounter |
19 | xx * | TlmPayload | UptimeCounter |
20. | xx * | TlmPayload | UptimeCounter |
21 | xx * | TlmPayload | UptimeCounter |
* -可以变化
Eddystone-UID包结构
Eddystone-UID是受支持的广告包类型之一。您可以在后面找到关于协议的更多信息链接.
在本例中,PDU有效负载由“AdvA”(长6字节)和“AdvData”(包含数据信息的字段)字段组成。雷竞技网站MikroTik default CompleteUUID=03 03 aa fe;ServiceData=17 16 aa fe。
“AdvData”字段结构:
CommonPayload | 广告有效载荷的一部分,对于所有Eddystone的帧类型都是通用的 |
|
UidPayload | Eddystone-UID帧负载 |
|
Eddystone-UID PDU负载结构
0 | 03 |
CommonPayload | CompleteUUID |
1 | 03 | CommonPayload | CompleteUUID |
2 | aa | CommonPayload | CompleteUUID |
3. | 菲 | CommonPayload | CompleteUUID |
4 | 17 | CommonPayload | ServiceData |
5 | 16 | CommonPayload | ServiceData |
6 | aa | CommonPayload | ServiceData |
7 | 菲 | CommonPayload | ServiceData |
8 | 00 | CommonPayload | FrameType |
9 | xx * | UidPayload | 等数据 |
10 | b2 | UidPayload | Nspace |
11 | b9 | UidPayload | Nspace |
12 | 8 d | UidPayload | Nspace |
13 | e4 | UidPayload | Nspace |
14 | c8 | UidPayload | Nspace |
15 | 1 c | UidPayload | Nspace |
16 | 47 | UidPayload | Nspace |
17 | c2 | UidPayload | Nspace |
18 | b1 | UidPayload | Nspace |
19 | 4 e | UidPayload | Nspace |
20. | xx * | UidPayload | 实例 |
21 | xx * | UidPayload | 实例 |
22 | xx * | UidPayload | 实例 |
23 | xx * | UidPayload | 实例 |
24 | xx * | UidPayload | 实例 |
25 | xx * | UidPayload | 实例 |
26 | 00 | UidPayload | RFU1 |
27 | 00 | UidPayload | RFU2 |
* -可以变化