通过 Swift BLE 连接到 Metawear
Connecting to Metawear via Swift BLE
我正在使用 Metawear Motion R 板。我想读取加速度计和陀螺仪数据。虽然制造商确实提供了一个 C++ SDK,我可以用它来读取这些数据,但我发现它不稳定并且在出现问题时很难调试。结果,我只想通过 Swift.
读取加速度计和陀螺仪数据
如果我要使用他们的 SDK,下面的代码会让我调用和配置设备:
// configure accelerometer
mbl_mw_acc_set_odr(self.connectedDevice?.board, 50.0);
mbl_mw_acc_set_range(self.connectedDevice?.board, 4.0);
mbl_mw_acc_write_acceleration_config(self.connectedDevice?.board);
// configure gyroscope
mbl_mw_gyro_bmi160_set_odr(self.connectedDevice?.board, MBL_MW_GYRO_BMI160_ODR_50Hz);
mbl_mw_gyro_bmi160_set_range(self.connectedDevice?.board, MBL_MW_GYRO_BMI160_RANGE_125dps);
mbl_mw_gyro_bmi160_write_config(self.connectedDevice?.board);
// read the accelerometer
let acc_signal = mbl_mw_acc_get_acceleration_data_signal(self.connectedDevice?.board);
mbl_mw_datasignal_subscribe(acc_signal, bridge(obj: self)) { (context, data) in
let obj: MblMwCartesianFloat = data!.pointee.valueAs();
let _self: ExerciseViewController = bridge(ptr: context!);
_self.add_imu_records(record: ImuRecord(x: obj.x, y: obj.y, z: obj.z, time: Date()), isAccelerometer: true);
}
// read the gyroscope
let gyro_signal = mbl_mw_gyro_bmi160_get_rotation_data_signal(self.connectedDevice?.board);
mbl_mw_datasignal_subscribe(gyro_signal, bridge(obj: self)) { (context, data) in
let obj: MblMwCartesianFloat = data!.pointee.valueAs();
let _self: ExerciseViewController = bridge(ptr: context!);
_self.add_imu_records(record: ImuRecord(x: obj.x, y: obj.y, z: obj.z, time: Date()), isAccelerometer: false);
}
mbl_mw_acc_enable_acceleration_sampling(self.connectedDevice?.board);
mbl_mw_acc_start(self.connectedDevice?.board);
mbl_mw_gyro_bmi160_enable_rotation_sampling(self.connectedDevice?.board);
mbl_mw_gyro_bmi160_start(self.connectedDevice?.board);
要仅使用 Swift 开始实施配置和日志记录逻辑,我遵循两个指南:Decoding metawear , Core BLE Tutorial
我下面的代码库将连接到 MetaWear 设备并查看不同的特性。
import Foundation
import CoreBluetooth
public class BleCommunicator : NSObject, CBCentralManagerDelegate {
private let metaWearService = CBUUID(string: "326A9000-85CB-9195-D9DD-464CFBBAE75A")
private let metaWearConfigurationCharacteristic = CBUUID(string: "326A9001-85CB-9195-D9DD-464CFBBAE75A")
private let metawearDataCharacteristic = CBUUID(string: "326A9006-85CB-9195-D9DD-464CFBBAE75A")
private var centralManager : CBCentralManager!
private var myDevice: CBPeripheral!
override init() {
super.init()
centralManager = CBCentralManager(delegate: self, queue: nil)
}
public func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
centralManager.scanForPeripherals(withServices: nil)
case .unknown:
print("### check ble connection is unknown");
case .resetting:
print("### check ble connection is resetting");
case .unsupported:
print("### check ble connection is unsupported");
case .unauthorized:
print("### check ble connection is unauthorized");
case .poweredOff:
print("### check ble connection is poweredOff");
@unknown default:
print("### check ble connection");
}
}
public func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral,
advertisementData: [String: Any], rssi RSSI: NSNumber) {
if (peripheral.name == "MetaWear") {
myDevice = peripheral
myDevice.delegate = self
centralManager.stopScan()
centralManager.connect(myDevice)
}
}
public func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
myDevice.discoverServices([metaWearService])
}
}
extension BleCommunicator : CBPeripheralDelegate {
public func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
guard let services = peripheral.services else { return }
for service in services {
peripheral.discoverCharacteristics(nil, for: service)
}
}
public func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
guard let characteristics = service.characteristics else { return }
for characteristic in characteristics {
if (characteristic.uuid == metawearDataCharacteristic) {
peripheral.readValue(for: characteristic)
peripheral.setNotifyValue(true, for: characteristic)
}
}
}
public func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
switch characteristic.uuid {
case metawearDataCharacteristic:
sensor(from: characteristic)
default:
print("Unhandled Characteristic UUID: \(characteristic.uuid)")
}
}
private func sensor(from characteristic: CBCharacteristic) {
guard let characteristicData = characteristic.value,
let _ = characteristicData.first else { return }
let data = String(bytes: characteristicData, encoding: .utf8)
print("### we have this:: \(data) ");
}
}
一次 运行,传感器功能显示一个空的 20 字节数组。我怀疑这是因为在读取数据之前需要配置设备。 (正如我在 C++ 代码块中所做的那样)第一个教程提到通过 metaWearConfigurationCharacteristic 将预格式化的模式发送到 metawear 板,如下所示,但我找不到任何文档来复制它:
Input - 13032600, where 13 is gyro module, 03 is config opcode, 26 is
output data rate (38 hz), 00 is angular data range.
问题:
如何配置和读取加速度计和陀螺仪传感器,如使用 C++ 代码所示,使用 Swift?
我对 C++ SDK 进行了逆向工程,因此,我编写了这段代码来初始化设备:
public func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
guard let characteristics = service.characteristics else { return }
for characteristic in characteristics {
if (characteristic.uuid == metawearDataCharacteristic) {
peripheral.readValue(for: characteristic)
peripheral.setNotifyValue(true, for: characteristic)
} else if (characteristic.uuid == metaWearConfigurationCharacteristic) {
let configurationSeq : [[UInt8]] = createInitSequence()
for seq in configurationSeq {
peripheral.writeValue(Data(seq), for: characteristic, type: CBCharacteristicWriteType.withoutResponse)
}
}
}
}
private func createInitSequence() -> [[UInt8]] {
let sequence : [[UInt8]] = [[3, 4, 1], [19, 5, 1],
[3, 2, 1, 0], [3, 1, 1],
[19, 2, 1,0], [19, 1, 1]]
return sequence;
}
为了读取通知上的数据,我更新了下面的函数如下:
private let factorAcceleration: Float = 8192.0000
private let factorGyroscope: Float = 262.399994
private func sensor(from characteristic: CBCharacteristic) {
guard let characteristicData = characteristic.value,
let _ = characteristicData.first else { return }
let data = characteristic.value!
var values = [UInt8](repeating: 0, count: data.count)
data.copyBytes(to: &values, count: data.count)
if (values[0] == 3) {
// this is the accelerometer
let xAxis = Float(Int16(values[2..<4])) / factorAcceleration
let yAxis = Float(Int16(values[4..<6])) / factorAcceleration
let zAxis = Float(Int16(values[6..<8])) / factorAcceleration
print("accelerometer x=\(xAxis), y=\(yAxis), z=\(zAxis)");
}
if (values[0] == 19) {
// this is the gyroscope
let xAxis = Float(Int16(values[2..<4])) / factorGyroscope
let yAxis = Float(Int16(values[4..<6])) / factorGyroscope
let zAxis = Float(Int16(values[6..<8])) / factorGyroscope
print("gyroscope x=\(xAxis), y=\(yAxis), z=\(zAxis)");
}
}
从一个,我把unit8字节解析成了一个float。此代码将允许此转换:
extension Numeric {
init<D: DataProtocol>(_ data: D) {
var value: Self = .zero
let size = withUnsafeMutableBytes(of: &value, { data.copyBytes(to: [=12=])} )
assert(size == MemoryLayout.size(ofValue: value))
self = value
}
}
我正在使用 Metawear Motion R 板。我想读取加速度计和陀螺仪数据。虽然制造商确实提供了一个 C++ SDK,我可以用它来读取这些数据,但我发现它不稳定并且在出现问题时很难调试。结果,我只想通过 Swift.
读取加速度计和陀螺仪数据如果我要使用他们的 SDK,下面的代码会让我调用和配置设备:
// configure accelerometer
mbl_mw_acc_set_odr(self.connectedDevice?.board, 50.0);
mbl_mw_acc_set_range(self.connectedDevice?.board, 4.0);
mbl_mw_acc_write_acceleration_config(self.connectedDevice?.board);
// configure gyroscope
mbl_mw_gyro_bmi160_set_odr(self.connectedDevice?.board, MBL_MW_GYRO_BMI160_ODR_50Hz);
mbl_mw_gyro_bmi160_set_range(self.connectedDevice?.board, MBL_MW_GYRO_BMI160_RANGE_125dps);
mbl_mw_gyro_bmi160_write_config(self.connectedDevice?.board);
// read the accelerometer
let acc_signal = mbl_mw_acc_get_acceleration_data_signal(self.connectedDevice?.board);
mbl_mw_datasignal_subscribe(acc_signal, bridge(obj: self)) { (context, data) in
let obj: MblMwCartesianFloat = data!.pointee.valueAs();
let _self: ExerciseViewController = bridge(ptr: context!);
_self.add_imu_records(record: ImuRecord(x: obj.x, y: obj.y, z: obj.z, time: Date()), isAccelerometer: true);
}
// read the gyroscope
let gyro_signal = mbl_mw_gyro_bmi160_get_rotation_data_signal(self.connectedDevice?.board);
mbl_mw_datasignal_subscribe(gyro_signal, bridge(obj: self)) { (context, data) in
let obj: MblMwCartesianFloat = data!.pointee.valueAs();
let _self: ExerciseViewController = bridge(ptr: context!);
_self.add_imu_records(record: ImuRecord(x: obj.x, y: obj.y, z: obj.z, time: Date()), isAccelerometer: false);
}
mbl_mw_acc_enable_acceleration_sampling(self.connectedDevice?.board);
mbl_mw_acc_start(self.connectedDevice?.board);
mbl_mw_gyro_bmi160_enable_rotation_sampling(self.connectedDevice?.board);
mbl_mw_gyro_bmi160_start(self.connectedDevice?.board);
要仅使用 Swift 开始实施配置和日志记录逻辑,我遵循两个指南:Decoding metawear , Core BLE Tutorial
我下面的代码库将连接到 MetaWear 设备并查看不同的特性。
import Foundation
import CoreBluetooth
public class BleCommunicator : NSObject, CBCentralManagerDelegate {
private let metaWearService = CBUUID(string: "326A9000-85CB-9195-D9DD-464CFBBAE75A")
private let metaWearConfigurationCharacteristic = CBUUID(string: "326A9001-85CB-9195-D9DD-464CFBBAE75A")
private let metawearDataCharacteristic = CBUUID(string: "326A9006-85CB-9195-D9DD-464CFBBAE75A")
private var centralManager : CBCentralManager!
private var myDevice: CBPeripheral!
override init() {
super.init()
centralManager = CBCentralManager(delegate: self, queue: nil)
}
public func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
centralManager.scanForPeripherals(withServices: nil)
case .unknown:
print("### check ble connection is unknown");
case .resetting:
print("### check ble connection is resetting");
case .unsupported:
print("### check ble connection is unsupported");
case .unauthorized:
print("### check ble connection is unauthorized");
case .poweredOff:
print("### check ble connection is poweredOff");
@unknown default:
print("### check ble connection");
}
}
public func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral,
advertisementData: [String: Any], rssi RSSI: NSNumber) {
if (peripheral.name == "MetaWear") {
myDevice = peripheral
myDevice.delegate = self
centralManager.stopScan()
centralManager.connect(myDevice)
}
}
public func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
myDevice.discoverServices([metaWearService])
}
}
extension BleCommunicator : CBPeripheralDelegate {
public func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
guard let services = peripheral.services else { return }
for service in services {
peripheral.discoverCharacteristics(nil, for: service)
}
}
public func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
guard let characteristics = service.characteristics else { return }
for characteristic in characteristics {
if (characteristic.uuid == metawearDataCharacteristic) {
peripheral.readValue(for: characteristic)
peripheral.setNotifyValue(true, for: characteristic)
}
}
}
public func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
switch characteristic.uuid {
case metawearDataCharacteristic:
sensor(from: characteristic)
default:
print("Unhandled Characteristic UUID: \(characteristic.uuid)")
}
}
private func sensor(from characteristic: CBCharacteristic) {
guard let characteristicData = characteristic.value,
let _ = characteristicData.first else { return }
let data = String(bytes: characteristicData, encoding: .utf8)
print("### we have this:: \(data) ");
}
}
一次 运行,传感器功能显示一个空的 20 字节数组。我怀疑这是因为在读取数据之前需要配置设备。 (正如我在 C++ 代码块中所做的那样)第一个教程提到通过 metaWearConfigurationCharacteristic 将预格式化的模式发送到 metawear 板,如下所示,但我找不到任何文档来复制它:
Input - 13032600, where 13 is gyro module, 03 is config opcode, 26 is output data rate (38 hz), 00 is angular data range.
问题: 如何配置和读取加速度计和陀螺仪传感器,如使用 C++ 代码所示,使用 Swift?
我对 C++ SDK 进行了逆向工程,因此,我编写了这段代码来初始化设备:
public func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
guard let characteristics = service.characteristics else { return }
for characteristic in characteristics {
if (characteristic.uuid == metawearDataCharacteristic) {
peripheral.readValue(for: characteristic)
peripheral.setNotifyValue(true, for: characteristic)
} else if (characteristic.uuid == metaWearConfigurationCharacteristic) {
let configurationSeq : [[UInt8]] = createInitSequence()
for seq in configurationSeq {
peripheral.writeValue(Data(seq), for: characteristic, type: CBCharacteristicWriteType.withoutResponse)
}
}
}
}
private func createInitSequence() -> [[UInt8]] {
let sequence : [[UInt8]] = [[3, 4, 1], [19, 5, 1],
[3, 2, 1, 0], [3, 1, 1],
[19, 2, 1,0], [19, 1, 1]]
return sequence;
}
为了读取通知上的数据,我更新了下面的函数如下:
private let factorAcceleration: Float = 8192.0000
private let factorGyroscope: Float = 262.399994
private func sensor(from characteristic: CBCharacteristic) {
guard let characteristicData = characteristic.value,
let _ = characteristicData.first else { return }
let data = characteristic.value!
var values = [UInt8](repeating: 0, count: data.count)
data.copyBytes(to: &values, count: data.count)
if (values[0] == 3) {
// this is the accelerometer
let xAxis = Float(Int16(values[2..<4])) / factorAcceleration
let yAxis = Float(Int16(values[4..<6])) / factorAcceleration
let zAxis = Float(Int16(values[6..<8])) / factorAcceleration
print("accelerometer x=\(xAxis), y=\(yAxis), z=\(zAxis)");
}
if (values[0] == 19) {
// this is the gyroscope
let xAxis = Float(Int16(values[2..<4])) / factorGyroscope
let yAxis = Float(Int16(values[4..<6])) / factorGyroscope
let zAxis = Float(Int16(values[6..<8])) / factorGyroscope
print("gyroscope x=\(xAxis), y=\(yAxis), z=\(zAxis)");
}
}
从一个
extension Numeric {
init<D: DataProtocol>(_ data: D) {
var value: Self = .zero
let size = withUnsafeMutableBytes(of: &value, { data.copyBytes(to: [=12=])} )
assert(size == MemoryLayout.size(ofValue: value))
self = value
}
}