1. 程式人生 > >Bluetooth---藍芽

Bluetooth---藍芽

轉載自:

https://blog.csdn.net/it1039871366/article/details/46441507

Tieto公司某藍芽大牛寫得《程式設計師》投稿文章

Android 4.2藍芽介紹

藍芽一詞源於公元十世紀丹麥國王HaraldBlatand名字中的Blatand。Blatand的英文之意就是Blue tooth。這是因為這位讓丹麥人引以為傲的國王酷愛吃藍莓以至於牙齦都被染成藍色。由於Blatand統一了丹麥和挪威,所以,作為無線通訊技術的一種,藍芽技術之所以取名Bluetooth可謂志向遠大。不過,在以Android為代表的智慧機出現以前,藍芽在早期智慧機甚至功能機中一直扮演著“雞肋”的角色。那麼,隨著無線通訊技術的快速發展以及Android的普及,藍芽能給我們帶來哪些新的變化呢?

本文將從藍芽核心規範的發展歷史、最具應用前景的幾個藍芽Profile以及Android 4.2中藍芽實現情況等幾個方面向讀者介紹藍芽技術。

一  藍芽規範介紹

作為一種通用的無線通訊技術,規範自然是藍芽技術的核心。藍芽規範可分為兩個層次,如圖1所示:

圖1  藍芽規範的層次結構

由圖1可知藍芽規範包括:

  • Core Specification(核心規範),用於規定藍芽裝置必須實現的通用功能和協議層次。它由軟體和硬體模組組成,兩個模組之間的資訊和資料通過主機控制介面(HCI)的解釋才能進行傳遞。
  • Profiles(藍芽應用規範),它從應用場景的角度為藍芽技術的使用制定了不同的規範。這也是和大眾日常生活接觸最多的一部分。藍芽支援很多Profiles,下文將介紹幾種使用最廣泛的藍芽應用規範。

 

1.1  藍芽核心規範介紹

核心規範是藍芽協議家族的基礎,自藍芽技術聯盟(Bluetooth SIG,Special Interest Group)在1999年頒佈藍芽核心規範1.0版本以來,到目前為止藍芽SIG一共釋出了七個重要版本。每一個版本都促使藍芽技術朝著更快、更安全、更省電的方向發展。表1所示為藍芽核心規範[①]發展歷史。

表1  藍芽核心規範發展介紹

 

版本

規範釋出日期

增強功能

0.7

1998年10月19日

Baseband、LMP

0.8

1999年1月21日

HCI、L2CAP、RFCOMM

0.9

1999年4月30日

OBEX與IrDA的互通性

1.0 Draft

1999年7月5日

SDP、TCS

1.0 A

1999年7月26日

第一個正式版本

1.0 B

2000年10月1日

安全性,廠商裝置之間連線相容性

1.1

2001年2月22日

IEEE 802.15.1

1.2

2003年11月5日

快速連線、自適應跳頻、錯誤檢測和流程控制、同步能力

2.0 + EDR

2004年11月9日

EDR傳輸率提升至2-3Mbps

2.1 + EDR

2007年7月26日

擴充套件查詢響應、簡易安全配對、暫停與繼續加密、Sniff省電

3.0 + HS

2009年4月21日

交替射頻技術、802.11協議適配層、電源管理、取消了UMB的應用

4.0 +BLE

2010年6月30日

低功耗物理層和鏈路層、AES加密、Attribute Protocol(ATT)、Generic Attribute Profile(GATT)、Security Manager(SM)

 

表1中,

  • EDR:全稱為Enhanced Data Rate。通過提高多工處理和多種藍芽裝置同時執行的能力,EDR使得藍芽裝置的傳輸速度可達3Mbps。
  • HS:全稱為High Speed。HS使得Bluetooth能利用WiFi作為傳輸方式進行資料傳輸,其支援的傳輸速度最高可達24Mbps。其核心是在802.11的基礎上,通過整合802.11協議適配層,使得藍芽協議棧可以根據任務和裝置的不同,選擇正確的射頻。
  • BLE:全稱為Bluetooth Low Energy。藍芽規範4.0最重要的一個特性就是低功耗。BLE使得藍芽裝置可通過一粒鈕釦電池供電以維持續工作數年之久。很明顯,BLE使得藍芽裝置在鐘錶、遠端控制、醫療保健及運動感應器等市場具有極光明的應用場景。

雖然藍芽4.0規範3年就釋出,但目前使用最廣泛的藍芽核心規範版本還是3.0。智慧手機中只有Iphone 4S,Iphone5,三星GallaxyS3、S4、Note2等少數裝置支援藍芽4.0。不過,Google已經在Android 4.3中添加了對4.0的支援。很明顯,隨著Android的持續推進和眾多廠商的齊力支援,筆者估計在未來較短的一段時間內,藍芽核心規範4.0將得到迅速普及。表2是經典藍芽與低功耗藍芽的一些區別:

表2  經典藍芽與低功耗藍芽的區別

 

技術規範

經典藍芽(2.1 &3.0)

低功耗藍芽(4.0)

無線電頻率

2.4GHz

2.4GHz

距離

10米/100米

30米

資料速率

1-3Mbps

1Mbps

應用吞吐量

0.7-2.1Mbps

0.2Mbps

傳送資料的總時間

100ms

<6ms

耗電量

1

0.01至0.5

最大操作電流

<30mA

<15mA(最高執行時為15 mA)

主要用途

手機遊戲機耳機,立體聲音訊流,汽車PC

