skip to Main Content

I am writing a code in swift to get data from a BLE device (Omron BP7000). I am able to pair with it and get the other service and read the data. But I am unable to get the data of the blood pressure, in didUpdateValueFor characteristic:. Other delegate of CoreBluetooth have been used and are working as expected. Just when it comes to read/write data to Current time and read data from the Blood pressure characteristic I get the issue. If anyone can post some suggest of how should I proceed will be helpful.

func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
      switch characteristic.uuid {
    
      case batteryLevelCBUUID:
          print("Battery level: (characteristic.value![0])")
          let valueBattery = characteristic.value?[0]
          batteryLabel.text = "Battery Level =" + " " + String(describing: characteristic.value!      [0]) + "%"
          centerView.isHidden = false
      case currentTimeCBUUID:
          peripheral.readValue(for: characteristic)
          let currentDate = Date()
          //           Write the date and time to the characteristic
          writeCurrentTime(to: characteristic, in: peripheral)
          //          print("(characteristic.value as Any) gives Current Time String  ")
          if let dateTime = extractDateTime(from: characteristic) {
              print("Date and Time:", dateTime)
          } else {
              print("Unable to extract date and time from the characteristic.")
          }
       case bloodPressureMeasurementCBUUID:
          decodeBloodPressureMeasurementData(from: characteristic)
          print("(characteristic.value as Any) gives 2A35 ")
       default:
          print("")

}
}

    func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
        if let error = error {
            // Handle write error
            print("Error writing value: (error.localizedDescription)")
        } else {
            // Handle successful write
            print("Value written successfully")

            // Check if the characteristic supports notifications
            if characteristic.properties.contains(.notify) {
                peripheral.setNotifyValue(true, for: characteristic)
            }
        }
    }


    func writeCurrentTime(to characteristic: CBCharacteristic, in peripheral: CBPeripheral) {
        // Get the current date and time
        let currentDate = Date()
        let calendar = Calendar.current
        var components = calendar.dateComponents([.year, .month, .day, .hour, .minute, .second, .weekday, .nanosecond], from: currentDate)
        
        // Create the value to be written
        var value = Data()
        value.append(Data(bytes: &components.year!, count: 2)) // Year (2 bytes)
        value.append(Data(bytes: "", count: 0))
        value.append(UInt8(components.month!)) // Month (1 byte)
        value.append(UInt8(components.day!)) // Day (1 byte)
        value.append(UInt8(components.hour!)) // Hours (1 byte)
        value.append(UInt8(components.minute!)) // Minutes (1 byte)
        value.append(UInt8(components.second!)) // Seconds (1 byte)
        value.append(UInt8(components.weekday!)) // Day of Week (1 byte)
        value.append(contentsOf: components.nanosecond!.bytes(2)) // Fractions of a second (2 bytes)
        
        // Write the value to the Current Time characteristic
        peripheral.writeValue(value, for: characteristic, type: .withResponse)
    }

    
    func decodeBloodPressureMeasurementData(from characteristic: CBCharacteristic) {
        guard let data = characteristic.value else {
            print("No data available for decoding.")
            return
        }

        let flags = data[0] // Flags byte
        let unitFlags = (flags & 0b00000001) // Bit 0 indicates the unit of blood pressure values
        let timestampFlags = (flags & 0b00000010) // Bit 1 indicates the presence of timestamp

        // Decode systolic, diastolic, and mean arterial pressure values
        let systolicPressure = (data[1] + (data[2] << 8))
        let diastolicPressure = (data[3] + (data[4] << 8))
        let meanArterialPressure = (data[5] + (data[6] << 8))

        // Decode pulse rate value
        let pulseRate = (data[7] + (data[8] << 8))


        // Decode unit of blood pressure values
        let unit: String = (unitFlags != 0) ? "kPa" : "mmHg"

        // Process and use the decoded values as needed
        print("Systolic Pressure: (systolicPressure) (unit)")
        print("Diastolic Pressure: (diastolicPressure) (unit)")
        print("Mean Arterial Pressure: (meanArterialPressure) (unit)")
        print("Pulse Rate: (pulseRate) bpm")
    }
    


  [1]: https://i.stack.imgur.com/fYaxj.png

2

