查閱了好多資料,看了好多東西,感覺VC上的藍芽編程好亂,看得頭都大了,下面把看到的和學到的整理一下(如有錯誤,請指正):
VC藍芽編程貌似方式不止一種,網上比較流行的是IVT的BlueSoleil_SDK,用windows上的兩種bluetooth的開放介面,一種是以熟悉的windows sockets方式,另外一種是新加入的BlueTooth APIs方式。在SDK的samples中只提到了windows sockets的方式。還有就是BlueZ的方式。
BlueSoleil_SDK相關資料:http://msdn.microsoft.com/en-us/library/aa362901(VS.85).aspx
下面具體說一下BlueZ(在下只是略懂皮毛,如有誤解,望大蝦們指點)
簡單的說,BlueZ是什麼呢??
答:BlueZ是一款強大的藍芽通訊協定棧,它擴充的API使得使用者方便操縱大量的藍芽資源。
下面是網上的一個執行個體,希望大家看過執行個體後稍微理解一些:
這個例子是一個尋找周邊藍牙裝置的簡單應用程式。程式首先擷取系統的藍牙裝置號,掃描周邊的藍牙裝置,然後尋找每一個被搜尋到的藍牙裝置的名稱。後邊有對資料結構和函數的詳細描述。
#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;
}
編譯需要使用gcc連結libbluetooth這個庫。
# gcc -o simplescan simplescan.c -lbluetooth
下面是相關解釋:
typedef struct {
uint8_t b[6];
} __attribute__((packed)) bdaddr_t;
藍牙裝置的地址採用結構體bdaddr_t來描述,BlueZ中隊藍芽地址的儲存和操縱都使用bdaddr_t結構體,BlueZ提供兩個函數來進行字串到藍芽地址的轉換。
int str2ba( const char *str, bdaddr_t *ba );
int ba2str( const bdaddr_t *ba, char *str );
str2ba把形如XX:XX:XX:XX:XX:XX(XX標識48位藍芽地址的16進位的一個位元組)的字串轉化6位元組的bdaddr_t結構, ba2str完成相反的功能。
本地藍芽適配器被分配一個從0開始的識別號碼。程式在分配系統資源時必須指定使用那一個藍芽適配器,通常的話系統只有一個藍芽適配器,把參數NULL傳給hci_get_route可以獲得第一個有效藍芽適配器識別號。
int hci_get_route( bdaddr_t *bdaddr );
int hci_open_dev( int dev_id );
[note]將適配器的裝置號指定為0是不恰當的,因為它並不總代表第一個可用的藍芽適配器。例如系統有兩個藍芽適配器,第一個被disable掉了,那麼第一個有效裝置號就是2。
如果存在多個藍芽適配器,選擇"01:23:45:67:89:AB"作為藍芽適配器的地址, 將指示這個地址的指標char *representation傳給hci_devid函數,用這個函數替代hci_get_route。
很多藍芽操作都需要開啟一個套介面, hci_open_dev函數可以開啟特定資源號的一個套介面,確切的說hci_open_dev開啟的通訊端建立了一條和本地藍芽適配器控制器的串連,而不是和遠端藍牙裝置的串連。使用這個套介面發送命令到藍芽控制器可以實現底層的藍芽操作,這部分在4.5中有詳細的討論。
選擇好本地藍芽適配器並進行系統資源分派後,程式就可以開始掃描周邊的藍牙裝置了,在這個常式中,hci_inquiry函數完成對藍牙裝置的搜尋,並將返回的裝置資訊資料記錄在變數ii中。遇到錯誤時,它將返回-1並設定errno變數。
int hci_inquiry(int dev_id, int len, int max_rsp, const uint8_t *lap,
inquiry_info **ii, long flags);
hci_inquiry 的參數需要使用裝置資源號而非套介面,所以我們使用hci_get_route函數的傳回值dev_id傳遞給它。查詢時間最長持續1.28 * len秒。max_rsp個設別返回的資訊都被儲存在變數ii中,這個變數必須有足夠的空間來儲存max_rsp返回的結果。我們推薦max_rsp取值255來完成標準10.24秒的查詢工作。
如果標誌位flag設定為IREQ_CACHE_FLUSH,那麼在進行查詢操作時會把先前一次查詢記錄的cache重新整理,否則flag設定為0的話,即便先前查詢的裝置已經不處於有效範圍內,先前查詢的記錄也將被返回。
inquiry_info結構體定義如下
typedef struct {
bdaddr_t bdaddr;
uint8_t pscan_rep_mode;
uint8_t pscan_period_mode;
uint8_t pscan_mode;
uint8_t dev_class[3];
uint16_t clock_offset;
} __attribute__ ((packed)) inquiry_info;
在大多數場合,我們僅用到成員bdaddr,它標識了裝置的藍芽地址。有些場合我們也會用到成員dev_class, 它標識了被檢測到的藍牙裝置的一些資訊(例如,識別這個裝置是列印裝置,電話,個人電腦等),詳細地對應關係可以參見藍牙裝置分配號[3]。其餘的成員在用於底層通訊,一般情況並不常用。感興趣的讀者可以閱讀藍芽核心規範[4]擷取更多的資訊。一旦周圍的藍牙裝置和其藍芽地址被檢測到,程式可以將此裝置的名稱提供給使用者,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函數在規定的逾時時間內使用套介面通過藍芽地址ba去擷取藍牙裝置的名稱,成功返回0,並將擷取的藍牙裝置名稱存入name中;失敗時返回-1並設定相應的errno。