Читать и уведомлять BLE Android - PullRequest
0 голосов
/ 02 октября 2018

Я написал код, в котором я могу прочитать значение уведомления от датчика скорости звука при его изменении.

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

Вот код:

public class MainActivity extends AppCompatActivity {

BluetoothManager btManager;
BluetoothAdapter btAdapter;
BluetoothLeScanner btScanner;
Button startScanningButton;
Button stopScanningButton;
TextView peripheralTextView;
private final static int REQUEST_ENABLE_BT = 1;
private static final int PERMISSION_REQUEST_COARSE_LOCATION = 1;

Boolean btScanning = false;
int deviceIndex = 0;
ArrayList<BluetoothDevice> devicesDiscovered = new ArrayList<BluetoothDevice>();
EditText deviceIndexInput;
Button connectToDevice;
Button disconnectDevice;
BluetoothGatt bluetoothGatt;

UUID HEART_RATE_SERVICE_UUID = convertFromInteger(0x180D);
UUID HEART_RATE_MEASUREMENT_CHAR_UUID = convertFromInteger(0x2A37);
UUID HEART_RATE_CONTROL_POINT_CHAR_UUID = convertFromInteger(0x2A39);
UUID CLIENT_CHARACTERISTIC_CONFIG_UUID = convertFromInteger(0x2902);
UUID BATTERY_LEVEL = convertFromInteger(0x2A19);
UUID BATTERY_SERVICE = convertFromInteger(0x180F);

public final static String ACTION_GATT_CONNECTED =
"com.example.bluetooth.le.ACTION_GATT_CONNECTED";
public final static String ACTION_GATT_DISCONNECTED =
"com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";
public final static String ACTION_GATT_SERVICES_DISCOVERED =
"com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";
public final static String ACTION_DATA_AVAILABLE =
"com.example.bluetooth.le.ACTION_DATA_AVAILABLE";
public final static String EXTRA_DATA =
"com.example.bluetooth.le.EXTRA_DATA";

public Map<String, String> uuids = new HashMap<String, String>();

// Stops scanning after 5 seconds.
private Handler mHandler = new Handler();
private static final long SCAN_PERIOD = 1000;

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

    peripheralTextView = (TextView) findViewById(R.id.PeripheralTextView);
    peripheralTextView.setMovementMethod(new ScrollingMovementMethod());
    deviceIndexInput = (EditText) findViewById(R.id.InputIndex);
    deviceIndexInput.setText("0");

    connectToDevice = (Button) findViewById(R.id.ConnectButton);
    connectToDevice.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
            connectToDeviceSelected();
        }
    });

    disconnectDevice = (Button) findViewById(R.id.DisconnectButton);
    disconnectDevice.setVisibility(View.INVISIBLE);
    disconnectDevice.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
            disconnectDeviceSelected();
        }
    });

    startScanningButton = (Button) findViewById(R.id.StartScanButton);
    startScanningButton.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
            startScanning();
        }
    });

    stopScanningButton = (Button) findViewById(R.id.StopScanButton);
    stopScanningButton.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
            stopScanning();
        }
    });
    stopScanningButton.setVisibility(View.INVISIBLE);

    btManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
    btAdapter = btManager.getAdapter();
    btScanner = btAdapter.getBluetoothLeScanner();

    if (btAdapter != null && !btAdapter.isEnabled()) {
        Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
    }

    // Make sure we have access coarse location enabled, if not, prompt the user to enable it
/*        if (this.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("This app needs location access");
        builder.setMessage("Please grant location access so this app can detect peripherals.");
        builder.setPositiveButton(android.R.string.ok, null);
        builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
            @Override
            public void onDismiss(DialogInterface dialog) {
                requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION);
            }
        });
        builder.show();
    }*/



    //client = new GoogleApiClient.Builder(this).addApi(AppIndex.API).build();
}

