ble入门)
一、几个基本角色概念两个角色(GAP 层定义)Peripheral(外设/从机) 你的戒指:发广播、被连接、提供数据。资源少、省电。Central(中心/主机) 手机:扫描、发起连接、主动读写。两个核心框架GAP(Generic Access Profile):管怎么被发现、怎么连接——广播、扫描、连接、断开。你日志里的[GAP ROLE N]就是它的状态机。GATT(Generic Attribute Profile):管连上之后怎么交换数据——服务、特征、读/写/通知。A5C3/A5C4/A5C5 就是 GATT 的东西。协议栈分层(从下到上,理解即可):PHY(2.4GHz 物理射频,1M/2M 调制) └ LL 链路层(广播、连接、跳频、CRC、重传)——跑在芯片 ROM 里 └ L2CAP通道复用、分包重组 └ ATT属性协议:读/写一个个属性 └ GATT把属性组织成 服务/特征 └ SMP配对加密,你现在没用 GAP横跨:广播/连接策略 设备名你写命令是在最上面 GATT/ATT 层,但底下 LL/PHY 一直在默默跳频、重传、维持连接。二、完整流程(对应你的日志)阶段 0:上电初始化SDK Version ID … / rfClk … / [ble-ring] service registered (8 attrs) SimpleBLEPeripheral_Init Done [ble-mac] static C2:09:4D:FC:B7:40 cfg0 [GAP ROLE 1]协议栈初始化、把 GATT 属性表(服务/特征)登记进去(service registered 8 attrs)。设好设备地址(那个出厂唯一 MAC)。[GAP ROLE 1]GAPROLE_STARTED:栈起来了,但还没开始广播。阶段 1:广播(Advertising)[GAP ROLE 2] GAPROLE_ADVERTISING设备周期性在3 个广播信道(37/38/39,频率 2402/2426/2480 MHz——特意避开 WiFi)上发广播包。广播间隔:每隔几十~几百毫秒发一轮(可配)。间隔越短越好被发现,但越费电。广播包内容:设备名(BS-Ring2)、Flags、有时带厂商数据。手机扫描看到的名字/地址就来自这里。这是单向广撒,谁都能收,还没有连接。阶段 2:扫描与发现(手机侧)手机在同样 3 个信道上监听(扫描)。听到你的广播包 → 显示在列表里(名字 地址 信号强度 RSSI)。手机可选发一个Scan Request问还有更多信息吗?,设备回Scan Response(我们把地址尾段塞这里)。这一步只是看见,还没连。阶段 3:建立连接(Connection)你在手机点连接 → 手机(此刻变成发起方)发一个CONNECT_IND(连接请求)包,里面包含一整套连接参数:接入地址(Access Address):这条连接的唯一标识。连接间隔(Connection Interval):每隔多久双方碰一次头(7.5ms~4s)。从机延迟(Slave Latency):从机可以跳过几次碰头不回(省电)。监督超时(Supervision Timeout):多久收不到对方就判定断了。跳频参数(信道图 hop):之后在37 个数据信道上自适应跳频通信(抗干扰)。设备收到 → 进入连接态:[GAP ROLE 5] GAPROLE_CONNECTED从这一刻起,双方在每个连接事件(Connection Event)按约定的连接间隔在跳频信道上短暂收发,平时射频关掉省电。这就是 BLE 省电的关键——不是一直开着,是约好时间见面。阶段 4:连接参数协商(你日志里这条)[2Mbps | DLE | MTU 247]连上后双方会协商把管道加粗加快:2Mbps PHY:物理层从 1M 提到 2M,传输更快。DLE(Data Length Extension):链路层单包数据从 27 字节扩到最多 251 字节。MTU 247:ATT 层单次能传的最大字节数从默认 23 提到 247。→ 意义:一次能塞更多数据、更快。你的命令帧最长 254 字节,MTU 247 基本一次一帧搞定(再大就要分包)。阶段 5:GATT 服务发现(上一条消息讲过)手机问你有哪些服务/特征?,设备把整张 ATT 表回出去 → 手机解析出 A5C3 服务、A5C4(可写)、A5C5(读通知)及各自句柄。这是 app 不用预先知道 UUID 就能操作的原因。阶段 6:数据交换(你正在做的)连上 发现完,才真正收发业务数据:写(Write):手机往A5C4写命令帧 → 设备[ble-ring] rx 14 B→ 解析 → handler。通知(Notify):设备要主动推数据,手机必须先订阅——往 A5C5 的CCCD(0x2902)写01 00(就是你点启用通知做的)。订阅后设备GATT_Notification把回包推到 A5C5 → 手机收到。这就是为什么没订阅时回包被丢(我们之前- -1的坑)。(还有Read/Indicate等方式,你这套用的是 Write Notify。)阶段 7:连接维持与断开连接期间,即使没数据,双方也按连接间隔定期碰头(空包)以维持链路;超过监督超时没碰上头 → 判定断开。断开(对方主动断 / 超时 / 距离远信号丢):[GAP ROLE 4] GAPROLE_WAITING_AFTER_TIMEOUT(刚断,等待期) [GAP ROLE 2] 重新开始广播断开时 CCCD 订阅会被清掉→ 重连后必须重新启用通知(你之前重连后第一次- -1就是这个)。三、把你设备的 GAP ROLE 状态串起来[GAP ROLE 1] STARTED 栈就绪,未广播 ↓ [GAP ROLE 2] ADVERTISING 广播中(手机能扫到) ↓ 手机发起连接 [GAP ROLE 5] CONNECTED 已连接 → 协商(2M/DLE/MTU)→ 发现 → 收发数据 ↓ 断开/超时 [GAP ROLE 4] WAITING_AFTER_TIMEOUT 刚断 ↓ [GAP ROLE 2] ADVERTISING 回到广播,等下次连接(还有 0INIT、3WAITING、6CONNECTED_ADV、7ERROR,你常见的是 1→2→5→4→2 这个循环。)四、几个容易混淆的点(给新手)概念一句话广播 vs 连接广播单向广撒谁都能收;连接一对一私有管道,要先建立GAP vs GATTGAP 管连不连;GATT 管连上后传什么数据MTU vs DLEMTU 是 ATT 层一次能传多少;DLE 是链路层单包多大。两层独立,都影响吞吐Write vs NotifyWrite手机推给设备(主动);Notify设备推给手机(需先订阅 CCCD)连接间隔 vs 广播间隔广播间隔多久发一次广播;连接间隔连上后多久碰一次头配对/绑定(SMP)加密 记住对方。你现在没用(明文、每次重新发现),量产可能要加五、你现在所处的位置 还没碰的部分已打通:广播 → 连接 → 服务发现 → Write 命令 → Notify 回包(电量查询验证过)。还没做/可选:配对加密(SMP):目前明文传输,谁连上都能读写。连接参数主动优化:从机可请求更省电的连接间隔/延迟(低功耗阶段会用到)。断连自动重连 / 白名单:只让绑定过的手机连。分包重组:超过一个 MTU 的大数据(历史记录、OTA)。