BLE节奏特征解析iOS蓝牙Swift

rkue9o1l  于 2022-10-31  发布在  Swift
关注(0)|答案(1)|浏览(210)

我正在尝试创建一个iOS应用程序来读取踏频传感器(Wahoo健身踏频)。这是蓝牙的特性0x2A5B (CSC Measurement)。在这个例子中,踏频是自行车踏板旋转的速度。
我在Swift中使用以下代码读取传感器的特性:版本1:

private func cadence(from characteristic: CBCharacteristic) -> Int {

    guard let characteristicData = characteristic.value else {return -1 }
    let byteArray = [UInt8](characteristicData)
    print(byteArray)

    let firstBitValue = byteArray[1] & 0x01 //set bit 1 (not 0)
    if firstBitValue == 1 { //IF crank revolution data is present, 1==true
        return Int(byteArray[2])
    } else {
        return 0
    }
}

当我打印byteArray时,我得到“[2,1,0,152,11]"。“2”和“0”永远不会改变。“1”的位置增加,永远不会减少。“152”和“11”的位置似乎是完全随机的,永远不会变到0。当曲柄完全停止时,它们也不会改变。在阅读文档时,我预计“11”将是最后一个事件曲柄时间。但它似乎没有改变,尽管我如何缓慢旋转传感器。
如何使用此数据从传感器获取节奏?
在Paul的帮助下,我对代码进行了修改,并得到了以下结果:

版本2

func cadence(from characteristic:CBCharacteristic, previousRotations:Int = 0) -> (rpm:Double, rotations:Int)? {
        guard let characteristicData = characteristic.value else {
            return nil
        }

        let byteArray = [UInt8](characteristicData)
        if  byteArray[0] & 0x02 == 2 {
            // contains cadence data
            let rotations = (Int(byteArray[2]) << 8) + Int(byteArray[1])
            var deltaRotations = rotations - previousRotations
            if deltaRotations < 0 {
                deltaRotations += 65535
            }
            let timeInt = (Int(byteArray[4]) << 8) + Int(byteArray[3])
            let timeMins =  Double(timeInt) / 1024.0 / 60.0
            let rpm = Double(deltaRotations) / timeMins

            return (rpm:rpm, rotations: rotations)
        }
        return nil
}

当前返回的RPM低于预期值,最高值约为53,最低值为3。将这些值与传感器开发人员的应用程序进行比较,后者显示约为50-70 rpm。

版本3:

func cadence(from characteristic:CBCharacteristic, previousTime: Int=0, previousRotations:Int = 0) -> (rpm:Double, time: Int, rotations:Int)? {
            guard let characteristicData = characteristic.value else {
                return nil
            }

            let byteArray = [UInt8](characteristicData)
            if  byteArray[0] & 0x02 == 2 {
                // contains cadence data
                let rotations = Int(byteArray[2])<<8 + Int(byteArray[1])
                var deltaRotations = rotations - previousRotations
                if deltaRotations < 0 {
                    deltaRotations += 65535
                }
                let timeInt = Int(byteArray[4])<<8 + Int(byteArray[3])
                var timeDelta = timeInt - previousTime
                if (timeDelta < 0) {
                    timeDelta += 65535
                }

                let timeMins =  Double(timeDelta) / 1024.0 / 60
                let rpm = Double(deltaRotations) / timeMins

                return (rpm:rpm, time: timeInt, rotations: rotations)
            }
            return nil
        }
rta7y2nd

rta7y2nd1#

这就是我最后做的

