串口协议(SPP)本笔记为作者再学习蓝牙Host协议栈的一些心得体会如有不对的地方请包含与谅解​ ————by wsoz串口协议(SPP)SPPSerial Port Profile串口协议是蓝牙协议栈中最常用的应用层Profile之一它基于RFCOMM协议为蓝牙设备提供虚拟串口功能使得蓝牙通信可以像传统RS-232串口一样简单易用。注意苹果不支持 SPP 协议协议定位SPP在蓝牙协议栈中的位置┌─────────────────────────────────┐ │ 应用程序 (Application) │ ├─────────────────────────────────┤ │ SPP (Serial Port Profile) │ ← 应用层Profile ├─────────────────────────────────┤ │ RFCOMM (串口仿真层) │ ├─────────────────────────────────┤ │ L2CAP (逻辑链路层) │ ├─────────────────────────────────┤ │ HCI (主机控制接口) │ ├─────────────────────────────────┤ │ Baseband (基带) │ └─────────────────────────────────┘核心特点特性说明简单易用提供类似串口的API开发者无需关心底层细节透明传输应用层数据直接透传SPP不做任何处理双向通信支持全双工数据传输广泛应用蓝牙打印机、GPS、数据采集器等大量使用简单理解SPP就是蓝牙版的串口线把两个设备的串口通过蓝牙无线连接起来。设备角色SPP定义了两种设备角色这些角色决定了连接建立的方向和SDP服务的注册方式。角色定义角色英文名称说明典型设备Server服务端提供SPP服务等待连接蓝牙打印机、GPS模块、传感器Client客户端主动发起连接使用SPP服务手机、电脑、平板角色关系图Client (客户端) Server (服务端) ┌─────────────┐ ┌─────────────┐ │ 手机/PC │ │ 蓝牙打印机 │ │ │ │ │ │ 主动发起 │ ──── 连接 ────→ │ 等待连接 │ │ 连接请求 │ │ 提供服务 │ │ │ ←─── 数据 ────→ │ │ └─────────────┘ └─────────────┘注意角色是逻辑概念与蓝牙底层的Master/Slave无关一个设备可以同时作为多个SPP连接的Server或Client角色在连接建立时确定连接期间不变SDP服务发现在建立SPP连接之前Client需要通过**SDPService Discovery Protocol**查询Server提供的SPP服务信息。SDP记录结构Server端需要在SDP数据库中注册SPP服务记录包含以下关键信息属性Attribute ID说明典型值ServiceClassIDList0x0001服务类别UUID0x1101 (SerialPort)ProtocolDescriptorList0x0004协议栈描述L2CAP RFCOMM Server ChannelServiceName0x0100服务名称“Serial Port”ServiceDescription0x0101服务描述可选描述服务用途ProtocolDescriptorList详解这是SDP记录中最关键的字段描述了协议栈层次和RFCOMM通道号ProtocolDescriptorList: - L2CAP (PSM 0x0003) ← RFCOMM使用的L2CAP PSM - RFCOMM (Server Channel N) ← SPP使用的RFCOMM通道号(1-30)Server Channel范围1-30RFCOMM协议规定0保留31用于其他用途SDP查询流程Client Server │ │ │ ① SDP Service Search Request │ 查询SPP服务 │ ──────────────────────────────────→│ (UUID0x1101) │ │ │ ② SDP Service Search Response │ 返回服务句柄 │←──────────────────────────────────│ │ │ │ ③ SDP Service Attribute Request │ 获取服务详细信息 │ ──────────────────────────────────→│ (包括Server Channel) │ │ │ ④ SDP Service Attribute Response │ 返回协议栈描述 │←──────────────────────────────────│ (RFCOMM Channel 5) │ │ │ 现在Client知道了Server的RFCOMM通道号 │关键信息提取Client从SDP响应中解析出RFCOMM Server Channel这个通道号将用于后续的RFCOMM连接建立连接建立流程SPP连接建立需要经过多个协议层的协商从底层到上层依次为ACL → L2CAP → RFCOMM → SPP。在建立SPP虚拟串口的过程中的步骤大概是通过 SDP 获取对端的 SPP 对应 RFCOMM 的 Server channel进行 authentication 认证以及 encryption加密这两个步骤都是可选的Authentication认证验证对方设备的身份确保连接的是预期的设备通过**配对Pairing**过程完成建立链路密钥Link Key用于后续的加密Encryption加密对蓝牙链路上传输的数据进行加密防止数据被第三方窃听基于认证过程中生成的链路密钥建立基于 L2CAP RFCOMM PSM 通道建立 RFCOMM Control 通道建立基于 RFCOMM 的 SPP server channel完整连接流程Client Server │ │ │ 第1步ACL连接建立 │ │ │ │ HCI Create Connection │ │ ─────────────────────────────────────────────→│ │ Connection Complete │ │←─────────────────────────────────────────────│ │ │ │ 第2步SDP查询 │ │ │ │ SDP Service Search Attribute Request │ │ ─────────────────────────────────────────────→│ │ SDP Response (Channel5) │ │←─────────────────────────────────────────────│ │ │ │ 第3步L2CAP通道建立 │ │ │ │ L2CAP Connection Request (PSM0x0003) │ │ ─────────────────────────────────────────────→│ │ L2CAP Connection Response │ │←─────────────────────────────────────────────│ │ │ │ L2CAP Configure Request │ │ ─────────────────────────────────────────────→│ │ L2CAP Configure Response │ │←─────────────────────────────────────────────│ │ L2CAP Configure Request │ │←─────────────────────────────────────────────│ │ L2CAP Configure Response │ │ ─────────────────────────────────────────────→│ │ │ │ 第4步RFCOMM会话建立 │ │ │ │ SABM (DLCI0) │ 建立控制通道 │ ─────────────────────────────────────────────→│ │ UA (DLCI0) │ │←─────────────────────────────────────────────│ │ │ │ 第5步RFCOMM数据通道建立 │ │ │ │ PN Command (DLCI10) │ 参数协商 │ ─────────────────────────────────────────────→│ (Server Channel5) │ PN Response │ │←─────────────────────────────────────────────│ │ │ │ SABM (DLCI10) │ 建立数据通道 │ ─────────────────────────────────────────────→│ │ UA (DLCI10) │ │←─────────────────────────────────────────────│ │ │ │ MSC Command (DLCI10) │ 交换串口状态 │ ─────────────────────────────────────────────→│ │ MSC Response │ │←─────────────────────────────────────────────│ │ MSC Command │ │←─────────────────────────────────────────────│ │ MSC Response │ │ ─────────────────────────────────────────────→│ │ │ │ SPP连接建立完成 │ │ │ │ UIH (DLCI10, 用户数据) │ 开始数据传输 │←─────────────────────────────────────────────→│关键步骤说明第1步ACL连接建立蓝牙底层的物理连接通过HCI命令完成建立后才能进行上层协议交互第2步SDP查询Client查询Server的SPP服务获取RFCOMM Server Channel号这一步是SPP特有的其他Profile也类似第3步L2CAP通道建立PSM固定为0x0003RFCOMM专用协商MTU等参数为RFCOMM提供可靠传输通道第4步RFCOMM会话建立在DLCI0上建立控制通道确定Initiator/Responder身份这是RFCOMM的必要步骤第5步RFCOMM数据通道建立DLCI计算DLCI Server Channel × 2 Direction Bit示例Server Channel5, Direction0 → DLCI10PN命令协商参数MTU、流控等MSC命令交换串口控制信号完成后即可传输用户数据数据传输SPP连接建立后应用层可以像使用串口一样进行数据收发。数据传输特性特性说明透明传输SPP不对数据做任何处理应用层数据原样传输无边界字节流模式接收端需要自行处理数据边界全双工双方可同时发送和接收数据可靠传输基于RFCOMM和L2CAP的可靠机制保证数据不丢失数据封装层次应用层数据 Hello World ↓ RFCOMM封装 [Address][Control][Length][Hello World][FCS] ↓ L2CAP封装 [Length][CID][RFCOMM帧] ↓ HCI封装 [HCI Header][L2CAP PDU] ↓ 空中传输 蓝牙基带数据包数据传输示例假设Client向Server发送字符串Test1. 应用层调用 spp_send(Test, 4) 2. RFCOMM层封装DLCI10 Address: 0x29 (DLCI10, C/R0, EA1) Control: 0xEF (UIH帧) Length: 0x09 (长度4, EA1) Data: 54 65 73 74 (Test) FCS: 0xXX 3. L2CAP层封装CID0x0041 Length: 0x0007 (RFCOMM帧长度) CID: 0x0041 (动态分配的CID) Payload: [RFCOMM帧] 4. HCI层发送 通过HCI ACL Data命令发送流控机制SPP支持两种流控方式1. Credit-Based流控推荐发送方 接收方 │ │ │ PN Command (K7) │ 协商初始Credit7 │ ─────────────────────────────────→│ │ PN Response (K7) │ │←─────────────────────────────────│ │ │ │ UIH (Credit0) [帧1] │ 发送数据消耗1个Credit │ ─────────────────────────────────→│ 剩余Credit: 6 │ UIH (Credit0) [帧2] │ │ ─────────────────────────────────→│ 剩余Credit: 5 │ ... │ │ UIH (Credit0) [帧7] │ │ ─────────────────────────────────→│ 剩余Credit: 0 │ │ │ ← 停止发送等待对方补充Credit ← │ │ │ │ UIH (Credit5) [数据] │ 接收方补充5个Credit │←─────────────────────────────────│ │ │ │ 继续发送... │Credit机制说明初始Credit在PN命令中协商每发送一个UIH帧消耗1个CreditCredit耗尽时必须停止发送接收方通过UIH帧的P/F位补充Credit2. FCon/FCoff流控传统方式发送方 接收方 │ │ │ UIH [数据] │ │ ─────────────────────────────────→│ │ UIH [数据] │ │ ─────────────────────────────────→│ │ │ │ FCoff Command │ 接收缓冲区满请求停止 │←─────────────────────────────────│ │ │ │ ← 停止发送 ← │ │ │ │ FCon Command │ 缓冲区空闲允许继续 │←─────────────────────────────────│ │ │ │ UIH [数据] │ 恢复发送 │ ─────────────────────────────────→│断开连接SPP连接断开需要按照协议栈层次逐层关闭。断开流程Client Server │ │ │ 第1步RFCOMM数据通道断开 │ │ │ │ DISC (DLCI10) │ │ ───────────────────────────────────→│ │ UA (DLCI10) │ │←───────────────────────────────────│ │ │ │ 第2步RFCOMM控制通道断开 │ │ │ │ DISC (DLCI0) │ │ ───────────────────────────────────→│ │ UA (DLCI0) │ │←───────────────────────────────────│ │ │ │ 第3步L2CAP通道断开 │ │ │ │ L2CAP Disconnection Request │ │ ───────────────────────────────────→│ │ L2CAP Disconnection Response │ │←───────────────────────────────────│ │ │ │ 第4步ACL连接断开可选│ │ │ │ HCI Disconnect │ │ ───────────────────────────────────→│ │ │ │ 连接完全断开 │注意必须先断开数据通道DLCI≠0再断开控制通道DLCI0L2CAP和ACL断开是可选的如果还有其他服务在使用可以保留任何一方都可以主动发起断开典型应用场景SPP因其简单易用被广泛应用于各种蓝牙设备。应用场景列表应用ClientServer数据类型蓝牙打印机手机/电脑打印机打印指令、图像数据GPS模块手机/导航仪GPS接收器NMEA定位数据数据采集器电脑/平板传感器设备温度、湿度等传感器数据蓝牙串口透传MCU设备MCU设备自定义协议数据OBD诊断仪手机汽车OBD设备车辆诊断数据POS机手机支付终端支付指令、交易数据典型应用示例蓝牙打印机手机 (Client) 蓝牙打印机 (Server) │ │ │ 1. 搜索蓝牙设备 │ │ 2. 发现打印机 │ │ 3. SDP查询SPP服务 │ │ 4. 建立SPP连接 │ │ ───────────────────────────────────→│ │ │ │ 5. 发送打印指令ESC/POS命令 │ │ ───────────────────────────────────→│ 解析打印指令 │ │ ↓ │ 6. 发送图像数据 │ 打印输出 │ ───────────────────────────────────→│ ↓ │ │ ↓ │ 7. 打印完成 │ ✓ │ │ │ 8. 断开连接 │ │ ───────────────────────────────────→│数据示例ESC/POS打印指令初始化打印机 1B 40 打印文本 48 65 6C 6C 6F (Hello) 换行 0A 切纸 1D 56 00SPP与其他Profile的对比Profile全称基础协议用途复杂度SPPSerial Port ProfileRFCOMM通用串口数据传输简单HFPHands-Free ProfileRFCOMM蓝牙耳机通话中等A2DPAdvanced Audio Distribution ProfileAVDTP蓝牙音频流传输复杂HIDHuman Interface Device ProfileL2CAP蓝牙键盘鼠标中等GATTGeneric Attribute ProfileATTBLE设备通信中等SPP的优势最简单的蓝牙Profile之一无需复杂的AT命令或音频编解码适合快速开发和原型验证兼容性好几乎所有蓝牙设备都支持实现要点Server端实现要点注册SDP服务记录设置ServiceClassIDList 0x1101指定RFCOMM Server Channel1-30添加服务名称和描述监听RFCOMM连接在指定的Server Channel上监听接受Client的连接请求处理PN、MSC等控制命令数据收发接收UIH帧中的用户数据发送数据时封装为UIH帧处理流控信号Client端实现要点SDP查询搜索UUID0x1101的服务解析ProtocolDescriptorList提取RFCOMM Server Channel建立连接建立L2CAP连接PSM0x0003建立RFCOMM会话DLCI0建立RFCOMM数据通道DLCIServer Channel×2数据收发发送数据时封装为UIH帧接收UIH帧并提取用户数据管理Credit流控常见问题问题原因解决方案连接失败Server Channel错误确保SDP查询正确使用正确的通道号数据丢失Credit耗尽及时补充Credit或增大初始Credit值连接不稳定信号干扰减少障碍物缩短距离传输速度慢MTU过小在PN命令中协商更大的N1值最大帧大小总结SPP是蓝牙协议栈中最实用的Profile之一它将复杂的蓝牙协议栈封装成简单的串口接口极大地降低了蓝牙应用的开发难度。核心要点SPP基于RFCOMM提供虚拟串口功能通过SDP发现服务和RFCOMM Server Channel连接建立需要经过ACL → L2CAP → RFCOMM → SPP多层协商数据传输透明应用层无需关心底层细节支持Credit-Based流控保证数据可靠传输适用场景需要简单数据传输的场景替代传统有线串口的无线方案快速原型开发和验证注意SPP 虚拟串口的“波特率”大多可忽略不决定蓝牙空口速率。PC 到设备真实路径是上位机 - 虚拟COM驱动 - RFCOMM/L2CAP - 蓝牙链路 - 设备SPP回调这里本质是蓝牙协议栈传输不是 UART 按波特率一位位发。真正影响速率的是蓝牙链路质量、包长、重传、调度、缓冲等。||传输速度慢| MTU过小 | 在PN命令中协商更大的N1值最大帧大小 |