onCharacteristicChanged 未被击中。 Android蓝牙

onCharacteristicChanged not being hit. Android BLE

我有一个应用程序正在使用 BLE 连接我的设备。从连接到设备、发现服务、编写特征和描述符,一切正常。但由于某种原因,onCharacteristicChanged 没有触发。目标是从特征中的特征检索数据。我尝试使用来自同一服务的不同特征,这与检索数据一样有效。不确定为什么这个特定的特性在其他特性起作用时不起作用。

这是我的代码: //描述符 public 最终静态 UUID UUID_CLIENT_CHARACTERISTIC_CONFIG = UUID.fromString(GattAttributes.CLIENT_CHARACTERISTIC_CONFIG);

// Huneo Service
public final static UUID UUID_HUNEO_SERVICE = UUID.fromString(GattAttributes.HUNEO_SERVICE);
public final static UUID UUID_HUNEO_VIBRATOR = UUID.fromString(GattAttributes.HUNEO_VIBRATOR);
public final static UUID UUID_HUNEO_MONITOR = UUID.fromString(GattAttributes.HUNEO_MONITOR);
public final static UUID UUID_HUNEO_RATES = UUID.fromString(GattAttributes.HUNEO_RATES);
public final static UUID UUID_HUNEO_LED = UUID.fromString(GattAttributes.HUNEO_LED);
public final static UUID UUID_HUNEO_ACCEL = UUID.fromString(GattAttributes.HUNEO_ACCEL);
public final static UUID UUID_HUNEO_GYRO = UUID.fromString(GattAttributes.HUNEO_GYRO);
public final static UUID UUID_HUNEO_COMBINED = UUID.fromString(GattAttributes.HUNEO_COMBINED);

// Battery Service
public final static UUID UUID_BATTERY_SERVICE = UUID.fromString(GattAttributes.BATTERY_SERVICE);
public final static UUID UUID_BATTERY_LEVEL = UUID.fromString(GattAttributes.BATTERY_LEVEL);

// Device Info Service
public final static UUID UUID_DEVICE_INFO_SERVICE = UUID.fromString(GattAttributes.DEVICE_INFO_SERVICE);
public final static UUID UUID_MODEL_NUMBER = UUID.fromString(GattAttributes.MODEL_NUMBER);
public final static UUID UUID_SERIAL_NUMBER = UUID.fromString(GattAttributes.SERIAL_NUMBER);
public final static UUID UUID_FIRMWARE_REV = UUID.fromString(GattAttributes.FIRMWARE_REV);
public final static UUID UUID_HARDWARE_REV = UUID.fromString(GattAttributes.HARDWARE_REV);
public final static UUID UUID_MANUFACTURER_NAME = UUID.fromString(GattAttributes.MANUFACTURER_NAME);

private final static String TAG = "UNITY";

private static final int STATE_DISCONNECTED = 0;
private static final int STATE_CONNECTING = 1;
private static final int STATE_CONNECTED = 2;
private final BluetoothDevice mDevice;
private final IBinder mBinder = new LocalBinder();

private TaskCompletionSource<Void> mConnectionTask = new TaskCompletionSource<>();
private TaskCompletionSource<String> mFirmwareRevTask = new TaskCompletionSource<>();
private TaskCompletionSource<Void> mServicesDiscovered = new TaskCompletionSource<>();
private TaskCompletionSource<Integer> mBatteryLevelTask = new TaskCompletionSource<>();
private TaskCompletionSource<SensorValues> mStartStreamingTask = new TaskCompletionSource<>();
private TaskCompletionSource<String> mSerialNumberTask = new TaskCompletionSource<>();

private SensorValues mSensorValues = new SensorValues();
private BluetoothGatt mBluetoothGatt;
private int mConnectionState = STATE_DISCONNECTED;
private boolean mIsStreaming = false;

