【低功耗蓝牙】广播数据分析
我是鹏老师
2022年01月26日 19:16
收录于文集
共6篇

摘要

本文章主要介绍了蓝牙广播的概念,蓝牙广播数据的组织格式,最后通过ESP32芯片平台验证了上述理论的正确性,给出了具体的代码供各位读者参考。

蓝牙广播

低功耗蓝牙工作在2.4GHz频段,频率范围为 2402MHz – 2480 MHz,每2MHz一个信道,共40个信道,其中为3个广播信道,剩余的37为个数据信道。

蓝牙信道

蓝牙广播就是在这 3 个广播信道上,以一定的数据格式通过电磁波发射数据。

蓝牙广播数据格式

蓝牙广播包的最大长度是37个字节,其中设备地址占用了6个字节,只有31个字节是可用的。这31个可用的字节又按照一定的格式来组织,被分割为n个AD Structure。如下图所示:

蓝牙广播数据格式

如上图所示,每个AD Structure包含又包含三部分,分别是:

Length(1字节),AD Type(1字节),AD Data(n字节)

其中Length = AD Type 长度 + AD Data 长度

例如,如下广播数据:

代码块
Python
自动换行
复制代码
0x04,0x09,0x41,0x42,0x43,0x03,0x19,0x80,0x01
复制成功

按照蓝牙的广播数据格式可以解析为 2 个AD Structure,如下图:

蓝牙广播数据分解

上述数据的第一个字节 0x04 是第一个AD Structure的长度,表示后面有4个字节的数据属于该AD Structure,分别是 0x09, 0x41, 0x42, 0x43, 后面的数据 0x03 是第二个 AD Structure 的长度,表示后面有3个字节的数据属于该AD Structure,分别是 0x19, 0x80, 0x01

通过上述方法,我们可以将蓝牙广播数据格式分割成若干个 AD Structure ,但是想要知道每一个 AD Structure 的含义,还必须知道广播结构体中 AD Type 的含义。AD Type 由蓝牙组织联盟指定并发布,可以在蓝牙官方网站上下载相关文档,常用的 AD Type 及其含义如下:

常见 AD Type

完整的的AD Type定义可以从这里下载:https://btprodspecificationrefs.blob.core.windows.net/assigned-numbers/Assigned%20Number%20Types/Generic%20Access%20Profile.pdf

现在我们有了 AD Type 的定义,接下来我们分析下列广播数据的具体含义:

代码块
Python
自动换行
复制代码
0x05,0x09,0x31,0x32,0x33,0x34,0x02,0x0A,0x08,0x06,0xFF,0x41,0x50,0x50,0x4C,0x45
复制成功

首先把他分割成一个个AD Structure,分割后如下图所示:

代码块
Python
自动换行
复制代码
0x05,0x09,0x31,0x32,0x33,0x34
0x02,0x0A,0x08
0x06,0xFF,0x41,0x50,0x50,0x4C,0x45
复制成功

一共可以分割成三个AD Structure: 第一个AD Structure的长度为 0x05,类型为 0x09(完成的设备名称),那么该AD Structure 余下的数据含义就是完整的设备名称,0x31,0x32,0x33,0x34 按照UTF-8编码,就是1234。

第二个AD Structure的长度为 0x02,类型为 0x0A(发射功率),那么后面的数据 0x08 表示的就是发射功率。

第三个AD Structure 的长度为 0x06,类型为 0xFF(厂商自定义数据),余下的数据 0x41,0x50,0x50,0x4C,0x45 , 就是厂商自定义的内容。

通过上面的示例,我想大部分读者应该都理解蓝牙广播数据格式了吧!接下来,我们就通过实践验证下上面的理论。

蓝牙广播数据实践

接下来的实验使用的是ESP32芯片平台,Micro Python编程语言,uPyCraft开发环境,nRF Connect 手机APP:

蓝牙实验平台

关于开发环境搭建,可以看看我之前的这个视频哈:

我们在开发环境中新建文件,键入如下代码,并运行:

代码块
Python
自动换行
复制代码
from machine import Pin
from time import sleep_ms
import ubluetooth   #导入BLE功能模块

ble = ubluetooth.BLE()  #创建BLE设备
ble.active(True)  #打开BLE

#设置BLE广播数据并开始广播
ble.gap_advertise(100, adv_data = b'\x02\x01\x06\x03\x09\x41\x42')
复制成功

上面的代码很简单,仅仅是导入蓝牙功能,创建BLE实体,打开BLE,设置BLE的广播参数。

广播参数主要设置了蓝牙的广播时间间隔为100mS,广播数据为:

打开nRF Connect 手机APP(开启蓝牙和定位的权限),可以搜搜的如下设备:

蓝牙设备搜索页面

进一步点击 RAW 按钮,可查看原始广播数据,示例如下: 

原始广播数据

特别注意:

ADType 0x01 表示的是设备标识,其含义如下:

设备标识的定义

我们的设备只支持LE(低功耗蓝牙),不支持BR/EDR(经典蓝牙),一般都将设备设为处于普通发现模式,所以我们只设置Bit1和Bit2,即0x06(b00000110)。

中文设备名称

播的时候,有家人问,能否将蓝牙设备名称改为中文呢?

当然是可以的,蓝牙名称采用的是UTF-8编码,理论上可以编码出任意国家的文字和字符。

Python语言中,可以使用 .encode("UTF-8&#​34;) 将字符串转换成UTF-8编码的数组,需要注意的是,一个汉字转换成 UTF-8 编码后,将占用3个字节的存储空间。

比如,要将蓝牙设备的名称改为 ,可以使用如下代码:

代码块
Python
自动换行
复制代码
from machine import Pin
from time import sleep_ms
import ubluetooth  #导入BLE功能模块

ble = ubluetooth.BLE()  #创建BLE设备
ble.active(True)  #打开BLE

#设置BLE广播数据并开始广播
ble.gap_advertise(100, adv_data = b'\x02\x01\x06\x04\x09' + "中".encode("UTF-8"))
复制成功

按照上面的方式,不仅可以将设备名称设置为中文,甚至可以设置成任何国家的语言或者符号,同学们可以自己试一试。

扫描请求和扫描响应

蓝牙设备除了可以通过上述方式,主动的发射广播数据外,还可以接受其他设备的扫描请求,从而响应额外的数据,二者的区别如下:

广播是蓝牙从机设备 主动 发出的数据。

而扫描响应是, 当蓝牙主机收到从机的广播数据后,如果想要进一步了解该从机设备的信息,可以向从机设备发送扫描请求,从机收到扫描请求后,向对应的主机回复扫描响应。

扫描响应示意图

扫描响应的数据格式和蓝牙广播的数据格式完全一样,其作用也基本一样,那为什么还要设置这么一个扫描响应数据呢?

我们前文中讲到,蓝牙的广播数据最多是31个字节,如果广播数据太多,这31个字节装不下时,我们就可以将一部分不太重要的数据放到扫描响应数据里面,来分担广播数据的工作。

通过如下方式可以设置蓝牙的扫描响应数据:

代码块
Python
自动换行
复制代码
from machine import Pin
from time import sleep_ms
import ubluetooth         #导入BLE功能模块

ble = ubluetooth.BLE()    #创建BLE设备
ble.active(True)          #打开BLE

#设置BLE广播数据并开始广播
ble.gap_advertise(100, adv_data = b'\x02\x01\x06\x03\x09\x41\x42', resp_data = b'\x03\xFF\x4C\x00')
复制成功

如上述代码,将扫描响应数据设置为 0x03,x0FF,0x4C,0x00

后面的文章会继续讲解蓝牙相关的知识,欢迎关注!