低功耗藍芽BLE以及iBeacon的開發筆記
題記:不要放棄自己進步的機會
背景
公司剛接了一個大專案,其中涉及低功耗藍芽BLE,使用ibeacon裝置來與微信的搖一搖功能互動,達到宣傳,以及使用者在廳店參加活動的效果.
那麼剛好我上一個專案完結,落到我頭上了,本來已經抱著辭職的心態來做了,是在是不會不會啊.
甲方要招標,使用它們提供的標準生產裝置,要求我寫一個控制軟體
1.掙扎的開始
各種百度,google,發現國內資料很少,有的也是商業公司提供的sdk,這並不符合我需要,
google倒是不少,然而以我的英語水平,望洋興嘆罷了@
不發牢騷了:
首先你需要了解這些資料
1、profile
profile可以理解為一種規範,一個標準的通訊協議,它存在於從機中。藍芽組織規定了一些標準的profile,例如 HID OVER GATT ,防丟器 ,心率計等。每個profile中會包含多個service,每個service代表從機的一種能力。2、service
service可以理解為一個服務,在ble從機中,通過有多個服務,例如電量資訊服務、系統資訊服務等,每個service中又包含多個characteristic特徵值。每個具體的characteristic特徵值才是ble通訊的主題。比如當前的電量是80%,所以會通過電量的characteristic特徵值存在從機的profile裡,這樣主機就可以通過這個characteristic來讀取80%這個資料3、characteristic
characteristic特徵值,ble主從機的通訊均是通過characteristic來實現,可以理解為一個標籤,通過這個標籤可以獲取或者寫入想要的內容。4、UUID
UUID,統一識別碼,我們剛才提到的service和characteristic,都需要一個唯一的uuid來標識
PS: 百度到的,我以一個過來人的身份保證這個很重要
2.資料,準備
資料看這裡:
這也是最重要的資料來源
接上文,說完資料,
這是一個很好的demo
sdk\samples\android-22\connectivity\BluetoothLeGatt
該目錄下為谷歌提供的demo,我的應用也是在它的基礎上改進而成的.
由以下部分組成:
1.一個服務 BluetoothLeService,主負責與藍芽裝置進行資料交換
2.兩個activity:DeviceControlActivity,DeviceScanActivity
見名知意,一個負責掃描裝置,而兩外一個與裝置進行互動
3.一個SampleGattAttributes
主要是儲存了一些UUID,我對它進行了加強
3.BLE裝置的使用步驟概況
1.首先要說明的一點是,要求Android版本為4.3及其以上
接著確認裝置支援
/**
* 支援BLE
* check to determine whether BLE is supported on the device
*/
public void isSupported(){
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, "裝置不支援BLE功能", Toast.LENGTH_SHORT).show();
}
}
2.確認手機支援並開啟藍芽的狀態下,你就可以進行裝置的搜尋了
有兩種模式
//第一種
//serviceUuids 指定裝置的UUID
//callback 搜尋的回撥
startLeScan(final UUID[] serviceUuids, final LeScanCallback callback)
//第二種
//其實就是不傳入serviceUuids的,就是說搜尋範圍內所有藍芽裝置
public boolean startLeScan(LeScanCallback callback) {
return startLeScan(null, callback);
}
3.同時記得停止搜尋,當然你也可以一直搜
PS:其實我見到的app大部分是不停的搜尋,這樣的話會有一個廣播資料持續重新整理的效果,那麼很多的距離判斷什麼的都是通過這種不停搜尋的形式來工作的
//mBluetoothAdapter 請自行檢視谷歌文件
//mLeScanCallback 這就是上面提過的LeScanCallback的例項
mBluetoothAdapter.stopLeScan(mLeScanCallback);
接著就是在回撥中拿到各個裝置資料了
/**
* 搜尋裝置回撥
*/
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi,
byte[] scanRecord) {
}
};
這裡有幾個值得說一下的東西
1.BluetoothDevice device
應該可以看的很清楚, device中包含了裝置的基本資訊
2.rssi訊號質量,也有人告訴我是質量
不過我這次並沒有使用到
3.byte[] scanRecord (重頭戲)
廣播響應包資料
一般包括兩個部分,我使用的主要是ServiceData域
在這裡需要注意的是,這兩個部分都存在於廣播資料,而且並不是兩部分都有,有可能是隻有一個部分,所以一定要看情裝置的硬體規範,因為這涉及到後面解析廣播資料
要知道,我們都是拿byte[]陣列中的某些部分對應特定值,一旦對應關係打亂,那麼資料解析就沒什麼正確性可言了
那麼這些廣播資料都是依據一定的規則指定 的,所以這時候你需要裝置廠商的文件來檢視對應的資料,以及其結構
PS:需要注意的一點是,這些資料都是byte[],你需要將它們轉化(一般常用的做法是,先轉化為16進位制的字串,而後再進行讀取解析)
4.讀取藍芽裝置內部資料
這裡涉及到兩個重要部分
service 以及 characteristic
一般來說
一個Service eg: FF01-XXXXXXXXXXX-XXXXX
下面可能有很多的characteristic
Marjor : FF16 -XXXXXX
Power : FF17 - XXX
….一堆的characteristic
- 如果需要寫一個東西
條件:
1.此特徵在哪個Service下,即該service的UUID
2.該特徵的UUID
3.該特徵的寫入格式,16進位制還是其他的東西
//程式碼片段1
//此方法獲取對應特徵值的物件BluetoothGattCharacteristic的實體
public BluetoothGattCharacteristic getCharacteristic(String service,String charact) {
if (mBluetoothGatt == null) return null;
if(mBluetoothGatt.getService(UUID.fromString(service))==null){return null ;}
return mBluetoothGatt.getService(UUID.fromString(service)).getCharacteristic(UUID.fromString(charact));
}
//程式碼片段2
if(!TextUtils.isEmpty(value)){//輸入非空
byte[] arrayOfByte= new byte[2];//傳入資料規定為2個byte
arrayOfByte[0] = ((byte)Integer.parseInt((value).substring(0, 2), 16));
arrayOfByte[1] = ((byte)Integer.parseInt((value).substring(2, 4), 16));
//上面操作,先切,再轉為16進位制
characteristic.setValue(arrayOfByte);
//characteristic實體就是片段1程式碼獲取到的
gattServer.writeCharacteristic(characteristic);
//所有的操作都封裝在一個service中,gattServer為其實體物件
}
//片段3就是服務中的寫入方法
/**
* 寫入一個數據
* @param characteristic
*/
public void writeCharacteristic(BluetoothGattCharacteristic characteristic) {
mBluetoothGatt.setCharacteristicNotification(characteristic, true);
mBluetoothGatt.writeCharacteristic(characteristic);
}
//程式碼片段4 這是回撥,
//在連線時就作為引數傳入了 mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.e(TAG, "連線至 GATT server.");
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.e(TAG, "斷開 GATT server.");
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.e(TAG, "建立連線成功");
} else {
Log.e(TAG, "連線狀態異常: " + status);
}
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.e(TAG, "寫入資料成功");
}
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic
characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.e(TAG, "讀取資料成功");
}
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
Log.e(TAG, "連線狀態發生改變");
};
PS:這裡的例子是寫入,其實讀取的操作與此類似,操作完成後在回撥裡面判斷狀態以及讀取資料即可
5.總結
在我開發的過程中遇到很多問題 ,這裡也非常感謝我的一位上司,在寫完這個應用之前,我是不會相信我能學會這個BLE的,但是事實是我比較完美的完成了這整個工作,並且給硬體廠商制定生產標準,測試硬體等等一系列的活我也都完成了。
只能是感嘆人就是要逼,突然就想起來我爹給我說過的話眼怕手不怕 。