Answers


  1. Chosen as BEST ANSWER

    I have found the answer for this issue. I was using Data() and instead of that when I used NSMutableData() it started working and connected to the BLE device (Omron BP 7000). Following is the code for the answer I have used.

    // Define the CurrentTime structure
    struct DateAndTime {
        var year: UInt16
        var month: UInt8
        var day: UInt8
        var hours: UInt8
        var minutes: UInt8
        var seconds: UInt8
    }
    
    struct CurrentTime {
        var dateData : DateAndTime
        var dayOfWeek: UInt8
        var fractions256: UInt8
        var adjustReason: UInt8
    }
    
    
    
    func writeCurrentTime(to characteristic: CBCharacteristic, in peripheral: CBPeripheral){
            // Get the current date and time
            let currentDate = Date()
            let calendar = Calendar.current
            let components = calendar.dateComponents([.year, .month, .day, .hour, .minute, .second, .weekday, .nanosecond], from: currentDate)
            // Send the bytes to the BLE device
            let data = NSMutableData()
            
            let time = DateAndTime(year: UInt16(components.year!), month: (UInt8(components.month!) + 1), day: (UInt8(components.day!)), hours: (UInt8(components.hour!)), minutes: (UInt8(components.minute!)), seconds: (UInt8(components.second!)))
            
            
            // Create an instance of CurrentTime with current date and time
            let currentTime = CurrentTime(dateData: time, dayOfWeek: (components.btDayOfWeek), fractions256: UInt8(components.nanosecond! * 256 / Int(NSEC_PER_SEC)), adjustReason: 0)    // Replace with the actual year
            
            // Convert the CurrentTime structure to Data
            let currentTimeData = withUnsafeBytes(of: currentTime) { Data($0) }
            data.append(currentTimeData)
            // Write the value to the Current Time characteristic
            peripheral.writeValue(data as Data, for: characteristic, type: .withResponse)
        }
    

  2. I’m assuming that your Current Time is part of the Current Time Service. Error code 128 (0x80) indicates "Data field ignored." So you’re either sending a field the device doesn’t support, or you’re sending something out of range. The docs also explain that all values are little-endian.

    The specs note that Current Time may optionally be written (with response) and is not permitted to be written without response. So you’re correct to write with response. Since you’re getting 0x80 rather than a general write failure, I will assume the characteristic is writable on this device.

    So, now turn to the GSS v9, section 3.62 for details on the Current Time characteristic. The structure is 10 octets, an Exact Time 256 (9 octets) followed by an Adjust Reason (1 octet).

    Now, turn to Section 3.90 that defines Exact Time 256. It is a Day Date Time struct (section 3.71) followed by a Factions256 uint8.

    You get the idea. Go step by step through the spec to build up the format. Do not guess. Never assume it’s obvious. Follow the spec. I’ll skip over some steps and assemble the whole format:

    year        : 16 (little-endian)
    month       : 8
    day         : 8
    hour        : 8
    minute      : 8
    second      : 8
    dayOfWeek   : 8 (1=Monday, 7=Sunday, 0=Unknown)
    fractions256: 8 (factional part of seconds in units of 1/256)
    adjustReason: 8 (0=manual, 1=reference update, 2=TZ change, 3=DST)
    

    For convenience, I often use this extension on FixedWidthIntegers:

    enum ByteOrder {
        case littleEndian, bigEndian
    }
    
    extension FixedWidthInteger {
        func data(byteOrder: ByteOrder) -> Data {
            switch byteOrder {
            case .littleEndian: return withUnsafeBytes(of: self.littleEndian) { Data($0) }
            case .bigEndian: return withUnsafeBytes(of: self.bigEndian) { Data($0) }
            }
        }
    }
    

    And the Bluetooth day-of-week is different than Foundation’s:

    extension DateComponents {
        // Monday=1 to Sunday=7, 0=unknown
        var btDayOfWeek: UInt8 {
            guard let dow = self.weekday else { return 0 }
            return dow == 1 ? 7 : UInt8(dow - 1)
        }
    }
    

    With those, I believe this would be the correct packet:

    var data = Data()
    data.append(UInt16(c.year!).data(byteOrder: .littleEndian))
    data.append(UInt8(c.month!))
    data.append(UInt8(c.day!))
    data.append(UInt8(c.hour!))
    data.append(UInt8(c.minute!))
    data.append(UInt8(c.second!))
    data.append(c.btDayOfWeek)
    data.append(UInt8(c.nanosecond! * 256 / Int(NSEC_PER_SEC)))
    data.append(0) // Adjust reason. 0 == Manual Time Update
    

    So then you’re decoding blood pressure. It looks like you’re treating them as integers, when they’re medfloat16, and you’re ignoring the documented scaling factors. Start with section 2.3 of the GSS, which explains how to interpret values generally, and particularly 2.3.2 about scalars. Then see section 3.31 that explains the Blood Pressure Measurement characteristic in detail. You may find the Personal Health Devices Transcoding white paper helpful. "medfloat16" is often referred to as SFLOAT, so you can read section 2.2.2 of the whitepaper for details and an example encoding.

    If you have questions about blood pressure after reading the above, open a new question addressing the specific issue you’re having. What is the input? What should the output be? What output are you getting instead?

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search