android BLE Peripheral 手機模擬設備發出BLE廣播 BluetoothLeAdvertiser


android 從4.3系統開始可以連接BLE設備,這個大家都知道了。iOS是從7.0版本開始支持BLE。

android 進入5.0時代時,開放了一個新功能,手機可以模擬設備發出BLE廣播, 這個新功能其實是 對標於 iOS系統的手機模擬iBeacon設備。


先介紹一下BLE的廣播, BLE設備之所以能被手機掃描到,是因為 BLE設備一直在每隔 一段時間廣播一次,這個廣播里面包含很多數據。

手機掃描BLE設備代碼如下:

        public void startScan(){
            bluetoothAdapter.startLeScan(leScanCallback);
        }
 
    public void stopScan(){
        bluetoothAdapter.stopLeScan(leScanCallback);
    }
    private LeScanCallback leScanCallback=new LeScanCallback() {
        
        @Override
        public void onLeScan(BluetoothDevice bluetoothdeivce, int rssi, byte[] scandata) {
            //把byte數組轉成16進制字符串,方便查看
            Log.e("TAG","scandata:"+ CYUtils.Bytes2HexString(scandata));
        }
    };
ok,這段代碼大家在做連接BLE設備進行通訊的時候,已經很熟悉了。其中的 byte數組 scandata就是 BLE設備的廣播數據。


那么接下來,我們開始使用 手機1 模擬成BLE設備來發送廣播,然后用手機2 來進行掃描查看廣播數據 scandata

首先獲取 BluetoothAdapter, 熟悉的代碼:

BluetoothManager bluetoothManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
 
bluetoothAdapter = bluetoothManager.getAdapter();

進行廣播的時候需要用到BluetoothLeAdvertiser,進行實例化:


mBluetoothLeAdvertiser = bluetoothAdapter.getBluetoothLeAdvertiser();
實例化好之后就可以進行廣播數據了,開啟廣播方法是:



BluetoothLeAdvertiser: 
public void startAdvertising(AdvertiseSettings settings,
            AdvertiseData advertiseData, final AdvertiseCallback callback)
其中, AdvertiseSettings 是廣播的一些設置,比如,廣播間隔,是否可以連接等等; AdvertiseData 就是廣播數據了, AdvertiseCallback是廣播回調,會告訴你廣播成功還是失敗。


先給一段完整廣播代碼如下:


    public void startAction(View v){
        byte[] broadcastData ={0x34,0x56};
        mBluetoothLeAdvertiser.startAdvertising(createAdvSettings(true, 0), createAdvertiseData(broadcastData), mAdvertiseCallback);
    }
    public void stopAction(View v) {
        mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
    }
 
    public AdvertiseSettings createAdvSettings(boolean connectable, int timeoutMillis) {
        AdvertiseSettings.Builder mSettingsbuilder = new AdvertiseSettings.Builder();
        mSettingsbuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY);
        mSettingsbuilder.setConnectable(connectable);
        mSettingsbuilder.setTimeout(timeoutMillis);
        AdvertiseSettings mAdvertiseSettings = mSettingsbuilder.build();
        return mAdvertiseSettings;
    }
 
    public AdvertiseData createAdvertiseData(byte[] data) {
        AdvertiseData.Builder mDataBuilder = new AdvertiseData.Builder();
        mDataBuilder.addManufacturerData(0x01AC, data);
        AdvertiseData mAdvertiseData = mDataBuilder.build();
        return mAdvertiseData;
    }
    private AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() {
        @Override
        public void onStartSuccess(AdvertiseSettings settingsInEffect) {
            super.onStartSuccess(settingsInEffect);
 
            ToastUtils.showToast(MainActivity.this, "開啟廣播成功", 2000);
        }
 
        @Override
        public void onStartFailure(int errorCode) {
            super.onStartFailure(errorCode);
            ToastUtils.showToast(MainActivity.this, "開啟廣播失敗 errorCode:" + errorCode, 2000);
        }
    };

其中,廣播數據broadcastData 我暫時直接先定死為2個字節 0x3456,同樣在createAdvertiseData里面

也有定死的數據 0x01AC . 開啟成功之后

我們使用手機2 來掃描看下廣播的數據是什么:


E/TAG: scandata:02011A05FFAC0134560000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
log打印出來的scandata 有效數據是 02011A05FFAC013456 .  給大家解釋一下這個數據的意思

