程序執行效果:打印出了周圍藍牙設備的MAC地址,和名字
$ ./a.out
34:B2:0A:94:8E:5F HUAWEI WATCH GT 2e-E5F
A4:50:46:8F:A3:F6 小米手機
E0:DC:FF:FC:21:64 小米手機
7C:B3:7B:29:99:4F Hisense A5
9C:5F:5A:D4:A8:FE OPPO A5
代碼:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
int main(int argc, char **argv)
{
inquiry_info *ii = NULL;
int max_rsp, num_rsp;
int dev_id, sock, len, flags;
int i;
char addr[19] = { 0 };
char name[248] = { 0 };
dev_id = hci_get_route(NULL);
sock = hci_open_dev( dev_id );
if (dev_id < 0 || sock < 0) {
perror("opening socket");
exit(1);
}
len = 8;
max_rsp = 255;
flags = IREQ_CACHE_FLUSH;
ii = (inquiry_info*)malloc(max_rsp * sizeof(inquiry_info));
num_rsp = hci_inquiry(dev_id, len, max_rsp, NULL, &ii, flags);
if( num_rsp < 0 ) perror("hci_inquiry");
for (i = 0; i < num_rsp; i++) {
ba2str(&(ii+i)->bdaddr, addr);
memset(name, 0, sizeof(name));
if (hci_read_remote_name(sock, &(ii+i)->bdaddr, sizeof(name),
name, 0) < 0)
strcpy(name, "[unknown]");
printf("%s %s\n", addr, name);
}
free( ii );
close( sock );
return 0;
}
要提前安裝開發庫:
Debian-based systems:
$ sudo apt-get install libbluetooth-dev bluez-utils
CentOS system:
$ yum install bluez-devel
編譯方法:
$ gcc -o simplescan simplescan.c -lbluetooth
標識藍牙MAC地址的結構體。
typedef struct {
uint8_t b[6];
} __attribute__((packed)) bdaddr_t;
bdaddr_t是用來標識藍牙的MAC地址的,藍牙的MAC地址格式:"XX:XX:XX:XX:XX:XX ",XX是16進制的數字,所有bluez提供了2個便利函數:
把字符串的MAC地址,轉化成bdaddr_t格式。
int str2ba( const char *str, bdaddr_t *ba );
把bdaddr_t格式的MAC地址,轉換成字符串格式
int ba2str( const bdaddr_t *ba, char *str );
函數:hci_get_route,參數是藍牙MAC地址,返回設備id,后續的函數hci_open_dev要使用設備id。由於本電腦只有一個藍牙適配器,所以參數給的是NULL,也就是0.
Local Bluetooth adapters are assigned identifying numbers starting with 0, and a program must specify which adapter to use when allocating system resources. Usually, there is only one adapter or it doesn't matter which one is used, so passing NULL
to hci_get_route
will retrieve the resource number of the first available Bluetooth adapter.
Note:It is not a good idea to hard-code the device number 0, because that is not always the id of the first adapter. For example, if there were two adapters on the system and the first adapter (id 0) is disabled, then the first available adapter is the one with id 1.
int hci_get_route( bdaddr_t *bdaddr );
如果知道了字符串格式的MAC地址了的話,可以使用hci_devid函數,來返回設備id。
int dev_id = hci_devid( "01:23:45:67:89:AB" );
函數:int hci_open_dev,根據參數(設備id),和本電腦的藍牙控制芯片建立連接,也就是返回一個socket。所以此函數內部調用了:
socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)
之后又調用了bind函數。
注意:不是和周圍別的藍牙設備建立連接。
Most Bluetooth operations require the use of an open socket. hci_open_dev
is a convenience function that opens a Bluetooth socket with the specified resource number [2]. To be clear, the socket opened by hci_open_dev
represents a connection to the microcontroller on the specified local Bluetooth adapter, and not a connection to a remote Bluetooth device. Performing low level Bluetooth operations involves sending commands directly to the microcontroller with this socket, and Section 4.5 discusses this in greater detail.
int hci_open_dev( int dev_id );
和本電腦的藍牙控制芯片建立連接后,就可以搜索周圍的藍牙設備了,使用函數:hci_inquiry
int hci_inquiry(int dev_id, int len, int max_rsp, const uint8_t *lap, inquiry_info **ii, long flags);
結構體inquiry_info:保存周圍藍牙設備的MAC地址等信息。除了bdaddr和dev_class外,其余的字段都是做低級操作的。
typedef struct {
bdaddr_t bdaddr;//MAC地址
uint8_t pscan_rep_mode;
uint8_t pscan_period_mode;
uint8_t pscan_mode;
uint8_t dev_class[3];//設備的類型,手機?電腦?PDA?等
uint16_t clock_offset;
} __attribute__ ((packed)) inquiry_info;
hci_inquiry函數執行成功后,返回0,並把周圍的設備的MAC地址放入inquiry_info **ii里。max_rsp制定了最多可以放多少個。所以提前要開辟足夠的內存空間。
flags如果設置成IREQ_CACHE_FLUSH ,則會重新搜索;如果是0,則返回上一次搜索的結果。
如果搜索到了周圍的設備,則可以使用hci_read_remote_name,返回周圍設備的名字
int hci_read_remote_name(int sock, const bdaddr_t *ba, int len,
char *name, int timeout)
hci_read_remote_name
tries for at most timeout
milliseconds to use the socket sock
to query the user-friendly name of the device with Bluetooth address ba
. On success, hci_read_remote_name
returns 0 and copies at most the first len
bytes of the device's user-friendly name into name
. On failure, it returns -1 and sets errno
accordingly