Android BLE通知数据包丢失

时间:2015-08-03 03:08:16

标签: android bluetooth-lowenergy

我正在开发一个硬件设备,它通过BLE将连续的数据流发送到Android应用程序。 Android应用程序将此数据作为GATT通知接收,然后处理此数据并将其保存到数据库。

项目的配置细节如下:

  1. 电话 - Moto E第一代
  2. Android版 - Android 5.1 - Lollipop
  3. iOS - iPhone 4& 5,在iOS 7& 8
  4. 硬件 - CC2541
  5. Connection_Interval:40 ms(在硬件固件中设置)。
  6. 每个连接间隔发送的数据包数:4(在硬件固件中设置)。
  7. 问题

    当数据从硬件设备传输到Android手机上运行的BLE数据捕获应用程序时,不会收到所有数据包。它只接收大约35-45个数据包,而预期的数据包数为50.

    更令人惊讶的是,当我们使用BLE数据包嗅探器时,Android手机嗅探和显示的数据之间存在完美匹配(这是不完整/不正确的数据)。这让我相信,当连接到Android手机而不发送所有数据时,硬件的行为会有所不同。

    当我们使用与iOS BLE数据捕获应用程序相同的硬件时,数据会被正确接收。

    我对Android中BLE数据捕获的这种行为感到困惑和无能为力。 iOS设备上的应用程序如何能够正确捕获所有数据,而Android手机上的应用程序根本无法正确捕获数据?

    在Android上使用BLE应用时,是否有人遇到过数据包丢失/数据不正确的问题?欢迎提出任何意见。非常感谢你的帮助。

    Android应用程序使用标准BLE代码通过BLE连接到设备。我正在使用的Android代码如下所示:

    import android.annotation.TargetApi;
    import android.app.Activity;
    import android.bluetooth.BluetoothAdapter;
    import android.bluetooth.BluetoothDevice;
    import android.bluetooth.BluetoothGatt;
    import android.bluetooth.BluetoothGattCallback;
    import android.bluetooth.BluetoothGattCharacteristic;
    import android.bluetooth.BluetoothGattDescriptor;
    import android.bluetooth.BluetoothGattService;
    import android.bluetooth.BluetoothManager;
    import android.bluetooth.BluetoothProfile;
    import android.bluetooth.le.BluetoothLeScanner;
    import android.bluetooth.le.ScanCallback;
    import android.bluetooth.le.ScanFilter;
    import android.bluetooth.le.ScanResult;
    import android.bluetooth.le.ScanSettings;
    import android.content.Context;
    import android.content.Intent;
    import android.content.pm.PackageManager;
    import android.nfc.Tag;
    import android.os.Build;
    import android.os.Bundle;
    import android.os.Handler;
    import android.support.v7.app.ActionBarActivity;
    import android.util.Log;
    import android.widget.Toast;
    
    import java.util.ArrayList;
    import java.util.List;
    
    @TargetApi(21)
    public class BLETestActivity extends Activity {
        private BluetoothAdapter mBluetoothAdapter;
        private int REQUEST_ENABLE_BT = 1;
        private Handler mHandler;
        private static final long SCAN_PERIOD = 10000;
        private BluetoothLeScanner mLEScanner;
        private ScanSettings settings;
        private List<ScanFilter> filters;
        private BluetoothGatt mGatt;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_bletest);
            mHandler = new Handler();
            if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
                Toast.makeText(this, "BLE Not Supported",
                        Toast.LENGTH_SHORT).show();
                finish();
            }
            final BluetoothManager bluetoothManager =
                    (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
            mBluetoothAdapter = bluetoothManager.getAdapter();
    
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
                Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
            } else {
                if (Build.VERSION.SDK_INT >= 21) {
                    Log.i("innnnn","spinnnn - " + Build.VERSION.SDK_INT);
                    mLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
                    settings = new ScanSettings.Builder()
                            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
                            .build();
                    filters = new ArrayList<ScanFilter>();
                }
                scanLeDevice(true);
            }
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
                scanLeDevice(false);
            }
        }
    
        @Override
        protected void onDestroy() {
            if (mGatt == null) {
                return;
            }
            mGatt.close();
            mGatt = null;
            super.onDestroy();
        }
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            if (requestCode == REQUEST_ENABLE_BT) {
                if (resultCode == Activity.RESULT_CANCELED) {
                    //Bluetooth not enabled.
                    finish();
                    return;
                }
            }
            super.onActivityResult(requestCode, resultCode, data);
        }
    
        private void scanLeDevice(final boolean enable) {
            if (enable) {
                mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        if (Build.VERSION.SDK_INT < 21) {
                            mBluetoothAdapter.stopLeScan(mLeScanCallback);
                        } else {
                            mLEScanner.stopScan(mScanCallback);
    
                        }
                    }
                }, SCAN_PERIOD);
                if (Build.VERSION.SDK_INT < 21) {
                    mBluetoothAdapter.startLeScan(mLeScanCallback);
                } else {
                    mLEScanner.startScan(filters, settings, mScanCallback);
                }
            } else {
                if (Build.VERSION.SDK_INT < 21) {
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
                } else {
                    mLEScanner.stopScan(mScanCallback);
                }
            }
        }
    
    
        private ScanCallback mScanCallback = null;/* new ScanCallback() {
            @Override
            public void onScanResult(int callbackType, ScanResult result) {
                Log.i("callbackType", String.valueOf(callbackType));
                Log.i("result", result.toString());
                BluetoothDevice btDevice = result.getDevice();
                connectToDevice(btDevice);
            }
    
            @Override
            public void onBatchScanResults(List<ScanResult> results) {
                for (ScanResult sr : results) {
                    Log.i("ScanResult - Results", sr.toString());
                }
            }
    
            @Override
            public void onScanFailed(int errorCode) {
                Log.e("Scan Failed", "Error Code: " + errorCode);
            }
        };*/
    
        private BluetoothAdapter.LeScanCallback mLeScanCallback =
                new BluetoothAdapter.LeScanCallback() {
                    @Override
                    public void onLeScan(final BluetoothDevice device, int rssi,
                                         byte[] scanRecord) {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                Log.i("onLeScan", device.toString());
                                connectToDevice(device);
                            }
                        });
                    }
                };
    
        public void connectToDevice(BluetoothDevice device) {
            if (mGatt == null) {
                mGatt = device.connectGatt(this, false, gattCallback);
                scanLeDevice(false);// will stop after first device detection
            }
        }
    
        private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
            @Override
            public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
                Log.i("onConnectionStateChange", "Status: " + status);
                switch (newState) {
                    case BluetoothProfile.STATE_CONNECTED:
                        Log.i("gattCallback", "STATE_CONNECTED");
                        gatt.discoverServices();
                        break;
                    case BluetoothProfile.STATE_DISCONNECTED:
                        Log.e("gattCallback", "STATE_DISCONNECTED");
                        break;
                    default:
                        Log.e("gattCallback", "STATE_OTHER");
                }
    
            }
    
            @Override
            public void onServicesDiscovered(BluetoothGatt gatt, int status) {
                List<BluetoothGattService> services = gatt.getServices();
               // Log.i("onServicesDiscovered", services.toString());
    
                for(int i=0;i<services.size();i++) {
                    List<BluetoothGattCharacteristic> charList = services.get(i).getCharacteristics();
                    for (int j = 0; j < charList.size();j++) {
                        BluetoothGattCharacteristic characteristic = charList.get(j);
                        if (characteristic.getUuid().compareTo(Constants.HEART_DATA_UUID) == 0) {
                            Log.i(BLETestActivity.class.getSimpleName(), "Characteristic UUID is " + characteristic.getUuid());
                            mGatt.setCharacteristicNotification(characteristic, true);
                            BluetoothGattDescriptor descriptor = characteristic.getDescriptor(Constants.HEART_DATA_DESCRIPTOR_UUID);
                            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                            mGatt.writeDescriptor(descriptor);
                        }
                    }
                }
            }
    
            public void onCharacteristicChanged(BluetoothGatt gatt,
                                                BluetoothGattCharacteristic characteristic) {
                // Retrieve the byte values
                // Convert them to hex values
                byte[] data = characteristic.getValue();
    
                // The following array contains data in the follwing form:
                // 0-1 - running counter counts
                // 2-4, 5-7, 8-10  - Sample 1 data for channel 1, channel2 and channel 3
                // 11-13, 14-16, 17-19 - Sample 2 data for channel 1, channel 2 and channel 3
    
    //            Log.i(TAG," ------ " + Thread.currentThread().getId());
                String heartBeatDataInHex = bytesToHex(data);
    
    
                // An error packet is received after every 17 samples.
                // Checking to make sure that this is not an error packet
    
                if (!(heartBeatDataInHex.substring(4, 10).equalsIgnoreCase("FFFFFF") && heartBeatDataInHex.substring(26, 40).equalsIgnoreCase("FFFFFFFFFFFFFF"))) {
                    Log.i("testdata", heartBeatDataInHex + " ---- " + Thread.currentThread().getId());
                }
            }
    
            @Override
            public void onCharacteristicRead(BluetoothGatt gatt,
                                             BluetoothGattCharacteristic
                                                     characteristic, int status) {
                Log.i("onCharacteristicRead", characteristic.toString());
                gatt.disconnect();
            }
    
        };
    
        private String bytesToHex(byte[] bytes) {
            final char[] hexArray = "0123456789ABCDEF".toCharArray();
            char[] hexChars = new char[bytes.length * 2];
            for ( int j = 0; j < bytes.length; j++ ) {
                int v = bytes[j] & 0xFF;
                hexChars[j * 2] = hexArray[v >>> 4];
                hexChars[j * 2 + 1] = hexArray[v & 0x0F];
            }
            return new String(hexChars);
        }
    }
    

1 个答案:

答案 0 :(得分:4)

我遇到过类似的问题! 我做了一些测试,我已经确定最小连接间隔是48ms,android api等级为18。 (connection interval for BLE on Galaxy S3 Android 4.3)。

可行的解决方案:

  1. 使连接间隔变慢(小于48毫秒)。

  2. 从api level 21到upper,你可以改变CONNECTION_PRIORITY android developer来修改间隔连接,但这意味着更多的能量消耗。