Characteristic.Value from Bluetooth Reading in Swift

Characteristic.value from Bluetooth reading in Swift

It's not super easy.

Weight Measurement

You may need to write something like this:

let data = Data([0x02,0xac,0x08,0xe1,0x07,0x01,0x0a,0x14,0x00,0x29])

struct DataReader {
var data: Data
var offset: Int = 0

mutating func read() -> UInt8 {
let result = data[offset]
offset += MemoryLayout<UInt8>.size
return result
}
mutating func read() -> UInt16 {
let subdata = data.subdata(in: offset..<offset+MemoryLayout<UInt16>.size)
let result: UInt16 = subdata.withUnsafeBytes {(bytes: UnsafePointer<UInt16>) in
bytes.pointee.littleEndian
}
offset += MemoryLayout<UInt16>.size
return result
}
}

typealias BLEUserID = UInt8

struct BLEDateTime {
var year: UInt16
var month: UInt8
var day: UInt8
var hours: UInt8
var minutes: UInt8
var seconds: UInt8
}
extension DataReader {
mutating func read() -> BLEDateTime {
let year: UInt16 = read()
let month: UInt8 = read()
let day: UInt8 = read()
let hours: UInt8 = read()
let minutes: UInt8 = read()
let seconds: UInt8 = read()
return BLEDateTime(year: year, month: month, day: day, hours: hours, minutes: minutes, seconds: seconds)
}
}
struct WeightMeasurement: CustomStringConvertible {
struct Flags: OptionSet {
var rawValue: UInt8
init(rawValue: UInt8) {
self.rawValue = rawValue
}
static let measurementImperial = Flags(rawValue: 1<<0)
static let timeStampPresent = Flags(rawValue: 1<<1)
static let userIDPresent = Flags(rawValue: 1<<2)
static let bmiAndHeightPresent = Flags(rawValue: 1<<3)
}
var isMeasuremntImperial: Bool
var weight: Decimal
var timeStamp: BLEDateTime?
var userID: BLEUserID?
var bmi: Decimal?
var height: Decimal?

var description: String {
return weight.description
}
}
extension DataReader {
mutating func read() -> WeightMeasurement {
let flags = WeightMeasurement.Flags(rawValue: read())
let isMeasuremntImperial = flags.contains(.measurementImperial)
let weight: Decimal
if isMeasuremntImperial {
weight = Decimal(read() as UInt16) * Decimal(string: "0.01")!
} else {
//SI
weight = Decimal(read() as UInt16) * Decimal(string: "0.005")!
}
var timeStamp: BLEDateTime?
if flags.contains(.timeStampPresent) {
timeStamp = read()
}
var userID: BLEUserID?
if flags.contains(.userIDPresent) {
userID = read()
}
var bmi: Decimal?
var height: Decimal?
if flags.contains(.bmiAndHeightPresent) {
bmi = Decimal(read() as UInt16) * Decimal(string: "0.1")!
if isMeasuremntImperial {
height = Decimal(read() as UInt16) * Decimal(string: "0.1")!
} else {
//SI
height = Decimal(read() as UInt16) * Decimal(string: "0.001")!
}
}
return WeightMeasurement(isMeasuremntImperial: isMeasuremntImperial, weight: weight, timeStamp: timeStamp, userID: userID, bmi: bmi, height: height)
}
}

var reader = DataReader(data: data, offset: 0)
let weightMeasurement: WeightMeasurement = reader.read()
print(weightMeasurement) //->11.1
print(weightMeasurement.timeStamp!) //->BLEDateTime(year: 2017, month: 1, day: 10, hours: 20, minutes: 0, seconds: 41)

(Sorry, not fully tested and you may need some modification.)

Reading a BLE Peripheral Characteristic and checking its value?

Updated For Swift3

After you execute that method, the delegate of your peripheral is going to asynchronously receive the peripheral(_:didUpdateValueFor:error:) method. In that method you can query the value of the passed characteristic parameter. value will be an NSData which you can pull the bytes out of. E.g.

// MARK: - CBPeripheralDelegate
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if let e = error {
print("ERROR didUpdateValue \(e)")
return
}
guard let data = characteristic.value else { return }
...
}

The value method actually returns an Optional around the expected Data, so a let guard is the way to go.

Usually a characteristic will have a simple value encoded in it's up-to-20-byte Data payload. E.g. maybe it's a simple UInt16 counter. To

To convert between these Data glumps and meaningful numbers, have a look at the answer to round trip Swift number types to/from Data (I've included my own implementation of that below).

So for example, if you know that the characteristic of interest is some counter that is a meant to be extracted as a UInt16, I would fill out the above example with something like:

// MARK: - CBPeripheralDelegate
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if let e = error {
print("ERROR didUpdateValue \(e)")
return
}
guard let data = characteristic.value else { return }
print("counter is \(UInt16(data:data))")
}