手機,遊戲機,PC,體育健身醫療保健,汽車,家用電子自動化工業

 

那麼,藍芽核心規範4.0有什麼特別之處呢?藍芽核心規範4.0的模組如圖2所示:

圖2  藍芽核心規範4.0的模組

由圖2可知,藍芽核心規範4.0的模組增加了以下幾個藍芽低功耗元件。

  • GATT表示伺服器屬性和客戶端屬性,描述了屬性伺服器中使用的服務層次,特點和屬性。BLE裝置使用它作為藍芽低功耗應用規範的服務發現。
  • ATT實現了屬性客戶端和伺服器之間的點對點協議。ATT客戶端給ATT伺服器傳送請命令。ATT伺服器向ATT客戶端傳送回覆和通知。
  • SMP用於生成對等協議的加密金鑰和身份金鑰。SMP管理加密金鑰和身份金鑰的儲存,它通過生成和解析裝置的地址來識別藍芽裝置。

1.2  藍芽應用規範[②]

藍芽SIG根據不同的應用場景定義了不同的藍芽應用規範,截止到現在,釋出了40個藍芽應用規範。本節介紹最常用的五個的藍芽應用規範。

1.2.1  Advanced Audio Distribution Profile

Advanced Audio Distribution Profile 簡稱為A2DP(高質量音訊分發規範)定義瞭如何將立體聲質量的音訊通過流媒體的方式從媒體源傳輸到接收器上。A2DP使用Asynchronous Connectionless Link(ACL,藍芽非同步傳輸)通道傳輸高質量音訊內容,它依賴於Generic Audio/Video Distribution Profile(GAVDP,通用音訊/視訊分發規範)。A2DP必須支援低複雜度及Sub-bandCodec(SBC,低頻寬編解碼),可選支援MPEG1,2音訊,MPEG2、4AAC。A2DP的應用場景如圖4[1]所示:A2DP的應用場景如圖3所示:

圖3  A2DP的應用場景

由圖3可知,A2DP有兩種應用場景分別是播放和錄音。

  • 播放場景是具有藍芽功能的播放器通過A2DP向藍芽耳機或藍芽立體聲揚聲器傳送高質量音訊。
  • 錄音場景是具有藍芽功能的麥克風通過A2DP向藍芽錄音器傳送高質量音訊。

和A2DP相關的規範有Video Distribution Profile(VDP,視訊分發規範),Audio/Video Remote Control Profile(AVRCP,音訊/視訊運程控制規範)。

1.2.2  Object Push Profile

OPP(物件推送規範)定義了推送伺服器和客戶端之間基於Generic Object Exchange Profile(GOEP,通用物件交換規範)進行物件交換的規範。OPP的應用場景如圖4所示:

圖4  OPP的應用場景

由圖4可知,OPP主要用於手機與手機或者手機與電腦之間通過藍芽進行檔案操作。可交換的檔案型別有電話本,備忘錄,日程表等文字檔案,還有視訊,聲音,圖片,音樂等多媒體檔案。

Wi-Fi Direct(WiFi直連)[③]和藍芽OPP有相同的功能。WiFi直連是WiFi裝置之間不需要無線路由器,直接進行物件交換。它的優點是傳輸距離長、速度快,缺點是功耗高。

1.2.3  Hands-Free Profile

HFP(HFP,擴音規範)定義了藍芽音訊閘道器裝置如何通過藍芽擴音裝置撥打和接聽電話。HFP的應用場景如圖5所示:

圖5  HFP的應用場景

由圖5可知,HFP包括兩個角色:

  • Audio Gateway(AG,音訊閘道器)和Hands-Free Unit(HF,擴音裝置)。AG是音訊輸入和輸出的裝置,典型的AG裝置是手機。HF是執行音訊閘道器的遠端音訊輸入輸出裝置。
  • HFP常見的場景是汽車上的車載套件,當車載套件和耳機通過藍芽方式連線到手機時,通過無線藍芽耳機撥打和接聽電話。

