【问题标题】:Android - BLE connection parameter and Storing BLE sensor data in SQLite DatabaseAndroid - BLE 连接参数和在 SQLite 数据库中存储 BLE 传感器数据
【发布时间】:2017-07-08 05:09:23
【问题描述】:

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

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

我正在两部 Android 手机上进行测试,它们都使用 Android 4.4.2 版本,并且正在使用 TI BLE 嗅探器来监控 BLE 流量。

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

我的问题是,当 BLE 服务活动进入 SQLite 数据库时,我正在尝试记录这些数据。日志记录适用于间隔为 7.5 毫秒的电话。然而, 该应用程序以 48.75 毫秒的间隔锁定手机。 (通常,该手机的连接不太稳定)。我认为那是因为它正在处理 19 个数据包,这些数据包彼此重叠。

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

  1. 有没有比直接从 BLE 服务活动记录数据更好的方法? These SQLite Android Developer pages 建议使用异步任务,但这似乎不合适,因为数据不会进入 UI 线程。

我的代码sn-ps: 这是我的连接代码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);
}

【问题讨论】:

  • 这是FAQ;在单个事务中写入多个条目。

标签: java android sqlite bluetooth-lowenergy packets


【解决方案1】:

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

2) Android 上的 BTLE 堆栈不是最好的,我建议在没有任何处理的情况下获取数据,并在收到所有数据后进行处理。 请注意 gatt 回调发生在不是您的 ui 线程的随机线程上。两个调用可能来自两个不同的线程,如果一个调用写入数据库,另一个调用也开始写入,那么您会遇到麻烦。

我的建议:

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

【讨论】:

  • 感谢 NikkyD。我同意你关于 Android BTLE 堆栈的看法。我喜欢 Handler 的建议,下次我有这么多数据时会使用它(我们更改了设备接口以发送更少的数据作为解决方法)。如果可以的话,我会 +1。
猜你喜欢
  • 2012-03-19
  • 1970-01-01
  • 1970-01-01
  • 2022-11-18
  • 2017-01-02
  • 2017-06-18
  • 1970-01-01
  • 2017-05-29
  • 1970-01-01
相关资源
最近更新 更多