Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android BLE與終端通信(五)——Google API BLE4.0低功耗藍牙文檔解讀之案例初探

Android BLE與終端通信(五)——Google API BLE4.0低功耗藍牙文檔解讀之案例初探

編輯:關於Android編程

Google API:http://developer.android.com/guide/topics/connectivity/bluetooth-le.html

其實大家要學習Android的技術,Google的API就是最詳細的指導書了,而且通俗易懂,就算看不懂英語,翻譯翻譯再結合代碼也能看個大概的,真的很贊喲,我們直接解讀吧!

Google的API中前面就是一大片的概念,這其實在我之前的篇幅中都有說過的

一.概述

Generic Attribute Profile(GATT)—GATT配置文件是一個通用規范,用於在BLE鏈路上發送和接收被稱為“屬性”的數據塊。目前所有的BLE應用都基於GATT。 藍牙SIG規定了許多低功耗設備的配置文件。配置文件是設備如何在特定的應用程序中工作的規格說明。注意一個設備可以實現多個配置文件。例如,一個設備可能包括心率監測儀和電量檢測。

Attribute Protocol(ATT)—GATT在ATT協議基礎上建立,也被稱為GATT/ATT。ATT對在BLE設備上運行進行了優化,為此,它使用了盡可能少的字節。每個屬性通過一個唯一的的統一標識符(UUID)來標識,每個String類型UUID使用128 bit標准格式。屬性通過ATT被格式化為characteristics和services。

Characteristic 一個characteristic包括一個單一變量和0-n個用來描述characteristic變量的descriptor,characteristic可以被認為是一個類型,類似於類。

Descriptor Descriptor用來描述characteristic變量的屬性。例如,一個descriptor可以規定一個可讀的描述,或者一個characteristic變量可接受的范圍,或者一個characteristic變量特定的測量單位。

Service service是characteristic的集合。例如,你可能有一個叫“Heart Rate Monitor(心率監測儀)”的service,它包括了很多characteristics,如“heart rate measurement(心率測量)”等。你可以在bluetooth.org 找到一個目前支持的基於GATT的配置文件和服務列表。

二.角色和責任

這裡有兩個概念

中央 VS 外圍設備。 適用於BLE連接本身。中央設備掃描,尋找廣播;外圍設備發出廣播。 GATT 服務端 VS GATT 客戶端。決定了兩個設備在建立連接後如何互相交流。
為了方便理解,想象你有一個Android手機和一個用於活動跟蹤BLE設備,手機支持中央角色,活動跟蹤器支持外圍(為了建立BLE連接你需要注意兩件事,只支持外圍設備的兩方或者只支持中央設備的兩方不能互相通信)。
當手機和運動追蹤器建立連接後,他們開始向另一方傳輸GATT數據。哪一方作為服務器取決於他們傳輸數據的種類。例如,如果運動追蹤器想向手機報告傳感器數據,運動追蹤器是服務端。如果運動追蹤器更新來自手機的數據,手機會作為服務端。
在這份文檔的例子中,android app(運行在android設備上)作為GATT客戶端。app從gatt服務端獲得數據,gatt服務端即支持Heart Rate Profile(心率配置)的BLE心率監測儀。但是你可以自己設計android app去扮演GATT服務端角色。也就是自定義

三.案例

好的,Google文檔中,也舉了一個例子說明,我們要使用BLE的時候,必須有要加上兩個權限


這其實你要使用藍牙這個硬件都是要加上這個權限的,但是這裡Google又聲明了一點

如果n你想聲明你的軟件只為具有BLE的設備提供服務的話,你應該要在清單文件中加入

    

如果改為false的話,那其他藍牙也是可以使用的,我們創建一個工程——BLETest

這裡寫圖片描述

和傳統藍牙一樣,我們添加完權限之後就要去判斷這個設備是否支持BLE

//判斷是否支持BLE設備
if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
    Toast.makeText(this, "此設備不支持BLE", Toast.LENGTH_SHORT).show();
      finish();
   }

這步操作也只是你設置為false的時候才是必須的,因為你如果你設置為true,那你只給BLE服務,那這個判斷也就是多余的了,緊接著,我們還需要去判斷藍牙是否開啟,如果沒有開啟,我們就去開啟他,這次雖然也是用BluetoothAdapter 去獲取,但是這裡用了一個新的類BluetoothManager ,先初始化

//初始化藍牙適配器
BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();

然後再去開啟

//打開藍牙
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
        }

這裡的REQUEST_ENABLE_BT也就是一個回調的標志,無須理會

 public static final int REQUEST_ENABLE_BT = 0;

寫了這個之後,當我們藍牙沒有開啟的時候就會去友好的提示用戶開啟了

這裡寫圖片描述vcnosbjWrrrzvs3No9a5yajD6Lvy1d/J6NbDyajD6MqxvOSjrLK7yLvE477Nv+CxxsHLPC9wPg0KPHByZSBjbGFzcz0="brush:java;"> //搜索設備 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) private void scanLeDevice(final boolean enable) { if (enable) { // 經過預定掃描期後停止掃描 mHandler.postDelayed(new Runnable() { @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) @Override public void run() { mScanning = false; mBluetoothAdapter.stopLeScan(mLeScanCallback); } }, SCAN_PERIOD); mScanning = true; mBluetoothAdapter.startLeScan(mLeScanCallback); } else { mScanning = false; mBluetoothAdapter.stopLeScan(mLeScanCallback); } }