和HFP相關的規範有Headset Profile(HSP,耳機規範),Phonebook Access Profile(PBAP,電話簿訪問規範。

1.2.4  Heart Rate Profile

HRP(心率規範)定位與和醫療/健康相關的應用場景中,它使得藍芽裝置能與心率感測器互動。相關場景如圖6所示:

圖6  HRP的角色關係和應用場景

由圖6可知:

  • 左圖是HRP定義的角色關係。HRP中有兩個角色:心率感應器和收集器。心率感應器是GATT伺服器,是測量心率的裝置,它包含心率服務和裝置資訊服務,心率服務匯出心率測量資料;收集器是GATT客戶端,是從心率感應器接收心率測量資料和其它資料的裝置。
  • 右圖是HRP的應用場景。心率規範用於讓裝置獲得心率感測器的心率測量和其它資料。例如,護士或醫生可以用心率感測器測量病人的心率,並把心率資料傳到筆記本或手持裝置上。

隨著人口老齡化,醫療裝置和醫護人員資源不足,可以運用藍芽健康規範實現遠端醫療。筆者所在的Tieto公司在Android平臺上運用心率規範開發了心率測量的原型程式,詳細介紹請看視訊http://www.youtube.com/watch?v=r_t-hstRgDs&feature=youtu.be

和HRP相關的健康規範有Glucose Profile(GLP,血糖規範),Blood Pressure Profile(BLP,血壓規範BLP),Health Thermometer Profile(HTP,健康體溫計規範)。

1.2.5  Cycling Speed and Cadence Profile

CSCP(自行車速度和步調規範)讓人們在騎自行車鍛鍊時跟蹤速度和節奏。CSCP也基於GATT的規範。自行車速度和步調規範的角色關係和應用場景如圖7所示:

圖7  CSCP的角色關係和應用場景

由圖7可知:

  • 左圖是CSCP的角色關係。CSCP定義了兩個角色:自行車速度和步調感應器和收集器。CSC感應器是GATT伺服器,向收集器報告車輪轉速資料或軸轉速資料。CSC感應器包含CSC服務和裝置資訊服務;收集器是GATT客戶端,從CSC感應器接收自行車的速度和步調資料。
  • 右圖是CSCP的應用場景。感測器測量被廣泛應用於運動和健身,通過感測器來監視和控制訓練強調,以及在多個訓練中衡量進展情況。自行車速度感測器和自行車踏頻感測器是使用者測量車輪速度或蹬踏節奏的裝置。任何裝置實現CSC規範可以與CSC感測器連線並接收資料。

和CSCP相關的規範有Running Speed and Cadence Profile(RSCS,跑步速度和步調規範)。

二  Android中的Bluetooth

Android 4.2之前,Google一直使用的是Linux官方藍芽協議棧,即知名老牌開源專案BlueZ。BlueZ實際上是由高通公司在2001年5月基於GPL協議釋出的一個開源專案,該專案僅釋出一個月後就被Linux之父Linux Torvalds納入了Linux核心,並做為Linux 2.4.6核心的官方藍芽協議棧。隨著Android裝置的流行,BlueZ也得到了極大的完善和擴充套件。例如Android 4.1中BlueZ的版本升級為4.93,它支援藍芽核心規範4.0,並實現了絕大部分的Profiles。

BlueZ現在正處於其巔峰時期,但好景不長。從Android 4.2即Jelly Bean開始,Google便在Android原始碼中推出了它和博通公司一起開發的BlueDroid以替代BlueZ。雖然因為時間及成熟度的原因,大部分手機廠商在Android 4.2中仍繼續使用BlueZ。但據筆者瞭解,BlueZ的創始者,高通公司也將在基於其晶片的Android參考設計中去除BlueZ,並僅支援BlueDroid。

BlueZ的未來如何筆者姑且不論。不過,能讓高通改弦易轍,BlueDroid自有其合理之處。相比BlueZ,BlueDroid最值得稱道的地方就是其框架結構變得更為簡潔和清晰。另外,藉助HAL(Hardware Abstraction Layer,硬體抽象層),BlueDroid終於不再和dbus有任何瓜葛。圖8所示為Android 4.2中BlueDroid的框架結構圖[④]

圖8  Android 4.2BlueDroid框架結構圖

由圖8可知,Android4.2中BlueDroid框架包括以下幾個部分:

  • 應用程式通過android.bluetooth package下的API來呼叫系統的Bluetooth功能。
  • 應用層空間增加了一個名為Bluetooth的App。它做為系統的bluetooth核心程序而存在。其內部將通過JNI來呼叫Bluetooth HAL層以完成各種藍芽請求。
  • Bluetooth HAL也屬於Android 4.2新增模組,它由藍芽核心規範硬體抽象層和藍芽應用規範硬體抽象層組成。由於HAL層的隔離作用,上層程式碼可輕鬆移植到不同晶片平臺。
  • 作為整個藍芽服務的核心,Bluetooth Stack模組則由Bluetooth Application Layer(縮寫為BTA)和Bluetooth Embedded System(縮寫為BTE)兩大部分組成。BTA實現了藍芽裝置管理、狀態管理及一些應用規範。而BTE則通過HCI與廠商藍芽晶片互動以實現了藍芽協議棧的通用功能和相關協議。另外,BTE還包括一個統一核心介面(GKI),藍芽晶片廠商可藉助GKI快速輕鬆得移植藍芽協議棧到其他作業系統或手機平臺上。
  • Vendor Extentions(廠商擴充套件):開發者可以新增自定義擴充套件以實現廠商特定的模組和元件。

除了BlueDroid外,在今年的Google I/O大會,谷歌公司還宣佈將於與蘋果、微軟和黑莓等公司共同支援Bluetooth Smart Ready(BSR,藍芽智慧就緒)和Bluetooth Smart(BS,藍芽智慧)技術。這項技術使藍芽裝置或應用可以非常容易地連線全球成千上萬的藍芽裝置,藍芽使用者的生活也因此變得更加簡單。BSR和BS都是建立在藍芽核心規範4.0和GATT應用規範。即將釋出的Android 4.3(MR2)支援BSR技術,使得BS的開發者可以輕易地將其裝置和應用與Android BSR裝置進行連線和釋出。藍芽使用者運用BS的智慧應用配件(如健康監控或醫療裝置)收集資料,再傳送到支援BSR裝置(如智慧手機或平板)上。

另外,藍芽SIG也正在研發工具Bluetooth Application Accelerator(藍芽應用加速器)。據可靠訊息,該工具將隨Android 4.3釋出,並將幫助開發者在Android 4.3上快速開發藍芽應用,從而加快相關產品的研發時間。

三  總結

本文對藍芽核心規範、藍芽應用規範以及Android 4.2中的藍芽協議棧BlueDroid進行了一些簡單介紹。

從筆者瞭解的情況來看,BlueDroid雖然對BlueZ大有取而代之的趨勢,但現在它對藍芽應用規範的支援還不夠完善。例如BlueDroid僅支援AVRCP 1.0,而非最新的AVRCP 1.5。所以,國內某些晶片或手機廠商若能及早完成BlueZ相關模組到BlueDroid的移植工作,相信能幫助它們在競爭日趨白日化的移動世界中拔得先機。

另外,作為一種成熟、低功耗無線通訊技術的先鋒,藍芽未來在可穿戴裝置領域中也將扮演越來越重要的作用。那時,藍芽或許就會真正像“牙齒”一樣成為各種裝置中不可或缺的一部分了。

 


 

[①]http://zh.wikipedia.org/wiki/Bluetooth

[②]詳情可參考http://developer.bluetooth.org/TechnologyOverview/Pages/Profiles.aspx

[③]關於WFD,讀者可參考http://blog.csdn.net/innost/article/details/8474683

[④]http://source.android.com/devices/bluetooth.html

 

 

android 4.0 BLE開發官方文件介紹

 

安卓4.3(API 18)為BLE的核心功能提供平臺支援和API,App可以利用它來發現裝置、查詢服務和讀寫特性。相比傳統的藍芽,BLE更顯著的特點是低功耗。這一優點使android App可以與具有低功耗要求的BLE裝置通訊,如近距離感測器、心臟速率監視器、健身裝置等。

關鍵術語和概念


  • Generic Attribute Profile(GATT)—GATT配置檔案是一個通用規範,用於在BLE鏈路上傳送和接收被稱為“屬性”的資料塊。目前所有的BLE應用都基於GATT。 藍芽SIG規定了許多低功耗裝置的配置檔案。配置檔案是裝置如何在特定的應用程式中工作的規格說明。注意一個裝置可以實現多個配置檔案。例如,一個裝置可能包括心率監測儀和電量檢測。
  • Attribute Protocol(ATT)—GATT在ATT協議基礎上建立,也被稱為GATT/ATT。ATT對在BLE裝置上執行進行了優化,為此,它使用了儘可能少的位元組。每個屬性通過一個唯一的的統一識別符號(UUID)來標識,每個String型別UUID使用128 bit標準格式。屬性通過ATT被格式化為characteristics和services。
  • Characteristic 一個characteristic包括一個單一變數和0-n個用來描述characteristic變數的descriptor,characteristic可以被認為是一個型別,類似於類。
  • Descriptor Descriptor用來描述characteristic變數的屬性。例如,一個descriptor可以規定一個可讀的描述,或者一個characteristic變數可接受的範圍,或者一個characteristic變數特定的測量單位。
  • Service service是characteristic的集合。例如,你可能有一個叫“Heart Rate Monitor(心率監測儀)”的service,它包括了很多characteristics,如“heart rate measurement(心率測量)”等。你可以在bluetooth.org 找到一個目前支援的基於GATT的配置檔案和服務列表。

角色和責任

以下是Android裝置與BLE裝置互動時的角色和責任:

  • 中央 VS 外圍裝置。 適用於BLE連線本身。中央裝置掃描,尋找廣播;外圍裝置發出廣播。
  • GATT 服務端 VS GATT 客戶端。決定了兩個裝置在建立連線後如何互相交流。

為了方便理解,想象你有一個Android手機和一個用於活動跟蹤BLE裝置,手機支援中央角色,活動跟蹤器支援外圍(為了建立BLE連線你需要注意兩件事,只支援外圍裝置的兩方或者只支援中央裝置的兩方不能互相通訊)。

當手機和運動追蹤器建立連線後,他們開始向另一方傳輸GATT資料。哪一方作為伺服器取決於他們傳輸資料的種類。例如,如果運動追蹤器想向手機報告感測器資料,運動追蹤器是服務端。如果運動追蹤器更新來自手機的資料,手機會作為服務端。

在這份文件的例子中,android app(執行在android裝置上)作為GATT客戶端。app從gatt服務端獲得資料,gatt服務端即支援Heart Rate Profile(心率配置)的BLE心率監測儀。但是你可以自己設計android app去扮演GATT服務端角色。更多資訊見BluetoothGattServer

BLE許可權


為了在app中使用藍芽功能,必須宣告藍芽許可權BLUETOOTH。利用這個許可權去執行藍芽通訊,例如請求連線、接受連線、和傳輸資料。

如果想讓你的app啟動裝置發現或操縱藍芽設定,必須宣告BLUETOOTH_ADMIN許可權。注意:如果你使用BLUETOOTH_ADMIN許可權,你也必須宣告BLUETOOTH許可權。

在你的app manifest檔案中宣告藍芽許可權。

 

1

2

 

<uses-permission android:name="android.permission.BLUETOOTH"/>

<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

如果想宣告你的app只為具有BLE的裝置提供,在manifest檔案中包括:

 

1

 

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

但是如果想讓你的app提供給那些不支援BLE的裝置,需要在manifest中包括上面程式碼並設定required="false",然後在執行時可以通過使用PackageManager.hasSystemFeature()確定BLE的可用性。

 

1

2

3

4

5

 

// 使用此檢查確定BLE是否支援在裝置上,然後你可以有選擇性禁用BLE相關的功能

if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {

Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();

finish();

}

設定BLE


你的app能與BLE通訊之前,你需要確認裝置是否支援BLE,如果支援,確認已經啟用。注意如果<uses-feature.../>設定為false,這個檢查才是必需的。

如果不支援BLE,那麼你應該適當地禁用部分BLE功能。如果支援BLE但被禁用,你可以無需離開應用程式而要求使用者啟動藍芽。使用BluetoothAdapter兩步完成該設定。

  1. 獲取 BluetoothAdapter

    所有的藍芽活動都需要藍芽介面卡。BluetoothAdapter代表裝置本身的藍芽介面卡(藍芽無線)。整個系統只有一個藍芽介面卡,而且你的app使用它與系統互動。下面的程式碼片段顯示瞭如何得到介面卡。注意該方法使用getSystemService()]返回BluetoothManager,然後將其用於獲取介面卡的一個例項。Android 4.3(API 18)引入BluetoothManager。

 

