android藍牙BLE(三) —— 廣播

android BLE系列:

android藍牙BLE(一) —— 掃描android

android藍牙BLE(二) —— 通訊c#

android藍牙BLE(三) —— 廣播bash

android藍牙BLE(四) —— 實戰ide

手機做爲外設

        在藍牙開發中,有些狀況是不須要鏈接的,只要外設廣播本身的數據便可,例如蘋果的ibeacon。自Android 5.0更新藍牙API後,手機能夠做爲外設廣播數據。post

廣播包有兩種:測試

  • 廣播包(Advertising Data)
  • 響應包(Scan Response)

        其中廣播包是每一個外設都必須廣播的,而響應包是可選的。每一個廣播包的長度必須是31個字節,若是不到31個字節 ,則剩下的全用0填充。 補全。這部分的數據是無效的 ui

廣播數據單元

        廣播包中包含若干個廣播數據單元,廣播數據單元也稱爲 AD Structure。this

廣播數據單元 = 長度值Length + AD type + AD Data。spa

長度值Length只佔一個字節,而且位於廣播數據單元的第一個字節code

概念的東西有些抽象,先看看下面的廣播報文:

        0x表明這串字符串是十六進制的字符串。兩位十六進制數表明一個字節。由於兩個字符組成的十六進制字符串最大爲FF,即255,而Java中byte類型的取值範圍是-128到127,恰好能夠表示一個255的大小。因此兩個十六進制的字符串表示一個字節。

        繼續查看報文內容,開始讀取第一個廣播數據單元。讀取第一個字節:0x07,轉換爲十進制就是7,即表示後面的7個字節是這個廣播數據單元的數據內容。超過這7個字節的數據內容後,表示是一個新的廣播數據單元。

        而第二個廣播數據單元,第一個字節的值是0x16,轉換爲十進制就是22,表示後面22個字節爲第二個廣播數據單元。

        在廣播數據單元的數據部分中,第一個字節表明數據類型(AD type),決定數據部分表示的是什麼數據。(即廣播數據單元第二個字節爲AD type)

AD Type的類型以下:

  • Flags:TYPE = 0x01。用來標識設備LE物理鏈接。

        bit 0: LE 有限發現模式
        bit 1: LE 普通發現模式
        bit 2: 不支持 BR/EDR
        bit 3: 對 Same Device Capable(Controller) 同時支持 BLE 和 BR/EDR
        bit 4: 對 Same Device Capable(Host) 同時支持 BLE 和 BR/EDR
        bit 5..7: 預留

        這bit 1~7分別表明着發送該廣播的藍牙芯片的物理鏈接狀態。當bit的值爲1時,表示支持該功能。 例:

  • Service UUID。廣播數據中能夠將設備支持的GATT Service的UUID廣播出來,來告知中心設備其支持的Service。
    對於不一樣bit的UUID,其對應的類型也有不一樣:

        非完整的16bit UUID: TYPE = 0x02;

        完整的16bit UUID 列表: TYPE = 0x03;

        非完整的32bit UUID 列表: TYPE = 0x04;

        完整的32bit UUID 列表: TYPE = 0x05;

        非完整的128bit UUID 列表: TYPE = 0x06;

        完整的128bit UUID: TYPE = 0x07;

  • TX Power Level: TYPE = 0x0A,表示設備發送廣播包的信號強度。 數值範圍:±127 dBm。
  • 設備名字,DATA 是名字的字符串,能夠是設備的全名,也能夠是設備名字的縮寫。

        縮寫的設備名稱: TYPE = 0x08

        完整的設備名稱: TYPE = 0x09

  • Service Data: Service 對應的數據。

        16 bit UUID Service: TYPE = 0x16, 前 2 字節是 UUID,後面是 Service 的數據;

        32 bit UUID Service: TYPE = 0x20, 前 4 字節是 UUID,後面是 Service 的數據;

        128 bit UUID Service: TYPE = 0x21, 前 16 字節是 UUID,後面是 Service 的數據;

  • 廠商自定義數據: TYPE = 0xFF。 廠商數據中,前兩個字節表示廠商ID,剩下的是廠商自定義的數據。

BLE廣播

藍牙廣播的數據格式大體講了一下,有助於下面的廣播操做的理解。