這樣,當我們接收到搜索設備的回調時便可以直接添加在Adapter上,要注意,所演示的也是Google提供的Demo

//搜索設備的回調
    private BluetoothAdapter.LeScanCallback mLeScanCallback =
            new BluetoothAdapter.LeScanCallback() {

                @Override
                public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            mLeDeviceListAdapter.addDevice(device);
                            mLeDeviceListAdapter.notifyDataSetChanged();
                        }
                    });
                }
            };

連接GATT服務端

這僅僅是第一步,我們要讓BLE擁有強大的能力,就需要連接他的GATT服務端

    /**
     * 1.上下文
     * 2.自動連接
     * 3.BluetoothGattCallback回調
     */
    mBluetoothGatt = device.connectGatt(this, false, mGattCallback);

連接到GATT服務端時,由BLE設備做主機,並返回一個BluetoothGatt實例,然後你可以使用這個實例來進行GATT客戶端操作。請求方(軟件)是GATT客戶端。BluetoothGattCallback用於傳遞結果給用戶,例如連接狀態,以及任何進一步GATT客戶端操作。

在這個例子中,這個BLE APP提供了一個activity(DeviceControlActivity)來連接,顯示數據,顯示該設備支持的GATT services和characteristics。根據用戶的輸入,這個activity與BluetoothLeService通信,通過Android BLE API實現與BLE設備交互。

package com.lgl.bletest;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.util.Log;

import java.util.UUID;

/**
 * Created by LGL on 2016/5/13.
 */
public class BluetoothLeService extends BluetoothClass.Service {

    private BluetoothManager mBluetoothManager; //藍牙管理器
    private BluetoothAdapter mBluetoothAdapter; //藍牙適配器
    private String mBluetoothDeviceAddress; //藍牙設備地址
    private BluetoothGatt mBluetoothGatt;
    private int mConnectionState = STATE_DISCONNECTED;
    private static final int STATE_DISCONNECTED = 0; //設備無法連接
    private static final int STATE_CONNECTING = 1;  //設備正在連接狀態
    private static final int STATE_CONNECTED = 2;   //設備連接完畢
    public final static String ACTION_GATT_CONNECTED =
            "com.example.bluetooth.le.ACTION_GATT_CONNECTED";
    public final static String ACTION_GATT_DISCONNECTED =
            "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";
    public final static String ACTION_GATT_SERVICES_DISCOVERED =
            "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";
    public final static String ACTION_DATA_AVAILABLE =
            "com.example.bluetooth.le.ACTION_DATA_AVAILABLE";
    public final static String EXTRA_DATA =
            "com.example.bluetooth.le.EXTRA_DATA";
    public final static UUID UUID_HEART_RATE_MEASUREMENT =
            UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT);
    //通過BLE API的不同類型的回調方法
    private final BluetoothGattCallback mGattCallback =
            new BluetoothGattCallback() {
                @Override
                public void onConnectionStateChange(BluetoothGatt gatt, int status,
                                                    int newState) {//當連接狀態發生改變
                    String intentAction;
                    if (newState == BluetoothProfile.STATE_CONNECTED) {//當藍牙設備已經連接
                        intentAction = ACTION_GATT_CONNECTED;
                        mConnectionState = STATE_CONNECTED;
                        broadcastUpdate(intentAction);
                        Log.i(TAG, "Connected to GATT server.");
                        Log.i(TAG, "Attempting to start service discovery:" +
                                mBluetoothGatt.discoverServices());
                    } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {//當設備無法連接
                        intentAction = ACTION_GATT_DISCONNECTED;
                        mConnectionState = STATE_DISCONNECTED;
                        Log.i(TAG, "Disconnected from GATT server.");
                        broadcastUpdate(intentAction);
                    }
                }

                @Override
                // 發現新服務端
                public void onServicesDiscovered(BluetoothGatt gatt, int status) {
                    if (status == BluetoothGatt.GATT_SUCCESS) {
                        broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
                    } else {
                        Log.w(TAG, "onServicesDiscovered received: " + status);
                    }
                }

                @Override
                // 讀寫特性
                public void onCharacteristicRead(BluetoothGatt gatt,
                                                 BluetoothGattCharacteristic characteristic,
                                                 int status) {
                    if (status == BluetoothGatt.GATT_SUCCESS) {
                        broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
                    }
                }

            };
}

當觸發特定的回調時,它調用適當的broadcastUpdate()輔助方法,通過這一個動作。注意,本節中的數據解析執行按照藍牙心率測量概要文件規范:

private void broadcastUpdate(final String action) {
    final Intent intent = new Intent(action);
    sendBroadcast(intent);
}

