Android - BLE连接参数和在SQLite数据库中存储BLE传感器数据

时间:2017-02-18 18:10:20

标签: java android sqlite bluetooth-lowenergy packets

我正在开发一款Android应用程序,它以大约每秒8000字节的速率从BLE传感器接收数据。

我的应用中的连接逻辑基于Google的BluetoothLeGatt示例。有用。我没有改变任何内容,也没有明确设置任何连接参数,例如连接事件之间的间隔(我不认为Android 4.4 API支持这种情况)。

我正在测试两部Android手机,均使用Android 4.4.2版,并使用TI BLE嗅探器监控BLE流量。

一部电话协商7.5毫秒间隔,每个连接事件交换三个20字节数据包。另一部电话在连接事件之间协商48.75ms,每个事件交换19个20字节数据包(每秒有效数据传输速率大致相同)。

我的问题是我在尝试将数据从BLE服务活动记录到SQLite数据库中。日志记录适用于间隔为7.5毫秒的电话。然而, 应用程序以48.75ms的间隔锁定电话。 (一般来说,手机的连接不太稳定)。我认为这是因为它正在处理19个数据包彼此叠加。

我的问题:
1.无论如何,我可以让两部手机(以及任何未来的设备)使用7.5毫秒间隔,因为这似乎更好吗?有没有办法控制Minimum/Maximum_CE_Length参数?

  1. 是否有更好的方式来记录数据而不是直接来自BLE服务活动? These SQLite Android Developer pages建议使用ASync任务,但这似乎不合适,因为数据不会进入UI线程。
  2. 我的代码片段: 这是我的连接代码directly from the BluetoothLeGatt sample

      /**
     * Connects to the GATT server hosted on the Bluetooth LE device.
     *
     * @param address The device address of the destination device.
     *
     * @return Return true if the connection is initiated successfully. The connection result
     *         is reported asynchronously through the
     *         {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
     *         callback.
     */
    public boolean connect(final String address) {
        if (mBluetoothAdapter == null || address == null) {
            Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
            return false;
        }
        // Previously connected device.  Try to reconnect.
        if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
                && mBluetoothGatt != null) {
            Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
            if (mBluetoothGatt.connect()) {
                mConnectionState = STATE_CONNECTING;
                return true;
            } else {
                return false;
            }
        }
        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
        if (device == null) {
            Log.w(TAG, "Device not found.  Unable to connect.");
            return false;
        }
        // We want to directly connect to the device, so we are setting the autoConnect
        // parameter to false.
        mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
        Log.d(TAG, "Trying to create a new connection.");
        mBluetoothDeviceAddress = address;
        mConnectionState = STATE_CONNECTING;
        return true;
    }`
    

    我的日志代码在broadcastUpdate函数中:

    private void broadcastUpdate(final String action,
                                 final BluetoothGattCharacteristic characteristic) {
        final Intent intent = new Intent(action);
        StringBuilder stringBuilder = new StringBuilder();
        StringBuilder descStringBuilder = new StringBuilder();
        byte[]  newData = characteristic.getValue();
        String dataString;
    
        if (newData != null && newData.length > 0) {
            if (UUID_SENSOR_FFF4.equals(characteristic.getUuid())) {
    
                totalDataBytes += newData.length;
                // https://stackoverflow.com/questions/8150155/java-gethours-getminutes-and-getseconds
                estimatedTime = System.currentTimeMillis();
                Date timeDiff = new Date(estimatedTime - startTime - 19 * 3600000);
                SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss.SSS");
                descStringBuilder.append("CHAR_FFF4\n");
                descStringBuilder.append("Total Data: " + totalDataBytes + " Bytes\n");
                descStringBuilder.append("Elapsed Time: " + timeFormat.format(timeDiff) + "\n");
    
                for (int i = 0; i < newData.length; i++){
                    byte[] tempArray = { newData[i+1], newData[i] };
                    ByteBuffer wrapper = ByteBuffer.wrap(tempArray);
                    short tempShort = wrapper.getShort();
                    i++;
                    stringBuilder.append( tempShort );
                    stringBuilder.append( ", ");
                }
                dataString =  stringBuilder.toString();
    
                values.put(NbmcContract.NmbcDeviceData.COLUMN_TIMESTAMP, estimatedTime );
                values.put(NbmcContract.NmbcDeviceData.COLUMN_DATA_STRING, dataString);
    
                long newRowId = db.insert(NbmcContract.NmbcDeviceData.TABLE_NAME, null, values);
                descStringBuilder.append("Row ID: " + newRowId + "\n");
    
    
            } else {
                descStringBuilder.append(getCharacteristicString(characteristic) + "\nDATA:  ");
    
                // We expect these characteristics to return ASCII strings
                if (    DEVICE_NAME_CHAR.equals(characteristic.getUuid()) ||
                        MODEL_NUM_CHAR.equals(characteristic.getUuid()) ||
                        SERIAL_NUM_CHAR.equals(characteristic.getUuid()) ||
                        FIRMWARE_REV_CHAR.equals(characteristic.getUuid()) ||
                        HARDWARE_REV_CHAR.equals(characteristic.getUuid()) ||
                        FIRMWARE_REV_CHAR.equals(characteristic.getUuid()) ||
                        SOFTWARE_REV_CHAR.equals(characteristic.getUuid()) ||
                        MANUF_NAME_STRING_CHAR.equals(characteristic.getUuid()))
                {
                    for (byte byteChar : newData) {
                        stringBuilder.append(String.format("%c", byteChar));
                    }
                }
                else {
                    for (byte byteChar : newData) {
                        stringBuilder.append(String.format("%02X", byteChar));
                    }
                }
                dataString =  stringBuilder.toString();
            }
            String descString = descStringBuilder.toString();
            intent.putExtra("DESC_STRING", descString);
    
            UUID uuid = characteristic.getUuid();
            String uuidString = uuid.toString();
            intent.putExtra("CHAR_UUID", uuidString);
            intent.putExtra("EXTRA_DATA", dataString);
        }
        sendBroadcast(intent);
    }
    

1 个答案:

答案 0 :(得分:0)

1)在API lvl21 +上你可以尝试bluetoothGatt.requestConnectionPriority(int connectionPriority)
但这就是你所能做的一切。

2) Android上的BTLE堆栈不是最好的,我建议在没有任何处理的情况下获取数据,并在收到所有数据后进行处理。 请注意 gatt回调发生在一个不是你的ui线程的随机线程上。两个调用可以来自两个不同的线程,如果一个写入数据库而另一个出现并开始写入,则会弄得一团糟。

我的建议:

在每次接收活动中,您复制 byte[]数组(因为Android可能会将给定数组用于将来的数据),并使用Handler将其发送到主线程。在主线程上,您在List或Collection中收集数组,一旦收集了一定量,您就开始一个新线程,给它列表并让它将数据输入到数据库中,同时主线程为new创建一个新列表数据