// Implements callback methods for GATT events that the app cares about.  For example,
// connection change and services discovered.
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            mConnectionState = STATE_CONNECTED;
            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) {
            mConnectionState = STATE_DISCONNECTED;
            mIsStreaming = false;
            Log.i(TAG, "Disconnected from GATT server.");
        }
    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            mServicesDiscovered.setResult(null);
            startStreaming();
            Log.w(TAG, "onServicesDiscovered success.");
        } else {
            Log.w(TAG, "onServicesDiscovered received: " + status);
        }
    }

    @Override
    public void onCharacteristicRead(BluetoothGatt gatt,
                                     BluetoothGattCharacteristic characteristic,
                                     int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            String characteristicUuid = characteristic.getUuid().toString();
            if (characteristicUuid.equals(GattAttributes.FIRMWARE_REV)) {
                String firmwareRev = characteristic.getStringValue(0);
                mFirmwareRevTask.setResult(firmwareRev);
            }

            if (characteristicUuid.equals(GattAttributes.BATTERY_LEVEL)) {
                int batteryLevel = characteristic.getIntValue(FORMAT_UINT8, 0);
                mBatteryLevelTask.setResult(batteryLevel);
            }

            if (characteristicUuid.equals(GattAttributes.SERIAL_NUMBER)) {
                String serialNumber = characteristic.getStringValue(0);
                mSerialNumberTask.setResult(serialNumber);
            }
        }
    }

    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt,
                                      BluetoothGattCharacteristic characteristic,
                                      int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            if (characteristic.getUuid().equals(UUID_HUNEO_MONITOR)) {
                if (characteristic.getIntValue(FORMAT_UINT8, 0) == 1) {
                    startCombined();
                } else {
                    stopCombined();
                }
            }
        }
    }

    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt,
                                        BluetoothGattCharacteristic characteristic) {
        String characteristicUuid = characteristic.getUuid().toString();
        //Combined value changed
        if (characteristicUuid.equals(GattAttributes.HUNEO_COMBINED)) {
            byte[] data = characteristic.getValue();
            int id = unsignedShortToInt(data[0], data[1]);
            int x = unsignedShortToInt(data[2], data[3]);
            int y = unsignedShortToInt(data[4], data[5]);
            int z = unsignedShortToInt(data[6], data[7]);

            mSensorValues.combined = new CartesianValues(id,x,y,z);
            mStartStreamingTask.setResult(mSensorValues);
        }
    }

    @Override
    public void onDescriptorWrite(BluetoothGatt gatt,
                                  BluetoothGattDescriptor descriptor,
                                  int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            //Combined
            if (descriptor.getCharacteristic().getUuid().equals(UUID_HUNEO_COMBINED)) {
                mIsStreaming = Arrays.equals(descriptor.getValue(), BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                mStartStreamingTask.setResult(null);
            }
        }
    }
};

public AlwaysOnHuneoBoard(BluetoothDevice device) {
    this.mDevice = device;
}

@Nullable
@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);
}

public static class LocalBinder extends Binder {

}

/**
 * Connect to the device
 *
 * @param ct CancellationToken that will make task return if cancelled
 * @return when task is complete the device is connected
 */
public Task<Void> connectAsync(CancellationToken ct) {
    if (ct.isCancellationRequested()) {
        Log.w(TAG, "Connection cancelled");
        mConnectionTask.setCancelled();
        return mConnectionTask.getTask();
    }

    if (mDevice == null) {
        Log.w(TAG, "BluetoothDevice not initialized");
        mConnectionTask.setError(new Exception("BluetoothDevice not initialized"));
        return mConnectionTask.getTask();
    }

    // Previously connected device. BluetoothGatt not closed. Try to reconnect.
    if (mBluetoothGatt != null) {
        Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
        if (mBluetoothGatt.connect()) {
            mConnectionState = STATE_CONNECTING;
            mConnectionTask.setResult(null);
            return mConnectionTask.getTask();
        }
    }

    mBluetoothGatt = mDevice.connectGatt(this, false, mGattCallback);
    Log.d(TAG, "Trying to create a new connection.");
    mConnectionState = STATE_CONNECTING;
    mConnectionTask.setResult(null);
    return mConnectionTask.getTask();
}

