BLE连接问题

时间:2018-06-29 19:02:43

标签: java android bluetooth bluetooth-lowenergy

我正在开发一个可连接到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

4 个答案:

答案 0 :(得分:0)

您错过了初始化mBluetoothAdapter

截至(在oncreate时):

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

代替:

mBluetoothAdapter.getDefaultAdapter();

初始化变量并准备使用它之后,只需检查是否需要RuntimePermissions即可使用蓝牙。

答案 1 :(得分:0)

android BLE通信中的很多东西都是异步的。

因此,当您调用Device.connectGatt()时,需要等待onConnectionStateChange回调再执行下一步。您尝试枚举服务的尝试将失败(可能很安静),因为此时连接状态尚未从0变为2。在您的日志中,您可以看到在调用getServices()来回叫连接状态已变为connected之前,已经进行了调用。

让事情变得棘手,以我的经验,您应该只从主线程进行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();
                }
            })
        }
    }

答案 2 :(得分:0)

对于将来需要查找此文件的任何人:如果收到类似于D / BluetoothGatt的消息:onConnectionUpdated()-设备= xx:xx:xx:xx:xx:xx:xx间隔= 6延迟= 0超时= 500状态= 0,您正在连接到设备,但“不足”以接收信息。对我来说,事实证明我必须单击设备才能第二次连接才能实际接收数据。我不知道为什么

答案 3 :(得分:0)

您的答案

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 您说的是第二点击设备,您的应用或其他ble设备。 非常感谢。