低功耗蓝牙(BLE)基本概念汇总:信道分布、数据组包到数据交互

您所在的位置:网站首页 web蓝牙广播发送和接收的区别 低功耗蓝牙(BLE)基本概念汇总:信道分布、数据组包到数据交互

低功耗蓝牙(BLE)基本概念汇总:信道分布、数据组包到数据交互

2024-07-15 17:12| 来源: 网络整理| 查看: 265

本文根据网上资料收集整理,介绍了一些关于低功耗蓝牙的入门知识,非常基础,有经验的请略过。

一、信道分布

一共40个信道,频段范围从2402Mhz-2480Mhz,每2Mhz一个信道,其中3个广播信道,37个数据信道

二、数据组包 (一)广播数据包

一个广播数据包最长37字节,有6字节用作蓝牙设备的MAC地址,我们只需要关注剩余的31个字节就可以了,这31个字节又给分为若干个广播数据体,蓝牙规范中称为AD Structure,每个结构体又分为三部分组成,分别是长度,类型,内容,其中长度占用一个字节,类型一个字节,内容占用若干个字节,长度=类型的字节数+内容占用的字节数=1+N

1.蓝牙MAC地址

蓝牙MAC地址,也称作 Bluetooth MAC (Media Access Control) 地址,这个和网卡的MAC地址类似,是一个48位的唯一硬件标识符,用于在蓝牙设备之间建立连接和通信。它由全球唯一的组织,即 IEEE(Institute of Electrical and Electronics Engineers)负责管理分配。

蓝牙地址通常表示为 12 个十六进制数(例如:00:11:22:33:44:55),其中前6个数字代表蓝牙适配器的厂商 ID,后6个数字是该适配器的独特序列号。蓝牙地址不同于 IP 地址,它们只在网络层次结构上唯一标识设备,而蓝牙地址则更加接近于物理层面上的设备地址。

需要注意的是,在蓝牙通讯过程中,设备不是直接使用蓝牙地址相互通信,而是通过蓝牙协议栈上的 L2CAP(Logical Link Control and Adaption Protocol)层进行通信,L2CAP 层使用其自己的 Channel ID 和 Connection Handle 来标识正在交换数据的蓝牙设备。

2.广播数据包

3.蓝牙广播类型

4、信号握手流程

扫描响应数据和广播数据格式是一样的

扫描响应数据是非必须的

扫描响应可作为广播数据的补充

打捞响应需要一定的触发条件(收到扫描请求)

5.蓝牙状态机

蓝牙链路层一共有5种状态,分别是就绪态,广播态,扫描态,发起态,连接态

蓝牙设备状态的转换:

设备上电后就会处于就绪态,发起广播就会进入广播态,如果被别的设备连接就会进入连接态,断开连接会再次回到就绪态,蓝牙主机设备可以在就绪态发起扫描,进入扫描态。如果发现了想要连接的设备,可以发起连接,此时将进入连接态,如果对对方接受了连接,则双方都会进入连接态,这就是蓝牙的状态机,我们在编程的时候需要控制蓝牙的状态或者根据状态的改变来做出一些动作。

6.广播类型 AD type(1字节) 序号valuename备注10x01 Flags20x02Incomplete List of 16-bit Service Class UUIDs30x03Complete List of 16-bit Service Class UUIDs40x04Incomplete List of 32-bit Service Class UUIDs50x05Complete List of 32-bit Service Class UUIDs60x06Incomplete List of 128-bit Service Class UUIDs70x07Complete List of 128-bit Service Class UUIDs80x08Shortened Local Name90x09Complete Local Name100x0ATx Power Level110x0DClass of Device120x0ESimple Pairing Hash C-192130x0FSimple Pairing Randomizer R-192140x10Device ID150x10Security Manager TK Value160x11Security Manager Out of Band Flags170x12Peripheral Connection Interval Range180x14List of 16-bit Service Solicitation UUIDs190x15List of 128-bit Service Solicitation UUIDs200x16Service Data - 16-bit UUID210x17Public Target Address220x18Random Target Address230x19Appearance240x1AAdvertising Interval250x1BLE Bluetooth Device Address260x1C LE Role270x1DSimple Pairing Hash C-256280x1ESimple Pairing Randomizer R-256290x1FList of 32-bit Service Solicitation UUIDs300x20Service Data - 32-bit UUID310x21Service Data - 128-bit UUID320x22LE Secure Connections Confirmation Value330x23LE Secure Connections Random Value340x24URI350x25Indoor Positioning360x26Transport Discovery Data370x27LE Supported Features380x28Channel Map Update Indication390x29PB-ADV400x2AMesh Message410x2BMesh Beacon420x2CBIGInfo430x2DBroadcast_Code440x2EResolvable Set Identifier450x2FAdvertising Interval - long460x30Broadcast_Name470x31Encrypted Advertising Data480x32Periodic Advertising Response Timing Information490x34Electronic Shelf Label500x3D3D Information Data3D Synchronization Profile510xFFManufacturer Specific Data 7. 服务和特性

BLE设备之间通信都是基于服务和特性。一个蓝牙设备中可以包含若干个服务,一个服务中可以包含若干个特性,每一个服务或者特性都要有一个UUID。

蓝牙的数据交互都是基于某个特性进行的,数据交互有5种方式,分别是Read,Write,Write WithOutRespons,Notify,Indication。

READ:主机读,有流控

WRITE:主机写,有流控

WRITE_WITHOUT_RESPOND:主机写了不回应,没有流控

NOTIFY:从机可以通知主机,不检查使能通知 CCC (不需要对方回应答包,没有流控)。

INDICATE:从机可以指示主机,不检查使能通知 CCC (需要对方回应答包,有流控)

 主机–>从机:READ、WRITE、WRITE_WITHOUT_RESPOND。

从机–>主机:NOTIFY、INDICATE。

 WRITE、WRITE_WITHOUT_RESPONSE 是 CLIENT 端(GATT 主机角色)向 SERVER 端(GATT 从机角色)执行的发送数 据操作。而 NOTIFY 和 INDICATE 是 SERVER 端向 CLIENT 执行的发送数据操作。操作是以 handle 的方式标识。

WRITE、INDICATE 的操作是需要对方响应回复命令,多用于数据交互带流控和可靠的传输方式。

WRITE_WITHOUT_RESPONSE 、NOTIFY 是不需要对方响应回复,多用于数据快速传输的方式。

另外增加私有的特征的特性值关键字有 DYNAMIC,AUTHENTICATION_REQUIRED,分别代表意思如下: DYNAMIC —数据可变处理,当有READ,WRITE,WRITE_WITHOUT_RESPONSE,会产生对应的回调函数 read_callback 和 write_callback 处理,执行获取长度,填入对应的数据等操作。 AUTHENTICATION_REQUIRED —需要配对加密认证标记,代表 CLIENT 端操作该特征的读写必需要经过配对加密后才能被允许,否则操作失败。SERVER 端可以使用该关键字,指示 CLIENT 端需要发起配对加密流程(SERVER 端常用的请求加密方式)。

8. UUID

蓝牙设备在应用层是通过服务和特性去实现的,用下面这张图进行表示,一个服务里面包含若干个特性,每个特性里面又可以有读写,通知等权限,每一个服务和特性都要有一个UUID,UUID是蓝牙组织定义的,用于区分各个服务和特性的标识符,总长度是128bit,比如下面就是两个标准的UUID

考虑到UUID太长,蓝牙组织设置看一个基地址,允许用户使用16bit的UUID与该基地址拼接形成128bit的UUID,比如16bit的UUID 2A37对应128bit的UUID是这样的。

网上UUID生成网站:https://www.uuid.online/

(二)数据包示例 1. 蓝牙广播自身信息

我们将esp32作为外设设别,要想被发现,就需要不断的广播自己的信息,这样才能被中心设备发现。esp32自带的蓝牙模块

import bluetooth #导入BLE功能模块 ble = bluetooth.BLE() #创建BLE设备 ble.active(True) #打开BLE #设置BLE广播数据并开始广播 ble.gap_advertise(100, adv_data = b'\x02\x01\x06\x03\x09\x41\x42')

gap_advertise函数就是在不断的广播蓝牙的信息(间隔100ms),打开手机的蓝牙调试软件,我们就会发现这个蓝牙设备。所有的外设设备都是在不断广播的,但是当蓝牙设备被连接时,其他中心设备将无法再搜索到该设备。这个和前面所说的蓝牙状态机转换相对应。

2. 广播数据包

蓝牙的信息按照一定的数据结构编码成二进制,最终就得到了 b'\x02\x01\x06\x03\x09\x41\x42'。蓝牙广播包的最大长度是37个字节,其中设备地址(AdvA)占用了6个字节,只有31个字节(AdvData)是可用的。这31个可用的字节又按照一定的格式来组织,被分割为n个AD Structure。

    AdvA:表示广播方的地址,即蓝牙设备的MAC地址,长度为6字节。     Data:表示数据包,AdvData由若干个广播数据单元(即AD Structure)组成。AD Structure的结构=Length+AD Type+AD data。         Length:表示该AD Structure数据的总长度,即为AD Type与AD Data的长度和(即不含 Length字段本身的1字节)。         AD Type:表示该广播数据代表的含义,如设备名、UUID等。         AD Data:表示具体的数据内容。

