【问题标题】:BLE connection issuesBLE 连接问题
【发布时间】:2018-06-29 19:02:43
【问题描述】:

我正在开发一个应用程序,该应用程序可以连接到 adafruitflora BLE 设备以接收来自它的信息。我希望应用程序显示找到的设备列表,并且当单击列表视图中的项目时,建立连接并且可以接收数据。最终,我想将所述数据转移到另一个活动中以实时绘制图表(如果可能的话)。发生了一些我不明白的事情,我希望有人能解释一下。

  1. 当它扫描设备时,列表视图会显示它们,但每个都有多个。
  2. 出于某种原因,onPauseonResume 使列表视图出现故障(显示设备,然后将其删除)
  3. 我如何知道何时有连接?
  4. 当我的代码中有 getRemoteDevice 时,出现运行时错误(尝试在空对象引用上调用虚拟方法 'android.bluetooth.BluetoothDevice android.bluetooth.BluetoothAdapter.getRemoteDevice(java.lang.String)')李>
  5. 当我尝试在 startScan 方法中使用过滤器和设置时,我的列表中什么也没有,我也尝试了带有设置的空过滤器,但仍然没有。

import android.annotation.SuppressLint;
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.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.content.Intent;
import android.os.Build;
import android.os.ParcelUuid;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

import no.nordicsemi.android.support.v18.scanner.BluetoothLeScannerCompat;
import no.nordicsemi.android.support.v18.scanner.ScanFilter;
import no.nordicsemi.android.support.v18.scanner.ScanResult;
import no.nordicsemi.android.support.v18.scanner.ScanSettings;

public class BluetoothDiscovery extends AppCompatActivity {

    private String TAG = "Bluetooth Device";
    private int REQUEST_ENABLE_BT = 5;

    private BluetoothAdapter mBluetoothAdapter;
    private BluetoothLeScannerCompat scanner;
    private ScanSettings settings;
    private UUID baseUUID = UUID.fromString("6e400001-b5a3-f393-e0a9-e50e24dcca9e"); // service UUID
    private UUID txUUID = UUID.fromString("6e400002-b5a3-f393-e0a9-e50e24dcca9e"); // TX UUID characteristic
    private UUID rxUUID = UUID.fromString("6e400003-b5a3-f393-e0a9-e50e24dcca9e"); // RX UUID characteristic
    private ScanFilter scanFilter;
    private BluetoothDevice device, mdevice;
    private BluetoothGatt mGatt;
    private boolean mScanning = false;
    private ArrayList<deviceShowFormat> foundDevices = new ArrayList<>();
    formattingAdapter BTadapter;

    Button scanButton;
    TextView fancyWords;
    ListView deviceList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bluetooth_discovery);

        mBluetoothAdapter.getDefaultAdapter();

        scanButton = findViewById(R.id.scanButt);
        scanButton.setText(getString(R.string.notScanning));

        fancyWords = findViewById(R.id.discoverText);
        fancyWords.setText(getString(R.string.nonScanTitle));

        deviceList = findViewById(R.id.deviceList);
        BTadapter = new formattingAdapter(BluetoothDiscovery.this, foundDevices);
        deviceList.setAdapter(BTadapter);


        scanner = BluetoothLeScannerCompat.getScanner();

        settings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_BALANCED).setReportDelay(500).build();

        scanFilter = new ScanFilter.Builder().setServiceUuid(new ParcelUuid(baseUUID)).build();

        //scanner.startScan(Arrays.asList(scanFilter), settings, mScanCallback);

        deviceList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @SuppressLint("LongLogTag")
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                deviceShowFormat mBTDevice = foundDevices.get(i);

                BluetoothDevice Device = mBTDevice.get_device();
                String deviceName = mBTDevice.get_device_name();
                String deviceAddress = mBTDevice.get_device_address();

                Log.d(TAG, "Selected device: " + Device.toString());
                Log.d(TAG, "Selected device name: " + deviceName);
                Log.d(TAG, "Selected device address: " + deviceAddress);



                //BluetoothDevice deviceConnect = mBluetoothAdapter.getRemoteDevice(deviceAddress);
                //deviceConnect.createBond();
                mGatt = Device.connectGatt(BluetoothDiscovery.this, false, mGattCallback);
                Toast.makeText(BluetoothDiscovery.this, "Selected device: " + deviceName, Toast.LENGTH_SHORT).show();

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                    Device.createBond();
                }

                Log.d(TAG, "" + Device.getBondState());
                if(Device.getBondState() == BluetoothDevice.BOND_BONDED){
                    Toast.makeText(BluetoothDiscovery.this, "Bluetooth device connected successfully", Toast.LENGTH_SHORT).show();
                }

                mGatt.getServices();
                mGatt.getConnectedDevices();

                Log.d("THIS IS THE DEVICES UUID", String.valueOf(Device.getUuids()));
                Log.d("DEVICE SERVICES", String.valueOf(mGatt.getServices()));
            }
        });
    }

    private final no.nordicsemi.android.support.v18.scanner.ScanCallback mScanCallback = new no.nordicsemi.android.support.v18.scanner.ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);

            Log.i("onScanResult", "device detected");

                device = result.getDevice();
                String deviceName = device.getName();
                String deviceAddress = device.getAddress();

                Log.d(TAG, "Scanned device: " + device.toString());
                Log.d(TAG, "Scanned device name: " + deviceName);
                Log.d(TAG, "Scanned device address: " + deviceAddress);

                foundDevices.add(new deviceShowFormat(device, deviceName, deviceAddress));
                BTadapter.notifyDataSetChanged();
        }
    };

    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);

            Log.i("onConnectionStateChange", "State Changed from: " + status + " to " + newState);

            if (newState == BluetoothProfile.STATE_CONNECTED){
                Toast.makeText(BluetoothDiscovery.this, "Attempting service discovery", Toast.LENGTH_SHORT).show();
                Log.i("onConnectionStateChange", "Attempting service discovery: " + gatt.discoverServices());
                gatt.discoverServices();
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED){
                Toast.makeText(BluetoothDiscovery.this, "Connection has been terminated", Toast.LENGTH_SHORT).show();
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status){
            super.onServicesDiscovered(gatt, status);

            Log.i("onServicesDiscovered", "Hey, we found a service");

            if (status != BluetoothGatt.GATT_SUCCESS){
                // Handle error
                Log.d("onServicesDiscovered" , "" + status);
                return;
            }

            BluetoothGattCharacteristic characteristic = gatt.getService(baseUUID).getCharacteristic(rxUUID);

            gatt.setCharacteristicNotification(characteristic, true);

            BluetoothGattDescriptor descriptor = characteristic.getDescriptor(txUUID);
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
            gatt.writeDescriptor(descriptor);
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status){

            Log.i("onCharacteristicRead", "Characteristic has been read");

            readCounterCharacteristic(characteristic);
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            if (mGatt.writeCharacteristic(characteristic)){
                Log.d("Characteristic changed", "Possibly looking for a write");
            }

            if (mGatt.readCharacteristic(characteristic)){
                readCounterCharacteristic(characteristic);
            }
        }

        private void readCounterCharacteristic(BluetoothGattCharacteristic characteristic){

            if (mGatt.readCharacteristic(characteristic)){
                byte[] data = characteristic.getValue();

                Log.d("READ DATA", data.toString());
            }

//            if (rxUUID.equals(characteristic.getUuid())){
//                //byte[] data = characteristic.getValue();
//                byte[] data = mGatt.readCharacteristic(characteristic);
//                //int value = Ints.fromByteArray(data);
//                Log.d("READ DATA", data.toString());
//            }
        }
    };

    public void toggleScan(View view){
        mScanning = !mScanning;

        if(mScanning){
            scanner.startScan(mScanCallback); //Arrays.asList(scanFilter) null, settings,
            scanButton.setText(getString(R.string.scanInProgress));
            fancyWords.setText(getString(R.string.ScanTitle));

        } else {
            scanner.stopScan(mScanCallback);
            scanButton.setText(getString(R.string.notScanning));
        }
    }

//    @Override
//    public void onPause(){
//        super.onPause();
//
////        if(mScanning){
////            mScanning = !mScanning;
//            scanner.stopScan(mScanCallback);
////        }
//
//        //Empty Adapter
//        //BTadapter.clear();
//        //BTadapter.notifyDataSetChanged();
//
//        //mdevice = device;
//
//    }
//
//    @Override
//    public void onResume(){
//        super.onResume();
//
//        if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
//            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
//            startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
//        }
//
//        //device = mdevice;
//    }


}

代码是我的应用程序中的第二个活动,在第一个蓝牙被初始化时,什么都没有。上面的代码有效,但我没有收到来自设备的任何数据,并且不确定它是否真正连接。从我得到的代码中显示的日志中:

Logcat Logcat2

我正在使用的资源:

https://github.com/NordicSemiconductor/Android-Scanner-Compat-Library

https://learn.adafruit.com/introducing-the-adafruit-bluefruit-le-uart-friend/uart-service

http://nilhcem.com/android-things/bluetooth-low-energy

【问题讨论】:

  • 您在尝试使用之前错过了初始化mBluetoothAdapter
  • 什么意思?我设置了mBluetoothAdapter,然后在onCreate方法中使用了mBluetoothAdapter.getDefaultAdapter()
  • 你错过了mBluetoothAdapter = instance creation之类的东西,检查答案

标签: java android bluetooth bluetooth-lowenergy


【解决方案1】:

你错过了初始化mBluetoothAdapter

截至(在 oncreate 时):

BluetoothManager manager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = manager.getAdapter();

代替:

mBluetoothAdapter.getDefaultAdapter();

在您的变量初始化并准备好使用之后,只需检查您是否需要 RuntimePermissions 才能使用蓝牙。

【讨论】:

  • 谢谢,我这样做了,没有任何损坏,这很好,但这个问题是回答哪个问题?我假设它是 getRemoteDevice 部分,因为其他一切都是一样的。在我的调试日志中,我仍然知道设备的 UUID 为空,并且没有服务。我使用 nRF UART 应用程序进行检查,它确实有这些东西。你能帮我指导一下吗?
  • 这是第四个答案,无法执行其他代码...现在您必须检查其他部分
【解决方案2】:

android BLE 通信中的大部分内容都是异步的。

所以当你调用 Device.connectGatt() 时,你需要等待 onConnectionStateChange 回调,然后再进行下一步操作。您枚举服务的尝试将失败(可能是悄悄地),因为此时您的连接状态尚未从 0 变为 2。在您的日志中,您可以看到您在调用 getServices() 之前调用了它,因为它回叫您说连接状态已变为已连接。

让事情变得更棘手,根据我的经验,您应该只从主线程进行 BluetoothDevice 交互。 (编辑:仅从主线程发送请求显然不是必需的)

因此,您执行的每个命令(连接、读取特征值、写入、断开连接、枚举服务等)都需要如下所示:

  1. 主线程:调用 (connect|read|write|enumerate) 函数
  2. 回调线程:Java 调用您的 BluetoothGattCallback,您应该找到一种方法(处理程序?)向主线程发出信号以执行您的下一步操作。
  3. 主线程:处理来自 BluetoothGattCallback 的结果并触发您将要做的下一件事。

顺便说一句:这在​​objective-c 和react-native-ble-plx 中要容易得多,因为它们会为您处理线程。如果你设计一个好的方法,从一开始就从回调线程获取结果到主线程,你会为自己省去很多痛苦。

将内容返回到 UI 线程的示例代码:

Handler m_handler;
public void onResume() {
  m_handler = new Handler(); // creates a Handler bound to the UI thread, which lets us fire messages back and forth between threads.
}

// ... other stuff ... 

public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        super.onConnectionStateChange(gatt, status, newState);

        Log.i("onConnectionStateChange", "State Changed from: " + status + " to " + newState);

        if (newState == BluetoothProfile.STATE_CONNECTED){
            // oh boy, we're connected.  Let's try to discover services!
            // m_handler.post() is a good way to make sure the thing you pass runs on the UI thread.
            m_handler.post(new Runnable() {
                @Override
                public void run() {
                  gatt.discoverServices();
                }
            })
        }
    }

【讨论】:

  • 所以我从中了解到Device.connectGatt() 应该是我的“onItemClick”中的最后一件事,既然如此,我将把后面的东西放在哪里以便它仍然运行?我不太确定回调是如何工作的,但它所描述的方式并没有回到它停止的地方?另外,线程是指活动吗?我已经看到使用的术语,但不确定这是否是什么意思。这是我几个月前开始这个项目以来最深入的回应,谢谢。
  • 我将编辑我的答案以提供一些示例代码,说明我是如何做到的,但我仍然不太满意。
  • 答案已编辑。这似乎是对线程的一个很好的介绍:katyscode.wordpress.com/2013/05/17/…。对于特定于 android 的线程概念,试试这个:developer.android.com/guide/components/processes-and-threads
  • “等到连接完成”的一种非常简单粗暴但快速的方法是在您的 connectGatt() 调用之后放置一个 Thread.sleep(5000) 。在 Thread.sleep(5000) 返回时,连接线程应该已经完成​​,您可能会恢复您的服务列表。请注意,如果连接花费的时间更长,那么您的程序将随机失败。
  • 没有要求从主线程执行 BLE 调用。大多数 BLE 回调是从 Binder 线程池中的一个线程调用的,并且保证一次只处理一个回调。因此,在回调中,您不应该有一个循环等待另一个回调被调用。
【解决方案3】:

对于将来查找此内容的任何人:如果您收到类似于 D/BluetoothGatt 的消息:onConnectionUpdated() - Device=xx:xx:xx:xx:xx:xx interval=6 latency=0 timeout=500 status =0 您正在连接到设备但“不足以”接收信息。对我来说,事实证明我必须第二次点击设备才能真正接收数据。我还不知道为什么

【讨论】:

    【解决方案4】:

    在你的回答中

    For anyone looking this up in the future: If you receive a message similar to D/BluetoothGatt: onConnectionUpdated() - Device=xx:xx:xx:xx:xx:xx interval=6 latency=0 timeout=500 status=0 you are connecting to the device but not "enough" to recieve info. For me it turned out i had to click the device to connect to a second time to actually recieve the data. I dont know why yet
    

    我不幸遇到了这个问题,你可以在我的问题中看到 BLE onDescriptorWrite is not trigger when writeDescriptor receive true 而你说的是二次点击设备,你的app或者其他ble设备。 非常感谢。

    【讨论】:

    • 对我来说,我不得不再次点击我尝试连接的设备
    猜你喜欢
    • 2020-02-06
    • 1970-01-01
    • 2016-10-30
    • 2020-12-29
    • 1970-01-01
    • 1970-01-01
    • 2016-04-18
    • 1970-01-01
    • 2022-12-17
    相关资源
    最近更新 更多