摘要
本文章主要讲解了蓝牙的发展史,蓝牙信号,蓝牙广播数据的格式。最后使用ESP32芯片MicroPython固件给出了蓝牙广播的具体代码,是蓝牙初学者很好的参考资料。
也可以参考下我在B站的蓝牙视频教程:
【ESP32教程】第二章: 低功耗蓝牙BLE相关概念及用法
蓝牙发展史
蓝牙(Bluetooth)是一种无线通信协议,图标如下图所示:
蓝色的背景,中间像是一把白色的小剪刀,蓝牙的图标为什么是这样的呢?
这要从蓝牙的发展史说起!
1994-1997年之间,包括爱立信,英特尔,诺基亚在内的一些科技公司,想要制定一种短距离无线通信协议,用于各种电子设备之间通讯,取代当时的有线通信形式。
各个公司都推出了自己的通信协议,不同厂商的设备并不兼容。
在1997年的一次通信会议上,各个厂商希望制定一种统一的通信协议,使的他们的设备相互兼容。
来自英特尔的一位工程师 Jim Kardach 提议使用“Bluetooth”作为通信协议的名称。
他当时正在阅读有关维京人和哈拉尔国王的历史小说,由于哈拉尔国王以统一了因宗教战争和领土争议而分裂的挪威与丹麦而闻名于世,国王的成就与此次会议的的理念不谋而合,所以该工程师提议使用哈拉尔国王的绰号“Bluetooth”作为通信协议的名称。
传说哈拉尔国王有一个坏死的牙齿,变成的蓝色,所以世人就给他起了个绰号“Bluetooth”。 哈拉尔国王真名拼写“Harald”,蓝牙组织将国王的真名“Harald”和绰号“Bluetooth”的首字母拼合在一起,配以蓝牙的底图,就形成了蓝牙图标。
蓝牙技术演进史:
蓝牙前言技术
目前蓝牙的最高版本是5.2,5.0以上新增的功能如下:
蓝牙5.0
引入 2Mbps 物理层,传输速率提升2倍
引入LongRang规范,传输距离提升4倍
蓝牙5.1
引入方向天线,实现高精度室内定位
蓝牙5.2
引入低功耗音频功能
低功耗蓝牙信道
蓝牙工作在2.4GHz频段,频率范围为 2402MHz – 2480 MHz,每 2MHz 一个信道,共40个信道,其中为3个广播信道,剩余的37为个数据信道。
蓝牙广播就是在这三个广播信道上,以一定的数据格式通过电磁波发射数据。
蓝牙广播数据格式
蓝牙广播包的最大长度是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
按照蓝牙的广播数据格式可以解析为:
通过上述方法,我们可以将蓝牙广播数据格式分割成若干个蓝牙广播结构体,但是先要知道每一个蓝牙广播结构体的含义,还必须知道广播结构体中AD Type 的含义。AD Type 由蓝牙组织联盟指定并发布,可以在蓝牙官方网站上下载相关文档,常用的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
(完成的设备名称),那么余下的数据含义就是完整的设备名称,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:
开发环境的大家可以参考下我之前的这篇文章:自制教学用ESP32开发板【ESP32_Py_Board】① 开发环境搭建
我们在开发环境中键入如下代码,并运行:
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')
打开手机APP(开启蓝牙和定位的权限),可以搜索到蓝牙名称为AB的设备,如下图所示:
上面的代码很简单,仅仅是导入蓝牙功能,创建BLE实体,打开BLE,设置BLE的广播参数。
广播参数主要设置了蓝牙的广播时间间隔为100mS,广播数据为0x02,0x01,0x06,0x03,0x09,0x41,0x42
。
特别地
ADType 0x01 表示的是设备标识,其含义如下:
数据位 | 含义 |
Bit0 | LE 有限可发现模式 |
Bit1 | LE 普通可发现模式 |
Bit2 | 不支持BR/EDR(经典蓝牙) |
BIt3 | 控制器端同时支持BR/EDR和LE |
Bit4 | 主机端同时自持BR/EDR和LE |
Bit5-7 | 保留 |
我们的设备只支持LE(低功耗蓝牙),不支持BR/EDR(经典蓝牙),一般我们都处于普通发现模式,所以我们只设置Bit1和Bit2,及0x06(b00000110)。 |
直播的时候,有家人问,能否将蓝牙设备名称改为中文呢?
当然是可以的,蓝牙名称采用的是UTF-8编码,理论上可以编码出任意国家的文字和字符。
在python
语言中,可以使用.encode("UTF-8")
将字符串传换成UTF-8编码的数组,需要注意的是,一个汉字转换成UTF-8
编码后,将占用三个字节的存储空间。
比如,要将蓝牙设备的名称改为中
,可以使用如下代码:
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个字节装不下时,我们就可以将一部分不太重要的数据放到扫描响应数据里面,来分担广播数据的工作。
蓝牙广播类型
按照蓝牙设备是否支持被链接,是否支持扫描响应,广播类型可以分为如下四类:
可连接的非定向广播: Connectable Undirected Event Type):
最常用的广播方式,可以被扫描,可以被连接。被连接后将进入连接状态。
可连接的定向广播:
主要用于快速连接的需求,其广播的数据中只包含自身地址和对方地址,对方收到发送给自己的定向广播后,会立即发起连接请求,达到快速建立连接的目的。定向广播在广播时会占满整个信道,造成信道拥堵,所以协议规定定向广播广播的最长时间不得超过1.28秒
不可连接的非定向广播:
仅仅发送广播数据,而不能被扫描或者连接。这也是唯一可用于只有发射机而没有接收机设备的广播类型。不可连接广播设备不会进入连接态。
(不可连接)可扫描的非定向广播(Scannable Undirected Event Type):
又称可发现广播,这种广播不能用于发起连接,但允许其他设备扫描该广播设备。这意味着该设备可以被发现,既可以发送广播数据,也可以响应扫描发送扫描回应数据,但不能建立连接。这是一种适用于广播数据的广播形式,动态数据可以包含于广播数据之中,而静态数据可以包含于扫描响应数据之中。
如果有不清楚的地方,欢迎在下方留言!
作者:我是鹏老师