最近需要在某个开发板上面通过蓝牙和手机蓝牙连接,并通过RFCOMM通信。还没有做过蓝牙RFCOMM相关工作,因此先在linux PC上面调试一下流程,并在此记录调试过程。

一、说明

RFCOMM协议基于L2CAP协议的串行(9针RS-232)仿真。

本文中实现了RFCOMM server和client通信。

二、设备

linux主机(Ubuntu-14.04)、linux虚拟机(Ubuntu-14.04)、Android手机一台、不知名蓝牙dongle_1(controller)、CSR 蓝牙dongle_2(controller)。

Linux主机+dongle_1作为server端; linux虚拟机+dongle_2作为client端;Android手机作为client端。

三、环境搭建

Server端:

1. 安装bluez协议栈

2. 查看bluetoothd进程是否启动:ps -ef|grep blue

root      1891     1  0  5月19 ?      00:00:00 /usr/sbin/bluetoothd

如果没有启动,执行:/usr/sbin/bluetoothd -C &

注:开始测试,如果把bluetoothd进程kill了,也能进行连接成功。后来发现如果不启动bluetoothd,就连接不成功。理论上说server端程序使用socket通信,应该不需要bluetoothd。

至今没有搞清楚原因。

3. 查看bluetooth service是否存在:service --status-all | grep blue

如果不存在,执行:service bluetooth start

4. 将dongle_1插入linux主机端;并配置。

1> 执行:hciconfig,观察dongle状态是否为UP RUNNING,如果不为UP RUNNING,则执行:hciconfig hci0 UP

注:hci0是根据hciconfig打印的BD Address来确定的。如果有两个dongle,有可能是hci1。

2> 使蓝牙设备可见(可被其他蓝牙设备扫描到,如手机)

执行命令:hciconfig piscan

然后执行hciconfig,观察状态是否为UP RUNNING PSCAN ISCAN

3> 添加SPP服务

sdptool add SP

也可以执行添加所有服务:

sdptool add --channel=1 DID SP DUN LAN FAX OPUSH FTP HS HF SAP NAP GN PANU HID CIP CTP A2SRC A2SNK SYNCML NOKID PCSUITE SR1

4> 关闭pin码验证

hciconfig hci0 noauth;

5. 编写并编译测试程序

gcc -o rfcomm_server rfcomm_server.c

Client端(linux虚拟机):

1. 安装bluez协议栈

2. 查看bluetoothd进程是否启动:ps -ef|grep blue,如果没有启动,则启动该进程。

3. 查看bluetooth service是否存在:service --status-all | grep blue

如果不存在,执行:service bluetooth start

4. 添加SPP服务,关闭pin码验证。

5. 创建RFCOMM设备节点:mknod /dev/rfcomm0 c 216 0

chmod 666 /dev/rfcomm0

6. 绑定server端蓝牙mac地址

rfcomm bind 0 00:19:86:00:2B:BD 1 //0表示rfcomm0, 00:19:86:00:2B:BD为server端的蓝牙地址,1为通道

7. 编写并编译rfcomm_client

Client端(Android手机)

  1. 下载蓝牙串口SPP应用程序

四、测试

1. Server + client(linux虚拟机)

1> 在server端执行rfcomm_server

2> 在client端执行rfcomm_client

可以在两端观察到写入和读出的数据

2. Server + Android手机

1> 在server端执行rfcomm_server

2> Client端,打开蓝牙SPP应用,扫描到server端的蓝牙设备,连接。即可和server端进行通信