先自定義UUID:

//UUID
public static UUID UUID_SERVICE = UUID.fromString("0000fff7-0000-1000-8000-00805f9b34fb");
複製代碼

        開啓廣播通常須要3~4對象:廣播設置(AdvertiseSettings)、廣播包(AdvertiseData)、掃描包(可選)、廣播回調(AdvertiseCallback)。

先看看廣播設置(AdvertiseSettings)如何定義:

//初始化廣播設置
mAdvertiseSettings = new AdvertiseSettings.Builder()
        //設置廣播模式,以控制廣播的功率和延遲。
        .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_POWER)
        //發射功率級別
        .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
        //不得超過180000毫秒。值爲0將禁用時間限制。
        .setTimeout(3000)
        //設置是否能夠鏈接
        .setConnectable(false)
        .build();
複製代碼

(1)、經過AdvertiseSettings.Builder#setAdvertiseMode() 設置廣播模式。其中有3種模式:

        一、在均衡電源模式下執行藍牙LE廣播:AdvertiseSettings#ADVERTISE_MODE_BALANCED
        二、在低延遲,高功率模式下執行藍牙LE廣播: AdvertiseSettings#ADVERTISE_MODE_LOW_LATENCY
        三、在低功耗模式下執行藍牙LE廣播:AdvertiseSettings#ADVERTISE_MODE_LOW_POWER

(2)、經過AdvertiseSettings.Builder#setAdvertiseMode() 設置廣播發射功率。共有4種功率模式:

        一、使用高TX功率級別進行廣播:AdvertiseSettings#ADVERTISE_TX_POWER_HIGH
        二、使用低TX功率級別進行廣播:AdvertiseSettings#ADVERTISE_TX_POWER_LOW
        三、使用中等TX功率級別進行廣播:AdvertiseSettings#ADVERTISE_TX_POWER_MEDIUM
        四、使用最低傳輸(TX)功率級別進行廣播:AdvertiseSettings#ADVERTISE_TX_POWER_ULTRA_LOW

(3)、經過AdvertiseSettings.Builder#setTimeout()設置持續廣播的時間,單位爲毫秒。最多180000毫秒。當值爲0則無時間限制,持續廣播,除非調用BluetoothLeAdvertiser#stopAdvertising()。

(4)、經過AdvertiseSettings.Builder#setConnectable()設置該廣播是否能夠鏈接的。

以前說過,外設必須廣播廣播包,掃描包是可選。但添加掃描包也意味着廣播更多得數據,便可廣播62個字節。

//初始化廣播包
mAdvertiseData = new AdvertiseData.Builder()
        //設置廣播設備名稱
        .setIncludeDeviceName(true)
        //設置發射功率級別
        .setIncludeDeviceName(true)
        .build();
        
//初始化掃描響應包
mScanResponseData = new AdvertiseData.Builder()
        //隱藏廣播設備名稱
        .setIncludeDeviceName(false)
        //隱藏發射功率級別
        .setIncludeDeviceName(false)
        //設置廣播的服務UUID
        .addServiceUuid(new ParcelUuid(UUID_SERVICE))
        //設置廠商數據
        .addManufacturerData(0x11,hexStrToByte(mData))
        .build();
複製代碼

可見不管是廣播包仍是掃描包,其廣播的內容都是用AdvertiseData類封裝的。

(1)、AdvertiseData.Builder#setIncludeDeviceName()方法,能夠設置廣播包中是否包含藍牙的名稱。

(2)、AdvertiseData.Builder#setIncludeTxPowerLevel()方法,能夠設置廣播包中是否包含藍牙的發射功率。

(3)、AdvertiseData.Builder#addServiceUuid(ParcelUuid)方法,能夠設置特定的UUID在廣播包中。

(4)、AdvertiseData.Builder#addServiceData(ParcelUuid,byte[])方法,能夠設置特定的UUID和其數據在廣播包中。

(5)、AdvertiseData.Builder#addManufacturerData(int,byte[])方法,能夠設置特定廠商Id和其數據在廣播包中。

        從AdvertiseData.Builder的設置中能夠看出,若是一個外設須要在不鏈接的狀況下對外廣播數據,其數據能夠存儲在UUID對應的數據中,也能夠存儲在廠商數據中。但因爲廠商ID是須要由Bluetooth SIG進行分配的,廠商間通常都將數據設置在廠商數據。

