最近需要在某個開發板上面通過藍牙和手機藍牙連接,並通過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手機)
- 下載藍牙串口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[6]; const char *ptr = str; int i; for (i = 0; i < 6; i++) { b[i] = (uint8_t) strtol(ptr, NULL, 16); if (i != 5 && !(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 = 0; i < 6; i++) d[i] = s[5-i]; } int ba2str(const bdaddr_t *ba, char *str) { uint8_t b[6]; baswap((bdaddr_t *) b, ba); return sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", b[0], b[1], b[2], b[3], b[4], b[5]); } 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); char write_buf[1204]="hello world"; char flag = 1; char count=0; // 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; bind(s, (struct sockaddr *)&loc_addr, sizeof(loc_addr)); // put socket into listening mode listen(s, 1); // 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, 0, sizeof(buf)); #if 0 // read data from the client bytes_read = read(client, buf, sizeof(buf)); if( bytes_read > 0 ) { printf("[rcv]:%s\n", buf); if(!strcmp(buf,"exit")) { flag = 0; } // write( client,write_buf,16 ); } usleep(5000); #endif #if 1 //write data to client strcpy( buf, "abcdefgh" ); bytes_read = 9; write( client,buf,bytes_read ); usleep(50000); #endif } // close connection close(client); close(s); return 0; }
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[64] = "hello"; char read_buff[128] = {0}; int read_n; int write_n; fd = open( "/dev/rfcomm0",O_RDWR); if( fd<0 ) printf( "open rfcomm0 error\n" ); while(1) { #if 0 // printf( "write hello to rfcomm\n" ); write_n = write( fd, buff, 64 ); if( write_n<0) printf( "write error\n" ); else if(write_n==0) printf( "write nothing\n" ); else printf( "write %d byte\n",write_n ); // sleep(1); #endif #if 1 memset( read_buff, 0, sizeof(read_buff) ); read_n = read( fd, read_buff, sizeof(read_buff) ); if( read_n > 0 ) { printf( "[receive]:%s\n",read_buff ); } usleep(50000); #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[6]; const char *ptr = str; int i; for (i = 0; i < 6; i++) { b[i] = (uint8_t) strtol(ptr, NULL, 16); if (i != 5 && !(ptr = strchr(ptr, ':'))) ptr = ":00:00:00:00:00"; ptr++; } } int main(int argc, char **argv) { struct sockaddr_rc addr = { 0 }; int s, status; char dest[18] = "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) 1; 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 == 0 ) { status = write(s, "hello!", 6); } if( status < 0 ) perror("uh oh"); close(s); return 0; }
網上資料,有文章介紹需要設置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