当前位置: 首页 > 工具软件 > BlueZ > 使用案例 >

Linux下基于Bluez开发USB蓝牙SPP服务端

宗晟
2023-12-01

 由于是在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下载

 类似资料: