Characteristic.value from Bluetooth reading in Swift

I’m trying to read and convert into String a reading from a BT device.

<CBCharacteristic: 0x1700a2e80, UUID = 2A9D, properties = 0x20, value = <02ac08e1 07010a14 0029>, notifying = YES>

Based on example I found online I did

  • Sending image file over Bluetooth 4.0 LE
  • How to get data out of bluetooth characteristic in Swift
  • iOS and bluetooth
  • Keep bluetooth sound when initializing AVAudioSession
  • ReadRSSI doesn't call the delegate method
  • Using iOS GameKit's “Bluetooth Bonjour” with other platforms
  • let u16 = (characteristic.value! as NSData).bytes.bindMemory(to: Int.self, capacity: characteristic.value!.count).pointee
    

    but my u16 is null, even though characteristic.value contains

    (lldb) dp characteristic.value! as NSData
    <02ac08e1 07010a14 0029>
    

    Can anyone point me in the right direction?

    Solutions Collect From Internet About “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.)