rfcomm_server.c

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h> int str2ba(const char *str, bdaddr_t *ba)
{
uint8_t b[];
const char *ptr = str;
int i; for (i = ; i < ; i++)
{
b[i] = (uint8_t) strtol(ptr, NULL, );
if (i != && !(ptr = strchr(ptr, ':')))
ptr = ":00:00:00:00:00";
ptr++;
}
} void baswap(bdaddr_t *dst, const bdaddr_t *src)
{
unsigned char *d = (unsigned char *) dst;
const unsigned char *s = (const unsigned char *) src;
int i;
for (i = ; i < ; i++)
d[i] = s[-i];
}
int ba2str(const bdaddr_t *ba, char *str)
{
uint8_t b[]; baswap((bdaddr_t *) b, ba);
return sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
b[], b[], b[], b[], b[], b[]);
} int main(int argc, char **argv)
{
struct sockaddr_rc loc_addr = { }, rem_addr = { };
char buf[] = { };
int s, client, bytes_read;
socklen_t opt = sizeof(rem_addr); char write_buf[]="hello world";
char flag = ;
char count=;
// 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) ;
bind(s, (struct sockaddr *)&loc_addr, sizeof(loc_addr)); // put socket into listening mode
listen(s, ); // accept one connection
client = accept(s, (struct sockaddr *)&rem_addr, &opt); ba2str( &rem_addr.rc_bdaddr, buf );
fprintf(stderr, "accepted connection from %s\n", buf); while( flag )
{
memset(buf, , sizeof(buf));
#if 0
// read data from the client
bytes_read = read(client, buf, sizeof(buf));
if( bytes_read > )
{
printf("[rcv]:%s\n", buf);
if(!strcmp(buf,"exit"))
{
flag = ;
}
// write( client,write_buf,16 );
}
usleep();
#endif
#if 1
//write data to client
strcpy( buf, "abcdefgh" );
bytes_read = ;
write( client,buf,bytes_read );
usleep();
#endif
} // close connection
close(client);
close(s);
return ;
}
rfcomm_client.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main( int argc, char **argv )
{
int fd ;
unsigned char buff[] = "hello";
char read_buff[] = {};
int read_n;
int write_n;
fd = open( "/dev/rfcomm0",O_RDWR); if( fd< )
printf( "open rfcomm0 error\n" ); while()
{
#if 0
// printf( "write hello to rfcomm\n" );
write_n = write( fd, buff, );
if( write_n<)
printf( "write error\n" );
else if(write_n==)
printf( "write nothing\n" );
else
printf( "write %d byte\n",write_n );
// sleep(1);
#endif
#if 1
memset( read_buff, , sizeof(read_buff) );
read_n = read( fd, read_buff, sizeof(read_buff) );
if( read_n > )
{
printf( "[receive]:%s\n",read_buff );
} usleep();
#endif
}
close(fd);
}
在网上查了一下资料,有rfcomm_client.c是创建socket,并bind、connect,但是我调试的时候执行该程序,会报错。原因没有找到。下面将代码贴出来,以后可以找一下原因。

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h> int str2ba(const char *str, bdaddr_t *ba)
{
uint8_t b[];
const char *ptr = str;
int i; for (i = ; i < ; i++)
{
b[i] = (uint8_t) strtol(ptr, NULL, );
if (i != && !(ptr = strchr(ptr, ':')))
ptr = ":00:00:00:00:00";
ptr++;
}
} int main(int argc, char **argv)
{
struct sockaddr_rc addr = { };
int s, status;
char dest[] = "00:19:86:00:2B:BD"; // 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) ;
str2ba( dest, &addr.rc_bdaddr );
printf( "connect device\n" );
// connect to server
status = connect(s, (struct sockaddr *)&addr, sizeof(addr)); // send a message
if( status == ) {
status = write(s, "hello!", );
} if( status < ) perror("uh oh"); close(s);
return ;
}

网上资料,有文章介绍需要设置rfcomm.conf, 个人以为如果需要client上电自动连接,可以用此方法进行设置(还需要进行其他配置),和用命令设置效果一样。

其中:不知名蓝牙dongle ,hciconfig -a信息如下:

root@localhost:bin# hciconfig -a

hci0:   Type: BR/EDR  Bus: USB

BD Address: 00:19:86:00:2B:BD  ACL MTU: 1021:8  SCO MTU: 64:1

UP RUNNING PSCAN

RX bytes:66976 acl:1214 sco:0 events:1729 errors:0

TX bytes:67686 acl:1976 sco:0 commands:203 errors:0

Features: 0xbf 0xfe 0xcf 0xfe 0xdb 0xff 0x7b 0x87

Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3

Link policy: RSWITCH SNIFF

Link mode: SLAVE ACCEPT

Name: 'localhost-0'

Class: 0x600100

Service Classes: Audio, Telephony

Device Class: Computer, Uncategorized

HCI Version: 4.0 (0x6)  Revision: 0x1000

LMP Version: 4.0 (0x6)  Subversion: 0x220e