1

2

3

4

 

// 初始化藍芽介面卡

final BluetoothManager bluetoothManager =

(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);

mBluetoothAdapter = bluetoothManager.getAdapter();

  1. 開啟藍芽

    接下來,你需要確認藍芽是否開啟。呼叫isEnabled())去檢測藍芽當前是否開啟。如果該方法返回false,藍芽被禁用。下面的程式碼檢查藍芽是否開啟,如果沒有開啟,將顯示錯誤提示使用者去設定開啟藍芽。

 

1

2

3

4

5

 

// 確保藍芽在裝置上可以開啟

if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {

Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);

startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);

}

發現BLE裝置


為了發現BLE裝置,使用startLeScan())方法。這個方法需要一個引數BluetoothAdapter.LeScanCallback。你必須實現它的回撥函式,那就是返回的掃描結果。因為掃描非常消耗電量,你應當遵守以下準則:

  • 只要找到所需的裝置,停止掃描。
  • 不要在迴圈裡掃描,並且對掃描設定時間限制。以前可用的裝置可能已經移出範圍,繼續掃描消耗電池電量。

下面程式碼顯示瞭如何開始和停止一個掃描:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

 

/**

* 掃描和顯示可以提供的藍芽裝置.

*/

public class DeviceScanActivity extends ListActivity {

private BluetoothAdapter mBluetoothAdapter;

private boolean mScanning;

private Handler mHandler;

// 10秒後停止尋找.

private static final long SCAN_PERIOD = 10000;

...

private void scanLeDevice(final boolean enable) {

if (enable) {

// 經過預定掃描期後停止掃描

mHandler.postDelayed(new Runnable() {

@Override

public void run() {

mScanning = false;

mBluetoothAdapter.stopLeScan(mLeScanCallback);

}

}, SCAN_PERIOD);

mScanning = true;

mBluetoothAdapter.startLeScan(mLeScanCallback);

} else {

mScanning = false;

mBluetoothAdapter.stopLeScan(mLeScanCallback);

}

...

}

...

}

