最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
当前位置: 首页 - 正文

Android BLE与终端通信(五)——Google API BLE4.0低功耗蓝牙文档解读之案例

来源:动视网 责编:小OO 时间:2025-10-03 04:17:20
文档

Android BLE与终端通信(五)——Google API BLE4.0低功耗蓝牙文档解读之案例

AndroidBLE与终端通信(五)——GoogleAPIBLE4.0低功耗蓝牙文档解读之案例初探一.概述我们直接翻译GenericAttributeProfile(GATT)—GATT配置文件是一个通用规范,用于在BLE链路上发送和接收被称为“属性”的数据块。目前所有的BLE应用都基于GATT。蓝牙SIG规定了许多低功耗设备的配置文件。配置文件是设备如何在特定的应用程序中工作的规格说明。注意一个设备可以实现多个配置文件。例如,一个设备可能包括心率监测仪和电量检测。AttributeProto
推荐度:
导读AndroidBLE与终端通信(五)——GoogleAPIBLE4.0低功耗蓝牙文档解读之案例初探一.概述我们直接翻译GenericAttributeProfile(GATT)—GATT配置文件是一个通用规范,用于在BLE链路上发送和接收被称为“属性”的数据块。目前所有的BLE应用都基于GATT。蓝牙SIG规定了许多低功耗设备的配置文件。配置文件是设备如何在特定的应用程序中工作的规格说明。注意一个设备可以实现多个配置文件。例如,一个设备可能包括心率监测仪和电量检测。AttributeProto
Android BLE与终端通信(五)——Google API BLE4.0低功耗蓝牙文档解读之案例初探

一.概述

我们直接翻译

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的设备提供服务的话,你应该要在清单文件中加入

        android:name="android.hardware.bluetooth_le"

android:required="true" />

如果改为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;

写了这个之后,当我们蓝牙没有开启的时候就会去友好的提示用户开启了

我们现在准备工作都有了之后我们就可以直接去搜索设备了,BLE的api和普通蓝牙的API还是有一定的区别的,就是有回调了,使用起来挺方便的,但是搜索是很费电的,所以切记要小心使用,最好是找到设备之后就停止扫描或者设置扫描时间,不然你就苦逼了

//搜索设备

    @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(intentwww.hunanwang.netAction);

                    }

                }

                @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 interfwww.shanxiwang.netace.

            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;

}

代码可能有点乱

文档

Android BLE与终端通信(五)——Google API BLE4.0低功耗蓝牙文档解读之案例

AndroidBLE与终端通信(五)——GoogleAPIBLE4.0低功耗蓝牙文档解读之案例初探一.概述我们直接翻译GenericAttributeProfile(GATT)—GATT配置文件是一个通用规范,用于在BLE链路上发送和接收被称为“属性”的数据块。目前所有的BLE应用都基于GATT。蓝牙SIG规定了许多低功耗设备的配置文件。配置文件是设备如何在特定的应用程序中工作的规格说明。注意一个设备可以实现多个配置文件。例如,一个设备可能包括心率监测仪和电量检测。AttributeProto
推荐度:
  • 热门焦点

最新推荐

猜你喜欢

热门推荐

专题
Top