For now I run everything in main thread, so far I noticed only once out of many times that UI gets a bit laggy.
I wonder what is the general practice of utilizint CoreBluetooth library regarding concurrency?
Could you provide some examples what exactly should be run in other queue, if anything?
My usage of bluetooth:
I scan for two peripheral devices, control them by sending approapriate value as CBPeripheralManager in order to make them start sending data from IMU (50Hz/100Hz depending on value).
I synchronize and normalize data from tags and write them into file using streamer.
After transmission is done, I send the data file manually by trigerring relevant action from button.
My code
class BluetoothTagController: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, CBPeripheralManagerDelegate{
static let sharedInstance = BluetoothTagController()
var transferCharacteristic:CBMutableCharacteristic!
var centralManager : CBCentralManager!
var sensorTagPeripheral : CBPeripheral!
var synchronizer:DataSynchronizer!
var sensorTagPeripheralArray : [CBPeripheral] = []
var peripheralManager: CBPeripheralManager!
var bluetoothIsON:Bool = false
var tag1Updating:Bool = false
var tag2Updating:Bool = false
var tag1Changed:Bool = false
var tag2Changed:Bool = false
var tagsIds:[String] = []
var peripheralCounter:Int = 0
var peripheralCounter2:Int = 0
var writeCounter:Int = 0
var timerSet:Bool = false
var haveBeenStarted:Bool = false
override init()
{
super.init()
centralManager = CBCentralManager(delegate: self, queue: nil)
peripheralManager = CBPeripheralManager(delegate: self, queue: nil)
self.synchronizer = DataSynchronizer(frequency: 1)
}
func peripheralManager(_ peripheral: CBPeripheralManager, didAdd service: CBService, error: Error?) {
print("WATCH OUT")
// print(service)
}
func setHaveBeenStarted( haveBeen: Bool) {
haveBeenStarted = haveBeen
}
func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) {
print("subscription started")
var intVal: NSInteger = 0
if haveBeenStarted == true {
intVal = 2
}
let valueData:Data = Data(buffer: UnsafeBufferPointer(start: &intVal, count: 1))
var didSend:Bool = self.peripheralManager.updateValue(valueData, for: self.transferCharacteristic, onSubscribedCentrals: nil)
print(didSend)
}
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
if peripheral.state != .poweredOn
{
print("no power")
self.bluetoothIsON = false
return
}
self.bluetoothIsON = true
print("powered on")
let serviceCBUUID = CBUUID(string: "5DC90000-8F79-462B-98D7-C1F8C766FA47")
let transferService:CBMutableService = CBMutableService(type: serviceCBUUID, primary: true)
let characteristicBUUID = CBUUID(string: "5DC90001-8F79-462B-98D7-C1F8C766FA47")
var intVal: NSInteger = 2
let valueData:Data = Data(buffer: UnsafeBufferPointer(start: &intVal, count: 1))
let transferCharacteristic = CBMutableCharacteristic(type: characteristicBUUID, properties: .notify, value: nil, permissions: .readable)
self.transferCharacteristic = transferCharacteristic
transferService.characteristics = [transferCharacteristic as CBCharacteristic]
self.peripheralManager.add(transferService)
self.peripheralManager.startAdvertising(nil)
}
func peripheralManagerDidStartAdvertising(_ peripheral: CBPeripheralManager, error: Error?) {
}
func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveRead request: CBATTRequest) {
print("didReceiveReadRequest")
//
}
func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didUnsubscribeFrom characteristic: CBCharacteristic) {
print("Unsubscribed")
// var intVal: NSInteger = 0
// let valueData:Data = Data(buffer: UnsafeBufferPointer(start: &intVal, count: 1))
// self.peripheralManager.updateValue(valueData, for: self.transferCharacteristic, onSubscribedCentrals: nil)
}
/******* CBCentralManagerDelegate *******/
// Check status of BLE hardware
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
// Scan for peripherals if BLE is turned on
central.scanForPeripherals(withServices: nil, options: nil)
}
else {
// Can have different conditions for all states if needed - show generic alert for now
}
}
// Check out the discovered peripherals to find Sensor Tag
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
print("array2 contains" + "\(self.sensorTagPeripheralArray.count)")
if SensorTag.sensorTagFound(advertisementData) == true {
// Update Status Label'
self.sensorTagPeripheral = peripheral
self.sensorTagPeripheral.delegate = self
self.centralManager.connect(peripheral, options: nil)
if !self.sensorTagPeripheralArray.contains(peripheral)
{
self.sensorTagPeripheralArray.append(peripheral)
self.tagsIds.append("\(peripheral.identifier)")
// self.centralManager.connectPeripheral(peripheral, options: nil)
}
else {
//showAlertWithText(header: "Warning", message: "SensorTag Not Found")
}
}
}
// Discover services of the peripheral
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("connected " + "\(peripheral.identifier)")
print("array contains" + "\(self.sensorTagPeripheralArray.count)")
numberOfTagsSending = numberOfTagsSending + 1
peripheral.discoverServices(nil)
}
// If disconnected, start searching again
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
// print("error")
// print(error)
// self.sensorTagPeripheralArray.arrayRemovingObject(peripheral)
// print(sensorTagPeripheralArray)
numberOfTagsSending = numberOfTagsSending - 1
print("removed")
synchronizer.alreadySynced = false
central.scanForPeripherals(withServices: nil, options: nil)
}
func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) {
print("ciekawe")
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
print("looking for p services")
print("discovered services " + "\(peripheral.identifier)")
for service in peripheral.services! {
let thisService = service as CBService
if SensorTag.validService(thisService) {
// Discover characteristics of all valid services
peripheral.discoverCharacteristics(nil, for: thisService)
}
}
}
// Enable notification and sensor for each characteristic of valid service
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
// print("discovered characteristic " + "\(peripheral.identifier)")
var enableValue = 1
let enablyBytes = Data(buffer: UnsafeBufferPointer(start: &enableValue, count: 1))
// print("\n")
for charateristic in service.characteristics! {
print(charateristic.uuid)
let thisCharacteristic = charateristic as CBCharacteristic
if SensorTag.validDataCharacteristic(thisCharacteristic) {
// Enable Sensor Notification
print( "valid char")
// print(thisCharacteristic)
peripheral.setNotifyValue(true, for: thisCharacteristic)
if thisCharacteristic.uuid == MagnetometerDataUUID{
peripheral.readValue(for: thisCharacteristic)
}
print("after notify set")
// print(self.sensorTagPeripheral.services)
}
if SensorTag.validConfigCharacteristic(thisCharacteristic) {
// Enable Sensor
print("more valid")
// print(thisCharacteristic)
// for peripheral in self.sensorTagPeripheralArray{
peripheral.writeValue(enablyBytes, for: thisCharacteristic, type: CBCharacteristicWriteType.withResponse)
}
}
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
print(error)
}
// var streamerTag1 = MyStreamer(fileString: "tag1.txt")
// var streamerTag2 = MyStreamer(fileString: "tag2.txt")
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
print(characteristic.value!)
if "\(peripheral.identifier)" == self.tagsIds[0]
{
switch characteristic.uuid
{
case MagnetometerDataUUID:
tag1Compensator.getTrimRegisterData(characteristic.value!)
case IRTemperatureDataUUID:
tag1Temperature = Double(UInt16(littleEndian: (characteristic.value! as NSData).bytes.bindMemory(to: UInt16.self, capacity: characteristic.value!.count).pointee))
case IMUDataUUID:
synchronizer.fillTagArray(characteristic.value!, tag: .first)
default:
return
}
}
else if (self.tagsIds.count > 1) && ("\(peripheral.identifier)" == self.tagsIds[1])
{
switch characteristic.uuid
{
case MagnetometerDataUUID:
tag2Compensator.getTrimRegisterData(characteristic.value!)
case IRTemperatureDataUUID:
tag2Temperature = Double(UInt16(littleEndian: (characteristic.value! as NSData).bytes.bindMemory(to: UInt16.self, capacity: characteristic.value!.count).pointee))
case IMUDataUUID:
synchronizer.fillTagArray(characteristic.value!, tag: .second)
default:
return
}
}
}
}