Проблемы с подключением BLE - PullRequest
0 голосов
/ 29 июня 2018

Я работаю над приложением, которое может подключаться к устройству adafruit flora BLE для получения информации от него. Я хочу, чтобы приложение отображало список найденных устройств, и при нажатии на элемент в представлении списка устанавливается соединение и данные могут быть получены. В конечном счете, я хочу перенести указанные данные в другой вид деятельности в режиме реального времени (если это возможно). Происходят некоторые вещи, которые я не понимаю, и я надеюсь, что кто-то может пролить свет на это.

  1. При сканировании устройств в списке отображаются их, но их кратно каждому.
  2. По какой-то причине onPause и onResume делают вид списка сбойным (отображает устройства, а затем удаляет их)
  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;
//    }


}

Код является вторым действием в моем приложении, в первом Bluetooth инициализируется и что-нет. Приведенный выше код работает, но я не получаю никаких данных с устройства и не уверен, что оно действительно подключено. из логов, показанных в коде, я получаю:

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

Ответы [ 3 ]

0 голосов
/ 05 июля 2018

Многое из того, что происходит в BLE Android, является асинхронным.

Поэтому, когда вы вызываете Device.connectGatt (), вам нужно дождаться обратного вызова onConnectionStateChange, прежде чем делать следующее. Ваши попытки перечислить службы потерпят неудачу (вероятно, незаметно), потому что состояние вашего соединения не изменилось с 0 до 2 в этот момент. В своем журнале вы можете видеть, что вы выполняете вызов getServices () до того, как он перезвонит вам, чтобы сообщить, что состояние соединения перешло к подключенному.

Делая вещи хитрее, по моему опыту, вы должны выполнять взаимодействие BluetoothDevice только из основного потока. (Изменить: только отправка запросов из основного потока, по-видимому, не является обязательным)

Поэтому каждая ваша команда (подключение, чтение значений характеристик, запись, отключение, перечисление служб и т. Д.) Должна выглядеть следующим образом:

  1. Основной поток: вызов функции (connect | read | write | enumerate)
  2. Поток обратного вызова: Java вызывает ваш BluetoothGattCallback, и вы должны найти способ (обработчик?), Чтобы сообщить основному потоку, чтобы он делал следующее.
  3. Основной поток: обработайте результат из BluetoothGattCallback и запустите следующую вещь, которую вы будете делать.

Слегка в стороне: это намного проще в target-c и response-native-ble-plx, потому что они обрабатывают потоки для вас. Если вы разработали хороший способ получения результатов из потока обратного вызова в основной поток с самого начала, вы избавите себя от большой боли.

Пример кода для возврата содержимого в поток пользовательского интерфейса:

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();
                }
            })
        }
    }
0 голосов
/ 31 июля 2018

Для тех, кто ищет это в будущем: Если вы получите сообщение, похожее на D / BluetoothGatt: onConnectionUpdated () - Устройство = xx: xx: xx: xx: xx: xx интервал = 6 задержек = 0 тайм-аут = 500 статус = 0 вы подключаетесь к устройству, но недостаточно для получения информации. Для меня оказалось, что я должен был щелкнуть устройство, чтобы подключиться во второй раз, чтобы фактически получить данные. Я пока не знаю почему

0 голосов
/ 03 июля 2018

Вы пропустили инициализацию mBluetoothAdapter

По состоянию на (при создании):

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

вместо:

mBluetoothAdapter.getDefaultAdapter();

После этого ваша переменная инициализируется и готова к использованию, просто проверьте, нужны ли вам RuntimePermissions для использования Bluetooth.

...