将数据写入 Android 中的蓝牙 LE 特性
Posted
技术标签:
【中文标题】将数据写入 Android 中的蓝牙 LE 特性【英文标题】:Writing data to Bluetooth LE characteristic in Android 【发布时间】:2016-07-09 21:46:38 【问题描述】:虽然有人问过类似的问题,但略有不同。我知道如何将数据传递给连接的 BLE 设备,但我认为我做错了什么,需要帮助。 下面的代码包含我的类中扩展 BroadcastReceiver 的所有方法。
-
我扫描并连接到由 `PEN_ADDRESS` 指定的设备。
在 `onServicesDiscovered` 方法中,我查找 `UUID` 包含 `abcd` 的服务。
然后我遍历此服务的特征,并在其“UUID”中查找具有特定字符串的三个特征。
第三个特征是可写特征,我试图通过调用方法写入数据`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);
;
【问题讨论】:
【参考方案1】:虽然 BLE API 本质上是异步的,但实际的信号传输不可避免地是同步的。在开始任何连接/写入/读取操作之前,您必须等待之前的连接/写入/读取调用回调。
在您的代码onServicesDiscovered(BluetoothGatt gatt, int status)
函数中,您在尝试编写特征之前调用了两次mGatt.writeDescriptor(descriptor)
。 API 将拒绝启动您的写入请求,因为它正忙于写入描述符,并为您的 mGatt.writeCharacteristic(charac)
调用返回 false。
所以在调用 writeCharacteristic 之前,只需等待 writeDescriptor 回调即可。这种性质没有很好的记录,但您可以找到一些来源 here 和 here。
【讨论】:
非常感谢@reTs。如果有效,我会尝试并更新。只是一个疑问,我还打算阅读两个特征(请参阅注释行 -gatt.readCharacteristic(readChar1);
和 gatt.readCharacteristic(readChar2);
)。我认为对于每个读/写特性,我需要调用mGatt.writeDescriptor(descriptor)
。我想我理解错了。
@abhinavDAIICT 您尝试将 BluetoothGattDescriptor.ENABLE_INDICATION_VALUE 写入描述符,这与读/写过程完全无关。那几行代码是用来开启特征指示的,完全是另一种传输方式。
当我尝试在 onServicesDiscovered 回调中更新我的所有值并且没有写入特征时,这解决了我的问题,因为我也在写入描述符。【参考方案2】:
感谢@reTs 和@pooja 的建议。问题是由于这两行 mGatt = null
connectBluetooth(mDevice);
在 STATE_DISCONNECTED。我想通了,但不记得确切的原因。发布它以防万一。
【讨论】:
【参考方案3】:您是否检查过“writechar1”传递给 writeCharac() 的 btcharacteristic 值是否为空?
【讨论】:
是的,我做到了,它不为空。以上是关于将数据写入 Android 中的蓝牙 LE 特性的主要内容,如果未能解决你的问题,请参考以下文章