将数据写入 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 = nullconnectBluetooth(mDevice); 在 STATE_DISCONNECTED。我想通了,但不记得确切的原因。发布它以防万一。

【讨论】:

【参考方案3】:

您是否检查过“writechar1”传递给 writeCharac() 的 btcharacteristic 值是否为空?

【讨论】:

是的,我做到了,它不为空。

以上是关于将数据写入 Android 中的蓝牙 LE 特性的主要内容,如果未能解决你的问题,请参考以下文章

Android 蓝牙 le gatt 特征通知问题

Android 蓝牙 LE 通知的问题

如何在 CoreBluetooth 上同时向多个蓝牙 LE 从站写入数据?

如何将数据传输到连接的蓝牙LE设备

Android蓝牙LE:连接后未发现服务

使用蓝牙 LE“Proximity”配置文件 Android 检测接近度