低功耗藍牙BLE外圍模式(peripheral)-使用BLE做爲服務端

低功耗藍牙BLE外圍模式(peripheral)-使用BLE做爲服務端

Android對外模模式(peripheral)的支持

從Android5.0開始才支持android

關鍵術語和概念

如下是關鍵BLE術語和概念的摘要:git

  • 通用屬性簡檔(GATT) - GATT簡檔是用於經過BLE鏈路發送和接收稱爲「屬性」的短數據塊的通常規範。 全部當前的低能量應用配置文件都基於GATT。
    藍牙SIG爲低能量設備定義了許多配置文件 。 配置文件是設備在特定應用程序中的工做方式的規範。 請注意,設備能夠實現多個配置文件。 例如,設備能夠包含心率監視器和電池水平檢測器。
  • 屬性協議(ATT) -GATT創建在屬性協議(ATT)之上。 這也稱爲GATT / ATT。 ATT通過優化,可在BLE設備上運行。 爲此,它使用盡量少的字節。 每一個屬性由通用惟一標識符(UUID)惟一標識,UUID是用於惟一標識信息的字符串ID的標準化128位格式。 由ATT傳送的屬性被格式化爲特徵和服務 。
  • 特性 -A特性包含描述特性值的單個值和0-n個描述符。 一個特性能夠被認爲是一個類型,相似於類。
  • 描述符 - 描述符是描述特徵值的定義屬性。 例如,描述符能夠指定人類可讀的描述,特徵值的可接受範圍或特徵值的特定的測量單位。
  • 服務 - 服務是一個集合的特色。 例如,您能夠有一個名爲「心率監視器」的服務,其中包括諸如「心率測量」的特徵。 您能夠在bluetooth.org上找到現有基於GATT的我的資料和服務的列表 。

角色和職責

如下是Android設備與BLE設備互動時適用的角色和職責:github

中央與外圍。 這適用於BLE鏈接自己。 處於中心角色的設備掃描,尋找廣告,而且外圍角色中的設備進行廣告。

GATT服務器與GATT客戶端。 這決定了兩個設備在創建鏈接後如何相互通訊。服務器

BLE權限

首先,須要在manifest中聲明使用藍牙和操做藍牙的權限app

在應用程序清單文件中聲明藍牙權限。 例如:

ide

若是您要聲明本身的應用只適用於支持BLE的設備,請在應用清單中包含如下內容:優化

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

不過,若是您想讓應用程式適用於不支援BLE的裝置,您仍應在應用的清單中加入這個元素,但required="false"設爲required="false" 。
而後在運行時,您能夠經過使用PackageManager.hasSystemFeature()肯定BLE可用性:ui

// Use this check to determine whether BLE is supported on the device.  Then
 // you can selectively disable BLE-related features.
 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
     Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
     finish();
 }

在android 6.0 之後,要想得到藍牙掃描結果,還須要下面的權限this

<manifest ... >
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     ...
     <!-- Needed only if your app targets Android 5.0 (API level 21) or higher.  -->
     <uses-feature android:name="android.hardware.location.gps" />
     ...
 </manifest>

設置藍牙

1.Get the BluetoothAdapter

得到藍牙適配器日誌

private BluetoothAdapter mBluetoothAdapter;
 ...
 // Initializes Bluetooth adapter.
 final BluetoothManager bluetoothManager =
         (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
 mBluetoothAdapter = bluetoothManager.getAdapter();

2.Enable Bluetooth

打開藍牙

// Ensures Bluetooth is available on the device and it is enabled.  If not,
 // displays a dialog requesting user permission to enable Bluetooth.
 if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
     Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
     startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
 }

3.初始化BLE藍牙廣播(廣告)

(1)廣播的設置
(2)設置廣播的數據
(3)設置響應的數據
(4)設置鏈接回調

private void initGATTServer() {
        AdvertiseSettings settings = new AdvertiseSettings.Builder()
                .setConnectable(true)
                .build();

        AdvertiseData advertiseData = new AdvertiseData.Builder()
                .setIncludeDeviceName(true)
                .setIncludeTxPowerLevel(true)
                .build();

        AdvertiseData scanResponseData = new AdvertiseData.Builder()
                .addServiceUuid(new ParcelUuid(UUID_SERVER))
                .setIncludeTxPowerLevel(true)
                .build();


        AdvertiseCallback callback = new AdvertiseCallback() {

            @Override
            public void onStartSuccess(AdvertiseSettings settingsInEffect) {
                Log.d(TAG, "BLE advertisement added successfully");
                showText("1. initGATTServer success");
                println("1. initGATTServer success");
                initServices(getContext());
            }

            @Override
            public void onStartFailure(int errorCode) {
                Log.e(TAG, "Failed to add BLE advertisement, reason: " + errorCode);
                showText("1. initGATTServer failure");
            }
        };

        BluetoothLeAdvertiser bluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
        bluetoothLeAdvertiser.startAdvertising(settings, advertiseData, scanResponseData, callback);
    }

