1. 程式人生 > >低功耗藍芽BLE以及iBeacon的開發筆記

低功耗藍芽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.資料,準備

資料看這裡:

  • 這也是最重要的資料來源
    Android SDK 中的doc文件

  • 接上文,說完資料,

    這是一個很好的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的,但是事實是我比較完美的完成了這整個工作,並且給硬體廠商制定生產標準,測試硬體等等一系列的活我也都完成了。
只能是感嘆人就是要逼,突然就想起來我爹給我說過的話眼怕手不怕