如果你只想掃描指定型別的外圍裝置,可以改為呼叫startLeScan(UUID[], BluetoothAdapter.LeScanCallback)),需要提供你的app支援的GATT services的UUID物件陣列。

作為BLE掃描結果的介面,下面是BluetoothAdapter.LeScanCallback的實現。

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

 

private LeDeviceListAdapter mLeDeviceListAdapter;

...

// Device scan callback.

private BluetoothAdapter.LeScanCallback mLeScanCallback =

new BluetoothAdapter.LeScanCallback() {

@Override

public void onLeScan(final BluetoothDevice device, int rssi,

byte[] scanRecord) {

runOnUiThread(new Runnable() {

@Override

public void run() {

mLeDeviceListAdapter.addDevice(device);

mLeDeviceListAdapter.notifyDataSetChanged();

}

});

}

};

注意:只能掃描BLE裝置或者掃描傳統藍芽裝置,不能同時掃描BLE和傳統藍芽裝置。

連線到GATT服務端


與一個BLE裝置互動的第一步就是連線它——更具體的,連線到BLE裝置上的GATT服務端。為了連線到BLE裝置上的GATT服務端,需要使用connectGatt( )方法。這個方法需要三個引數:一個Context物件,自動連線(boolean值,表示只要BLE裝置可用是否自動連線到它),和BluetoothGattCallback呼叫。

 

1

 

mBluetoothGatt = device.connectGatt(this, false, mGattCallback);

連線到GATT服務端時,由BLE裝置做主機,並返回一個BluetoothGatt例項,然後你可以使用這個例項來進行GATT客戶端操作。請求方(Android app)是GATT客戶端。BluetoothGattCallback用於傳遞結果給使用者,例如連線狀態,以及任何進一步GATT客戶端操作。

在這個例子中,這個BLE APP提供了一個activity(DeviceControlActivity)來連線,顯示資料,顯示該裝置支援的GATT services和characteristics。根據使用者的輸入,這個activity與BluetoothLeService通訊,通過Android BLE API實現與BLE裝置互動。

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

 

//通過BLE API服務端與BLE裝置互動

public class BluetoothLeService extends Service {

private final static String TAG = BluetoothLeService.class.getSimpleName();

private BluetoothManager mBluetoothManager; //藍芽管理器

private BluetoothAdapter mBluetoothAdapter; //藍芽介面卡

private String mBluetoothDeviceAddress; //藍芽裝置地址

private BluetoothGatt mBluetoothGatt;

private int mConnectionState = STATE_DISCONNECTED;

private static final int STATE_DISCONNECTED = 0; //裝置無法連線

private static final int STATE_CONNECTING = 1; //裝置正在連線狀態

private static final int STATE_CONNECTED = 2; //裝置連線完畢

public final static String ACTION_GATT_CONNECTED =

"com.example.bluetooth.le.ACTION_GATT_CONNECTED";

public final static String ACTION_GATT_DISCONNECTED =

"com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";

public final static String ACTION_GATT_SERVICES_DISCOVERED =

"com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";

public final static String ACTION_DATA_AVAILABLE =

"com.example.bluetooth.le.ACTION_DATA_AVAILABLE";

public final static String EXTRA_DATA =

"com.example.bluetooth.le.EXTRA_DATA";

public final static UUID UUID_HEART_RATE_MEASUREMENT =

UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT);

