Android BLE 无法从设备接收 Gatt 特性通知
Posted
技术标签:
【中文标题】Android BLE 无法从设备接收 Gatt 特性通知【英文标题】:Android BLE can't receive Gatt Characteristic notification from device 【发布时间】:2014-04-29 14:34:51 【问题描述】:当我在特性上写入值时,我试图从设备接收通知,但我没有收到任何东西。我启用了关于特性的通知,然后我写了值。我已经看到设备中的特性已更改其值,但我无法收到通知。这是我的代码:
设备活动:
public class DevicesActivity extends Activity
private BLEService mBluetoothLeService;
private String mDeviceAddress;
private boolean mConnected = false;
private BluetoothGattCharacteristic mNotifyCharacteristic;
private final ServiceConnection mServiceConnection = new ServiceConnection()
@Override
public void onServiceConnected(ComponentName componentName, IBinder service)
mBluetoothLeService = ((BLEService.LocalBinder) service).getService();
if (!mBluetoothLeService.initialize())
finish();
mBluetoothLeService.context = DevicesActivity.this;
mBluetoothLeService.connect(mDeviceAddress);
@Override
public void onServiceDisconnected(ComponentName componentName)
mBluetoothLeService = null;
;
private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver()
@Override
public void onReceive(Context context, Intent intent)
final String action = intent.getAction();
if (BLEService.ACTION_GATT_CONNECTED.equals(action))
mConnected = true;
invalidateOptionsMenu();
else if (BLEService.ACTION_GATT_DISCONNECTED.equals(action))
mConnected = false;
invalidateOptionsMenu();
else if (BLEService.ACTION_GATT_SERVICES_DISCOVERED.equals(action))
List<BluetoothGattService> servicesList;
servicesList = mBluetoothLeService.getSupportedGattServices();
Iterator<BluetoothGattService> iter = servicesList.iterator();
while (iter.hasNext())
BluetoothGattService bService = (BluetoothGattService) iter.next();
if (bService.getUuid().toString().equals(BLEUUID.SERVICE))
mService = bService;
else if (BLEService.ACTION_DATA_AVAILABLE.equals(action))
displayData(intent.getStringExtra(BLEService.EXTRA_DATA));
;
private static final String TAG = "BLEDevice";
public static final String EXTRA_BLUETOOTH_DEVICE = "BT_DEVICE";
private BluetoothAdapter mBTAdapter;
private BluetoothDevice mDevice;
private BluetoothGatt mConnGatt;
private int mStatus;
BluetoothGattService mService;
private EditText pinTxt;
private Button cancelBtn;
private Button unlockBtn;
private Button changePinBtn;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.activity_devices);
pinTxt = (EditText) findViewById(R.id.pin_txt);
cancelBtn = (Button) findViewById(R.id.cancel_btn);
unlockBtn = (Button) findViewById(R.id.unlock_btn);
changePinBtn = (Button) findViewById(R.id.change_pin_btn);
unlockBtn.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View view)
String aux = pinTxt.getText().toString();
mBluetoothLeService.sendCharacteristic(aux, getBTDeviceExtra());
);
mDevice = getBTDeviceExtra();
mDeviceAddress = mDevice.getAddress();
if (mServiceConnection == null)
Log.v("NULL", "mServiceConnection NULL");
Intent gattServiceIntent = new Intent(this, BLEService.class);
if (gattServiceIntent==null)
Log.v("NULL", "mServiceConnection NULL");
bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
mStatus = BluetoothProfile.STATE_DISCONNECTED;
private void displayData(String data)
if (data != null)
Toast.makeText(DevicesActivity.this, data, Toast.LENGTH_LONG);
@Override
protected void onDestroy()
super.onDestroy();
unbindService(mServiceConnection);
mBluetoothLeService = null;
@Override
protected void onPause()
super.onPause();
unregisterReceiver(mGattUpdateReceiver);
private static IntentFilter makeGattUpdateIntentFilter()
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BLEService.ACTION_GATT_CONNECTED);
intentFilter.addAction(BLEService.ACTION_GATT_DISCONNECTED);
intentFilter.addAction(BLEService.ACTION_GATT_SERVICES_DISCOVERED);
intentFilter.addAction(BLEService.ACTION_DATA_AVAILABLE);
return intentFilter;
@Override
protected void onResume()
super.onResume();
registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
if (mBluetoothLeService != null)
final boolean result = mBluetoothLeService.connect(mDeviceAddress);
private BluetoothDevice getBTDeviceExtra()
Intent intent = getIntent();
if (intent
== null) return null;
Bundle extras = intent.getExtras();
if (extras == null)
return null;
BluetoothDevice aux = extras.getParcelable(EXTRA_BLUETOOTH_DEVICE);
return aux;
BLE服务:
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback()
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
String intentAction;
if (newState == BluetoothProfile.STATE_CONNECTED)
intentAction = ACTION_GATT_CONNECTED;
mConnectionState = STATE_CONNECTED;
broadcastUpdate(intentAction);
Log.i(TAG, "Connected to GATT server.");
// Attempts to discover services after successful connection.
Log.i(TAG, "Attempting to start service discovery:" +
mBluetoothGatt.discoverServices());
else if (newState == BluetoothProfile.STATE_DISCONNECTED)
intentAction = ACTION_GATT_DISCONNECTED;
mConnectionState = STATE_DISCONNECTED;
Log.i(TAG, "Disconnected from GATT server.");
broadcastUpdate(intentAction);
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status)
if (status == BluetoothGatt.GATT_SUCCESS)
for (BluetoothGattService service : gatt.getServices())
if ((service == null) || (service.getUuid() == null))
continue;
if (BLEUUID.SERVICE.equalsIgnoreCase(service.getUuid().toString()))
mService = service;
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
else
Log.w(TAG, "onServicesDiscovered received: " + status);
@Override
public void onCharacteristicRead(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic,int status)
if (status == BluetoothGatt.GATT_SUCCESS)
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
broadcastUpdate(EXTRA_DATA, characteristic);
Log.i("CARAC","CARACTERISTICA LEIDA onCharacteristicRead()");
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic)
readCharacteristic(characteristic);
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
Log.i("CARAC","CAMBIO EN CARACTERISTICA");
;
private void broadcastUpdate(final String action)
final Intent intent = new Intent(action);
sendBroadcast(intent);
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
private void broadcastUpdate(final String action,final BluetoothGattCharacteristic characteristic)
final Intent intent = new Intent(action);
if (PIN_CHARACTERISTIC.equals(characteristic.getUuid()))
final String pin = characteristic.getStringValue(0);
intent.putExtra(EXTRA_DATA, String.valueOf(pin));
else
// For all other profiles, writes the data formatted in HEX.
final byte[] data = characteristic.getValue();
if (data != null && data.length > 0)
final StringBuilder stringBuilder = new StringBuilder(data.length);
for(byte byteChar : data)
stringBuilder.append(String.format("%02X ", byteChar));
Log.i("RECIBIDO", "RECIBIDOS DATOS");
intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString());
sendBroadcast(intent);
public class LocalBinder extends Binder
BLEService getService()
return BLEService.this;
@Override
public IBinder onBind(Intent intent)
return mBinder;
@Override
public boolean onUnbind(Intent intent)
// After using a given device, you should make sure that BluetoothGatt.close() is called
// such that resources are cleaned up properly. In this particular example, close() is
// invoked when the UI is disconnected from the Service.
close();
return super.onUnbind(intent);
private final IBinder mBinder = new LocalBinder();
public boolean initialize()
if (mBluetoothManager == null)
mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager == null)
return false;
mBluetoothAdapter = mBluetoothManager.getAdapter();
if (mBluetoothAdapter == null)
return false;
return true;
public boolean connect(final String address)
if (mBluetoothAdapter == null || address == null)
return false;
// Previously connected device. Try to reconnect.
if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
&& mBluetoothGatt != null)
if (mBluetoothGatt.connect())
mConnectionState = STATE_CONNECTING;
return true;
else
return false;
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
if (device == null)
return false;
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
mBluetoothDeviceAddress = address;
mConnectionState = STATE_CONNECTING;
return true;
public void disconnect()
if (mBluetoothAdapter == null || mBluetoothGatt == null)
return;
mBluetoothGatt.disconnect();
public void close()
if (mBluetoothGatt == null)
return;
mBluetoothGatt.close();
mBluetoothGatt = null;
Log.e("CIERRE", "CONEXION CERRADA");
public void readCharacteristic(BluetoothGattCharacteristic characteristic)
if (mBluetoothAdapter == null || mBluetoothGatt == null)
return;
mBluetoothGatt.readCharacteristic(characteristic);
Log.i("READ", "CARACTERISTICA LEIDA");
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled)
if (mBluetoothAdapter == null || mBluetoothGatt == null)
return;
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
if (PIN_CHARACTERISTIC.equals(characteristic.getUuid()))
BluetoothGattDescriptor descriptor =
new BluetoothGattDescriptor(UUID.nameUUIDFromBytes(BLEUUID.PIN_CHARACTERISTIC_CONFIG_DESCRIPTOR.getBytes()),
BluetoothGattDescriptor.PERMISSION_WRITE_SIGNED);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
public List<BluetoothGattService> getSupportedGattServices()
if (mBluetoothGatt == null) return null;
return mBluetoothGatt.getServices();
public void sendCharacteristic(String pin, BluetoothDevice device)
byte[] pinByte = pin.getBytes();
int pinInt = Integer.valueOf(pin);
BluetoothGattCharacteristic ch = (BluetoothGattCharacteristic) mService.getCharacteristic(UUID
.fromString(BLEUUID.PIN_CHARACTERISTIC_UUID));
ch.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
ch.setValue(pin);
Toast.makeText(context, "CARACTERISTICA ASIGNADA", Toast.LENGTH_SHORT).show();
connect(device.getAddress());
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
setCharacteristicNotification(ch, true);
if (mBluetoothGatt.writeCharacteristic(ch))
Toast.makeText(context, "CARACTERISTICA ESCRITA", Toast.LENGTH_SHORT).show();
BLEUUID
public class BLEUUID
// CARACTERISTICA PIN
public static final String SERVICE ="0000fff0-0000-1000-8000-00805f9b34fb";
public static final String PIN_CHARACTERISTIC_UUID="0000fff9-0000-1000-8000-00805f9b34fb";
public static final String PIN_CHARACTERISTIC_CONFIG_DESCRIPTOR="0x2902";
private static HashMap<String, String> attributes = new HashMap();
static
attributes.put(SERVICE, "Service");
attributes.put(PIN_CHARACTERISTIC_UUID, "Pin");
当我调试时,onCharacteristicChange() 永远不会执行。
有人知道问题出在哪里
【问题讨论】:
我不确定自己编写特性是否算作应该被通知的事情,这不是正常的用例。特性本身的变化通常是您收到的通知。例如。它代表温度或其他变化的测量值,以便您获得更新。 我发送 PIN 码,设备必须向我发送“OKAY”或“FAIL”。在 nRF Master Control 应用程序中,我看到发送 PIN 时的值变成“OKAY”,但我试图捕捉这个值但没有成功。 你确定特征的属性是notify
吗?可能是indicate
。这几乎是同一件事,但事实并非如此。为此您需要设置:descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
检查您的设备规格
是的,该属性已通知,尽管我也尝试使用 ENABLE_INDICATION_VALUE;它也没有工作。
我注意到android.bluetooth.BluetoothGattCharacteristic.getStringValue()
可能会崩溃。我正在开发的应用程序不会崩溃,但getStringValue()
后面的代码永远不会执行。我通过在它周围包裹 trycatch(Exception e) 来修复它。 见:BluetoothGatt﹕ Unhandled exception in callback java.lang.NullPointerException at android.bluetooth.BluetoothGattCharacteristic.getStringValue(BluetoothGattCharacteristic.java:506)
【参考方案1】:
以下是在 Android 上使用 BLE 的一般模式:
-
您尝试连接
您会收到一个回调,表明它已连接
您发现服务
你被告知服务被发现
你得到了特征
对于每个特征,您都会获得描述符
对于描述符,您将其设置为使用 BluetoothGattDescriptor.setValue() 启用通知/指示
您使用 BluetoothGatt.writeDescriptor() 编写描述符
您可以使用 BluetoothGatt.setCharacteristicNotification() 在本地启用特征通知。没有这个,您将不会被回调。
您会收到描述符已写入的通知
现在您可以将数据写入特征。在将任何内容写入任何特征之前,所有特征和描述符配置都已完成。
【讨论】:
我必须写入设备然后设备会广播一些数据。为此,我写了一个服务的特征。我通过接收器获得了一些数据。但这不是我需要的数据。设备将执行一些任务,然后它将广播数据。为此,我认为该服务的任何特征都将广播所需的数据。我正走在正确的道路上。实际上在通知集上我没有得到任何数据。请建议 @Ajit,我不确定你在问什么,但听起来你没有正确设置特征通知。我添加了新的第 9 步,并更新了第 7 步和第 8 步,使其更加清晰。 我明白了,但特点是有写属性,没有通知属性。 请查看***.com/questions/26800606/… 谢谢,你拯救了我的夜晚。以上是关于Android BLE 无法从设备接收 Gatt 特性通知的主要内容,如果未能解决你的问题,请参考以下文章
samsung ble api 无法从多个 GATT 特征中获取通知