為了看清楚,我分段如下: 02011A  05  FF AC01 3456   (注,這里的都是16進制數字)

02011A這3個字節,02表示后面一段數據長度為2字節,01表示數據類型是flag ,1A就是flag的數據了

05 表示后面的一段數據長度為 5個字節, FF一個字節,AC01 兩個字節,3456兩個字節,加起來一共5個字節,老鐵沒毛病

FF,是一個數據類型,這是我們通過代碼mDataBuilder.addManufacturerData(0x01AC, data); 添加廣播數據時候設置的

ManufacturerData 是指設備廠商自定義數據,FF 就是代表下面的數據實體是廠商數據.

第一個參數0x01AC,是廠商id,id長度為2個字節,不足2個字節系統會補0,可以看到log打印出來的是 AC01,順序是倒過來的,這點要注意!

如果我代碼是這樣寫的 :


mDataBuilder.addManufacturerData(0xAC, data); //只寫了一個字節的id
那么使用手機2 掃描出的scandata是:

 E/TAG: scandata:02011A05FFAC0034560000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
可以看到,AC后面系統自動補了00


廣播數據出除了可以添加ManufacturerData,還可以添加ServerUUID, 代碼如下:

 

public AdvertiseData createAdvertiseData(byte[] data) {
        AdvertiseData.Builder mDataBuilder = new AdvertiseData.Builder();
        mDataBuilder.addManufacturerData(0x01AC, data);
        mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"));
        AdvertiseData mAdvertiseData = mDataBuilder.build();
        return mAdvertiseData;
    }
代碼添加了一個 AE8F的 server uuid, 使用手機2 掃描的scandata 如下:


 

E/TAG: scandata:02011A05FFAC01345603038FAE00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
直接看03038FAE 這一段,第一個03 表示后面的一段數據長度為3個字節  第二個03 表示這個數據類型是 server uuid類型,uuid的數據就是8FAE,順序是倒過來的!

有人會問: 如果 我把addServiceUuid代碼放在 addManufacturerData 前面,掃描的數據順序是什么樣的呢?

答案 還是:


E/TAG: scandata:02011A05FFAC01345603038FAE00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
可以添加多個 server uuid嗎? 可以,代碼如下:
    public AdvertiseData createAdvertiseData(byte[] data) {
        AdvertiseData.Builder mDataBuilder = new AdvertiseData.Builder();
        mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"));
        mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"));
        mDataBuilder.addManufacturerData(0x01AC, data);
        AdvertiseData mAdvertiseData = mDataBuilder.build();
        return mAdvertiseData;
    }
掃描的結果是:



E/TAG: scandata:02011A05FFAC01345605038FAEE1FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
直接看0503 8FAE E1FF 這一段, 05 表示后面的一段數據長度是5個字節,03表示數據類型是 server uuid, 8FAE是第一個uuid, E1FF是第二個uuid


這個ServerUUID 有什么用呢? 

不知大家在掃描BLE設備的時候,有沒有注意到這個方法:

BluetoothAdapter
public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback)


這個方法也可以用來掃描BLE設備,但是多了一個參數, UUID數組, 這個掃描方法是用來過濾BLE設備用的,比如 你公司開發一個 藍牙防丟器APP,你使用 startLeScan(callback)這個方法掃描的話,你會發現你掃描到周圍的所有的BLE設備,同事戴的小米手環可能也被你掃描到,這樣讓用戶來選擇設備進行連接的話可能就比較迷糊,startLeScan(serviceUuids,callback) 這個方法在掃描的時候會過濾廣播里的數據,只有符合的BLE設備才會被掃描回調。 所以,你們公司的藍牙防丟器設備可以在廣播字段里加入特定的server uuid, app掃描的時候可以過濾其他設備。

我們來實現一下這個功能, 修改 手機2 的掃描代碼:


        public void startScan(){
                UUID[] serviceUuids = new UUID[] { UUID    .fromString("0000ae8f-0000-1000-8000-00805f9b34fb") };
        bluetoothAdapter.startLeScan(serviceUuids, leScanCallback);
    }
    public void stopScan(){
        bluetoothAdapter.stopLeScan(leScanCallback);
    }
    private LeScanCallback leScanCallback=new LeScanCallback() {
        
        @Override
        public void onLeScan(BluetoothDevice bluetoothdeivce, int rssi, byte[] scandata) {
            //把byte數組轉成16進制字符串,方便查看
            Log.e("TAG","scandata:"+ CYUtils.Bytes2HexString(scandata));
        }
    };
