Запись данных в Bluetooth LE характеристика в Android

Хотя подобные вопросы были заданы, но это немного отличается. Я знаю, как передавать данные на подключенное устройство BLE, но я думаю, что делаю что-то не так, для чего мне нужна помощь. Код ниже содержит все методы из моего класса, который расширяет BroadcastReceiver.

  1. Я сканирую и подключаюсь к устройству, указанному в `PEN_ADDRESS`.
  2. В методе onServicesDiscovered я ищу службу, в которой UUID содержит abcd.
  3. Затем я перебираю характеристики этих сервисов и ищу три характеристики с конкретными строками в их `UUID`.
  4. Третья характеристика - это записываемая характеристика, в которой я пытаюсь записать данные, вызывая метод `writeCharac(mGatt,writeChar1,123);` Переданные выше данные `123` являются просто фиктивными данными.

Я отлаживал свой код, пытаясь записать эту характеристику, но поставив точки останова внутри writeCharac метод, я обнаружил, что status значение равно false, что означает, что запись не была успешной. Я что-то здесь упускаю? Пожалуйста, помогите!

public class BackgroundReceiverFire extends BroadcastReceiver {
Context context;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothGatt mGatt;
private BluetoothLeService mBluetoothLeService;
private boolean mScanning;
private final String TAG = "READING: ";
private BluetoothDevice mDevice;
private Handler mHandler;
private static final int REQUEST_ENABLE_BT = 1;
private final String PEN_ADDRESS = "FB:23:AF:42:5C:56";
// Stops scanning after 10 seconds.
private static final long SCAN_PERIOD = 10000;

    public void onReceive(Context context, Intent intent) {
        this.context = context;
        Toast.makeText(context, "Started Scanning", LENGTH_SHORT).show();
        initializeBluetooth();
        startScan();
    }

    private void initializeBluetooth() {
        mHandler = new Handler();

        // Use this check to determine whether BLE is supported on the device.  Then you can
        // selectively disable BLE-related features.
        // Initializes a Bluetooth adapter.  For API level 18 and above, get a reference to
        // BluetoothAdapter through BluetoothManager.
        final BluetoothManager bluetoothManager =
                (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = bluetoothManager.getAdapter();

        // Checks if Bluetooth is supported on the device.
        if (mBluetoothAdapter == null) {
            Toast.makeText(this.context, "No Bluetooth", LENGTH_SHORT).show();
            return;
        }
    }

    private void startScan() {
        scanLeDevice(true);
    }

    private void stopScan() {
        scanLeDevice(false);
    }

    private void scanLeDevice(final boolean enable) {
        if (enable) {
            // Stops scanning after a pre-defined scan period.
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
                }
            }, SCAN_PERIOD);

