本文将详细介绍在 RV1126B 开发板上,使用 BlueZ 5.77 协议栈,通过 SPP(Serial Port Profile)与手机蓝牙串口 APP 进行通信的完整流程。
SPP 是经典蓝牙(BR/EDR)中一种非常实用的协议,它能在蓝牙设备间建立一个"虚拟串口",让数据传输变得像操作物理串口一样简单。这使得它非常适合嵌入式设备与手机等上位机进行数据交换。
1. 核心概念与准备工作
1.1 核心概念
1.1.1 在开始之前,需要先了解几个关键概念:
-
BlueZ :Linux 官方的开源蓝牙协议栈,RV1126B 上的蓝牙功能完全依赖它。
bluez5_utils是 Buildroot 中 BlueZ 5.x 版本的软件包名。 -
SPP (Serial Port Profile):蓝牙串口协议,它定义了设备间如何模拟串口进行通信。
-
RFCOMM:这是 SPP 使用的核心传输协议,它在 L2CAP 之上提供了一个简单的、基于通道的数据流,用于模拟 RS-232 串口。
-
SDP (Service Discovery Protocol):服务发现协议。服务端(RV1126B)通过 SDP 对外广播自己支持哪些服务(比如 SPP),客户端(手机)通过 SDP 来发现这些服务并获取连接所需的信息(如 RFCOMM 通道号)。
-
SPP(Serial Port Profile) 是经典蓝牙(BR/EDR)的"串口模拟"协议,由两部分拼成:
组成 承载 作用 RFCOMM L2CAP,PSM = 0x0003建立带流控的可靠数据通道 SDP L2CAP,PSM = 0x0001登记"我在哪个 RFCOMM channel 提供串口服务",供对端查询 关键标识:
名称 值 SPP 服务 UUID 00001101-0000-1000-8000-00805F9B34FB(短 UUID0x1101)RFCOMM PSM 0x0003SDP PSM 0x0001Public Browse Root 0x1002两个必须先纠正的认知:
- SPP 属于经典蓝牙,不是 BLE。 nRF Connect 这类 BLE 工具连不上 SPP,必须用支持 BR/EDR 串口的 APP。
- RFCOMM 上没有真正的波特率。 空口速率由 BR/EDR 物理层和 L2CAP/RFCOMM 参数决定;
RPN里协商的 9600/115200 只是给内核 TTY 层看的"礼貌参数",让termios表现得像真串口。数据乱码永远不是波特率问题,而是编码/分包问题。
还有一个 BlueZ 5 的关键事实 :
bluetoothd不提供任何 SPP 的 D-Bus 接口 (org.bluez.Serial早已移除)。它只负责 SDP 记录管理和配对鉴权,SPP 通道本身必须你自己在应用层实现。
1.2 环境准备
确保你的 RV1126B 系统已包含 BlueZ 工具集,主要包括 bluetoothd(后台服务)和 bluetoothctl(管理工具)。你可以使用以下命令检查:
bash
hciconfig -a # 查看蓝牙设备状态
bluetoothctl -v # 查看 BlueZ 版本
如果命令未找到,需要在 Buildroot 中确认 bluez5_utils 包已被选中并编译。
2. 服务端配置:让 RV1126B 成为 SPP 服务端
要让 RV1126B 作为服务端被手机连接,需要完成两个核心步骤:注册 SPP 服务 和启动 RFCOMM 监听。
2.1 使用 sdptool 注册 SPP 服务
首先,需要使用 sdptool 命令向本地的 SDP 数据库中添加一个 SPP 服务记录。这一步是告诉手机:"我支持 SPP 串口服务,请通过这个通道来找我"。
bash
# 注册 SPP 服务,使用默认的 RFCOMM 通道 1
sdptool add SP
# 或者可以指定一个通道,例如通道 3
sdptool add --channel=3 SP
关键点 :sdptool add SP 必须在手机尝试连接 之前 执行。如果连接后执行,手机已经完成服务查询,将无法发现该服务。
可能遇到的问题与解决:
-
如果提示
Failed to connect to SDP server,通常是因为bluetoothd未以兼容模式运行。你需要先停止现有服务,然后手动启动:bashkillall bluetoothd /usr/libexec/bluetooth/bluetoothd -C -n -d &这里的
-C参数用于启用对旧版命令行工具的兼容性。
2.2 使用 rfcomm 监听连接
服务注册成功后,就可以使用 rfcomm 命令来监听来自手机的连接请求了。
bash
# 监听 /dev/rfcomm0 设备,使用通道 1(需与 sdptool 注册的通道一致)
rfcomm listen /dev/rfcomm0 1
执行后,终端会显示 Waiting for connection on channel 1,此时 RV1126B 已准备好接受连接。
数据交互 :
当手机连接成功后,/dev/rfcomm0 这个虚拟串口设备就会被创建。之后,你可以像操作普通串口一样对它进行读写。
- 读取手机发来的数据:
cat /dev/rfcomm0 - 向手机发送数据:
echo "Hello from RV1126B" > /dev/rfcomm0
3. 进阶:编写 C 语言服务端程序
虽然命令行工具可以快速测试,但在实际项目中,通常需要编写一个程序来实现更复杂的逻辑。以下是一个使用 C 语言实现的 RFCOMM 服务端框架。
c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>
int main(int argc, char **argv) {
struct sockaddr_rc loc_addr = { 0 }, rem_addr = { 0 };
char buf[1024] = { 0 };
int s, client, bytes_read;
socklen_t opt = sizeof(rem_addr);
// 1. 创建 RFCOMM socket
s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
// 2. 绑定 socket 到本地蓝牙地址和通道 1
loc_addr.rc_family = AF_BLUETOOTH;
bacpy(&loc_addr.rc_bdaddr, BDADDR_ANY);
loc_addr.rc_channel = (uint8_t) 1;
bind(s, (struct sockaddr *)&loc_addr, sizeof(loc_addr));
// 3. 监听连接
listen(s, 1);
// 4. 接受来自手机 APP 的客户端连接
client = accept(s, (struct sockaddr *)&rem_addr, &opt);
// 5. 数据收发循环
while (1) {
memset(buf, 0, sizeof(buf));
bytes_read = read(client, buf, sizeof(buf));
if (bytes_read > 0) {
printf("Received: %s\n", buf);
// 回复手机
char reply[] = "ACK from RV1126B\n";
write(client, reply, sizeof(reply));
} else {
break;
}
}
close(client);
close(s);
return 0;
}
编译与运行 :
在 RV1126B 上使用交叉编译工具链编译:
bash
aarch64-buildroot-linux-gnu-gcc -o bt_server bt_server.c -lbluetooth
然后运行:
bash
./bt_server
注意 :此代码示例未包含 SDP 服务注册。在实际部署时,仍需在程序启动前或程序内部通过 system("sdptool add SP") 等方式完成注册。
4. 手机端连接与数据通信
- 打开手机蓝牙,并搜索附近设备。
- 找到并配对 名为
BlueZ 5.77(或你自定义的名称)的设备。 - 打开手机上的 SPP 蓝牙串口 APP(如 "Serial Bluetooth Terminal")。
- 在 APP 中扫描并连接到已配对的
BlueZ 5.77设备。 - 连接建立后,APP 界面会类似一个串口终端。此时即可在 APP 中输入文字并发送,RV1126B 的服务端程序(或
rfcomm命令行)会收到数据并显示。
5. 常见问题与故障排查
| 问题现象 | 可能原因与解决方案 |
|---|---|
hciconfig 或 sdptool 命令找不到 |
Buildroot 中未包含或未正确安装 bluez5-utils 包。请检查 Buildroot 配置并重新编译。 |
sdptool add SP 失败 |
bluetoothd 服务未以兼容模式运行。尝试使用 bluetoothd -C -n -d & 重启服务。 |
| 手机能配对,但无法连接 SPP 服务 | 1. SDP 服务未注册 :确保 sdptool add SP 在手机连接之前 已执行。 2. RFCOMM 未监听 :确保 rfcomm listen 或你的服务端程序正在运行。 3. 通道不匹配 :检查 sdptool 注册的通道与 rfcomm 监听的通道是否一致。 |
| 连接后立即断开 | 服务端程序可能在 accept() 后立即退出。确保服务端程序在 accept() 之后进入一个持续运行的循环。 |
| 数据传输失败或乱码 | 检查服务端和手机 APP 的串口参数(如波特率)是否匹配。虽然 SPP 通常会自动协商,但某些 APP 可能需要手动设置。 |
6. 总结
通过以上步骤,你可以在 RV1126B 上成功建立一个基于 SPP 的蓝牙串口服务,并与手机 APP 进行通信。
整个流程的核心在于:
- 注册服务 :使用
sdptool让手机能够发现 RV1126B 的 SPP 服务。 - 建立监听 :使用
rfcomm或编写 C 程序,在指定通道上等待连接。 - 数据交互 :连接建立后,通过虚拟串口
/dev/rfcomm0或 socket 进行双向数据传输。