// Device scan callback.
private ScanCallback leScanCallback = new ScanCallback() {
    @Override
    public void onScanResult(int callbackType, ScanResult result) {
        peripheralTextView.append("Index: " + deviceIndex + ", Device Name: " + result.getDevice().getName() + " rssi: " + result.getRssi() + ", MAC: " + result.getDevice().getAddress() + "\n");
        devicesDiscovered.add(result.getDevice());
        deviceIndex++;
        // auto scroll for text view
        final int scrollAmount = peripheralTextView.getLayout().getLineTop(peripheralTextView.getLineCount()) - peripheralTextView.getHeight();
        // if there is no need to scroll, scrollAmount will be <=0
        if (scrollAmount > 0) {
            peripheralTextView.scrollTo(0, scrollAmount);
        }
    }
};

// Device connect call back
private final BluetoothGattCallback btleGattCallback = new BluetoothGattCallback() {

    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
        // this will get called anytime you perform a read or write characteristic operation

        final byte[] teste = characteristic.getValue();

        final String batida = teste.toString();

        final String result = "result";

        int format = BluetoothGattCharacteristic.FORMAT_UINT8;

        final int heartRate = characteristic.getIntValue(format, 1);

        String TAG = "d";

        Log.d(TAG, String.format("Received heart rate: %d", heartRate));

        Log.v(result , batida);

        MainActivity.this.runOnUiThread(new Runnable() {
            public void run() {
                peripheralTextView.append("value of sensor (BPM) "+ heartRate + "\n");
            }
        });
    }

    @Override
    public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) {
        // this will get called when a device connects or disconnects
        System.out.println(newState);
        switch (newState) {
            case 0:
            MainActivity.this.runOnUiThread(new Runnable() {
                public void run() {
                    peripheralTextView.append("device disconnected\n");
                    connectToDevice.setVisibility(View.VISIBLE);
                    disconnectDevice.setVisibility(View.INVISIBLE);
                }
            });
            break;
            case 2:
            MainActivity.this.runOnUiThread(new Runnable() {
                public void run() {
                    peripheralTextView.append("device connected\n");
                    connectToDevice.setVisibility(View.INVISIBLE);
                    disconnectDevice.setVisibility(View.VISIBLE);
                }
            });

                // discover services and characteristics for this device
            bluetoothGatt.discoverServices();

            break;
            default:
            MainActivity.this.runOnUiThread(new Runnable() {
                public void run() {
                    peripheralTextView.append("we encounterned an unknown state, uh oh\n");
                }
            });
            break;
        }
    }

    @Override
    public void onServicesDiscovered(final BluetoothGatt gatt, final int status) {
        // this will get called after the client initiates a            BluetoothGatt.discoverServices() call
        MainActivity.this.runOnUiThread(new Runnable() {
            public void run() {
                peripheralTextView.append("device services have been discovered\n");
            }
        });
        displayGattServices(bluetoothGatt.getServices());

        BluetoothGattCharacteristic characteristic = gatt.getService(HEART_RATE_SERVICE_UUID).getCharacteristic(HEART_RATE_MEASUREMENT_CHAR_UUID);

        //BluetoothGattCharacteristic battery = gatt.getService(BATTERY_SERVICE).getCharacteristic(BATTERY_LEVEL);

        //gatt.readCharacteristic(battery);

        gatt.setCharacteristicNotification(characteristic, true);

        BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_UUID);

        descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);

        gatt.writeDescriptor(descriptor);
    }

    @Override
    public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
        BluetoothGattCharacteristic characteristic = gatt.getService(HEART_RATE_SERVICE_UUID).getCharacteristic(HEART_RATE_CONTROL_POINT_CHAR_UUID);

        characteristic.setValue(new byte[]{1,1});
        gatt.writeCharacteristic(characteristic);
    }

    @Override
    // Result of a characteristic read operation
    public void onCharacteristicRead(BluetoothGatt gatt,
     BluetoothGattCharacteristic characteristic,
     int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
        }
    }
};