掃描結果是這樣的:



05-23 16:13:30.522 10197-10197/jiqi.blescandemo E/TAG: scandata:02011A05FFAC01345605038FAEE1FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
05-23 16:13:30.625 10197-10197/jiqi.blescandemo E/TAG: scandata:02011A05FFAC01345605038FAEE1FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
05-23 16:13:30.735 10197-10197/jiqi.blescandemo E/TAG: scandata:02011A05FFAC01345605038FAEE1FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
05-23 16:13:30.847 10197-10197/jiqi.blescandemo E/TAG: scandata:02011A05FFAC01345605038FAEE1FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
05-23 16:13:30.955 10197-10197/jiqi.blescandemo E/TAG: scandata:02011A05FFAC01345605038FAEE1FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
05-23 16:13:31.061 10197-10197/jiqi.blescandemo E/TAG: scandata:02011A05FFAC01345605038FAEE1FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
05-23 16:13:31.192 10197-10197/jiqi.blescandemo E/TAG: scandata:02011A05FFAC01345605038FAEE1FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
05-23 16:13:31.283 10197-10197/jiqi.blescandemo E/TAG: scandata:02011A05FFAC01345605038FAEE1FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
05-23 16:13:31.369 10197-10197/jiqi.blescandemo E/TAG: scandata:02011A05FFAC01345605038FAEE1FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
05-23 16:13:31.480 10197-10197/jiqi.blescandemo E/TAG: scandata:02011A05FFAC01345605038FAEE1FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
可以發現,掃描結果里面只會出現擁有  AE8F 這個uuid的 BLE設備,搜索不到其他設備

注意:部分手機使用startLeScan(serviceUuids,callback)這個方法過濾設備 會掃描不到設備,即使這個設備UUID符合過濾條件,我歸結為手機/系統問題,如三星手機