我们按照AD Structure结构来解析一下上面的adv_data数据 在这里插入图片描述 AD Structure 1 字段           长度      取值    说明 Length       1字节    0x02    表示AD Type与AD Data的总长度。 AD Type    1字节    0x01    表示该广播数据代表的含义。此处取值固定为0x01,表示设备标识 AD Data    1字节    0x06    表示蓝牙设备的物理连接能力。esp32只支持LE(低功耗蓝牙),不支持BR/EDR(经典蓝牙),一般都将设备设为处于普通发现模式,所以我们只设置Bit1和Bit2,即0x06(b00000110)。

    bit0:LE受限可发现模式。     bit1:LE通用可发现模式。     bit2:不支持BR/EDR。     bit3:对Same Device Capable(控制器)同时支持BLE和BR/EDR。     bit4:对Same Device Capable(主机)同时支持BLE和BR/EDR。     bit5~7:预留。

AD Structure 2 字段           长度(字节)    取值                 说明 Length       1字节                 0x03              表示AD Type与AD Data的总长度。 AD Type    1字节                 0x09               表示蓝牙的名称 AD Data    2字节               0x41,0x42         蓝牙的名称,asiic表示的就是AB

3.修改中文蓝牙名称

蓝牙广播的数据最终都需要编码成utf-8,我们可以使用encode将中文名称编码成utf-8,然后构建成AD Structure的数据结构,将蓝牙模式与蓝牙名称拼接在一起广播出去

import bluetooth #导入BLE功能模块 ble = bluetooth.BLE() #创建BLE设备 ble.active(True) #打开BLE name = "中国蓝牙".encode() # 编码成utf-8格式 adv_mode = bytearray(b'\x02\x01\x06') # 正常蓝牙模式, ad struct 1 adv_name = bytearray((len(name) + 1, 0x09)) + name # 0x09是蓝牙名称,ad struct 2 adv_data = adv_mode + adv_name ble.gap_advertise(100, adv_data = adv_data)   4. 数据收发交互

连接到esp32的蓝牙之后,就要考虑怎么传输数据了,蓝牙的数据交互依靠的是服务和特性。

    服务services:蓝牙可以提供很多服务,例如键盘鼠标服务,心率监控服务,环境监测服务等,每个服务都有自己的uuid。     特性:有了服务,用户就可以通过调用这个服务获取自己想要的数据了,特性可以理解为接口数据。

直接说蓝牙服务跟特性太抽象了,举几个不太恰当的例子来帮助理解。比如我们要开发一个环境监控服务,这个服务有几个数据接口,例如读取温度的接口,读取湿度的接口,通过每个接口就可以获取想要的数据了,同样我们可以再实现一个电量监控服务,监控设备的电量,也提供一个电量读取的接口,随着服务的增多,我们可以考虑设计一个工厂类

class EnvService: def get_temperature(self): return "38.5" def get_humidity(self): return "0.25" class BatteryService: def get_battery(self): return "78" class FactoryService: def __call__(self, fs): if fs == "env": return EnvService() elif fs == "battery": return BatteryService() es = FactoryService("env") bs = FactoryService("battery") print(es.get_temperature()) print(es.get_humidity()) print(bs.get_batteryself())

    使用蓝牙的时候你怎么能让别人知道你定义的是什么服务和接口呢?蓝牙联盟使用uuid来区分不同的服务和特性。蓝牙联盟定义了非常多的标准服务和特性。当你使用0x1124大家就知道你是一个hid服务,当你使用0x1106大家就知道你是一个文件传输服务,当你使用0x180F就知道你是一个电量监控服务。知道了你是什么服务以后就可以通过相应的接口来进行通信。接口也是使用uuid来定义的,例如0x2A19就是电池电量的监控接口,通过这个uuid来进行数据交互。操作uuid来读取数据,听起来就很茫然,其实是你把这个uuid传入到蓝牙sdk中,sdk会返回一个句柄(接口)来给你操作

env_service_uuid = 0x181A # 定义服务的uuid,映射就是环境监控服务 # 定义特性(接口)的uuid, 还需要指明这个特性是否可读写 env_tmp_uuid = (0x2A6E, WRITE | READ) # 温度特性 env_hum_uuid = (0x2A6F, WRITE | READ) # 湿度特性 # 组成环境检测服务,服务的uuid以及特性的uuid env_service = (service_uuid, (env_tmp_uuid, env_hum_uuid)) services = (env_service, ) # 到蓝牙的sdk注册服务,拿到数据接口,通过这个接口可以读写数据 ((tem_handle, hum_handle, ), ) = ble.gatts_register_services(services) import struct # 温度特性写入数据 ble.gatts_write(tem_handle, struct.pack("


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3