private void broadcastUpdate(final String action,
 final BluetoothGattCharacteristic characteristic) {

    System.out.println(characteristic.getUuid());
}

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
    switch (requestCode) {
        case PERMISSION_REQUEST_COARSE_LOCATION: {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                System.out.println("coarse location permission granted");
            } else {
                final AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setTitle("Functionality limited");
                builder.setMessage("Since location access has not been granted, this app will not be able to discover beacons when in the background.");
                builder.setPositiveButton(android.R.string.ok, null);
                builder.setOnDismissListener(new DialogInterface.OnDismissListener() {

                    @Override
                    public void onDismiss(DialogInterface dialog) {
                    }

                });
                builder.show();
            }
            return;
        }
    }
}

public void startScanning() {
    System.out.println("start scanning");
    btScanning = true;
    deviceIndex = 0;
    devicesDiscovered.clear();
    peripheralTextView.setText("");
    peripheralTextView.append("Started Scanning\n");
    startScanningButton.setVisibility(View.INVISIBLE);
    stopScanningButton.setVisibility(View.VISIBLE);
    AsyncTask.execute(new Runnable() {
        @Override
        public void run() {
            btScanner.startScan(leScanCallback);
        }
    });

    mHandler.postDelayed(new Runnable() {
        @Override
        public void run() {
            stopScanning();
        }
    }, SCAN_PERIOD);
}

public void stopScanning() {
    System.out.println("stopping scanning");
    peripheralTextView.append("Stopped Scanning\n");
    btScanning = false;
    startScanningButton.setVisibility(View.VISIBLE);
    stopScanningButton.setVisibility(View.INVISIBLE);
    AsyncTask.execute(new Runnable() {
        @Override
        public void run() {
            btScanner.stopScan(leScanCallback);
        }
    });
}

public void connectToDeviceSelected() {
    peripheralTextView.append("Trying to connect to device at index: " + deviceIndexInput.getText() + "\n");
    int deviceSelected = Integer.parseInt(deviceIndexInput.getText().toString());
    bluetoothGatt = devicesDiscovered.get(deviceSelected).connectGatt(this, false, btleGattCallback);
}

public void disconnectDeviceSelected() {
    peripheralTextView.append("Disconnecting from device\n");
    bluetoothGatt.disconnect();
}

private void displayGattServices(List<BluetoothGattService> gattServices) {
    if (gattServices == null) return;

    // Loops through available GATT Services.
    for (BluetoothGattService gattService : gattServices) {

        final String uuid = gattService.getUuid().toString();
        System.out.println("Service discovered: " + uuid);
        MainActivity.this.runOnUiThread(new Runnable() {
            public void run() {
                peripheralTextView.append("Service disovered: "+uuid+"\n");
            }
        });
        new ArrayList<HashMap<String, String>>();
        List<BluetoothGattCharacteristic> gattCharacteristics =
        gattService.getCharacteristics();

        // Loops through available Characteristics.
        for (BluetoothGattCharacteristic gattCharacteristic :
            gattCharacteristics) {

            final String charUuid = gattCharacteristic.getUuid().toString();
        System.out.println("Characteristic discovered for service: " + charUuid);
        MainActivity.this.runOnUiThread(new Runnable() {
            public void run() {
                peripheralTextView.append("Characteristic discovered for service: "+charUuid+"\n");
            }
        });

    }
}
}

@Override
public void onStart() {
    super.onStart();
}

@Override
public void onStop() {
    super.onStop();

}

public UUID convertFromInteger(int i) {
    final long MSB = 0x0000000000001000L;
    final long LSB = 0x800000805f9b34fbL;
    long value = i & 0xFFFFFFFF;
    return new UUID(MSB | (value << 32), LSB);
}

}

Как я могу читать, не прерывая уведомление от BLE?

1 Ответ

0 голосов
/ 02 октября 2018

Как вы заметили, вы не можете запустить эти две операции одновременно (первая будет перезаписана и никогда не будет выполнена):

gatt.readCharacteristic(battery);
gatt.writeDescriptor(descriptor);

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

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

...