在被BLE設備鏈接後,將觸發 AdvertiseCallback 的 onStartSuccess,咱們在這以後,初始化GATT的服務

4.初始化GATT的服務

(1) 經過 mBluetoothManager.openGattServer() 得到 bluetoothGattServer

(2) 添加 服務,特徵,描述。這些內容要讓客戶端知道。

private void initServices(Context context) {
        bluetoothGattServer = mBluetoothManager.openGattServer(context, bluetoothGattServerCallback);
        BluetoothGattService service = new BluetoothGattService(UUID_SERVER, BluetoothGattService.SERVICE_TYPE_PRIMARY);

        //add a read characteristic.
        characteristicRead = new BluetoothGattCharacteristic(UUID_CHARREAD, BluetoothGattCharacteristic.PROPERTY_READ, BluetoothGattCharacteristic.PERMISSION_READ);
        //add a descriptor
        BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(UUID_DESCRIPTOR, BluetoothGattCharacteristic.PERMISSION_WRITE);
        characteristicRead.addDescriptor(descriptor);
        service.addCharacteristic(characteristicRead);

        //add a write characteristic.
        BluetoothGattCharacteristic characteristicWrite = new BluetoothGattCharacteristic(UUID_CHARWRITE,
                BluetoothGattCharacteristic.PROPERTY_WRITE |
                        BluetoothGattCharacteristic.PROPERTY_READ |
                        BluetoothGattCharacteristic.PROPERTY_NOTIFY,
                BluetoothGattCharacteristic.PERMISSION_WRITE);
        service.addCharacteristic(characteristicWrite);

        bluetoothGattServer.addService(service);
        Log.e(TAG, "2. initServices ok");
        showText("2. initServices ok");
    }

在 openGattServer 方法中,咱們須要傳入個回調
bluetoothGattServer = mBluetoothManager.openGattServer(context, bluetoothGattServerCallback);

5.配置數據交互回調

回調時間有:鏈接狀態變化,收發消息,通知消息

/**
     * 服務事件的回調
     */
    private BluetoothGattServerCallback bluetoothGattServerCallback = new BluetoothGattServerCallback() {

        /**
         * 1.鏈接狀態發生變化時
         * @param device
         * @param status
         * @param newState
         */
        @Override
        public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
            Log.e(TAG, String.format("1.onConnectionStateChange:device name = %s, address = %s", device.getName(), device.getAddress()));
            Log.e(TAG, String.format("1.onConnectionStateChange:status = %s, newState =%s ", status, newState));
            super.onConnectionStateChange(device, status, newState);
        }

        @Override
        public void onServiceAdded(int status, BluetoothGattService service) {
            super.onServiceAdded(status, service);
            Log.e(TAG, String.format("onServiceAdded:status = %s", status));
        }

        @Override
        public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
            Log.e(TAG, String.format("onCharacteristicReadRequest:device name = %s, address = %s", device.getName(), device.getAddress()));
            Log.e(TAG, String.format("onCharacteristicReadRequest:requestId = %s, offset = %s", requestId, offset));

            bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, characteristic.getValue());