/**
 * Disconnects an existing connection or cancel a pending connection. The disconnection result
 * is reported asynchronously through the
 * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
 * callback.
 */
public void disconnect() {
    if (mDevice == null || mBluetoothGatt == null) {
        Log.w(TAG, "BluetoothDevice not initialized");
        return;
    }
    mBluetoothGatt.disconnect();
}

/**
 * After using a given BLE device, the app must call this method to ensure resources are
 * released properly.
 */
public void close() {
    if (mBluetoothGatt == null) {
        return;
    }
    mBluetoothGatt.close();
    mBluetoothGatt = null;
}

/**
 * Determines if connected to the BLE device.
 *
 * @return true if connected, false otherwise.
 */
public boolean isConnected() {
    return mConnectionState == STATE_CONNECTED;
}

/**
 * Get the calibration for the sensor
 *
 * @return integer representation of the calibration
 */
public int getCalibration() {
    return 100;
}

/**
 * Get the firmware version of the sensor
 *
 * @return string representing the devices firmware version
 */
public String getFirmwareVersion() {
    try {
        // Wait for services to be discovered
        mServicesDiscovered.getTask().onSuccessTask(t -> {
            BluetoothGattService deviceInfoService = mBluetoothGatt.getService(UUID_DEVICE_INFO_SERVICE);
            BluetoothGattCharacteristic characteristic = deviceInfoService.getCharacteristic(UUID_FIRMWARE_REV);
            mBluetoothGatt.readCharacteristic(characteristic);
            mFirmwareRevTask.setResult(null);
            return mFirmwareRevTask.getTask();

            // Wait for deviceRevTask to be completed
        }).waitForCompletion(10, TimeUnit.SECONDS);

    } catch (InterruptedException e) {
        return "";
    }

    return mFirmwareRevTask.getTask().getResult();
}

/**
 * Get the device mac address
 *
 * @return string representation of the mac address
 */
public String getMacAddress() {
    return mDevice.getAddress();
}

/**
 * Get the model of the device
 *
 * @return string representation of the device model
 */
public String getModel() {
    return mDevice.getName();
}

public SensorValues getCombinedSensorValues() {
    if (!isConnected()) {
        Log.i("Unity", "Sensor disconnected, using default sensor values.");
        return mSensorValues;
    }
    if (!mIsStreaming) {
        Log.i("Unity", "Sensor not streaming, using default sensor values.");
        return mSensorValues;
    }
    try {
        mStartStreamingTask.getTask().waitForCompletion();
    } catch (InterruptedException ex) {
        ex.printStackTrace();
        disconnect();
    }
    return mSensorValues;
}

/**
 * Read the current battery level from the device
 *
 * @return Task containing the sensors battery level as an integer
 */
public Task<Integer> getBatteryLevelAsync() {
    // Wait for services to be discovered
    return mServicesDiscovered.getTask().onSuccessTask(t -> {
        BluetoothGattService deviceInfoService = mBluetoothGatt.getService(UUID_BATTERY_SERVICE);
        BluetoothGattCharacteristic characteristic = deviceInfoService.getCharacteristic(UUID_BATTERY_LEVEL);
        mBluetoothGatt.readCharacteristic(characteristic);
        return mBatteryLevelTask.getTask();
    });
}

public Task<String> getSerialNumber() {
    return mServicesDiscovered.getTask().onSuccessTask(t -> {
        BluetoothGattService deviceInfoService = mBluetoothGatt.getService(UUID_DEVICE_INFO_SERVICE);
        BluetoothGattCharacteristic characteristic = deviceInfoService.getCharacteristic(UUID_SERIAL_NUMBER);
        mBluetoothGatt.readCharacteristic(characteristic);
        return mSerialNumberTask.getTask();
    });
}
/**
 * Issue command to sensor to begin streaming combined data
 */
