zoukankan      html  css  js  c++  java
  • 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 = 
        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

  • 相关阅读:
    Jmeter中ftp测试下载默认路径及文件
    python中http请求方法库汇总
    快速解决mysql Lost connection to MySQL server at 'reading initial communication packet及can't connect to mysql server on 'localhost'
    Python GUI--Tkinter实践
    Shell脚步之监控iostat数据
    C++二进制字符串转十六进制字符串 十六进制字符串转二进制字符串
    C++调用openssl实现DES加密解密cbc模式 zeropadding填充方式 pkcs5padding填充方式 pkcs7padding填充方式
    DES加解密 cbc模式 的简单讲解 && C++用openssl库来实现的注意事项
    C++ 使用openssl库实现 DES 加密——CBC模式 && RSA加密——公加私解——私加公解
    C++ 解析json串
  • 原文地址:https://www.cnblogs.com/xiaorenwu702/p/4304327.html
Copyright © 2011-2022 走看看