//            super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
        }

        /**
         * 3. onCharacteristicWriteRequest,接收具體的字節
         * @param device
         * @param requestId
         * @param characteristic
         * @param preparedWrite
         * @param responseNeeded
         * @param offset
         * @param requestBytes
         */
        @Override
        public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] requestBytes) {
            Log.e(TAG, String.format("3.onCharacteristicWriteRequest:device name = %s, address = %s", device.getName(), device.getAddress()));
            Log.e(TAG, String.format("3.onCharacteristicWriteRequest:requestId = %s, preparedWrite=%s, responseNeeded=%s, offset=%s, value=%s", requestId, preparedWrite, responseNeeded, offset, OutputStringUtil.toHexString(requestBytes)));
            bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, requestBytes);
            //4.處理響應內容
            onResponseToClient(requestBytes, device, requestId, characteristic);
        }

        /**
         * 2.描述被寫入時,在這裏執行 bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS...  收,觸發 onCharacteristicWriteRequest
         * @param device
         * @param requestId
         * @param descriptor
         * @param preparedWrite
         * @param responseNeeded
         * @param offset
         * @param value
         */
        @Override
        public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
            Log.e(TAG, String.format("2.onDescriptorWriteRequest:device name = %s, address = %s", device.getName(), device.getAddress()));
            Log.e(TAG, String.format("2.onDescriptorWriteRequest:requestId = %s, preparedWrite = %s, responseNeeded = %s, offset = %s, value = %s,", requestId, preparedWrite, responseNeeded, offset, OutputStringUtil.toHexString(value)));

            // now tell the connected device that this was all successfull
            bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
        }

        /**
         * 5.特徵被讀取。當回覆響應成功後,客戶端會讀取而後觸發本方法
         * @param device
         * @param requestId
         * @param offset
         * @param descriptor
         */
        @Override
        public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {
            Log.e(TAG, String.format("onDescriptorReadRequest:device name = %s, address = %s", device.getName(), device.getAddress()));
            Log.e(TAG, String.format("onDescriptorReadRequest:requestId = %s", requestId));
//            super.onDescriptorReadRequest(device, requestId, offset, descriptor);
            bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, null);
        }

        @Override
        public void onNotificationSent(BluetoothDevice device, int status) {
            super.onNotificationSent(device, status);
            Log.e(TAG, String.format("5.onNotificationSent:device name = %s, address = %s", device.getName(), device.getAddress()));
            Log.e(TAG, String.format("5.onNotificationSent:status = %s", status));
        }

        @Override
        public void onMtuChanged(BluetoothDevice device, int mtu) {
            super.onMtuChanged(device, mtu);
            Log.e(TAG, String.format("onMtuChanged:mtu = %s", mtu));
        }

        @Override
        public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {
            super.onExecuteWrite(device, requestId, execute);
            Log.e(TAG, String.format("onExecuteWrite:requestId = %s", requestId));
        }
    };

6.處理來自客戶端發來的數據和發送回覆數據:

調用 bluetoothGattServer.notifyCharacteristicChanged 方法,通知數據改變。

/**
         * 4.處理響應內容
         *
         * @param reqeustBytes
         * @param device
         * @param requestId
         * @param characteristic
         */
        private void onResponseToClient(byte[] reqeustBytes, BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic) {
            Log.e(TAG, String.format("4.onResponseToClient:device name = %s, address = %s", device.getName(), device.getAddress()));
            Log.e(TAG, String.format("4.onResponseToClient:requestId = %s", requestId));
            String msg = OutputStringUtil.transferForPrint(reqeustBytes);
            println("4.收到:" + msg);
            showText("4.收到:" + msg);

            String str = new String(reqeustBytes) + " hello>";
            characteristicRead.setValue(str.getBytes());
            bluetoothGattServer.notifyCharacteristicChanged(device, characteristicRead, false);

            println("4.響應:" + str);
            showText("4.響應:" + str);
        }

交互流程:

(1) 當客戶端開始寫入數據時: 觸發回調方法 onDescriptorWriteRequest

(2) 在 onDescriptorWriteRequest 方法中,執行下面的方法表示 寫入成功 BluetoothGatt.GATT_SUCCESS

bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);

執行 sendResponse後,會觸發回調方法 onCharacteristicWriteRequest

(3) 在 onCharacteristicWriteRequest方法中

public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] requestBytes) {

這個裏能夠得到 來自客戶端發來的數據 requestBytes

(4) 處理響應內容,我寫了這個方法:

onResponseToClient(requestBytes, device, requestId, characteristic);
在這個方法中,經過 bluetoothGattServer.notifyCharacteristicChanged()方法 回覆數據

經過日誌,咱們看看事件觸發的順序

1.onConnectionStateChange:device name = null, address = 74:32:DE:49:3C:28
1.onConnectionStateChange:status = 0, newState =2
2.onDescriptorWriteRequest:device name = null, address = 74:32:DE:49:3C:28
2.onDescriptorWriteRequest:requestId = 1, preparedWrite = false, responseNeeded = true, offset = 0, value = [01,00,],
3.onCharacteristicWriteRequest:device name = null, address = 74:32:DE:49:3C:28
3.onCharacteristicWriteRequest:requestId = 2, preparedWrite=false, responseNeeded=false, offset=0, value=[41,54,45,30,0D,]
4.onResponseToClient:device name = null, address = 74:32:DE:49:3C:28
4.onResponseToClient:requestId = 2
4.收到:ATE0
4.響應:ATE0 hello>
5.onNotificationSent:device name = null, address = 74:32:DE:49:3C:28
5.onNotificationSent:status = 0

代碼託管到github:

https://github.com/vir56k/bluetoothDemo 找到 bleperipheraldemo 文件夾

相關文章
相關標籤/搜索