首页 > 代码库 > Bluetooth LE(低功耗蓝牙) - 第六部分(完)

Bluetooth LE(低功耗蓝牙) - 第六部分(完)

回顾:

 

     在本系列前面的文章中我们已经了解了,在我们从一个TI SensorTag中获取温度和湿度数据之前,我们需要经历的各种步骤。在本系列中的最后一篇文章,我们将完成注册并接收SensorTag的通知,并接收温度和湿度数据。

接收数据:

     现在,本地的代理组件知道了传感器所提供的服务,我们可以开始使用这些服务了。为了使用它们,我们首先需要获得服务,然后是该服务所包含的特征,最后是特征的描述符。

     一个GATT服务表现为一个 BluetoothGattService 对象,我们需要通过适当的UUID从 BluetoothGatt 实例中获得;一个GATT特征表示为一个 BluetoothGattCharacteristic  对象,我们可以通过适当的UUID从 BluetoothGattService 中得到;一个GATT描述符表现为一个 BluetoothGattDescriptor 对象,我们可以通过适当的UUID从BluetoothGattCharacteristic  对象中获得:

 

private static final UUID UUID_HUMIDITY_SERVICE =     UUID.fromString("f000aa20-0451-4000-b000-000000000000");private static final UUID UUID_HUMIDITY_DATA =http://www.mamicode.com/     UUID.fromString("f000aa21-0451-4000-b000-000000000000");private static final UUID UUID_HUMIDITY_CONF =     UUID.fromString("f000aa22-0451-4000-b000-000000000000");private static final UUID UUID_CCC =     UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");...private void subscribe(BluetoothGatt gatt) {    BluetoothGattService humidityService =         gatt.getService(UUID_HUMIDITY_SERVICE);    if(humidityService != null) {        BluetoothGattCharacteristic humidityCharacteristic =             humidityService.getCharacteristic(UUID_HUMIDITY_DATA);        BluetoothGattCharacteristic humidityConf =             humidityService.getCharacteristic(UUID_HUMIDITY_CONF);        if(humidityCharacteristic != null && humidityConf != null) {            BluetoothGattDescriptor config =                 humidityCharacteristic.getDescriptor(UUID_CCC);            if(config != null) {            }        }    }}

     为了使示例代码清晰,我已经移除了所有的错误信息展示代码,但在实际的应用程序中你应该考虑如何将错误信息通知给用户。

      从 UUID_HUMIDITY_DATA  特征中读取数据一件简单的事,只需调用gatt.readCharacteristic(humidityCharacteristic) 方法,并在BluetoothGattCallback  的实现类上复写onReadCharacteristic  方法(再次说明,这是一个异步执行的操作,所以可以在UI线程上调用readCharacteristic() 方法)。这样一次性的操作是没什么问题的,但我们应用程序要不断监测温度和湿度值,并将其报告给用户。相比注册监听特征值的发生,通过调用readCharacteristic() 定期轮询会更有效(译者注:感觉说反了,大家可以看看原文,不知道是不是我翻译错了: Instead we can get notifications whenever the value changes, which is much more efficient that polling and performing a readCharacteristic() periodically.)。

      注册监听特征值变化的通知我们要做三件事。首先我们必须把传感器打开,使SensorTag 开始收集适当的数据(实际上如果我们使用readCharacteristic()  方法,同样要打开传感器)。然后我们必须注册传感器以及我们需要通知的本地代理。这一步是特别针对于SensorTag的,其他设备可能不需要这样,或者他们可能需要另一种方法。

      为了打开传感器,我们必须写一个 0×01 值到 UUID_HUMIDITY_CONF 特性;为了收到通知,我们首先必须调用BluetoothGatt  实例的setCharacteristicNotification()  方法,然后写一个 {0x00,0x01}(两个字节)的值到 UUID_CCC 描述符。正如我们前面所提到的那样,UUID_CCC描述符不是SensorTag特定的(就像其他所有的UUID一样),这是一个标准关闭或打开通知的UUID。

      到目前为止,我们已经极大地受益于BLE的异步性API,因为我们不需要担心从UI线程调用它。不幸的是,它使我们面临一些小问题。我们希望下面的代码会做我们所要求的事,但是运行它不会开始收到通知:

gatt.setCharacteristicNotification(humidityCharacteristic, true);humidityConf.setValue(ENABLE_SENSOR);gatt.writeCharacteristic(humidityConf);config.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);gatt.writeDescriptor(config);

原因是我们项传感器写入值的操作是异步执行的,那不会起作用,我们必须等待一个写操作完后才可以开始下一个。如果我们不能等待,在第一个值正在写入的过程中尝试写入第二个值,那么第二值就会丢失。要克服他并不困难,我们可以维护一个写队列,当我们收到前一个写操作已经完成的通知后触发下一个写操作:

private static final byte[] ENABLE_SENSOR = {0x01};private static final Queue<Object> sWriteQueue =     new ConcurrentLinkedQueue<Object>();private static boolean sIsWriting = false;private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {    .    .    .    @Override    public void onCharacteristicWrite(BluetoothGatt gatt,         BluetoothGattCharacteristic characteristic,         int status) {        Log.v(TAG, "onCharacteristicWrite: " + status);        sIsWriting = false;        nextWrite();    }    @Override    public void onDescriptorWrite(BluetoothGatt gatt,         BluetoothGattDescriptor descriptor,         int status) {        Log.v(TAG, "onDescriptorWrite: " + status);        sIsWriting = false;        nextWrite();    }}...private void subscribe(BluetoothGatt gatt) {    BluetoothGattService humidityService =         gatt.getService(UUID_HUMIDITY_SERVICE);    if(humidityService != null) {        BluetoothGattCharacteristic humidityCharacteristic =             humidityService.getCharacteristic(UUID_HUMIDITY_DATA);        BluetoothGattCharacteristic humidityConf =             humidityService.getCharacteristic(UUID_HUMIDITY_CONF);        if(humidityCharacteristic != null &&             humidityConf != null) {            BluetoothGattDescriptor config =                 humidityCharacteristic.getDescriptor(UUID_CCC);            if(config != null) {                gatt.setCharacteristicNotification(                    humidityCharacteristic, true);                humidityConf.setValue(ENABLE_SENSOR);                write(humidityConf);                config.setValue(                    BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);                write(config);            }        }    }}private synchronized void write(Object o) {    if(sWriteQueue.isEmpty() && !sIsWriting) {        doWrite(o);    } else {        sWriteQueue.add(o);    }}private synchronized void nextWrite() {    if(!sWriteQueue.isEmpty() && !sIsWriting) {        doWrite(sWriteQueue.poll());    }}private synchronized void doWrite(Object o) {    if(o instanceof BluetoothGattCharacteristic) {        sIsWriting = true;        mGatt.writeCharacteristic(            (BluetoothGattCharacteristic)o);    } else if(o instanceof BluetoothGattDescriptor) {        sIsWriting = true;        mGatt.writeDescriptor((BluetoothGattDescriptor) o);    } else {        nextWrite();    }}

   所以在加入这个简单的写队列之后,我们只需要在我们的BluetoothGattCallback  实现类中加入相应的回调方法以开启监听通知事件。

private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {    .    .    .    @Override    public void onCharacteristicChanged(BluetoothGatt gatt,         BluetoothGattCharacteristic characteristic) {        Log.v(TAG, "onCharacteristicChanged: " +             characteristic.getUuid());        if(characteristic.getUuid().equals(UUID_HUMIDITY_DATA)) {            int t = shortUnsignedAtOffset(characteristic, 0);            int h = shortUnsignedAtOffset(characteristic, 2);            t = t - (t % 4);            h = h - (h % 4);            float humidity = (-6f) + 125f * (h / 65535f);            float temperature = -46.85f +                 175.72f/65536f * (float)t;            Log.d(TAG, "Value: " + humidity + ":" + temperature);            Message msg = Message.obtain(null, MSG_DEVICE_DATA);            msg.arg1 = (int)(temperature * 100);            msg.arg2 = (int)(humidity * 100);            sendMessage(msg);        }    }}private static Integer shortUnsignedAtOffset(    BluetoothGattCharacteristic characteristic, int offset) {    Integer lowerByte = characteristic.getIntValue(        BluetoothGattCharacteristic.FORMAT_UINT8, offset);    Integer upperByte = characteristic.getIntValue(        BluetoothGattCharacteristic.FORMAT_UINT8, offset + 1);     return (upperByte << 8) + lowerByte;}

    GATT特征中的数据包括温度值(位于第0个字节和第1个字节)和湿度值(位于第2个字节和第3个字节)。然后我们去掉任何不必要的状态位。计算方法直接来自 SensorTag 文档 。

      为了将这些数据展示在界面上,我们使用 Message 的 arg1和arg2 两个成员变量,在将其转换为所需的Int值之前我们先乘100。之后,我们将除以100以返回一个float值。就像以前一样,在此不会展示UI的代码,但在源代码中会有。

      如果我们将程序运行起来,我们可以看到显示处理的摄氏温度以及湿度百分比 【视频在youtube 上,需要穿越才能看到哦】:

      http://www.youtube.com/embed/dyr01b2iy-I?version=3&rel=1&fs=1&showsearch=0&showinfo=1&iv_load_policy=1&wmode=transparent

下期预告:

     所以现在我们完成了一个拥有我们想要功能的应用,但这个博客的主题是Styling Android,因此下周我们将开始一个新的系列,我们以这个应用程序作为基础,并真正的让该程序的UX(用户体验)更平滑一点,UI(用户界面)看起来好一点!

     

     本文的源代码在这里 可以找到。

 

-----------------------------------------------------不怎么华丽的分割线----------------------------------------------------------------------

原文链接:http://blog.stylingandroid.com/archives/2531

© 2014, Mark Allison. All rights reserved. This article originally appeared on Styling Android.

Portions of this page are modifications based on work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License   

 

 

 

 

 

 

 

 

 

 

Bluetooth LE(低功耗蓝牙) - 第六部分(完)