這樣,我們知道,廣播數據可以添加ManufacturerData,還可以添加ServerUUID, 還有嗎? 有,代碼如下:

    public AdvertiseData createAdvertiseData(byte[] data) {
        AdvertiseData.Builder mDataBuilder = new AdvertiseData.Builder();
        mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"));
        mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"));
        mDataBuilder.addServiceData( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"),new byte[]{0x64,0x12});
        mDataBuilder.addManufacturerData(0x01AC, data);
        AdvertiseData mAdvertiseData = mDataBuilder.build();
        return mAdvertiseData;
    }

掃描結果如下:

 

E/TAG: scandata:02011A05FFAC01345605038FAEE1FF05168FAE64120000000000000000000000000000000000000000000000000000000000000000000000000000000000
直接看05168FAE6412 這一段,05依然表示下面一段數據長度為5個字節,16表示數據類型為 server data, 8FAE表示這個數據的uuid是AE8F, 6412就是數據本體了.

那么這個 server data能做什么呢?比如有這樣一個 產品:溫度計,溫度計硬件在廣播字段里的server data里面加入它測量的溫度,這樣APP可以不連接溫度計設備 只通過掃描就知道溫度了,是不是很方便.

以下有幾個坑請大家注意一下:

  情況1:

 

    public AdvertiseData createAdvertiseData(byte[] data) {
        AdvertiseData.Builder mDataBuilder = new AdvertiseData.Builder();
       // mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"));
        mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"));
        mDataBuilder.addServiceData( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"),new byte[]{0x64,0x12});
        mDataBuilder.addManufacturerData(0x01AC, data);
        AdvertiseData mAdvertiseData = mDataBuilder.build();
        return mAdvertiseData;
    }

     我注釋了一句代碼,廣播字段里我沒有 添加 ae8f這個 uuid,而直接添加了 ae8f的data 為 0x6412,那么掃描結果如何?

     使用startLeScan(serviceUuids,callback)過濾 ae8f這個uuid,沒有掃描結果;

     使用startLeScan(callback),掃描結果如下:

E/TAG: scandata:02011A05FFAC0134560303E1FF05168FAE641200000000000000000000000000000000000000000000000000000000000000000000000000000000000000

     可以看到是有 ae8f對應的數據 6412,但是server uuid里面是沒有 ae8f的.

  情況2:

     代碼順序1:

        mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"));
        mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"));
        mDataBuilder.addServiceData( ParcelUuid.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"),new byte[]{0x22,0x44});
        mDataBuilder.addServiceData( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"),new byte[]{0x54,0x12});
 
//掃描結果
E/TAG: scandata:02011A05FFAC01345605038FAEE1FF05168FAE54120000000000000000000000000000000000000000000000000000000000000000000000000000000000

    代碼順序2:

 

        mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"));
        mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"));
        mDataBuilder.addServiceData( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"),new byte[]{0x54,0x11});
        mDataBuilder.addServiceData( ParcelUuid.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"),new byte[]{0x22,0x43});
 
//掃描結果
E/TAG: scandata:02011A05FFAC01345605038FAEE1FF05168FAE54110000000000000000000000000000000000000000000000000000000000000000000000000000000000

    代碼順序3:

           

        mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"));
        mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"));
        mDataBuilder.addServiceData( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"),new byte[]{0x54,0x1A});
        mDataBuilder.addServiceData( ParcelUuid.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"),new byte[]{0x22,0x47});
 
//掃描結果
E/TAG: scandata:02011A05FFAC0134560503E1FF8FAE05168FAE541A0000000000000000000000000000000000000000000000000000000000000000000000000000000000

    代碼順序4:

    

        mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"));
        mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"));
        mDataBuilder.addServiceData( ParcelUuid.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"),new byte[]{0x22,0x48});
        mDataBuilder.addServiceData( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"),new byte[]{0x54,0x1B});
 
//掃描結果
E/TAG: scandata:02011A05FFAC0134560503E1FF8FAE05168FAE541B0000000000000000000000000000000000000000000000000000000000000000000000000000000000

    情況2總結:從上面4個代碼順序的結果來看,總是掃描到 ae8f這個uuid對應的數據,沒有第二個 server data,但是為什么每次都是ae8f?我TM也不知道!!



AdvertiseData介紹完畢,下面再稍微介紹一下 AdvertiseSettings

        AdvertiseSettings.Builder mSettingsbuilder = new AdvertiseSettings.Builder();
        mSettingsbuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY);
        mSettingsbuilder.setConnectable(connectable);
        mSettingsbuilder.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH);
        mSettingsbuilder.setTimeout(0);
        AdvertiseSettings mAdvertiseSettings = mSettingsbuilder.build();

 setAdvertiseMode(int advertiseMode)
         設置廣播的模式,低功耗,平衡和低延遲三種模式;
         對應  AdvertiseSettings.ADVERTISE_MODE_LOW_POWER  ,ADVERTISE_MODE_BALANCED ,ADVERTISE_MODE_LOW_LATENCY
         從左右到右,廣播的間隔會越來越短 

 setConnectable(boolean connectable)
          設置是否可以連接。
          廣播分為可連接廣播和不可連接廣播,一般不可連接廣播應用在iBeacon設備上,這樣APP無法連接上iBeacon設備

 setTimeout(int timeoutMillis)
          設置廣播的最長時間,最大值為常量AdvertiseSettings.LIMITED_ADVERTISING_MAX_MILLIS = 180 * 1000;  180秒
          設為0時,代表無時間限制會一直廣播
 setTxPowerLevel(int txPowerLevel)
          設置廣播的信號強度
          常量有AdvertiseSettings.ADVERTISE_TX_POWER_ULTRA_LOW,ADVERTISE_TX_POWER_LOW,ADVERTISE_TX_POWER_MEDIUM,ADVERTISE_TX_POWER_HIGH 
          從左到右分別表示強度越來越強. 
          舉例:當設置為ADVERTISE_TX_POWER_ULTRA_LOW時,
          手機1和手機2放在一起,手機2掃描到的rssi信號強度為-56左右,
          當設置為ADVERTISE_TX_POWER_HIGH  時, 掃描到的信號強度為-33左右,
          信號強度越大,表示手機和設備靠的越近



     好了,關於BluetoothLeAdvertiser 的用法介紹完畢!!!!



可能有人會說,bluetoothAdapter.startLeScan(leScanCallback); 這個方法過時了怎么辦,那可以看一下我的另一篇文章

《android BLE 掃描BLE設備 BluetoothLeScanner》


源碼附件:

模擬BLE廣播源碼:http://pan.baidu.com/s/1bptOQyb

手機2掃描打印的源碼就不放出了,很簡單。

android BLE Peripheral 手機模擬設備發出BLE廣播 BluetoothLeAdvertiser

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM