如何使用CoreBluetooth从1822 PulseOximeter的蓝牙LE数据中提取SFLOAT值 [英] How to extract SFLOAT value from Bluetooth LE data for 1822 PulseOximeter using CoreBluetooth

查看:314
本文介绍了如何使用CoreBluetooth从1822 PulseOximeter的蓝牙LE数据中提取SFLOAT值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用一个iOS应用,该应用将使用 iOS 11.4 上的 CoreBluetooth 从启用了 Bluetooth LE 的设备上读取脉搏血氧仪数据>在 Swift 4.1 中.

I'm working on an iOS app that is to read pulse oximeter data from a Bluetooth LE enabled device, using CoreBluetooth on iOS 11.4 in Swift 4.1.

我已找到 CBCentralManager ,搜索外围设备,找到了感兴趣的 CBPeripheral ,我确认它具有 0x1822 脉搏血氧仪服务,如蓝牙SIG 此处. (您可能需要注册Bluetooth SIG才能访问该链接.它是免费的,但需要一两天.)

I've got the CBCentralManager searching for peripherals, I find the CBPeripheral I am interested in, I verify that it has the 0x1822 Pulse Oximeter Service, as described by Bluetooth SIG here. (You may need to register with Bluetooth SIG to access that link. It's free but takes a day or two.)

然后,我连接到它,然后发现服务:

After that, I connect to it, then I discover services:

func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
    peripheral.discoverServices(nil)
}

然后在我的 peripheral:didDiscoverServices 中,我发现了GATT的特征:

Then in my peripheral:didDiscoverServices I discover GATT characteristics:

func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?){
    for service in peripheral.services ?? [] {
        if service.uuid.uuidString == "1822" {
            peripheral.discoverCharacteristics(nil, for: service)
        }
    }
}

从中,我看到可用的以下特征( CBCharacteristic.uuid ):0x2A5F,0x2A5E,0x2a60和0x2A52.然后,我订阅 0x2A5F 的更新,这是 PLX连续测量,在

From that I see the the following characteristics (CBCharacteristic.uuid) available: 0x2A5F, 0x2A5E, 0x2a60, and 0x2A52. I then subscribe to updates for 0x2A5F, which is PLX Continuous Measurement, which is described here:

if service.uuid.uuidString == "1822" && characteristic.uuid.uuidString == "2A5F" {
    // pulseox continuous
    print("[SUBSCRIBING TO UPDATES FOR SERVICE 1822 'PulseOx' for Characteristic 2A5F 'PLX Continuous']")
    peripheral.setNotifyValue(true, for: characteristic)    
}

然后,我开始通过 peripheral:didUpdateValueFor 方法接收回20字节的数据包:

I then begin to receive back 20-byte packets in my peripheral:didUpdateValueFor method:

func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {

    if characteristic.service.uuid.uuidString == "1822" && characteristic.uuid.uuidString == "2A5F" {
        if let data = characteristic.value {
            var values = [UInt8](repeating:0, count:data.count)
            data.copyBytes(to: &values, count: data.count)
        }
    }
}

从参考文档中可以看到,第一个字节是一堆位域,描述了数据包中包含哪些可选值.接下来的2个字节是 SpO2PR-Normal-SpO2 (充氧)读数的 SFLOAT 值,接下来的2个字节是另一个 SFLOAT 值为了 SpO2PR-Normal-PR (脉冲率)值.

From the reference doc you can see the first byte is a bunch of bitfields describing which optional values are included in the packet. The next 2 bytes are an SFLOAT value for the SpO2PR-Normal - SpO2 (oxygenation) reading, and the following 2 bytes are another SFLOAT value for the SpO2PR-Normal - PR (pulse rate) value.

蓝牙SIG列出了 SFLOAT 作为 IEEE-11073 16位SFLOAT 此处. IEEE的关于IEEE-11073的文件未公开列出,并且可以购买,但我希望避免这种情况.

Bluetooth SIG lists an SFLOAT as an IEEE-11073 16-bit SFLOAT here. IEEE's document on IEEE-11073 is not publicly listed, and is available for purchase, but I'd prefer to avoid that.

任何想法如何解码?我在堆栈溢出中发现了另一个问题,该问题引用了正常的

Any idea how to decode? I found another question on Stack Overflow referencing the normal 32-bit Float, but that question was for a different type of Float, and its answer was not applicable.

推荐答案

在这里是

Xcode 9.4.1 Xcode 10.0 beta 3

iOS 11.4.1 iOS 12.0 beta 3

Swift 4.1.2 Swift 4.2

func floatFromTwosComplementUInt16(_ value: UInt16, havingBitsInValueIncludingSign bitsInValueIncludingSign: Int) -> Float {
    // calculate a signed float from a two's complement signed value
    // represented in the lowest n ("bitsInValueIncludingSign") bits
    // of the UInt16 value
    let signMask: UInt16 = UInt16(0x1) << (bitsInValueIncludingSign - 1)
    let signMultiplier: Float = (value & signMask == 0) ? 1.0 : -1.0

    var valuePart = value
    if signMultiplier < 0 {
        // Undo two's complement if it's negative
        var valueMask = UInt16(1)
        for _ in 0 ..< bitsInValueIncludingSign - 2 {
            valueMask = valueMask << 1
            valueMask += 1
        }
        valuePart = ((~value) & valueMask) &+ 1
    }

    let floatValue = Float(valuePart) * signMultiplier

    return floatValue
}

func extractSFloat(values: [UInt8], startingIndex index: Int) -> Float {
    // IEEE-11073 16-bit SFLOAT -> Float
    let full = UInt16(values[index+1]) * 256 + UInt16(values[index])

    // Check special values defined by SFLOAT first
    if full == 0x07FF {
        return Float.nan
    } else if full == 0x800 {
        return Float.nan // This is really NRes, "Not at this Resolution"
    } else if full == 0x7FE {
        return Float.infinity
    } else if full == 0x0802 {
        return -Float.infinity // This is really negative infinity
    } else if full == 0x801 {
        return Float.nan // This is really RESERVED FOR FUTURE USE
    }

    // Get exponent (high 4 bits)
    let expo = (full & 0xF000) >> 12
    let expoFloat = floatFromTwosComplementUInt16(expo, havingBitsInValueIncludingSign: 4)

    // Get mantissa (low 12 bits)
    let mantissa = full & 0x0FFF
    let mantissaFloat = floatFromTwosComplementUInt16(mantissa, havingBitsInValueIncludingSign: 12)

    // Put it together
    let finalValue = mantissaFloat * pow(10.0, expoFloat)

    return finalValue
}

extraSFloat 方法采用Uint8数组和该数组的索引来指示SFLOAT的位置.例如,如果数组是两个字节(仅是SFLOAT的两个字节),那么您会说:

The extraSFloat method takes a Uint8 array and an index into that array to indicate where the SFLOAT is. For example, if the array was two bytes (just the two bytes of the SFLOAT), then you would say:

let floatValue = extractSFloat(values: array, startingIndex: 0)

之所以这样做,是因为在处理Bluetooth数据时,我总是以一个包含我需要解码的数据的 UInt8 值数组结束.

I did it this way because when working with Bluetooth data I always ended up with an array of UInt8 values containing the data I needed to decode.

这篇关于如何使用CoreBluetooth从1822 PulseOximeter的蓝牙LE数据中提取SFLOAT值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