蓝牙RFCOMM通信的更多相关文章

  1. Android 蓝牙 socket通信

    Android中蓝牙模块的使用 使用蓝牙API,Android应用程序能够执行以下功能: 扫描其他蓝牙设备查询本地已经配对的蓝牙适配器建立RFCOMM通道通过服务发现来连接其他设备在设备间传输数据管理 ...

  2. BluetoothChat用于蓝牙串口通信的修改方法

    本人最近在研究嵌入式的串口通信,任务是要写一个手机端的遥控器用来遥控双轮平衡小车.界面只用了一个小时就写好了,重要的问题是如何与板子所带的SPP-CA蓝牙模块进行通信. SPP-CA模块自带代码,在这 ...

  3. Android单片机与蓝牙模块通信实例代码

    Android单片机与蓝牙模块通信实例代码 参考路径:http://www.jb51.net/article/83349.htm 啦啦毕业了,毕业前要写毕业设计,需要写一个简单的蓝牙APP进行交互,通 ...

  4. 玩转X-CTR100 l STM32 l STM32F4 l 蓝牙串口通信

    我造轮子,你造车,创客一起造起来!更多塔克创新资讯[塔克社区 www.xtark.cn ][塔克博客 www.cnblogs.com/xtark/ ]      蓝牙串口通信模块,X-CTR100控制 ...

  5. Android Studio 的蓝牙串口通信(附Demo源码下载)

    根据相关代码制作了一个开源依赖包,将以下所有的代码进行打包,直接调用即可完成所有的操作.详细说明地址如下,如果觉得有用可以GIthub点个Star支持一下: 项目官网 Kotlin版本说明文档 Jav ...

  6. Android 蓝牙串口通信工具类 SerialPortUtil 3.0.+

    建议使用4.+版本,避免一些不必要的bug.4.+版本文档地址:https://www.cnblogs.com/shanya/articles/16062256.html SerialPortUtil ...

  7. 蓝牙—RFCOMM协议

    RFCOMM是一个简单的协议,其中针对9针RS-232串口仿真附加了部分条款.可支持在两个蓝牙设备之间同时保持高达60路的通信连接.RFCOMM的目的是针对如何在两个不同设备上的应用之间保证一条完整的 ...

  8. python绝技 — 扫描蓝牙RFCOMM信道

    RFCOMM协议 RFCOMM通过蓝牙L2CAP协议模拟了RS232串口.这会与另一台设备建立一个蓝牙连接,模拟一条普通的串行线缆,使用户能够用其他设备控制这一台设备,通过蓝牙打电话,发短信,读取手机 ...

  9. 经测试稳定可用的蓝牙链接通信Demo,记录过程中遇到的问题的思考和解决办法,并整理后给出一个Utils类可以简单调用来实现蓝牙功能

    说明:这是本人在蓝牙开发过程中遇到过的问题记录和分析,以及解决办法. 在研究过程中,许多的前人给出的解决方案和思路指导对我相当有帮助,但并非都是可采取的解决方法, 经过本人对这些方法的测试和使用过后, ...

随机推荐

  1. Kafka 入门(二)--数据日志、副本机制和消费策略

    一.Kafka 数据日志 1.主题 Topic Topic 是逻辑概念. 主题类似于分类,也可以理解为一个消息的集合.每一条发送到 Kafka 的消息都会带上一个主题信息,表明属于哪个主题. Kafk ...

  2. 风速风向 UV 相互转换

    这里以c#为例将风的uv分量转成风向风速(别的语言类似) 风向是以y轴正方向为零度顺时针转 UV转风速风向 1 double v ;//v分量 2 double u;//u分量 3 double fx ...

  3. Asp.net Core 3.1基于AspectCore实现AOP,实现事务、缓存拦截器

    最近想给我的框架加一种功能,就是比如给一个方法加一个事务的特性Attribute,那这个方法就会启用事务处理.给一个方法加一个缓存特性,那这个方法就会进行缓存. 这个也是网上说的面向切面编程AOP. ...

  4. Html5 表单元素基础

    表单元素 1.定义: 表单是提供让读者在网页上输入,勾选和选取数据,以便提交给服务器数据库的工具.(邮箱注册,用户登录,调查问卷等) 2.表单元素(下拉框,输入框……) 3.表单主结构: <fo ...

  5. 如何阅读一本书——分析阅读Pre

    如何阅读一本书--分析阅读Pre 前情介绍 作者: 莫提默.艾德勒 查尔斯.范多伦 初版:1940年,一出版就是全美畅销书榜首一年多.钢铁侠Elon.Musk学过. 需要注意的句子: 成功的阅读牵涉到 ...

  6. matplotlib绘制子图

    fig,subs = plt.subplots(2,2) subs[0][0].plot(data_math_C1) subs[0][0].set_title('C_1 曲线') subs[0][1] ...

  7. Python字符串内建函数_下

    Python字符串内建函数: 1.join(str) : 使用调用的字符串对 str 进行分割,返回值为字符串类型 # join(str) : # 使用调用的字符串对 str 进行分割. strs = ...

  8. 8-Pandas之如何查找存在缺失值的行(any与all详解)

    若有一份数据,简略如下:   国家 啤酒消耗量 烈酒消耗量 红酒消耗量 总酒精消耗量 所在大洲 0 Afghanistan 0.0 0.0 0.0 0.0 AS 1 Albania 89.0 132. ...

  9. LVS-DR实现mysql负载均衡集群

    lvs-dr实现mysql负载均衡集群 环境说明: 服务器的操作系统均为centos7,vip和rip在同一网段,使用lvs-dr模型来实现mysql集群服务 所有服务器均已配置好处VIP外的静态IP ...

  10. phpredis 扩展之操作 Redis,记下来记下来!

    phpredis 是 redis 的 php 的一个扩展,效率是相当高有链表排序功能,对创建内存级的模块业务关系很有用 $redis = new Redis(); $redis->connect ...