另外能夠經過BluetoothAdapter#setName()設置廣播的名稱

//獲取藍牙設配器
BluetoothManager bluetoothManager = (BluetoothManager)
        getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
//設置設備藍牙名稱
mBluetoothAdapter.setName("daqi");
複製代碼

先看一個例子,咱們分別在廣播包掃描包中設置AdvertiseData.Builder的每一種廣播報文參數,獲得一下報文內容:

(1)、Type = 0x01 表示設備LE物理鏈接。
(2)、Type = 0x09 表示設備的全名
(3)、Type = 0x03 表示完整的16bit UUId。其值爲0xFFF7。
(4)、Type = 0xFF 表示廠商數據。前兩個字節表示廠商ID,即廠商ID爲0x11。後面的爲廠商數據,具體由用戶自行定義。
(5)、Type = 0x16 表示16 bit UUID的數據,因此前兩個字節爲UUID,即UUID爲0xF117,後續爲UUID對應的數據,具體由用戶自行定義。

最後繼承AdvertiseCallback自定義廣播回調。

private class daqiAdvertiseCallback extends AdvertiseCallback {
    //開啓廣播成功回調
    @Override
    public void onStartSuccess(AdvertiseSettings settingsInEffect){
        super.onStartSuccess(settingsInEffect);
        Log.d("daqi","開啓服務成功");
    }

    //沒法啓動廣播回調。
    @Override
    public void onStartFailure(int errorCode) {
        super.onStartFailure(errorCode);
        Log.d("daqi","開啓服務失敗,失敗碼 = " + errorCode);
    }
}
複製代碼

初始化完畢上面的對象後,就能夠進行廣播:

//獲取BLE廣播的操做對象。
//若是藍牙關閉或此設備不支持藍牙LE廣播,則返回null。
mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
//mBluetoothLeAdvertiser不爲空,且藍牙已開打
if(mBluetoothAdapter.isEnabled()){
    if (mBluetoothLeAdvertiser != null){
         //開啓廣播
        mBluetoothLeAdvertiser.startAdvertising(mAdvertiseSettings,
            mAdvertiseData, mScanResponseData, mAdvertiseCallback);
    }else {
        Log.d("daqi","該手機不支持ble廣播");
    }
}else{
    Log.d("daqi","手機藍牙未開啓");
}

複製代碼

        廣播主要是經過BluetoothLeAdvertiser#startAdvertising()方法實現,但在以前須要先獲取BluetoothLeAdvertiser對象。

BluetoothLeAdvertiser對象存在兩個狀況獲取爲Null:

        一、手機藍牙模塊不支持BLE廣播
        二、藍牙未開啓

        因此在調用BluetoothAdapter#getBluetoothLeAdvertiser()前,須要先調用判斷藍牙已開啓,並判斷在BluetoothAdapter中獲取的BluetoothLeAdvertiser是否爲空(測試過某些華爲手機mBluetoothAdapter.isMultipleAdvertisementSupported()爲false,可是能發送ble廣播)。

        與廣播成對出現就是BluetoothLeAdvertiser.stopAdvertising()中止廣播了,傳入開啓廣播時傳遞的廣播回調對象,便可關閉廣播:

mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback)
複製代碼

啓動GATT Service 和 Characteristic

        雖然經過廣播告知外邊自身擁有這些Service,但手機自身並無初始化Gattd的Service。致使外部的中心設備鏈接手機後,並不能找到對應的GATT Service 和 獲取對應的數據。

BluetoothGattService service = new BluetoothGattService(UUID_SERVICE,
                                BluetoothGattService.SERVICE_TYPE_PRIMARY);
複製代碼

建立BluetoothGattService時,傳入兩個參數:UUID和Service類型。

Service類型有兩個級別:

  • BluetoothGattService#SERVICE_TYPE_PRIMARY 主服務
  • BluetoothGattService#SERVICE_TYPE_SECONDARY 次要服務(存在於主服務中的服務)

        咱們都知道Gatt中,Service的下一級是Characteristic,Characteristic是最小的通訊單元,經過對Characteristic進行讀寫操做來進行通訊。

