Android - BLE connection parameter and Storing BLE sensor data in SQLite Database

孤街浪徒 提交于 2019-12-25 08:30:22

问题


I am developing an Android app that receives data from a BLE sensor at a rate of about 8000 bytes per second.

The connection logic in my app is based upon Google's BluetoothLeGatt sample. It works. I haven't changed anything and don't explicitly set any connection parameters like interval between connection events (I don't think the Android 4.4 APIs support that).

I am testing on two Android phones, both using Android version 4.4.2 and am using the TI BLE sniffer to monitor BLE traffic.

One phone negotiates a 7.5ms interval and exchanges three 20-byte packets per connection event. The other phone negotiates 48.75ms between connection events and exchanges nineteen 20-byte packets per event (the effective data transfer rate per second is about the same).

My problem is that I am trying to log the data from the BLE Service activity as it comes in to a SQLite database. The logging works for the phone with the 7.5ms interval. However, the app locks up for the phone with the 48.75ms interval. (In general, that phone's connection is a lot less stable). I assume that's because it is getting processing for 19 packets right on top of each other.

My questions:
1. Is there anyway I can make both phones (and any future devices) use the 7.5ms interval since that seems to work better? Is there a way to control the Minimum/Maximum_CE_Length parameters?

  1. Is there a better way to log the data than directly from the BLE service activity? These SQLite Android Developer pages suggest using an ASync task but that doesn't seem appropriate since the data isn't going to the UI thread.

My code snippets: This is my connection code 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;
}`

My logging code is in the broadcastUpdate function:

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:


1) on API lvl21+ you can try bluetoothGatt.requestConnectionPriority(int connectionPriority)
But that is all you can do.

2) The BTLE stack on Android is not the best, i would suggest taking the data without any processing and do the processing after all the data has been received. Please note that the gatt callbacks happen on a random thread that is NOT your ui-thread. Two calls can originate from two different threads and if one writes into a database and the other comes along and starts to write as well you get a mess.

My suggestion:

On every receiving event you copy the byte[] array (as Android may reuse the given array for future data) and send it to the main thread with a Handler. On the main thread you collect the arrays in a List or Collection and once a certain amount has been collected you start a new thread, give it the list and let it enter the data into a database while the main thread creates a new list for new data.



来源:https://stackoverflow.com/questions/42318854/android-ble-connection-parameter-and-storing-ble-sensor-data-in-sqlite-databas

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!