问题
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