func onCSC(from characteristic: CBCharacteristic) -> Double{
    guard let characteristicData = characteristic.value else { return -1 }
    let byteArray = [UInt8](characteristicData)
    let firstBitValue  = byteArray[0] & 0x01 // Bit1 [2] == 0010 & 0000 == 0000 == 0 (Dec) Wheel Rev FALSE (For Spd)
    let secondBitValue = byteArray[0] & 0x02 // Bit2 [2] == 0010 & 0010 == 0010 == 2 (Dec) Crank Rev TRUE  (For Cad)

    if firstBitValue > 0 {
      let cumWheelRev   = Int(byteArray[3])<<8 + Int(byteArray[2])<<8 + Int(byteArray[1])<<8 + Int(byteArray[0])
      let lastWheelTime = Int(byteArray[5])<<8 + Int(byteArray[4])
      spdCSC = Double(getWheelRpm(pCumWheelRev: cumWheelRev,
                                  pLastWheelTime: lastWheelTime,
                                  pPrevCumWheelRev: prevCSCCumWheelRev,
                                  pPrevWheelTime: prevCSCWheelTime,
                                  hasPower: false)
                          )        

    // Store the current WheelRev & WheelTime for next iteration
    prevComboCSCCumWheelRev = cumWheelRev
    prevComboCSCWheelTime = lastWheelTime
    }

    if secondBitValue > 0 {
      let cumCrankRev   = Int(byteArray[2])<<8 + Int(byteArray[1])
      let lastCrankTime = Int(byteArray[4])<<8 + Int(byteArray[3])

      var deltaRotations = cumCrankRev - prevCumCrankRev
      if deltaRotations < 0 { deltaRotations += 65535 }

      var timeDelta = lastCrankTime - prevCrankTime
      if (timeDelta < 0) { timeDelta += 65535 }
      // In Case Cad Drops, we use PrevRPM 
      // to substitute (up to 2 seconds before reporting 0)
      if (timeDelta != 0) {
        prevCrankStaleness = 0
        let timeMins =  Double(timeDelta) / 1024.0 / 60
        rpm = Double(deltaRotations) / timeMins
        prevRPM = rpm
      } else if (timeDelta == 0 && prevCrankStaleness < 2 ) {
        rpm = prevRPM
        prevCrankStaleness += 1
      } else if (prevCrankStaleness >= 2) {
        rpm = 0.0
      }

      prevCumCrankRev = cumCrankRev
      prevCrankTime = lastCrankTime
      return rpm
    }
    return -1
  }

func getWheelRpm( pCumWheelRev: Int, pLastWheelTime: Int, pPrevCumWheelRev: Int, pPrevWheelTime: Int, hasPower: Bool ) -> Double {
    let cumWheelRev = pCumWheelRev
    let lastWheelTime = pLastWheelTime
    let prevCumWheelRev = pPrevCumWheelRev
    let prevWheelTime = pPrevWheelTime
    var wheelRpm: Double = -1.0

    let deltaRotations = cumWheelRev - prevCumWheelRev
    var timeDelta = lastWheelTime - prevWheelTime
    if (timeDelta < 0) { timeDelta += 65535 }

    if (timeDelta != 0 && prevCumWheelRev != 0) { // Ignore the first data point cos prevCumWheeRev is always 0
      // Alternate Calculation
      // let timeMins =  Double(timeDelta) / 1024.0 / 60
      // rpm1 = Double(deltaRotations) / timeMins
      wheelRpm =  (hasPower ? 2048.0 : 1024.0) * (Double(deltaRotations) * 60.0) / Double(timeDelta)
    }
    //    print("1 getWheelRpm wheelRpm:\(wheelRpm) deltaRotations:\(deltaRotations)[\(cumWheelRev) - \(prevCumWheelRev)] timeDelta:\(timeDelta) [\(lastWheelTime) - \(prevWheelTime)]")

    return wheelRpm
  }
}

根据www.example.com上的规格bluetooth.com

//  byte1 uint8: flags
  //   bit 0 = 1: Wheel Revolution Data is present
  //   bit 1 = 1: Crank Revolution Data is present
  //
  //   byte2/3 The next two fields are present only if bit 0 above is 1:
  //    uint32: Cumulative Wheel Revolutions
  //    uint16: Last Wheel Event Time, in 1024ths of a second
  //
  //   byte 3/4 The next two fields are present only if bit 10 above is 1:
  //    uint16: Cumulative Crank Revolutions
  //    uint16: Last Crank Event Time, in 1024ths of a second

  //    Flag       : 2 (0x2)
  //    CumWheel   : 6 (0x6)
  //    LastWheel  : 0 (0x0)
  //    CumCrank   : 231 (0xe7)
  //    LastCrankTm: 30 (0x1e)

相关问题