本文章主要介绍了蓝牙广播的概念,蓝牙广播数据的组织格式,最后通过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 长度
例如,如下广播数据:
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 的定义,接下来我们分析下列广播数据的具体含义:
0x05,0x09,0x31,0x32,0x33,0x34,0x02,0x0A,0x08,0x06,0xFF,0x41,0x50,0x50,0x4C,0x45 首先把他分割成一个个AD Structure,分割后如下图所示:
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:

蓝牙实验平台
关于开发环境搭建,可以看看我之前的这个视频哈:

我们在开发环境中新建文件,键入如下代码,并运行:
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") 将字符串转换成UTF-8编码的数组,需要注意的是,一个汉字转换成 UTF-8 编码后,将占用3个字节的存储空间。
比如,要将蓝牙设备的名称改为 中 ,可以使用如下代码:
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个字节装不下时,我们就可以将一部分不太重要的数据放到扫描响应数据里面,来分担广播数据的工作。
通过如下方式可以设置蓝牙的扫描响应数据:
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
后面的文章会继续讲解蓝牙相关的知识,欢迎关注!