//通過BLE API的不同型別的回撥方法

private final BluetoothGattCallback mGattCallback =

new BluetoothGattCallback() {

@Override

public void onConnectionStateChange(BluetoothGatt gatt, int status,

int newState) {//當連線狀態發生改變

String intentAction;

if (newState == BluetoothProfile.STATE_CONNECTED) {//當藍芽裝置已經連線

intentAction = ACTION_GATT_CONNECTED;

mConnectionState = STATE_CONNECTED;

broadcastUpdate(intentAction);

Log.i(TAG, "Connected to GATT server.");

Log.i(TAG, "Attempting to start service discovery:" +

mBluetoothGatt.discoverServices());

} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {//當裝置無法連線

intentAction = ACTION_GATT_DISCONNECTED;

mConnectionState = STATE_DISCONNECTED;

Log.i(TAG, "Disconnected from GATT server.");

broadcastUpdate(intentAction);

}

}

@Override

// 發現新服務端

public void onServicesDiscovered(BluetoothGatt gatt, int status) {

if (status == BluetoothGatt.GATT_SUCCESS) {

broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);

} else {

Log.w(TAG, "onServicesDiscovered received: " + status);

}

}

@Override

// 讀寫特性

public void onCharacteristicRead(BluetoothGatt gatt,

BluetoothGattCharacteristic characteristic,

int status) {

if (status == BluetoothGatt.GATT_SUCCESS) {

broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);

}

}

...

};

...

}

當一個特定的回撥被觸發的時候,它會呼叫相應的broadcastUpdate()輔助方法並且傳遞給它一個action。注意在該部分中的資料解析按照藍芽心率測量配置檔案規格進行。

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

 

private void broadcastUpdate(final String action) {

final Intent intent = new Intent(action);

sendBroadcast(intent);

}

private void broadcastUpdate(final String action,

final BluetoothGattCharacteristic characteristic) {

final Intent intent = new Intent(action);

// 這是心率測量配置檔案。

if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {

int flag = characteristic.getProperties();

int format = -1;

if ((flag & 0x01) != 0) {

format = BluetoothGattCharacteristic.FORMAT_UINT16;

Log.d(TAG, "Heart rate format UINT16.");

} else {

format = BluetoothGattCharacteristic.FORMAT_UINT8;

Log.d(TAG, "Heart rate format UINT8.");

}

final int heartRate = characteristic.getIntValue(format, 1);

Log.d(TAG, String.format("Received heart rate: %d", heartRate));

intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));

} else {

// 對於所有其他的配置檔案,用十六進位制格式寫資料

final byte[] data = characteristic.getValue();

if (data != null && data.length > 0) {

final StringBuilder stringBuilder = new StringBuilder(data.length);

for(byte byteChar : data)

stringBuilder.append(String.format("%02X ", byteChar));

intent.putExtra(EXTRA_DATA, new String(data) + "\n" +

stringBuilder.toString());

}

}

sendBroadcast(intent);

}

返回DeviceControlActivity, 這些事件由一個BroadcastReceiver來處理:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

 

// 通過服務控制不同的事件

// ACTION_GATT_CONNECTED: 連線到GATT服務端

// ACTION_GATT_DISCONNECTED: 未連線GATT服務端.

// ACTION_GATT_SERVICES_DISCOVERED: 未發現GATT服務.

// ACTION_DATA_AVAILABLE: 接受來自裝置的資料,可以通過讀或通知操作獲得。

private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {

@Override

public void onReceive(Context context, Intent intent) {

final String action = intent.getAction();

if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {

mConnected = true;

updateConnectionState(R.string.connected);

invalidateOptionsMenu();

} else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {

mConnected = false;

updateConnectionState(R.string.disconnected);

invalidateOptionsMenu();

clearUI();

} else if (BluetoothLeService.

ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {

// 在使用者介面上展示所有的services and characteristics

displayGattServices(mBluetoothLeService.getSupportedGattServices());

} else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {

displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA));

}

}

};

讀取BLE變數


你的android app完成與GATT服務端連線和發現services後,就可以讀寫支援的屬性。例如,這段程式碼通過服務端的services和 characteristics迭代,並且將它們顯示在UI上。

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

 

