获得蓝牙低功耗 (BLE) 设备通知的步骤是啥?
Posted
技术标签:
【中文标题】获得蓝牙低功耗 (BLE) 设备通知的步骤是啥?【英文标题】:What are the steps to get notified by Bluetooth Low Energy (BLE) device?获得蓝牙低功耗 (BLE) 设备通知的步骤是什么? 【发布时间】:2014-01-03 15:45:43 【问题描述】:我正在开发一款低功耗蓝牙 (BLE) 应用。我有一个测量重量的 BLE 设备(秤)。我可以连接这个设备。但我不知道如何从中读取数据(重量值)。
我想知道我的应用是否连接到任何 BLE 设备,那么通过哪些步骤获得设备通知以获取更新数据。
好的,以下是我正在使用的 Activity..
public class BlogBLEActivity extends Activity implements OnItemClickListener
private final static String TAG = BlogBLEActivity.class.getSimpleName();
private BluetoothAdapter bluetoothAdapter;
BluetoothManager bluetoothManager;
boolean hasBleFeature = false;
TextView tvMessage;
int messageId = R.string.doesnt_support_ble;
int colorId = android.R.color.holo_red_light;
private boolean mScanning;
private Handler handler = new Handler();
private static final long SCAN_PERIOD = 10000;
private static final int REQUEST_ENABLE_BT = 1209;
ListView listView;
ArrayList<BluetoothDevice> listDevices;
BleDeviceAdapter bleDeviceAdapter;
TextView tvHumidity;
TextView tvTemperature;
TextView tvPressure;
boolean isConnected = false;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.blog_ble);
initParameters();
initViews();
scanLeDevice(true);
@SuppressLint("NewApi")
void initParameters()
hasBleFeature = getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
Log.i(TAG, "hasBleFeature : " + hasBleFeature);
if (hasBleFeature)
messageId = R.string.supports_ble;
colorId = android.R.color.holo_blue_light;
else
messageId = R.string.doesnt_support_ble;
colorId = android.R.color.holo_red_light;
bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();// BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled())
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
listDevices = new ArrayList<BluetoothDevice>();
bleDeviceAdapter = new BleDeviceAdapter(this, listDevices);
void initViews()
tvHumidity = (TextView) findViewById(R.id.blog_ble_tv_humidity);
tvTemperature = (TextView) findViewById(R.id.blog_ble_tv_temprature);
tvPressure = (TextView) findViewById(R.id.blog_ble_tv_pressure);
tvMessage = (TextView) findViewById(R.id.blog_ble_tv_message);
tvMessage.setText(getResources().getString(messageId));
tvMessage.setTextColor(getResources().getColor(colorId));
listView = (ListView) findViewById(R.id.blog_ble_list_view);
listView.setAdapter(bleDeviceAdapter);
listView.setOnItemClickListener(this);
@SuppressLint("NewApi")
void scanLeDevice(final boolean enable)
if (enable)
handler.postDelayed(new Runnable()
@SuppressLint("NewApi")
@Override
public void run()
mScanning = false;
bluetoothAdapter.stopLeScan(leScanCallback);
, SCAN_PERIOD);
mScanning = false;
bluetoothAdapter.startLeScan(leScanCallback);
else
mScanning = false;
bluetoothAdapter.stopLeScan(leScanCallback);
@SuppressLint("NewApi")
private BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback()
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord)
runOnUiThread(new Runnable()
@Override
public void run()
if (device != null)
bleDeviceAdapter.add(device);
bleDeviceAdapter.notifyDataSetChanged();
);
;
class BleDeviceAdapter extends ArrayAdapter<BluetoothDevice>
public BleDeviceAdapter(Context context, List<BluetoothDevice> objects)
super(context, R.layout.row_ble_device, R.id.row_ble_device_tv_name, objects);
@SuppressLint("NewApi")
@Override
public View getView(int position, View convertView, ViewGroup parent)
View row = super.getView(position, convertView, parent);
ViewHolder holder = (ViewHolder) row.getTag();
if (holder == null)
holder = new ViewHolder(row);
row.setTag(holder);
BluetoothDevice device = getDevice(position);
holder.tvName.setText("" + device.getName());
Log.i(TAG, "" + device.getName());
return row;
BluetoothDevice getDevice(int position)
return (BluetoothDevice) listView.getAdapter().getItem(position);
@SuppressLint("NewApi")
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3)
BluetoothDevice device = getDevice(position);
Toast.makeText(this, "" + device.getName(), Toast.LENGTH_SHORT).show();
BluetoothGatt connectGatt = device.connectGatt(this, false, mGattCallback);
/* Client Configuration Descriptor */
private static final UUID CONFIG_DESCRIPTOR = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
private static final UUID KITCHEN_SCALE_SERVICE = UUID.fromString("0000780a-0000-1000-8000-00805f9b34fb");
private static final UUID KITCHEN_SCALE_FEATURE_CHAR = UUID.fromString("00008aa0-0000-1000-8000-00805f9b34fb");
private static final UUID KITCHEN_SCALE_MEASUREMENT_CHAR = UUID.fromString("00008aa1-0000-1000-8000-00805f9b34fb");
private static final UUID KITCHEN_SCALE_INTERMEDIATE_CHAR = UUID.fromString("00008aa2-0000-1000-8000-00805f9b34fb");
/*
* In this callback, we've created a bit of a state machine to enforce that
* only one characteristic be read or written at a time until all of our
* sensors are enabled and we are registered to get notifications.
*/
@SuppressLint("NewApi")
private BluetoothGattCallback mGattCallback = new BluetoothGattCallback()
/* State Machine Tracking */
private int mState = 0;
private void reset()
mState = 0;
private void advance()
mState++;
/*
* Send an enable command to each sensor by writing a configuration
* characteristic. This is specific to the SensorTag to keep power low
* by disabling sensors you aren't using.
*/
private void enableNextSensor(BluetoothGatt gatt)
BluetoothGattCharacteristic characteristic;
switch (mState)
case 0:
Log.i(TAG, "Enabling weight scale");
characteristic = gatt.getService(KITCHEN_SCALE_SERVICE).getCharacteristic(KITCHEN_SCALE_FEATURE_CHAR);
Log.i(TAG, "Feature Properties : "+characteristic.getProperties());
characteristic.setValue(new byte[]
0x09 );
break;
default:
mHandler.sendEmptyMessage(MSG_DISMISS);
Log.i(TAG, "All Sensors Enabled");
return;
gatt.writeCharacteristic(characteristic);
/*
* Read the data characteristic's value for each sensor explicitly
*/
private void readNextSensor(BluetoothGatt gatt)
BluetoothGattCharacteristic characteristic;
switch (mState)
case 0:
Log.i(TAG, "Reading weight cal");
characteristic = gatt.getService(KITCHEN_SCALE_SERVICE).getCharacteristic(KITCHEN_SCALE_MEASUREMENT_CHAR);
break;
default:
mHandler.sendEmptyMessage(MSG_DISMISS);
Log.i(TAG, "All Sensors Enabled");
return;
gatt.readCharacteristic(characteristic);
/*
* Enable notification of changes on the data characteristic for each
* sensor by writing the ENABLE_NOTIFICATION_VALUE flag to that
* characteristic's configuration descriptor.
*/
private void setNotifyNextSensor(BluetoothGatt gatt)
BluetoothGattCharacteristic characteristic;
switch (mState)
case 0:
Log.i(TAG, "Set notify weight ");
characteristic = gatt.getService(KITCHEN_SCALE_SERVICE).getCharacteristic(KITCHEN_SCALE_MEASUREMENT_CHAR);
break;
default:
mHandler.sendEmptyMessage(MSG_DISMISS);
Log.i(TAG, "All Sensors Enabled");
return;
// Enable local notifications
gatt.setCharacteristicNotification(characteristic, true);
// Enabled remote notifications
BluetoothGattDescriptor desc = characteristic.getDescriptor(CONFIG_DESCRIPTOR);
desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(desc);
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
Log.i(TAG, "Connection State Change: " + status + " -> " + connectionState(newState));
if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED)
/*
* Once successfully connected, we must next discover all the
* services on the device before we can read and write their
* characteristics.
*/
gatt.discoverServices();
mHandler.sendMessage(Message.obtain(null, MSG_PROGRESS, "Discovering Services..."));
else if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_DISCONNECTED)
/*
* If at any point we disconnect, send a message to clear the
* weather values out of the UI
*/
mHandler.sendEmptyMessage(MSG_CLEAR);
else if (status != BluetoothGatt.GATT_SUCCESS)
/*
* If there is a failure at any stage, simply disconnect
*/
gatt.disconnect();
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status)
Log.i(TAG, "Services Discovered: " + status);
if (status == BluetoothGatt.GATT_SUCCESS)
Log.i(TAG, "No of services discovered: " + gatt.getServices().size());
mHandler.sendMessage(Message.obtain(null, MSG_PROGRESS, "No of services discovered: " + gatt.getServices().size()));
List<BluetoothGattService> services = gatt.getServices();
for (BluetoothGattService bluetoothGattService : services)
UUID uuid = bluetoothGattService.getUuid();
Log.e(TAG, ""+uuid.toString());
List<BluetoothGattCharacteristic> characteristics = bluetoothGattService.getCharacteristics();
for (BluetoothGattCharacteristic bluetoothGattCharacteristic : characteristics)
UUID uuidC = bluetoothGattCharacteristic.getUuid();
Log.i(TAG, "Gatt Properties : "+bluetoothGattCharacteristic.getProperties());
Log.i(TAG, ""+uuidC.toString());
CharacteristicHelper helper = new CharacteristicHelper(bluetoothGattCharacteristic);
Log.i(TAG, "isRead : "+helper.isRead());
Log.i(TAG, "isWrite : "+helper.isWrite());
Log.i(TAG, "isNotify : "+helper.isNotify());
Log.i(TAG, "isWriteNoResponse : "+helper.isWriteNoResponse());
// mHandler.sendMessage(Message.obtain(null, MSG_PROGRESS,
// "Enabling Sensors..."));
/*
* With services discovered, we are going to reset our state machine
* and start working through the sensors we need to enable
*/
reset();
enableNextSensor(gatt);
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
Log.i(TAG, "onCharacteristicRead");
// For each read, pass the data up to the UI thread to update the
// display
/**methodToUpdateUI().*/
// After reading the initial value, next we enable notifications
setNotifyNextSensor(gatt);
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
Log.i(TAG, "onCharacteristicWrite");
// After writing the enable flag, next we read the initial value
readNextSensor(gatt);
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)
Log.i(TAG, "onCharacteristicChanged");
/*
* After notifications are enabled, all updates from the device on
* characteristic value changes will be posted here. Similar to
* read, we hand these up to the UI thread to update the display.
*/
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status)
Log.i(TAG, "onDescriptorWrite");
// Once notifications are enabled, we move to the next sensor and
// start over with enable
advance();
enableNextSensor(gatt);
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status)
Log.i(TAG, "Remote RSSI: " + rssi);
private String connectionState(int status)
switch (status)
case BluetoothProfile.STATE_CONNECTED:
return "Connected";
case BluetoothProfile.STATE_DISCONNECTED:
return "Disconnected";
case BluetoothProfile.STATE_CONNECTING:
return "Connecting";
case BluetoothProfile.STATE_DISCONNECTING:
return "Disconnecting";
default:
return String.valueOf(status);
;
/*
* We have a Handler to process event results on the main thread
*/
private static final int MSG_PROGRESS = 201;
private static final int MSG_DISMISS = 202;
private static final int MSG_CLEAR = 301;
private Handler mHandler = new Handler()
@SuppressLint("NewApi")
@Override
public void handleMessage(Message msg)
BluetoothGattCharacteristic characteristic;
switch (msg.what)
case MSG_PROGRESS:
tvMessage.setText((String) msg.obj);
break;
case MSG_DISMISS:
tvMessage.setText("Service Enabled");
break;
case MSG_CLEAR:
tvMessage.setText("");
break;
;
在我的活动中,首先我正在扫描所有可用的设备并准备 ListView。单击列表项时,我连接到该特定设备。当设备的状态变为已连接时,我会发现服务。我有设备服务及其特征的 UUID。但我不确定如何写入任何特定特征或启用或从中读取数据。 虽然我已经尝试过这个东西,但我没有看到任何成功。
如果有人对此有任何想法,请帮助我。
【问题讨论】:
阅读文档:developer.android.com/guide/topics/connectivity/… 确保您设置的移动设备的 UUID 与固件设备的 UUID 相同。固件设备也启用通知功能。 你有什么解决办法吗? 你可以在How to Answer[1] [1]:***.com/a/32496316/3343174找到你需要的东西 【参考方案1】:有一个需要我使用的设备
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE)
而不是
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
如本问题所述
Android BLE API: GATT Notification not received
【讨论】:
这是不重要的事情......重要的区别是 INDICATION 与 NOTIFICATION 取决于设备 非常感谢,在 Android 版本 21 上启用指示值帮助我获得了 onCharacteristicChanged 回调。以前,我开发了一个在没有明确启用指示值的情况下运行良好的应用程序,但该应用程序适用于较新的 Android 操作系统版本。似乎“旧”版本需要不同的设置,例如通过描述符启用指示/通知。【参考方案2】:参考开发者门户提供的示例应用程序“bluetoothlegatt”的源代码。
样品服务: http://developer.android.com/samples/BluetoothLeGatt/src/com.example.android.bluetoothlegatt/BluetoothLeService.html
使用服务: http://developer.android.com/samples/BluetoothLeGatt/src/com.example.android.bluetoothlegatt/DeviceControlActivity.html
此示例包含具有读取和通知属性的特征。所以你肯定会找到你的解决方案。请转到以下代码部分:(您可能会弄清楚)
public void readCharacteristic(BluetoothGattCharacteristic characteristic)
if (mBluetoothAdapter == null || mBluetoothGatt == null)
Log.w(TAG, "BluetoothAdapter not initialized");
return;
mBluetoothGatt.readCharacteristic(characteristic);
【讨论】:
【参考方案3】:您必须区分值是通知还是指示,并使用descriptor.setValue 设置相应的值。如果设置错误,将无法获取值。
【讨论】:
【参考方案4】:这个对我有用:
要通知主设备某些特性发生变化,请在您的外设上调用此函数:
private BluetoothGattServer server;
//init....
//on BluetoothGattServerCallback...
//call this after change the characteristic
server.notifyCharacteristicChanged(device, characteristic, false);
在您的主设备中:发现服务后启用 setCharacteristicNotification:
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status)
super.onServicesDiscovered(gatt, status);
services = mGatt.getServices();
for(BluetoothGattService service : services)
if( service.getUuid().equals(SERVICE_UUID))
characteristicData = service.getCharacteristic(CHAR_UUID);
for (BluetoothGattDescriptor descriptor : characteristicData.getDescriptors())
descriptor.setValue( BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
mGatt.writeDescriptor(descriptor);
gatt.setCharacteristicNotification(characteristicData, true);
if (dialog.isShowing())
mHandler.post(new Runnable()
@Override
public void run()
dialog.hide();
);
现在您可以检查您的特征值是否发生变化,例如 onCharacteristicRead 函数:
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
Log.i("onCharacteristicRead", characteristic.toString());
byte[] value=characteristic.getValue();
String v = new String(value);
Log.i("onCharacteristicRead", "Value: " + v);
【讨论】:
以上是关于获得蓝牙低功耗 (BLE) 设备通知的步骤是啥?的主要内容,如果未能解决你的问题,请参考以下文章
Android BLE低功耗蓝牙开发极简系列(二)之读写操作