private void broadcastUpdate(final String action,
                             final BluetoothGattCharacteristic characteristic) {
    final Intent intent = new Intent(action);

    // This is special handling for the Heart Rate Measurement profile. Data
    // parsing is carried out as per profile specifications.
    if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
        int flag = characteristic.getProperties();
        int format = -1;
        if ((flag & 0x01) != 0) {
            format = BluetoothGattCharacteristic.FORMAT_UINT16;
            Log.d(TAG, "Heart rate format UINT16.");
        } else {
            format = BluetoothGattCharacteristic.FORMAT_UINT8;
            Log.d(TAG, "Heart rate format UINT8.");
        }
        final int heartRate = characteristic.getIntValue(format, 1);
        Log.d(TAG, String.format("Received heart rate: %d", heartRate));
        intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
    } else {
        // For all other profiles, writes the data formatted in HEX.
        final byte[] data = characteristic.getValue();
        if (data != null && data.length > 0) {
            final StringBuilder stringBuilder = new StringBuilder(data.length);
            for(byte byteChar : data)
                stringBuilder.append(String.format("%02X ", byteChar));
            intent.putExtra(EXTRA_DATA, new String(data) + "\n" +
                    stringBuilder.toString());
        }
    }
    sendBroadcast(intent);
}

返回DeviceControlActivity, 這些事件由一個BroadcastReceiver來處理:

// Handles various events fired by the Service.
// ACTION_GATT_CONNECTED: connected to a GATT server.
// ACTION_GATT_DISCONNECTED: disconnected from a GATT server.
// ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.
// ACTION_DATA_AVAILABLE: received data from the device. This can be a
// result of read or notification operations.
private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();
        if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
            mConnected = true;
            updateConnectionState(R.string.connected);
            invalidateOptionsMenu();
        } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
            mConnected = false;
            updateConnectionState(R.string.disconnected);
            invalidateOptionsMenu();
            clearUI();
        } else if (BluetoothLeService.
                ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
            // Show all the supported services and characteristics on the
            // user interface.
            displayGattServices(mBluetoothLeService.getSupportedGattServices());
        } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
            displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA));
        }
    }
};

讀取BLE變量

你的android app完成與GATT服務端連接和發現services後,就可以讀寫支持的屬性。例如,這段代碼通過服務端的services和 characteristics迭代,並且將它們顯示

在UI上。

public class DeviceControlActivity extends Activity {
    ...
    // Demonstrates how to iterate through the supported GATT
    // Services/Characteristics.
    // In this sample, we populate the data structure that is bound to the
    // ExpandableListView on the UI.
    private void displayGattServices(List gattServices) {
        if (gattServices == null) return;
        String uuid = null;
        String unknownServiceString = getResources().
                getString(R.string.unknown_service);
        String unknownCharaString = getResources().
                getString(R.string.unknown_characteristic);
        ArrayList> gattServiceData =
                new ArrayList>();
        ArrayList>> gattCharacteristicData
                = new ArrayList>>();
        mGattCharacteristics =
                new ArrayList>();

        // / 循環可用的Characteristics.
        for (BluetoothGattService gattService : gattServices) {
            HashMap currentServiceData =
                    new HashMap();
            uuid = gattService.getUuid().toString();
            currentServiceData.put(
                    LIST_NAME, SampleGattAttributes.
                            lookup(uuid, unknownServiceString));
            currentServiceData.put(LIST_UUID, uuid);
            gattServiceData.add(currentServiceData);

            ArrayList> gattCharacteristicGroupData =
                    new ArrayList>();
            List gattCharacteristics =
                    gattService.getCharacteristics();
            ArrayList charas =
                    new ArrayList();
           // Loops through available Characteristics.
            for (BluetoothGattCharacteristic gattCharacteristic :
                    gattCharacteristics) {
                charas.add(gattCharacteristic);
                HashMap currentCharaData =
                        new HashMap();
                uuid = gattCharacteristic.getUuid().toString();
                currentCharaData.put(
                        LIST_NAME, SampleGattAttributes.lookup(uuid,
                                unknownCharaString));
                currentCharaData.put(LIST_UUID, uuid);
                gattCharacteristicGroupData.add(currentCharaData);
            }
            mGattCharacteristics.add(charas);
            gattCharacteristicData.add(gattCharacteristicGroupData);
         }
    ...
    }
...
}

接收GATT通知

當設備上的特性改變時會通知BLE應用程序。這段代碼顯示了如何使用setCharacteristicNotification( )給一個特性設置通知。

//GATT服務端
private BluetoothGatt mBluetoothGatt;
BluetoothGattCharacteristic characteristic;
//是否打開
boolean enabled;
...
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
...
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
        UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);

如果對一個特性啟用通知,當遠程藍牙設備特性發送變化,回調函數onCharacteristicChanged( ))被觸發。

@Override
// 廣播更新
public void onCharacteristicChanged(BluetoothGatt gatt,
        BluetoothGattCharacteristic characteristic) {
    broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}

關閉客戶端App

當你的app完成BLE設備的使用後,應該調用close( ),系統可以合理釋放占用資源。

public void close() {
    if (mBluetoothGatt == null) {
        return;
    }
    mBluetoothGatt.close();
    mBluetoothGatt = null;
}

代碼可能有點亂,這裡提供一個Google的Demo功大家測試

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved