由于是在linux上运行的,所以参考的bluez的代码,用C语言实现了蓝牙的Server端。
原理也很简单,在服务器端,通过命令添加SPP服务
前提是先安装好bluez,一般是安装好的,没安装的话可以用
sudo apt-get install bluez
安装后设置蓝牙
关闭蓝牙
sudo hciconfig hci0 down
重置蓝牙
sudo hciconfig hci0 reset
打开蓝牙
sudo hciconfig hci0 up
让蓝牙可连接可发现
sudo hciconfig hci0 piscan
设置好以后,可以用hciconfig -a查看本机地址和其他信息。hciconfig是一个工具,修改都是临时性的
配置好以后,需要手动添加SPP服务
sdptool add SP
这句也很重要,我们要在设备上建立SPP协议的服务。也就是一个UUID=0x1101的服务,这句话默认是在channel=1上面。
这里也很重要是,代码中socket监听的端口
SerialPortServiceClass_UUID = '{00001101-0000-1000-8000-00805F9B34FB}'
参考这里https://www.jianshu.com/p/eb85cb690e86
有了这个服务,我们使用socket来接受其他客户端的连接请求,建立请求后收发数据。
这个是个大坑,在这个坑里呆了很久。。。。
网上都这么写,似乎只要执行了这句,sdp server就建好了一样。实际并不这样。
执行后,需要用下面的命令查看是否有这个服务
sudo sdptool browse local
audfly@linux:/opt/tmp$ sudo sdptool browse local
Failed to connect to SDP server on FF:FF:FF:00:00:00: No such file or directory
很不幸,执行这句报错了。解决办法就是修改系统中蓝牙服务的启动选项,-C的意思就是compat,兼容性模式运行蓝牙服务,因为bluez5.x的版本去掉了socket的方式连接蓝牙,所以我们需要手动开启。
sudo vim /lib/systemd/system/bluetooth.service
ExecStart=/usr/lib/bluez5/bluetooth/bluetoothd -C
sudo systemctl daemon-reload
sudo systemctl restart bluetooth.service
参考这里
https://qa.1r1g.com/sf/ask/2317769471/
开启后,重启添加会有提示成功。
后查看可以正常看到服务信息,
sudo sdptool browse local
rowsing FF:FF:FF:00:00:00 ...
Service RecHandle: 0x10000
Service Class ID List:
"PnP Information" (0x1200)
Profile Descriptor List:
"PnP Information" (0x1200)
Version: 0x0103
Browsing FF:FF:FF:00:00:00 ...
Service Search failed: Invalid argument
Service Name: Serial Port
Service Description: COM Port
Service Provider: BlueZ
Service RecHandle: 0x10001
Service Class ID List:
"Serial Port" (0x1101)
Protocol Descriptor List:
"L2CAP" (0x0100)
"RFCOMM" (0x0003)
Channel: 1
Language Base Attr List:
code_ISO639: 0x656e
encoding: 0x6a
base_offset: 0x100
Profile Descriptor List:
"Serial Port" (0x1101)
Version: 0x0100
Service Name: Message Notification
Service RecHandle: 0x10002
Service Class ID List:
"Message Access - MNS" (0x1133)
Protocol Descriptor List:
"L2CAP" (0x0100)
"RFCOMM" (0x0003)
Channel: 17
"OBEX" (0x0008)
Profile Descriptor List:
"Message Access" (0x1134)
Version: 0x0102
Browsing FF:FF:FF:00:00:00 ...
Service Search failed: Invalid argument
Service Name: Synchronization
Service RecHandle: 0x10009
Service Class ID List:
"IrMC Sync" (0x1104)
Protocol Descriptor List:
"L2CAP" (0x0100)
"RFCOMM" (0x0003)
Channel: 14
"OBEX" (0x0008)
Profile Descriptor List:
"IrMC Sync" (0x1104)
Version: 0x0100
其中可能仍然没有添加的服务。
经测试,是需要手动删除几个服务才能add SP成功,猜测可能是达到上限了
如果没有成功需要手动删除几个不需要的,执行下面的删除命令
sudo sdptool del 0x10004
del的后的id指的是 这个信息后的id
Service RecHandle: 0x10009
删除后,再次执行
sdptool add SP
此时应会有提示 一个成功提示类似如下的提示
niuben@niuben-VirtualBox:/media/sf_qt_bluetooth$ sudo sdptool add SP
Serial Port service registered
直到这里,才完成了SPP的服务的注册,接下来,才能用socket收发数据。
这里的 sudo sdptool add SP
默认是把这个服务放到channel =1的通道中,这个通道类似于socket的端口号。这里很重要。
在测试的server代码中需要用到,下面是代码
struct sockaddr_rc loc_addr = { 0 }, rem_addr = { 0 };
char buf[1024] = { 0 };
int s, client, bytes_read;
int opt = sizeof(rem_addr);
// allocate socket
s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
// bind socket to port 1 of the first available
// local bluetooth adapter
loc_addr.rc_family = AF_BLUETOOTH;
loc_addr.rc_bdaddr = *BDADDR_ANY;
loc_addr.rc_channel = (uint8_t) 1;//这里的通道就是SPP的通道
bind(s, (struct sockaddr *)&loc_addr, sizeof(loc_addr));
// put socket into listening mode
listen(s, 1);
printf("orther_server listen success\n");
// accept one connection
//这里accept没有做并发处理,只能连接要给设,后续可自行扩展
client = accept(s, (struct sockaddr *)&rem_addr, &opt);
ba2str( &rem_addr.rc_bdaddr, buf );
printf("client connected success\n");
fprintf(stderr, "accepted connection from %s\n", buf);
//这里循环读数据也没有做线程处理,一直在读,应该放到线程种,同样的写数据一样的道理
for(;;)
{
memset(buf, 0, sizeof(buf));
// read data from the client
bytes_read = read(client, buf, sizeof(buf));
if( bytes_read > 0 ) {
printf("received [%s]\n", buf);
}
}
// close connection
close(client);
close(s);
return 0;
客户端连接的时候同样的需要设置通道为1,但是地址需要注意地址的填写。
一开始客户端连接不上,搞得很沮丧。我已经几乎打算放弃了,后来通过在客户端的设备上使用
dmesg | grep -i blue
发现提示说他的连接地址和我代码中的不一致,虽然我写对了,但是str2ba是我从网上下载下来,修改的。
问题就是他是逆序的,同样的,在初始化地址那里需要用逆序,这样他才能用正确的地址
或者传入正序的地址,然后修改一下str2ba函数,让他逆序一下就行了。
当然了客户端的配置简单一些,不需要配置许多乱七八糟的东西,这个代码也在linux上运行且是arm版本的linux系统。
经测试,数据一口气发送很大的数据,测试客户端发送了16M的数据。
int main(int argc, char **argv)
{
struct sockaddr_rc addr = { 0 };
int s, status;
//char dest[18] = "8C:88:2B:01:6E:17";
char dest[18] = "17:6E:01:2B:88:8C"; //我在这里直接把地址逆序了,实际地址与此相反
// allocate a socket
s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
// set the connection parameters (who to connect to)
addr.rc_family = AF_BLUETOOTH;
addr.rc_channel = (uint8_t) 1;
str2ba( dest, &addr.rc_bdaddr );
// addr.rc_bdaddr.b[0]=0x00;
// addr.rc_bdaddr.b[1]=0x13;
// addr.rc_bdaddr.b[2]=0xEF;
// addr.rc_bdaddr.b[3]=0xF8;
// addr.rc_bdaddr.b[4]=0x70;
// addr.rc_bdaddr.b[5]=0xE0;
printf( "connect device\n" );
// connect to server
status = connect(s, (struct sockaddr *)&addr, sizeof(addr));
// send a message
static char data[16*1024*1024]={1};
for(int i=0;i!=16*1024*1024;++i){
data[i]=i;
}
if( status == 0 ) {
printf("connected success\n");
status = write(s, data,16*1024*1024);
}
if( status < 0 ) {
printf("connected 00:13:EF:F8:70:E0 failed,status = %d\n",status);
}
close(s);
return 0;
}
后续:
有些linux设备上加-C仍然不行,是因为蓝牙的service并不是上文提到的那个文件,这个service是蓝牙模块的守护进程。
需要继续排查问题,因为最终是需要在arm设备上运行server端的。
需要解决arm版本linux上创建
SP服务的问题
就是这个问题,Failed to connect to SDP server on FF:FF:FF:00:00:00: No such file or directory
由于我用的是英伟达的NXTX2的开发板
在NX TX2的开发板上面需要修改这里的配置,一样的道理,都是加-C
gedit /lib/systemd/system/bluetooth.service.d/nv-bluetooth-service.conf&
[Service]
#--noplugin=audio,a2dp,avrcp
ExecStart=
ExecStart=/usr/lib/bluetooth/bluetoothd -d -C
ExecStartPost=/usr/bin/sdptool add --channel=1 SP
USB蓝牙运行过程中如果遇到驱动文件错误
请参考这个解决办法
https://huaweicloud.csdn.net/635637e7d3efff3090b5aeaf.html?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~activity-2-122438204-blog-52122447.pc_relevant_vip_default&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~activity-2-122438204-blog-52122447.pc_relevant_vip_default&utm_relevant_index=3
Fetch rtl8723b_config and rtl8761b_fw from
wget https://raw.githubusercontent.com/Realtek-OpenSource/android_hardware_realtek/rtk1395/bt/rtkbt/Firmware/BT/rtl8723b_config
wget https://raw.githubusercontent.com/Realtek-OpenSource/android_hardware_realtek/rtk1395/bt/rtkbt/Firmware/BT/rtl8723b_fw
sudo mv rtl8723b_config /lib/firmware/rtl_bt/rtl8723b_config.bin
sudo mv rtl8723b_fw /lib/firmware/rtl_bt/rtl8723b_fw.bin
sudo modprobe btusb
sudo systemctl restart bluetooth.service
hciconfig -a # will show that the bluetooth-device is up
最后一个很有用的文档,讲解bluez开发的手册和一些简单的demo,文中示例皆来自本文档。文中讲解了hciconfig的用法,非常有用!
Bluetooth for Programmers.pdf,这是一位C友人上传的BluetoothforProgrammers.zip_BluetoothEssentialsforProgrammers-Linux文档类资源-CSDN下载