//初始化特徵值
mGattCharacteristic = new BluetoothGattCharacteristic(UUID_CHARACTERISTIC,
        BluetoothGattCharacteristic.PROPERTY_WRITE|
                BluetoothGattCharacteristic.PROPERTY_NOTIFY|
                BluetoothGattCharacteristic.PROPERTY_READ,
        BluetoothGattCharacteristic.PERMISSION_WRITE|
                BluetoothGattCharacteristic.PERMISSION_READ);
複製代碼

建立BluetoothGattCharacteristic時,傳入三兩個參數:UUID、特徵屬性 和 權限屬性。

特徵屬性表示該BluetoothGattCharacteristic擁有什麼功能,即能對BluetoothGattCharacteristic進行什麼操做。其中主要有3種:

  • BluetoothGattCharacteristic#PROPERTY_WRITE 表示特徵支持寫
  • BluetoothGattCharacteristic#PROPERTY_READ 表示特徵支持讀
  • BluetoothGattCharacteristic#PROPERTY_NOTIFY 表示特徵支持通知

權限屬性用於配置該特徵值所具備的功能。主要兩種:

  • BluetoothGattCharacteristic#PERMISSION_WRITE 特徵寫權限
  • BluetoothGattCharacteristic#PERMISSION_READ 特徵讀權限

        當特徵值只有讀權限時,調用BluetoothGatt#writeCharacteristic()對特徵值進行修改時,將返回false,沒法寫入。並不會觸發BluetoothGattCallback#onCharacteristicWrite()回調。

        當特徵值只有寫權限時,調用BluetoothGatt#readCharacteristic()對特徵值進行讀取時,將返回false,沒法寫入。並不會觸發BluetoothGattCallback#onCharacteristicRead()回調。

        Characteristic下還有Descriptor,初始化BluetoothGattDescriptor時傳入:Descriptor UUID 和 權限屬性

//初始化描述
mGattDescriptor = new BluetoothGattDescriptor(UUID_DESCRIPTOR,BluetoothGattDescriptor.PERMISSION_WRITE);
複製代碼

爲Service添addCharacteristic,爲Characteristic添加Service:

//Service添加特徵值
mGattService.addCharacteristic(mGattCharacteristic);
mGattService.addCharacteristic(mGattReadCharacteristic);
//特徵值添加描述
mGattCharacteristic.addDescriptor(mGattDescriptor);
複製代碼

        經過藍牙管理器mBluetoothManager獲取Gatt Server,用來添加Gatt Service。添加完Gatt Service後,外部中心設備鏈接手機時,將能獲取到對應的GATT Service 和 獲取對應的數據

//初始化GattServer回調
mBluetoothGattServerCallback = new daqiBluetoothGattServerCallback();

if (mBluetoothManager != null)
    mBluetoothGattServer = mBluetoothManager.openGattServer(this, mBluetoothGattServerCallback);
boolean result = mBluetoothGattServer.addService(mGattService);
if (result){
    Toast.makeText(daqiActivity.this,"添加服務成功",Toast.LENGTH_SHORT).show();
}else {
    Toast.makeText(daqiActivity.this,"添加服務失敗",Toast.LENGTH_SHORT).show();
}
複製代碼

        定義Gatt Server回調。當中心設備鏈接該手機外設、修改特徵值、讀取特徵值等狀況時,會獲得相應狀況的回調。

private class daqiBluetoothGattServerCallback extends BluetoothGattServerCallback{

    //設備鏈接/斷開鏈接回調
    @Override
    public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
        super.onConnectionStateChange(device, status, newState);
    }

    //添加本地服務回調
    @Override
    public void onServiceAdded(int status, BluetoothGattService service) {
        super.onServiceAdded(status, service);
    }
    
    //特徵值讀取回調
    @Override
    public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
        super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
    }
    
    //特徵值寫入回調
    @Override
    public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
        super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
    }
    
    //描述讀取回調
    @Override
    public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {
        super.onDescriptorReadRequest(device, requestId, offset, descriptor);
    }
    
    //描述寫入回調
    @Override
    public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
        super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value);
    }
}
複製代碼

最後開啓廣播後,用nRF鏈接後看到的特徵值信息以下圖所示:(加多了一個只能都的特徵值)

相關文章
相關標籤/搜索