public class DeviceControlActivity extends Activity {

...

// 演示如何遍歷支援GATT Services/Characteristics

// 這個例子中,我們填充繫結到UI的ExpandableListView上的資料結構

private void displayGattServices(List<BluetoothGattService> gattServices) {

if (gattServices == null) return;

String uuid = null;

String unknownServiceString = getResources().

getString(R.string.unknown_service);

String unknownCharaString = getResources().

getString(R.string.unknown_characteristic);

ArrayList<HashMap<String, String>> gattServiceData =

new ArrayList<HashMap<String, String>>();

ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData

= new ArrayList<ArrayList<HashMap<String, String>>>();

mGattCharacteristics =

new ArrayList<ArrayList<BluetoothGattCharacteristic>>();

// 迴圈可用的GATT Services.

for (BluetoothGattService gattService : gattServices) {

HashMap<String, String> currentServiceData =

new HashMap<String, String>();

uuid = gattService.getUuid().toString();

currentServiceData.put(

LIST_NAME, SampleGattAttributes.

lookup(uuid, unknownServiceString));

currentServiceData.put(LIST_UUID, uuid);

gattServiceData.add(currentServiceData);

ArrayList<HashMap<String, String>> gattCharacteristicGroupData =

new ArrayList<HashMap<String, String>>();

List<BluetoothGattCharacteristic> gattCharacteristics =

gattService.getCharacteristics();

ArrayList<BluetoothGattCharacteristic> charas =

new ArrayList<BluetoothGattCharacteristic>();

// 迴圈可用的Characteristics.

for (BluetoothGattCharacteristic gattCharacteristic :

gattCharacteristics) {

charas.add(gattCharacteristic);

HashMap<String, String> currentCharaData =

new HashMap<String, String>();

uuid = gattCharacteristic.getUuid().toString();

currentCharaData.put(

LIST_NAME, SampleGattAttributes.lookup(uuid,

unknownCharaString));

currentCharaData.put(LIST_UUID, uuid);

gattCharacteristicGroupData.add(currentCharaData);

}

mGattCharacteristics.add(charas);

gattCharacteristicData.add(gattCharacteristicGroupData);

}

...

}

...

}

接收GATT通知


當裝置上的特性改變時會通知BLE應用程式。這段程式碼顯示瞭如何使用setCharacteristicNotification( )給一個特性設定通知。

 

1

2

3

4

5

6

7

8

9

10

11

 

private BluetoothGatt mBluetoothGatt;

BluetoothGattCharacteristic characteristic;

boolean enabled;

...

mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);

...

BluetoothGattDescriptor descriptor = characteristic.getDescriptor(

UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));

descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);

mBluetoothGatt.writeDescriptor(descriptor);

如果對一個特性啟用通知,當遠端藍芽裝置特性發送變化,回撥函式onCharacteristicChanged( ))被觸發。

 

1

2

3

4

5

6

 

@Override

// 廣播更新

public void onCharacteristicChanged(BluetoothGatt gatt,

BluetoothGattCharacteristic characteristic) {

broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);

}

關閉客戶端App


當你的app完成BLE裝置的使用後,應該呼叫close( )),系統可以合理釋放佔用資源。

 

1

2

3

4

5

6

7

 

public void close() {

if (mBluetoothGatt == null) {

return;

}

mBluetoothGatt.close();

mBluetoothGatt = null;

}

 

 

Android 藍芽4.0 BLE 理解

 

本文簡單結合兩篇文章

http://blog.csdn.net/hellogv/article/details/24267685

http://blog.csdn.net/jimoduwu/article/details/21604215

在BLE協議中,有兩個角色,周邊(Periphery)和中央(Central),一箇中央可以同時連線多個周邊,但是一個周邊某一時刻只能連線一箇中央。但是不管是Periphery還是Central都是可以實現 GATT server 和 GATT client去傳輸資料,但是無法同時都是。

大概瞭解了概念後,看看Android BLE SDK的四個關鍵類(class):

a) BluetoothGattServer作為周邊來提供資料;BluetoothGattServerCallback返回周邊的狀態。

b) BluetoothGatt作為中央來使用和處理資料;BluetoothGattCallback返回中央的狀態和周邊提供的資料。

因為我們討論的是Android的BLE SDK,下面所有的BluetoothGattServer代表周邊,BluetoothGatt代表中央。

          

一.建立一個周邊(雖然目前周邊API在Android手機上不工作,但還是看看)

 a)先看看周邊用到的class,藍色橢圓

b)說明:

每一個周邊BluetoothGattServer,包含多個服務Service,每一個Service包含多個特徵Characteristic。

1.new一個特徵:character = new BluetoothGattCharacteristic(
UUID.fromString(characteristicUUID),
BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_READ);

2.new一個服務:service = new BluetoothGattService(UUID.fromString(serviceUUID),
BluetoothGattService.SERVICE_TYPE_PRIMARY);

3.把特徵新增到服務:service.addCharacteristic(character);

4.獲取BluetoothManager:manager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);

5.獲取/開啟周邊:BluetoothGattServer server = manager.openGattServer(this,
new BluetoothGattServerCallback(){...}); 

6.把service新增到周邊:server.addService(service);

7.開始廣播service:Google還沒有廣播Service的API,等吧!!!!!所以目前我們還不能讓一個Android手機作為周邊來提供資料。

 

二.建立一箇中央(這次不會讓你失望,可以成功建立並且連線到周邊的)

a)先看看中央用到的class,藍色橢圓

 

b)說明:

為了拿到中央BluetoothGatt,可要爬山涉水十八彎:

1.先拿到BluetoothManager:bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);

2.再拿到BluetoothAdapt:btAdapter = bluetoothManager.getAdapter();

3.開始掃描:btAdapter.startLeScan( BluetoothAdapter.LeScanCallback);

4.從LeScanCallback中得到BluetoothDevice:public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {.....}

5.用BluetoothDevice得到BluetoothGatt:gatt = device.connectGatt(this, true, gattCallback);

終於拿到中央BluetoothGatt了,它有一堆方法(查API吧),呼叫這些方法,你就可以通過BluetoothGattCallback和周邊BluetoothGattServer互動了。

官方有給出BLE 通訊的sample ,下面是牛人簡化了程式碼,簡化得簡單明瞭

本文來自http://blog.csdn.net/hellogv/ ,引用必須註明出處!

最近穿戴裝置發展得很火,把相關技術也帶旺了,其中一項是BLE(Bluetooth Low Energy)。BLE是藍芽4.0的核心Profile,主打功能是快速搜尋,快速連線,超低功耗保持連線和傳輸資料,弱點是資料傳輸速率低,由於BLE的低功耗特點,因此普遍用於穿戴裝置。Android 4.3才開始支援BLE API,所以請各位客官把本文程式碼執行在藍芽4.0和Android 4.3及其以上的系統,另外本文所用的BLE終端是一個藍芽4.0的串列埠藍芽模組。

PS:我的i9100刷了4.4系統後,竟然也能跟BLE藍芽模組通訊。

 

BLE分為三部分Service、Characteristic、Descriptor,這三部分都由UUID作為唯一標示符。一個藍芽4.0的終端可以包含多個Service,一個Service可以包含多個Characteristic,一個Characteristic包含一個Value和多個Descriptor,一個Descriptor包含一個Value。一般來說,Characteristic是手機與BLE終端交換資料的關鍵,Characteristic有較多的跟許可權相關的欄位,例如PERMISSION和PROPERTY,而其中最常用的是PROPERTY,本文所用的BLE藍芽模組竟然沒有標準的Characteristic的PERMISSION。Characteristic的PROPERTY可以通過位運算子組合來設定讀寫屬性,例如READ|WRITE、READ|WRITE_NO_RESPONSE|NOTIFY,因此讀取PROPERTY後要分解成所用的組合(本文程式碼已含此分解方法)。

 

本文程式碼改自Android 4.3 Sample的BluetoothLeGatt,把冗餘程式碼去掉,獲取的BLE裝置資訊都通過Log,還有一些必要的讀寫藍芽方法,應該算是簡化到大家一看就可以懂了。本文程式碼可以到http://download.csdn.net/detail/hellogv/7228819下載。接下來貼出本文執行的結果,首先是連線BLE裝置後,枚舉出裝置所有Service、Characteristic、Descriptor,並且手機會往Characteristic uuid=0000ffe1-0000-1000-8000-00805f9b34fb寫入“send data->”字串,BLE終端收到資料通過串列埠傳到PC串列埠助手(見PC串列埠助手的截圖):

04-21 18:28:25.465: E/DeviceScanActivity(12254): -->service type:PRIMARY
04-21 18:28:25.465: E/DeviceScanActivity(12254): -->includedServices size:0
04-21 18:28:25.465: E/DeviceScanActivity(12254): -->service uuid:00001800-0000-1000-8000-00805f9b34fb
04-21 18:28:25.465: E/DeviceScanActivity(12254): ---->char uuid:00002a00-0000-1000-8000-00805f9b34fb
04-21 18:28:25.465: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.465: E/DeviceScanActivity(12254): ---->char property:READ
04-21 18:28:25.465: E/DeviceScanActivity(12254): ---->char uuid:00002a01-0000-1000-8000-00805f9b34fb
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char property:READ
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char uuid:00002a02-0000-1000-8000-00805f9b34fb
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char property:READ|WRITE|
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char uuid:00002a03-0000-1000-8000-00805f9b34fb
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.475: E/DeviceScanActivity(12254): ---->char property:READ|WRITE|
04-21 18:28:25.475: E/DeviceScanActivity(12254): ---->char uuid:00002a04-0000-1000-8000-00805f9b34fb
04-21 18:28:25.475: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.475: E/DeviceScanActivity(12254): ---->char property:READ
04-21 18:28:25.475: E/DeviceScanActivity(12254): -->service type:PRIMARY
04-21 18:28:25.475: E/DeviceScanActivity(12254): -->includedServices size:0
04-21 18:28:25.475: E/DeviceScanActivity(12254): -->service uuid:00001801-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char uuid:00002a05-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char property:INDICATE
04-21 18:28:25.480: E/DeviceScanActivity(12254): -------->desc uuid:00002902-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: E/DeviceScanActivity(12254): -------->desc permission:UNKNOW
04-21 18:28:25.480: E/DeviceScanActivity(12254): -->service type:PRIMARY
04-21 18:28:25.480: E/DeviceScanActivity(12254): -->includedServices size:0
04-21 18:28:25.480: E/DeviceScanActivity(12254): -->service uuid:0000ffe0-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char uuid:0000ffe1-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char property:READ|WRITE_NO_RESPONSE|NOTIFY|
04-21 18:28:25.490: E/DeviceScanActivity(12254): -------->desc uuid:00002902-0000-1000-8000-00805f9b34fb
04-21 18:28:25.490: E/DeviceScanActivity(12254): -------->desc permission:UNKNOW
04-21 18:28:25.490: E/DeviceScanActivity(12254): -------->desc uuid:00002901-0000-1000-8000-00805f9b34fb
04-21 18:28:25.490: E/DeviceScanActivity(12254): -------->desc permission:UNKNOW
04-21 18:28:26.025: E/DeviceScanActivity(12254): onCharRead BLE DEVICE read 0000ffe1-0000-1000-8000-00805f9b34fb -> 00

這裡紅字是由BluetoothGattCallback的onCharacteristicRead()回撥而打出Log

 

 

以下Log是PC上的串列埠工具通過BLE模組傳送過來,由BluetoothGattCallback的 onCharacteristicChanged()打出Log
04-21 18:30:18.260: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:18.745: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:19.085: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:19.350: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:19.605: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:19.835: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:20.055: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:20.320: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:20.510: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:20.735: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:21.000: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone

接下來貼出本文核心程式碼:

 

[java] view plaincopy在CODE上檢視程式碼片派生到我的程式碼片

  1. public class DeviceSc