            mScanning = true;
            //Scanning for the device
            mBluetoothAdapter.startLeScan(mLeScanCallback);
        } else {
            mScanning = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        }
    }

    private BluetoothAdapter.LeScanCallback mLeScanCallback =
            new BluetoothAdapter.LeScanCallback() {
                @Override
                public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
                    if (device.getAddress().matches(PEN_ADDRESS)) {
                        connectBluetooth(device);
                        Toast.makeText(context, "Device Found: " + device.getAddress(), Toast.LENGTH_LONG).show();

                    }
                }
            };

    private void connectBluetooth(BluetoothDevice insulinPen) {
        if (mGatt == null) {
            Log.d("connectToDevice", "connecting to device: " + insulinPen.toString());
            mDevice = insulinPen;
            mGatt = insulinPen.connectGatt(context, true, gattCallback);
            scanLeDevice(false);// will stop after first device detection
        }
    }

    private void enableBluetooth() {
        if (!mBluetoothAdapter.isEnabled()) {
            mBluetoothAdapter.enable();
        }
        scanLeDevice(true);
    }

    private void disableBluetooth() {
        if (mBluetoothAdapter.isEnabled()) {
            mBluetoothAdapter.disable();
        }
    }

    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:
                    gatt.discoverServices();
                    break;
                case BluetoothProfile.STATE_DISCONNECTED:
                    Log.e("gattCallback", "STATE_DISCONNECTED");
                    Log.i("gattCallback", "reconnecting...");
                    BluetoothDevice mDevice = gatt.getDevice();
                    mGatt = null;
                    connectBluetooth(mDevice);
                    break;
                default:
                    Log.e("gattCallback", "STATE_OTHER");
                    break;
            }

        }

        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            mGatt = gatt;
            List<BluetoothGattService> services = mGatt.getServices();
            Log.i("onServicesDiscovered", services.toString());
            Iterator<BluetoothGattService> serviceIterator = services.iterator();
            while(serviceIterator.hasNext()){
                BluetoothGattService bleService = serviceIterator.next();
                if(bleService.getUuid().toString().contains("abcd")){
                    //Toast.makeText(context,"Got the service",Toast.LENGTH_SHORT);
                    BluetoothGattCharacteristic readChar1 = bleService.getCharacteristics().get(0);
                    for (BluetoothGattDescriptor descriptor : readChar1.getDescriptors()) {
                        descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
                        mGatt.writeDescriptor(descriptor);
                        mGatt.setCharacteristicNotification(readChar1, true);
                    }
                    //mGatt.readCharacteristic(readChar1);

                    BluetoothGattCharacteristic readChar2 = bleService.getCharacteristics().get(1);
                    for (BluetoothGattDescriptor descriptor : readChar2.getDescriptors()) {
                        descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
                        mGatt.writeDescriptor(descriptor);
                        mGatt.setCharacteristicNotification(readChar2, true);
                    }
                    //mGatt.readCharacteristic(readChar2);

                    BluetoothGattCharacteristic writeChar1 = bleService.getCharacteristics().get(2);
                    for (BluetoothGattDescriptor descriptor : writeChar1.getDescriptors()) {
                        descriptor.setValue( BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
                        mGatt.writeDescriptor(descriptor);
                        mGatt.setCharacteristicNotification(writeChar1, true);
                    }
                    writeCharac(mGatt,writeChar1,123);
                }
            }

            //gatt.readCharacteristic(therm_char);

        }
        public void writeCharac(BluetoothGatt gatt, BluetoothGattCharacteristic charac, int value ){
            if (mBluetoothAdapter == null || gatt == null) {
                Log.w(TAG, "BluetoothAdapter not initialized");
                return;
            }
/*
            BluetoothGattCharacteristic charac = gattService
                    .getCharacteristic(uuid);
*/
            if (charac == null) {
                Log.e(TAG, "char not found!");
            }

            int unixTime = value;
            String unixTimeString = Integer.toHexString(unixTime);
            byte[] byteArray = hexStringToByteArray(unixTimeString);
            charac.setValue(byteArray);
            boolean status = mGatt.writeCharacteristic(charac);
            if(status){
                Toast.makeText(context,"Written Successfully",Toast.LENGTH_SHORT).show();
            }else{
                Toast.makeText(context,"Error writing characteristic",Toast.LENGTH_SHORT).show();
            }
        }

        public byte[] hexStringToByteArray(String s) {
            int len = s.length();
            byte[] data = new byte[len/2];

            for(int i = 0; i < len; i+=2){
                data[i/2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
            }

            return data;
        }

        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic
                                                 characteristic, int status) {
            Log.i("onCharacteristicRead", characteristic.toString());
            String characteristicValue = characteristic.getValue().toString();
            Log.d("CHARACTERISTIC VALUE: ", characteristicValue);
            gatt.disconnect();
        }

        public void onCharacteristicChanged(BluetoothGatt gatt,
                                            BluetoothGattCharacteristic
                                                    characteristic) {
            String value = characteristic.getValue().toString();
            Log.d(TAG,value);
        }

    };

3 ответа

Хотя API BLE является асинхронным по своей природе, фактическая передача сигнала неизбежно является синхронной. Вы должны дождаться предыдущего вызова подключения / записи / чтения к обратному вызову, прежде чем начинать любую операцию подключения / записи / чтения.

В вашем коде onServicesDiscovered(BluetoothGatt gatt, int status) функция, которую вы назвали mGatt.writeDescriptor(descriptor) дважды, прежде чем пытаться написать характеристику. API откажется запустить ваш запрос на запись, так как он занят написанием дескриптора, и вернет false для вашего mGatt.writeCharacteristic(charac) вызов.

Так что просто дождитесь обратного вызова writeDescriptor перед вызовом writeCharacteristic. Эта природа плохо документирована, но вы можете найти источник здесь и здесь.

Спасибо @reTs и @pooja за предложения. Проблема была из-за этих двух строк
mGatt = null
connectBluetooth(mDevice);
в STATE_DISCONNECTED. Я понял это, но не помню точную причину. Размещение на всякий случай, если это полезно.

Вы проверили, является ли 'writechar1' btcharacteristic значением, которое вы передаете writeCharac(), пустым?

Другие вопросы по тегам