蓝牙入门理解
其实蓝牙和其他各种通讯协议是一样的都是通讯的方式和过程不相同。就像是一个人要到一个目的地方式有很多种例如汽车轮船飞机。其承载的内容都是一致的同一个数据帧--同一个人只不过方式不同。一、蓝牙协议结构其实在硬件上蓝牙协议就分为两个一个是host--mcu微控制器另一个是controller--蓝牙射频芯片。两者的链接亦有多种方式或集成一个芯片或使用spi等。这里的链接方式就对应了HCI的方式。所以HCI就是用来表达host和controller的一个链接中间层。而最后蓝牙协议则是主要集中在host中定义了host对数据接收和处理方式而controller更多的是硬件上的操作将射频信号转化为电信号拼接成常见的数据帧。而对数据帧的处理就是这个蓝牙协议栈结合hostmcu的功能所在。1. MCU微控制器作用MCU是蓝牙设备的核心负责运行蓝牙协议栈、处理数据、控制外设以及执行应用程序逻辑。功能运行蓝牙协议栈如BLE协议栈。 管理射频硬件通过SPI、UART等接口。 处理用户应用程序如传感器数据采集、逻辑控制等。2. 射频硬件RF Transceiver作用射频硬件负责发送和接收蓝牙信号2.4GHz频段。功能调制和解调蓝牙信号。 实现蓝牙物理层PHY和链路层Link Layer的功能。常见射频硬件 Nordic nRF52系列集成射频和MCU。 TI CC2640系列。 ESP32集成Wi-Fi和蓝牙。3. 蓝牙协议栈作用蓝牙协议栈是MCU上运行的软件负责实现蓝牙的通信协议。分层结构分以下三层物理层PHY处理射频信号。链路层Link Layer管理连接、广播和数据包传输。主机控制接口HCIMCU与射频硬件之间的通信接口。逻辑链路控制与适配协议L2CAP数据包的分段和重组。属性协议ATT定义数据的读写操作。通用属性配置文件GATT定义服务和特征值。通用访问配置文件GAP管理设备发现和连接。小结硬件上分为mcu与射频硬件mcu通过蓝牙协议去驱动射频硬件即完成了蓝牙功能。二、蓝牙协议的合作蓝牙的就像包裹层层封装。数据本身要发的那几个字节比如心率 70蓝牙协议一层一层往上包保证能安全发到对方就像物品 → 小袋子 → 盒子 → 快递单 → 运输蓝牙也是应用数据 → GATT → L2CAP → Link Layer → PHY → 无线电发出去ble controller是员工接受mcu的指令。角色对应硬件 / 层级具体工作MCU老板应用层 / GATT/ATT/L2CAP/GAPHost 层1. 给员工定规则设置广播名、连接参数、GATT 服务2. 不参与具体执行只等员工汇报结果3. 处理最终数据比如把心率值显示在屏幕。BLE Controller员工LL 层 / PHY 层1. 严格执行老板的指令按参数广播、扫描、建立连接2. 自主完成 LL 层核心工作跳频、时序、CRC 校验、加密3. 只把结果汇报给老板比如 “已连接手机”“收到读心率指令”。三、蓝牙协议栈我们从Host层开始层层向上解析。蓝牙协议栈主要分为两个部分配对链接--GAP和SM 在两个设备互相发现链接的配置和操作就由这两个层面完成数据传输--GATT和ATT :在链接之后两个设备互相传输数据则是通过这两个层进行操作而最后一个 L2CAP则是一个分岔口使得传输的数据通过L2CAP时能够被分拣到对应的 配对链接块 或 数据传输块。1. GAPGAP(Generic Access Profile)通用访问协议GAP 是所有的蓝牙设备均需实现的Profile主要用于描述device discovery设备发现、connection连接、security requirement安全要求和authentication认证 的行为和方法。GAP可以简单理解为“对LL层的配置”。但是不是直接 “配置 LL 层”而是「定义设备的对外行为规则」并通过协议栈向下传递这些规则最终由 LL 层Controller执行这些规则。简单说GAP 管「规则 / 策略」比如 “我要可被发现”“我要广播 100ms 一次”“我是 Peripheral 角色”LL 层管「执行 / 落地」严格按照 GAP 定义的规则硬件级执行广播、扫描、连接等操作。但是同时GAP 不止管 LL 层GAP 还定义了「设备配对 / 安全」「角色切换」「连接参数协商」等规则这些规则不仅作用于 LL 层还会涉及 ATT/GATT 层的安全逻辑GAP 还是 “行为规范” 而非 “配置工具”GAP 是蓝牙联盟定义的「通用访问规范」是一套标准而不是直接操作 LL 层的函数LL 层是这套标准的 “硬件执行者”。2. GATTGATT(Generic Attribute profile)通用属性协议定义了服务的流程、格式及其所包含的特征包含特征的发现、读取、写入、通知、指示主要用来规范attribute中的数据内容并将不同attribute进行分组分类。从机中可以有多个服务service一个服务中可以有多个特征值Characteristic每个特征值又有自己的属property属性的取值有读、写、通知Notify。每个服务和特征值都有唯一的UUID标识标准UUID为128位协议栈中一般为16位。而我的理解是GATT就是像给Modbus的寄存器定义数据含义读写属性。用「Modbus 地址定义」对标「GATT 数据定义」逐点对齐维度Modbus 地址定义GATT 数据定义核心共性核心目的规定「哪个地址对应什么数据」避免混乱规定「哪个 Handle/UUID 对应什么数据」避免混乱给裸数据赋予「业务含义」数据标识地址如 40001 温度、40002 湿度Handle如 0x0012 心率/ UUID如 0x2A37 心率特征值用唯一标识绑定「位置 - 数据」数据属性读 / 写 / 只读如 40001 只读、40003 可写读 / 写 / Notify/Indicate如心率特征值 可读 Notify定义数据的操作权限数据分类功能码区分如 03 读保持寄存器、06 写单个寄存器Service 分类如 0x180D 心率服务、0x180F 电池服务按业务逻辑分组数据使用场景上位机读 40001 → 知道是温度按规则解析手机读 0x2A37 → 知道是心率按规则解析双方按约定解析数据不用传 “数据说明”3. ATTATT(Attribute Protocol)配置属性协议用于发现、读取和写入对端设备上的属性的规范它分为两个角色Server和Client通常从机为服务端主机为客户端服务端提供拥有关联值的属性集 客户端发现、读、写这些属性服务端也可以主动通知客户端。属性类型用UUID(16bit or 128 bit)的形式来表现属性句柄用于标识一个属性服务器上的所有属性都会分配一个唯一非零的属性句柄属性权限使用许可、认证许可、授权许可属性值 0-512 byte。ATT 不是 “定义规则” 的层而是 “执行规则” 的层 —— 它是 GATT 数据规则的「搬运执行者」也是 BLE 数据交互的「底层操作指令集」类比到 Modbus 里ATT 就是「实现读 / 写寄存器的底层指令」。层级核心角色Modbus 类比运行阶段GAP连接规则定义者配置 LL 层行为串口通信参数配置波特率、校验位初始化阶段为主GATT数据字典定义者给数据赋含义 / 权限Modbus 地址表定义40001 温度初始化阶段为主ATT数据操作执行者按 GATT 规则搬数据Modbus 读 / 写指令03 读寄存器、06 写寄存器运行阶段全程参与4. L2CAPL2CAP(Logical Link Control and Adaption Protocol)链路控制和适配协议对LL进行了一次简单封装LL只关心传输的数据本身L2CAP就要区分是加密通道还是普通通道同时还要对连接间隔进行管理。作用把蓝牙的数据分成不同通道让 ATT、安全加密、其他数据互不干扰分包、拼包对应例子心率数据走通道 1配对加密走通道 2L2CAP 交通分流互不干扰5. Link Layer链路层 LLLink layer链路层链路管理是整个协议栈的核心定义了空中接口数据包格式、比特流处理程序例如错误检查、状态机以及用于无线通信和链路控制的协议主要负责信道管理、广播和扫描、创建和保持连接、收发空中包和加密链路。现实角色两个人面对面喊话、建立对话它干的事手环广播“我在这我叫 XX 手环”手机扫描“我听到你了”双方建立连接规定什么时候发、什么时候收、跳频、加密保证包不乱、不丢对应例子手环广播 → LL 层在工作手机点连接 → LL 层握手连接成功后LL 层维持对话节奏6. Physical Layer物理层 PHY现实角色无线电波 / 空气它只干一件事把 0 和 1 变成电磁波发出去再把电磁波变回 0 和 1。对应例子手环把数据变成2.4GHz 无线电手机天线接收这个信号不关心数据是什么只负责收发比特你可以理解PHY 路 交通工具四、蓝牙开发理解1. 初始化 BLE 控制器Controller 层作用初始化 BLE 硬件核心LL/PHY 层给后续所有操作打基础相当于给 “快递运输车” 通电、检查车况。核心操作使能 BLE 控制器时钟 / 电源配置控制器模式仅 BLE / 双模ESP32 需 menuconfig 选初始化控制器与 MCU 的通信接口如 HCI。编码重点// 伪代码ESP-IDF NimBLE 示例 nimble_port_init(); // 初始化 NimBLE 端口含 Controller nimble_port_freertos_init(ble_host_task); // 启动 BLE 任务这一步是 “一次性配置”协议栈封装好你只需调用初始化 API。2. 配置 GAP 层连接规则作用定义设备的「对外连接规则」告诉 Controller 怎么广播、怎么被发现、是什么角色相当于定 “快递揽收规则”。核心操作设定角色Peripheral被连接如传感器/ Central主动连接如网关配置广播参数广播间隔、是否可连接、设备名称配置连接参数连接超时、从机延迟、心跳间隔。编码重点// 伪代码配置 Peripheral 广播 struct ble_gap_adv_params adv_params { .conn_mode BLE_GAP_CONN_MODE_UNDIR, // 可连接 .disc_mode BLE_GAP_DISC_MODE_GEN, // 可被发现 .interval_min 100, // 广播间隔100ms .interval_max 100, }; ble_gap_adv_start(BLE_OWN_ADDR_PUBLIC, NULL, BLE_HS_FOREVER, adv_params, gap_event_cb, NULL); // 启动广播编码量 5%一次性配置核心是填对广播 / 连接参数只需处理 GAP 事件回调如连接成功 / 断开。3. 定义 GATT 层数据字典作用定义「数据的业务规则」相当于给 BLE 数据建 “Modbus 地址表”告诉协议栈 “哪个 Handle 对应什么数据、有什么权限”。核心操作创建 Service业务分类如心率服务、电池服务在 Service 下创建 Characteristic具体数据项如心率值、电池电量给 Characteristic 配置UUID、操作权限读 / 写 / Notify、Handle 地址绑定读 / 写回调函数核心告诉协议栈 “读这个数据时该执行什么逻辑”。编码重点// 伪代码定义心率服务特征值 // 1. 定义服务 UUID0x180D 是蓝牙标准心率服务 static const struct ble_gatt_svc_def gatt_svcs[] { { .type BLE_GATT_SVC_TYPE_PRIMARY, .uuid BLE_UUID16_DECLARE(0x180D), // 心率服务UUID .characteristics (struct ble_gatt_chr_def[]) { { .uuid BLE_UUID16_DECLARE(0x2A37), // 心率特征值UUID .flags BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY, // 权限 .access_cb heart_rate_read_cb, // 绑定读回调 }, { 0 }, // 结束标记 }, }, { 0 }, }; // 2. 注册 GATT 服务 ble_gatts_count_cfg(gatt_svcs); ble_gatts_add_svcs(gatt_svcs);编码量10%一次性配置核心是 “UUID 权限 回调绑定”这一步是 “数据规则定义”后续所有数据交互都基于此。4. 编写 GATT 回调函数业务逻辑核心作用实现「数据的实际处理逻辑」协议栈触发回调时执行 “读传感器、解析写入数据、计算数值” 等核心业务。核心操作读回调实时获取 / 计算数据如读心率传感器、读电池电压写回调解析上位机写入的数据执行硬件操作如设置闹钟、控制 LEDNotify 逻辑主动推送数据如心率变化时调用 API 推送给上位机。编码重点// 1. 读心率回调你写的核心业务逻辑 static int heart_rate_read_cb(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) { // 步骤1读硬件传感器你的业务逻辑 uint8_t heart_rate read_hr_sensor(); // 你自己实现的传感器读取函数 // 步骤2把数据写入协议栈缓冲区给 ATT 层自动发送 os_mbuf_append(ctxt-om, heart_rate, 1); return 0; } // 2. 主动推送心率Notify void send_heart_rate_notify(uint8_t heart_rate) { struct ble_gatt_chr *chr ble_gatt_chr_find_uuid(BLE_UUID16_DECLARE(0x2A37), gatt_svcs); ble_gattc_notify(0, chr-value_handle, heart_rate, 1); // 主动推送 }编码量85%是你开发的核心工作所有和 “硬件交互、数据计算、业务规则” 相关的代码都在这里。5. 启动广播 / 扫描GAP 层执行作用让 Controller 按 GAP 配置的规则开始广播Peripheral或扫描Central相当于 “快递运输车按规则出发揽收”。核心操作Peripheral调用ble_gap_adv_start()启动广播Central调用ble_gap_disc_start()启动扫描发现设备后发起连接。编码重点只需调用协议栈 API无需复杂逻辑关注连接事件回调如gap_event_cb中处理连接成功。6. 运行时数据交互自动执行作用协议栈自动处理底层数据传输你只需响应回调相当于 “快递分拣、运输全自动化你只需要处理包裹内容”。核心流程全自动无需编码上位机发读 / 写指令 → LL 层拆包 → L2CAP 分通道 → ATT 层校验权限ATT 层触发你写的 GATT 回调 → 你返回数据 / 处理写入ATT 层打包数据 → L2CAP 分通道 → LL 层封装无线包 → 发回上位机。编码重点无需写任何底层代码只需维护业务数据如定时更新传感器值。7. 异常处理可选进阶作用保障稳定性处理连接断开、超时、权限错误等场景。核心操作在 GAP 事件回调中处理 “连接断开”重新启动广播在 GATT 回调中校验数据合法性如写入的闹钟时间不能超 23 点处理 ATT 层权限错误如拒绝非法写入。五、初始化用例gap的配置分三个方面层级核心内容对应代码 / 函数你的理解匹配度1. 静态基础配置初始化 GAP 标准服务、设置不依赖协议栈运行的静态属性设备名、GAP 服务本身gap_init()ble_svc_gap_init()ble_svc_gap_device_name_set()✅ 完全匹配静态数据初始化2. 动态广播配置初始化依赖协议栈同步的动态参数设备地址、广播数据 / 扫描响应、广播间隔并启动广播adv_init()start_advertising()地址获取 广播参数配置 ble_gap_adv_start()✅ 核心匹配动态数据 开启广播3. 事件驱动处理处理广播 / 连接全生命周期事件连接成功 / 断开、广播完成、订阅 / MTU 更新等保证设备行为符合预期gap_event_handler由ble_gap_adv_start()注册协议栈自动触发✅ 完全匹配链接相关事件处理gatt的配置则是/** GATT server initialization* 1. Initialize GATT service* 2. Update NimBLE host GATT services counter* 3. Add GATT services to server*/