// Data Extensions:
protocol DataConvertible {
init(data:Data)
var data:Data { get }
}

extension DataConvertible {
init(data:Data) {
guard data.count == MemoryLayout<Self>.size else {
fatalError("data size (\(data.count)) != type size (\(MemoryLayout<Self>.size))")
}
self = data.withUnsafeBytes { $0.pointee }
}

var data:Data {
var value = self
return Data(buffer: UnsafeBufferPointer(start: &value, count: 1))
}
}

extension UInt8:DataConvertible {}
extension UInt16:DataConvertible {}
extension UInt32:DataConvertible {}
extension Int32:DataConvertible {}
extension Int64:DataConvertible {}
extension Double:DataConvertible {}
extension Float:DataConvertible {}

Swift get characteristic.value in decimal

The value for org.bluetooth.characteristic.battery_level (0x2A19) seems to be a single uint8 (UInt8 in Swift). And the result of characteristic.value! is Data, which is a Collection of UInt8, and in your case, it contains only one UInt8 value.

Try this:

print(characteristic.value![0])

But, when you need to use other various characteristics in your app (it's a very common scenario for apps using BLE), the linked article shown in leanne's comment would be very useful.

How to get data out of bluetooth characteristic in Swift

This seems like it should be simple enough, but trying to confirm what you're saying. Does “bytes” end up with a value of “16447d03”? It's a pointer to the data, not the data itself, so you need to do something like this to get the actual value:

var data = characteristic.value
var values = [UInt8](count:data.length, repeatedValue:0)
data.getBytes(&values, length:data.length)

In this, “values” is an array containing the actual values.

From a private discussion we had, you listed the output as:

[22, 77, 22, 3] 
[22, 78, 27, 3, 18, 3]
[22, 79, 2, 3]
[22, 78, 15, 3]

The first byte is the flags, which has been 22 in all the cases you've listed. This makes sense as it's all from the same heart rate hardware.

The bits are grouped like this:
| 3 bits are reserved | 1 bit for RR-Interval | 1 bit for Energy Expended Status | 2 bits for Sensor Contact Status | 1 bit for Heart Rate Value Format |
22 is 00010110 in binary, which is | 000 | 1 | 0 | 11 | 0 |.

Heart Rate Value Format bit: 0 (Heart Rate Value Format is set to UINT8)

Sensor Contact Status bits: 3 (Sensor Contact feature is supported and contact is detected)

Energy Expended Status bit: 0 (Energy Expended field is not present)

RR-Interval bit: 1 (One or more RR-Interval values are present)

This means that the following byte is the heart rate (C1 field) and the remaining bytes are RR-Interval values, whatever they are (C4 field).

So for this data the heart rates were 77, 78, 79, 78.

How to write a value to characteristc for BLE Device in ios swift

The information about the PDB is somewhat difficult to decipher but contains good information. It sounds as if the PCBs contain a Bluetooth-to-UART module. One characteristic is for sending data to the PCB, one characteristic is for receiving data from the PCB.

I still don't understand the commands that need to be sent to the PCB. If you have access to the Android code, then you can find the answers there.

Below is the minimal code that could potentially:

let batteryServiceUUID = CBUUID(string: "FFB0")
let rxCharacteristicUUID = CBUUID(string: "FFB1")
let txCharacteristicUUID = CBUUID(string: "FFB2")

var centralManager: CBCentralManager!
var batteryPeripheral: CBPeripheral? = nil
var batteryService: CBService? = nil
var txCharacteristic: CBCharacteristic? = nil
var rxCharacteristic: CBCharacteristic? = nil

func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
centralManager.scanForPeripherals(withServices: [batteryServiceUUID])
}
}

func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
batteryPeripheral = peripheral
batteryPeripheral!.delegate = self
centralManager.stopScan()
centralManager.connect(batteryPeripheral!)
}

func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
batteryPeripheral!.discoverServices([batteryServiceUUID])
}

func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
peripheral.discoverCharacteristics(nil, for: peripheral.services![0])
}

func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
for characteristic in service.characteristics! {
if characteristic.uuid == rxCharacteristicUUID {
rxCharacteristic = characteristic
peripheral.setNotifyValue(true, for: rxCharacteristic!)
} else if (characteristic.uuid == txCharacteristicUUID) {
txCharacteristic = characteristic
}
}
sendInitialCommand()
}

func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if let value = characteristic.value {
print("Data received")
print(value as NSData)
}
}

func sendInitialCommand() {
let cmdBytes: [UInt8] = [0x55, 0xe1, 0x00, 0x0a]
let cmd = Data(cmdBytes)
batteryPeripheral!.writeValue(cmd, for: txCharacteristic!, type: .withoutResponse)
}


Related Topics



Leave a reply



Submit