Getting a String from a byte (Corebluetooth, Swift)

眉间皱痕 提交于 2021-01-29 20:21:22

问题


Just making this to see if someone could help me with some BLE problems. I'm trying to make an app which gets the stuff from my rowing machine but I can't figure out how to turn bytes into like a string? Its hard to explain but here's my VC:

import UIKit
import CoreBluetooth


let rowerServiceCBUUID = CBUUID(string: "CE060000-43E5-11E4-916C-0800200C9A66")
let characteristic1CBUUID = CBUUID(string: "CE060031-43E5-11E4-916C-0800200C9A66")
let characteristic2CBUUID = CBUUID(string: "2AD1")
class HRMViewController: UIViewController {
  @IBOutlet weak var heartRateLabel: UILabel!
  @IBOutlet weak var bodySensorLocationLabel: UILabel!
  var centralManager: CBCentralManager!
  var pmPeripheral: CBPeripheral!
  var wattValue: Int!
  override func viewDidLoad() {
    super.viewDidLoad()
    centralManager = CBCentralManager(delegate: self, queue: nil)
    // Make the digits monospaces to avoid shifting when the numbers change
    heartRateLabel.font = UIFont.monospacedDigitSystemFont(ofSize: heartRateLabel.font!.pointSize, weight: .regular)
  }

  func onHeartRateReceived(_ heartRate: Int) {
    heartRateLabel.text = String(heartRate)
    print("BPM: \(heartRate)")
  }
}
extension HRMViewController: CBCentralManagerDelegate {
  func centralManagerDidUpdateState(_ central: CBCentralManager) {
      print("Central state update")
      if central.state != .poweredOn {
          print("Central is not powered on")
      } else {
          print("Central scanning for", rowerServiceCBUUID);
          centralManager.scanForPeripherals(withServices: [rowerServiceCBUUID],
                                            options: [CBCentralManagerScanOptionAllowDuplicatesKey : true])
      }
  }

  func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
    print(peripheral)
    peripheral.delegate = self
    pmPeripheral = peripheral
    pmPeripheral.delegate = self
    centralManager.stopScan()
    centralManager.connect(pmPeripheral!)
  }
  func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
    print("Connected!")
    pmPeripheral.discoverServices(nil)
  }

}
extension HRMViewController: CBPeripheralDelegate {
  func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
    guard let services = peripheral.services else { return }
    for service in services {
      print(service)
      print(service.characteristics ?? "characteristics are nil")
      peripheral.discoverCharacteristics(nil, for: service)
    }
  }
  func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?){
    guard let characteristics = service.characteristics else { return }
    for characteristic in characteristics {
      print(characteristic)
      if characteristic.properties.contains(.read) {
        print("\(characteristic.uuid): properties contains .read")
      }
      if characteristic.properties.contains(.notify) {
        print("\(characteristic.uuid): properties contains .notify")
      }
      peripheral.readValue(for: characteristic)
    }
  }
  func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic,
                  error: Error?) {
    switch characteristic.uuid {
      case characteristic1CBUUID:
        let bodySensorLocation = bodyLocation(from: characteristic)
        bodySensorLocationLabel.text = bodySensorLocation
      default:
        print("Unhandled Characteristic UUID: \(characteristic.uuid)")
    }
  }
  private func bodyLocation(from characteristic: CBCharacteristic) -> String {
    guard let characteristicData = characteristic.value,
      let byte = characteristicData.first else { return "Error" }
    switch byte {
      case 0: return "0"
      case 1: return "1"
      case 2: return "2"
      case 3: return "3"
      case 4: return "4"
      case 5: return "5"
      case 6: return "6"
      case 7: return "7"
      case 8: return "8"
      default:
        return "Reserved for future use"
    }
  }
}

Specifically the bottom most text is where I'm stuck, every Corebluetooth guide swaps numbers into text but mine is a string array (As shown here: https://youtu.be/rXDCBVQXdbg)

I'm stuck and don't know where to go from here. Any help with this would be appreciated!


回答1:


From the UUIDs you're using, you're currently focusing on the C2 rowing general status characteristic documented on page 11 of the Concept2 Performance Monitor Bluetooth Smart Communications Interface Definition.

The documentation says that the message will consists of 19 bytes:

  • Byte 0: Elapsed Time Lo (0.01 sec lsb)
  • Byte 1: Elapsed Time Mid
  • Byte 2: Elapsed Time High
  • Byte 3: Distance Lo (0.1 m lsb)
  • Byte 4: Distance Mid
  • Byte 5: Distance High
  • Byte 6: Workout Type (enum)
  • ...

The elapsed time in stored in 3 bytes: byte 0 is the least significant byte (LSB), followed by the medium and the high byte. And the unit is 0.01s. So to get seconds, it must be divided by 100.

Distance is stored similarly. The unit is 0.1m.

The workout type is given in byte 6 as an enumeration. So text is not contained. Just a numeric code.

To decode it, use code like below. The function onRowingGeneralStatusUpdate(for:) replaces your confusingly named function bodyLocation(from:):

func onRowingGeneralStatusUpdate(characteristic: CBCharacteristic) {
    guard let data = characteristic.value else { return }

    // elapsed time in sec
    let elapsedTime: Double = Double(Int(data[0]) + 256 * Int(data[1]) + 65536 * Int(data[2])) / 100.0
    // distance in m
    let distance: Double = Double(Int(data[3]) + 256 * Int(data[4]) + 65536 * Int(data[5])) / 10.0
    let workout: String =  workoutType(for: data[6])
}

func workoutType(for code: UInt8) -> String {
    switch code {
    case 0:
        return "Just row, no splits"
    case 1:
        return "Just row, splits"
    case 2:
        return "Fixed dist, no splits"
    case 3:
        return "Fixed dist, splits"
    case 4:
        return "Fixed time, no splits"
    case 5:
        return "Fixed time, splits"
    case 6:
        return "Fixed time, interval"
    case 7:
        return "Fixed dist, interval"
    case 8:
        return "Variable, interval"
    case 9:
        return "Variable, undef rest, interval"
    case 10:
        return "Fixed, calorie"
    case 11:
        return "Fixed, watt-minutes"
    case 12:
        return "Fixed cals, interval"
    default:
        return ""
    }
}


来源:https://stackoverflow.com/questions/61770645/getting-a-string-from-a-byte-corebluetooth-swift

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!