读取并通知BLE android
Posted
技术标签:
【中文标题】读取并通知BLE android【英文标题】:Read and notify BLE android 【发布时间】:2018-10-01 21:09:07 【问题描述】:我编写了一个代码,当它发生变化时,我可以从心率传感器读取通知值。
但我还需要读取设备的电池电量,这是模拟设备的另一项服务,但每当我尝试读取电池值时,第一部分甚至不发送来自心率的通知值.
代码如下:
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】:正如您所观察到的,您不能同时启动这两个操作(第一个操作将被覆盖并且永远不会执行):
gatt.readCharacteristic(battery);
gatt.writeDescriptor(descriptor);
相反,您必须执行第一个操作,等待它完成(在您的示例中,等待调用onCharacteristicRead
),然后然后开始第二个操作。实现这一点的一种常见方法是使用queue 来存储待处理的操作,然后将项目从队列前面弹出并在其余代码中一次执行一个。
这种需要排队是 Android BLE API 的一个令人讨厌的方面,文档中没有描述。有关更多详细信息,请查看我的 talk 或 list of resources Android BLE。
【讨论】:
我明白了,所以你的意思是我不能“同时”阅读所有内容?因为目标是连接到具有 4 项服务的设备,并不断监测心率、血液中的氧气百分比、温度和电池电量。 最终可能会同时进行所有持续监控,但您的 Android 应用代码中每个通知的设置必须串行进行,而不是并行进行。因此,要设置多个通知,您可以执行以下操作:调用gatt.writeDescriptor
设置第一个通知 -> 等待调用 onDescriptorWrite
-> 调用 gatt.writeDescriptor
设置第二个通知 -> 等待onDescriptorWrite
被调用->调用gatt.writeDescriptor
设置第三个通知->等待onDescriptorWrite
被调用等
同一主题的更多内容:***.com/questions/47097298/…以上是关于读取并通知BLE android的主要内容,如果未能解决你的问题,请参考以下文章