public void startStreaming() {
    if (mIsStreaming) {
        return;
    }
    //Enable notifications for combined, set result
    //Write 0 to Monitor Characteristic
    BluetoothGattService huneoService = mBluetoothGatt.getService(UUID_HUNEO_SERVICE);
    BluetoothGattCharacteristic monitorCharacteristic = huneoService.getCharacteristic(UUID_HUNEO_MONITOR);
    monitorCharacteristic.setValue(1, FORMAT_UINT8, 0);
    mBluetoothGatt.writeCharacteristic(monitorCharacteristic);
}

/**
 * Issue command to sensor to stop streaming accel and gyro data
 */
public void stopStreaming() {
    if (!mIsStreaming) {
        return;
    }
    //Disable notifications for combined
    BluetoothGattService huneoService = mBluetoothGatt.getService(UUID_HUNEO_SERVICE);
    BluetoothGattCharacteristic monitorCharacteristic = huneoService.getCharacteristic(UUID_HUNEO_MONITOR);
    monitorCharacteristic.setValue(0, FORMAT_UINT8, 0);
    mBluetoothGatt.writeCharacteristic(monitorCharacteristic);
}

/**
 * Trigger vibration haptics on the sensor
 */
public void triggerHaptics() {
    mServicesDiscovered.getTask().onSuccessTask(t -> {
        BluetoothGattService huneoService = mBluetoothGatt.getService(UUID_HUNEO_SERVICE);

        // Set vibration characteristic to 1
        BluetoothGattCharacteristic characteristic = huneoService.getCharacteristic(UUID_HUNEO_VIBRATOR);
        characteristic.setValue(0, FORMAT_UINT8, 0);
        mBluetoothGatt.writeCharacteristic(characteristic);

        return t;
    });
}

//Start Combined Notifications
private void startCombined() {
    mServicesDiscovered.getTask().onSuccessTask(t -> {
        BluetoothGattService huneoService = mBluetoothGatt.getService(UUID_HUNEO_SERVICE);

        // Set Combined characteristic to send notifications
        BluetoothGattCharacteristic combinedCharacteristic = huneoService.getCharacteristic(UUID_HUNEO_COMBINED);
        mBluetoothGatt.setCharacteristicNotification(combinedCharacteristic, true);

        //Set Combined Client Characteristic Config Descriptor to enable notifications
        BluetoothGattDescriptor combinedDescriptor = combinedCharacteristic.getDescriptor(UUID.fromString(GattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
        combinedDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
        mBluetoothGatt.writeDescriptor(combinedDescriptor);

        return t;
    });
}

//Stop Combined notifications
private void stopCombined() {
    mServicesDiscovered.getTask().onSuccessTask(t -> {
        BluetoothGattService huneoService = mBluetoothGatt.getService(UUID_HUNEO_SERVICE);

        // Set Accelerometer characteristic to send notifications
        BluetoothGattCharacteristic combinedCharacteristic = huneoService.getCharacteristic(UUID_HUNEO_COMBINED);
        mBluetoothGatt.setCharacteristicNotification(combinedCharacteristic, false);

        // Set Accelerometer Client Characteristic Config Descriptor to enable notifications
        BluetoothGattDescriptor combinedDescriptor = combinedCharacteristic.getDescriptor(UUID.fromString(GattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
        combinedDescriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
        mBluetoothGatt.writeDescriptor(combinedDescriptor);

        return t;
    });
}

// Convert two bytes into a short
private int unsignedShortToInt(byte firstByte, byte secondByte) {
    ByteBuffer bb = ByteBuffer.allocate(2);
    bb.order(ByteOrder.LITTLE_ENDIAN);
    bb.put(firstByte);
    bb.put(secondByte);
    short shortVal = bb.getShort(0);
    return shortVal >= 0 ? shortVal : 0x10000 + shortVal;
}

}

希望有人能帮助我。我没主意了。

找到解决方案。第三方忘记提到我必须设置正确的值才能启用该特性。我试过了,它奏效了!终于得到了数据值。