CBCentralManager gets stuck when connecting to a device.
After starting to scan, it finds the device and reaches the centralManager: didDiscover: method, then tries to connect to it, but the device stays in connecting status for a while and then disconnects afterwards.
It's easily fixed by restarting the Bluetooth on device, but this isn't a solution. Does anyone have an idea why this happens?
Code:
//MARK: CBCentralManagerDelegate methods
func centralManagerDidUpdateState(_ central: CBCentralManager) {
self.delegate?.centralServiceDidUpdateState(self)
guard let centralManager = self.centralManager else { return }
switch centralManager.state {
case .poweredOn: self.scan()
case .poweredOff: print("poweredOff")
case .resetting: print("resetting")
case .unauthorized: print("unauthorized")
case .unknown: print("unknown")
case .unsupported: print("unsupported")
}
}
func scan() {
self.centralManager?.scanForPeripherals(withServices: nil)
print("Scanning started")
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
self.delegate?.centralService(self, discover: peripheral.identifier.uuidString)
let retrievedPeripherals = central.retrievePeripherals(withIdentifiers: [])
for retrievedPeripheral in retrievedPeripherals {
central.connect(retrievedPeripheral, options: nil)
}
if !retrievedPeripherals.contains(peripheral) {
// And connect
self.discoveredPeripheral = peripheral
self.discoveredPeripheral?.delegate = self
central.stopScan()
central.connect(peripheral, options: nil)
self.rssis[peripheral.identifier.uuidString] = "\(RSSI)"
print("Connecting to peripheral \(peripheral)")
}
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
print("Failed to connect to \(peripheral). (\(error!.localizedDescription))")
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("Peripheral Connected with identifier: \(peripheral.identifier.uuidString)")
self.discoveredPeripheral?.discoverServices(nil)
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
print("Discover services for peripheralIdentifier: \(peripheral.identifier.uuidString)")
if let error = error {
print("Error discovering services: \(error.localizedDescription)")
return
}
// Loop through the newly filled peripheral.services array, just in case there's more than one.
for service in peripheral.services ?? [] {
peripheral.discoverCharacteristics(nil, for: service)
}
}
Also I've that log from console:
2017-05-21 21:09:32.049137 MyApp[5871:2645747] [Fabric] Unable to locate application icon
2017-05-21 21:09:32.051153 MyApp[5871:2645747] [Crashlytics] Version 3.8.4 (121)
Scanning started
self.peripheralManager powered on.
Connecting to peripheral <CBPeripheral: 0x1700f9100, identifier = B5E6ACBF-307C-4C13-BB6A-09F4A92ABEC3, name = (null), state = connecting>
Problem is what not centralManager: didFailToConnect, not centralManager: didConnect is not called.
Maybe this issue somehow related to null name of peripheral?
UPDATE #1:
I've added
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 10.0) {
print("Connecting to peripheral \(peripheral)")
}
And I get this in the console:
Connecting to peripheral <CBPeripheral: 0x1700f9100, identifier = B5E6ACBF-307C-4C13-BB6A-09F4A92ABEC3, name = (null), state = connecting>
So peripheral still exist after 10 seconds. So it's not a problem with deallocated and strong references.
Related
UPDATE 15/6 - 1 - ALTERNATIVE METHOD
As per Paulw's suggestion, I changed the strategy and instead of discovering for peripherals, I connected with peripheral and it worked fine when either peripheral or central are in foreground. The problem occurs when:
1) I setup notifications on delegate calls.
2) Both peripheral and central are in background.
3) As I bring them closer, I see no delegate calls.
4) As soon as I bring either peripheral or central in foreground, I get didDiscover call and everything works fine even if I now put app in both phones in background.
In short, the central does not discover peripheral if both are in background state.
This is the code for both parts:
func startAsPeripheral() {
peripheralManager = CBPeripheralManager(delegate: self, queue: DispatchQueue.global(qos: .background))
}
func startAsCentral() {
centralManager = CBCentralManager(delegate: self, queue: DispatchQueue.global(qos: .background))
}
}
extension BluetoothManager: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
LogManager.shared.log(object: "Central state update")
LogManager.shared.log(object: central.state)
if central.state != .poweredOn {
LogManager.shared.log(object: "Central is not powered on")
} else {
LogManager.shared.log(object: "Central scanning for: " + self.serviceUUID.uuidString);
self.centralManager.scanForPeripherals(withServices: [self.serviceUUID], options: [CBCentralManagerScanOptionAllowDuplicatesKey : false])
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
self.centralManager.stopScan()
self.peripheral = peripheral
self.peripheral.delegate = self
self.centralManager.connect(self.peripheral, options: nil)
let name = advertisementData[CBAdvertisementDataLocalNameKey] as? String
LogManager.shared.log(object: "Peripheral (" + (name ?? "unknown") + "): " + RSSI.stringValue)
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
if peripheral == self.peripheral {
LogManager.shared.log(object: "Connected.")
peripheral.discoverServices([serviceUUID])
}
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
if peripheral == self.peripheral {
LogManager.shared.log(object: "Disconnected")
self.peripheral = nil
LogManager.shared.log(object: "Central scanning for: " + self.serviceUUID.uuidString);
self.centralManager.scanForPeripherals(withServices: [self.serviceUUID], options: [CBCentralManagerScanOptionAllowDuplicatesKey: false])
}
}
}
extension BluetoothManager: CBPeripheralDelegate {
func peripheral( _ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
LogManager.shared.log(object: "Services: \(peripheral.services!)")
for service in peripheral.services! {
peripheral.discoverCharacteristics(nil, for: service)
}
}
func peripheral( _ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
LogManager.shared.log(object: "Characteristics: \(service.characteristics!)")
}
}
extension BluetoothManager: CBPeripheralManagerDelegate {
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
switch peripheral.state {
case .unknown:
LogManager.shared.log(object: "Bluetooth Device is UNKNOWN")
case .unsupported:
LogManager.shared.log(object: "Bluetooth Device is UNSUPPORTED")
case .unauthorized:
LogManager.shared.log(object: "Bluetooth Device is UNAUTHORIZED")
case .resetting:
LogManager.shared.log(object: "Bluetooth Device is RESETTING")
case .poweredOff:
LogManager.shared.log(object: "Bluetooth Device is POWERED OFF")
case .poweredOn:
LogManager.shared.log(object: "Bluetooth Device is POWERED ON")
let service = serviceUUID
let myService = CBMutableService(type: service, primary: true)
let writeCharacteristics = CBMutableCharacteristic(type: characteristricUUID, properties: .write, value: nil, permissions: .writeable)
myService.characteristics = [writeCharacteristics]
peripheralManager.add(myService)
peripheralManager.startAdvertising([CBAdvertisementDataLocalNameKey : "Spacedemic", CBAdvertisementDataServiceUUIDsKey : [service]])
LogManager.shared.log(object: "Started Advertising")
default:
LogManager.shared.log(object: "Unknown State")
}
}
}
ORIGINAL QUESTION
I have an app used for contact tracing. I configure it to work as both central and peripheral, both should work in background AND foreground. This is for contact tracing, so users won't have the application in foreground state in most cases. Advertising works fine as what I have observed, both in background and foreground. The problem here is that when central is in background, it sometimes gets into didDiscover only one time and sometimes never. Thus if a peripheral is in range, I am unable to detect the RSSI from central or if the user moves away from the central. In contrast, when in foreground, I get frequent updates with RSSI in didDiscover method.
How can I solve this? Or is there a workaround to get the desired results?
I have a device which I connect to using BLE. I start by checking if BLE is powered on, if so, I start scanning. Then after the device is discovered, I store the peripheral in a variable, then I try to connect to it. The connection succeeds. After that, I starting discovering the services and characteristics, and I manage to read them successfully. The problem is whenever I try to write data to the device I always get this error.
Domain=CBErrorDomain Code=7 "The specified device has disconnected from us." UserInfo={NSLocalizedDescription=The specified device has disconnected from us.
Here is my code:
public func centralManagerDidUpdateState(_ central: CBCentralManager)
{
switch central.state {
case .unauthorized:
print("App does not support BLE")
case .poweredOff:
print("Bluetooth is turned off")
case .poweredOn:
print("Bluetooth is turned on")
print("Start scanning peripherals...")
central.scanForPeripherals(withServices: nil, options: nil)
default:
break
}
}
// To receive cell broadcast data
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
let device = Device();
if(device.fromScanData(peripheral: peripheral, name: peripheral.name, rssi: RSSI.intValue, advertisementData: advertisementData))
{
if(device.Name.isEmpty || device.Name.count == 0){
return;
}
if(device.SN.isEmpty || device.SN.count != 8){
return;
}
if self.device.SN == device.SN
{
if !isDeviceConnected
{
print("Stop scanning")
print("Connecting...")
self.peripheral = peripheral
self.peripheral?.delegate = self
bluetoothManager.stopScan()
bluetoothManager.connect(self.peripheral!, options: nil)
}
}
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("Connected successfully");
print("Trying to get equipment services and features...");
device.Peripheral = peripheral
self.peripheral?.discoverServices(nil)
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
print("Failed to connect");
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
print("Connection has been disconnected");
isDeviceConnected = false;
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?){
guard let services = peripheral.services else {
if error != nil
{
print("Did Discover Services Error:\(String(describing: error!))")
}
else
{
print("Empty services")
}
return
}
print("Device Services......");
print("-------------- ("+peripheral.identifier.uuidString+")Service Services information: --------------------");
print("Number of services:\(services.count)")
for service in services
{
print("---- Service UUID:\(service.uuid) -----");
self.peripheral?.discoverCharacteristics(nil, for: service);
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?){
guard let characteristics = service.characteristics else {
if error != nil
{
print("Did Discover Characteristics Error:\(String(describing: error!))")
}
else
{
print("Empty Characteristics")
}
return
}
print("Feature Characteristic UUID List:");
for characteristic in characteristics
{
print("UUID: \(characteristic.uuid)");
self.peripheral?.readValue(for: characteristic);
}
}
func checkToken() {
print("Start Writing")
device.IsSaveOverwrite = true
let characteristic = characteristicList[characteristicHandle.GetCharacteristicUUID(type: .IsSaveOverwrite)]
let data = characteristicHandle.GetValue(device: device, type: .IsSaveOverwrite)
peripheral?.writeValue(data, for: characteristic!, type: CBCharacteristicWriteType.withResponse);
}
//Read the property successfully callback
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if(error != nil)
{
print("Did Update Characteristic with UUID \(characteristic.uuid) Error:\(String(describing: error!))")
return;
}
readConfigs.ConfigRespond(uuid: characteristic.uuid.uuidString);
device = characteristicHandle.SetDevice(device: device, type: characteristicHandle.GetCharacteristicType(uuid: characteristic.uuid), bytes: characteristic.value!);
// Check if all characteristics have been read, if so, start writing
if readConfigs.IsComplete() && readConfiguration == false
{
readConfiguration = true
hideActivityIndicator()
print("Read all characeteristics")
checkToken()
}
}
// Never gets called
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
print("Write Config")
}
Could someone please point out what am missing?
I try to develop an iOS app using Swift. This app will be controlled Texas Instrument's CC2650 module via Bluetooth. I'm using iOS Core Bluetooth library. I took Bluetooth connection codes from
https://github.com/hoiberg/HM10-BluetoothSerial-iOS
import UIKit
import CoreBluetooth
var serial: BluetoothSerial!
final class BluetoothSerial: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
var delegate: BluetoothSerialDelegate!
var centralManager: CBCentralManager!
}
var writeType: CBCharacteristicWriteType = .withResponse
init(delegate: BluetoothSerialDelegate) {
super.init()
self.delegate = delegate
centralManager = CBCentralManager(delegate: self, queue: nil)
}
func startScan() {
guard centralManager.state == .poweredOn else { return }
let uuid = CBUUID(string: "0x180A")
centralManager.scanForPeripherals(withServices: [uuid], options: nil)
let peripherals = centralManager.retrieveConnectedPeripherals(withServices: [uuid])
for peripheral in peripherals {
let StrenghtOfRSSI = peripheral.readRSSI()
delegate.serialDidDiscoverPeripheral(peripheral, RSSI: nil)
}
}
func stopScan() {
centralManager.stopScan()
}
func connectToPeripheral(_ peripheral: CBPeripheral) {
pendingPeripheral = peripheral
centralManager.connect(peripheral, options: nil)
}
func sendMessageToDevice(_ message: String) {
guard isReady else { return }
if let data = message.data(using: String.Encoding.init(rawValue: UInt(message)!)) {
connectedPeripheral?.writeValue(data, for: writeCharacteristic!, type: .withResponse)
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
delegate.serialDidDiscoverPeripheral(peripheral, RSSI: RSSI)
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.delegate = self
pendingPeripheral = nil
connectedPeripheral = peripheral
delegate.serialDidConnect(peripheral)
peripheral.discoverServices([CBUUID(string: "F0001110-0451-4000-B000-000000000000")])
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
connectedPeripheral = nil
pendingPeripheral = nil
delegate.serialDidDisconnect(peripheral, error: error as NSError?)
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
pendingPeripheral = nil
delegate.serialDidFailToConnect(peripheral, error: error as NSError?)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
connectedPeripheral = nil
pendingPeripheral = nil
delegate.serialDidChangeState()
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
for service in peripheral.services! {
peripheral.discoverCharacteristics(nil, for: service)
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
for characteristic in service.characteristics! {
if bluetoothCounter == false {
if characteristic.uuid == CBUUID(string: "F0001112-0451-4000-B000-000000000000") {
peripheral.setNotifyValue(true, for: characteristic)
writeCharacteristic = characteristic
delegate.serialIsReady(peripheral)
}
} else {
if characteristic.uuid == CBUUID(string: "F0001113-0451-4000-B000-000000000000") {
peripheral.setNotifyValue(true, for: characteristic
writeCharacteristic = characteristic
delegate.serialIsReady(peripheral)
}
}
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
let data = characteristic.value
guard data != nil else { return }
delegate.serialDidReceiveData(data!)
if let str = String(data: data!, encoding: String.Encoding.utf8) {
delegate.serialDidReceiveString(str)
} else {
print("Received an invalid string!")
}
}
}
But I have a problem with it. When my app scan for the peripheral, it can not find CC2650 module directly. However, there is something interesting situation happening. When I open BLE Scanner app (https://itunes.apple.com/us/app/ble-scanner-4-0/id1221763603?mt=8) I can discover my CC2650 module and send messages. After that, I open my app I can also discover my CC2650 and can send messages. My app cannot find CC2650 module directly.
I cannot solve this problem. I try everything which I found about Bluetooth connection.
I need some help with it.
Thank you guys.
I would first check to see the status of the BLE adapter using something like:
func centralManagerDidUpdateState(_ central: CBCentralManager) {
var consoleMsg = ""
switch(central.state) {
case .poweredOff:
consoleMsg = "BLE is off"
case .poweredOn:
consoleMsg = "BLE is on"
self.centralManager.scanForPeripherals(withServices: nil, options: nil)
case .resetting:
consoleMsg = "BLE is resetting"
case .unauthorized:
consoleMsg = "BLE is UA"
case .unknown:
consoleMsg = "BLE status unknown"
case .unsupported:
consoleMsg = "BLE is unsupported"
}
self.delegate?.statusUpdated(statusText: consoleMsg)
print("\(consoleMsg) \n", terminator: "")
}
This code will replace your startScan method, if it prints "BLE is on" you are okay and it will start scanning for peripherals.
From your code it looks like you aren't actually connecting to a peripheral either.
For example, you call centralManager.connect(peripheral, options: nil) only within connectToPeripheral and I don't see a call to that from within the serialDidDiscoverPeripheral section.
You need to scan, then register for the didDiscoverPeripheral delegate method, check to see if that peripheral is the one you want (via a UUID or device name, you can get this from the BLE explorer app you are using), then connect to it, then scan for services then interact with them (read, write, notify etc).
You can add:
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if let peripheralName = peripheral.name {
print(peripheralName)
if (peripheralName == "Your peripheral name"){
self.peripheral = peripheral
self.peripheral.delegate = self // This is to subscribe to delegate methods from the peripheral
self.centralManager.stopScan() // Found your peripheral so stop scanning
self.centralManager.connect(self.peripheral, options: nil)
}
}
}
That will connect (and print out to the console) to a peripheral. Then you need to scan for services (you can start this within the didConnect delegate method:
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.delegate = self
peripheral.discoverServices(nil)
}
Then you appear to have the code for discovering services and characteristics so that should all work ok.
Side note:
You call
let peripherals = centralManager.retrieveConnectedPeripherals(withServices: [uuid]) which will only retrieve already connected peripherals, so if you connect to it via another app (e.g. the BLE explorer) that is why it appears in your app.
I'm trying to discover services from a BLE device I've connected to from my iOS app, but when my didDiscoverServices function gets called, the peripheral services array is empty.
The app connects to the device, runs peripheral.discoverServices(nil) and then the didDiscoverServices function gets called, but there are no services returned.
I've read a number of bluetooth related answers and other examples online and as best I can tell, my code is no different to what it should be, except it is not working. I feel like I must've missed something somewhere but I'm not sure what.
I've added my console log below for what I get when running the code, and the bluetooth code at the bottom for reference.
Bluetooth initialised
BLE is powered on
Optional("Nordic_Blinky") found at -72
Scanning stopped
Connect request sent
Connected to <CBPeripheral: 0x1c0301a70, identifier = 0887CF7F-98C8-3FCF-2D10-873FFFFB2B65, name = Nordic_Blinky, state = connected>
Discovering services
Services -- Optional([]) and Error -- nil
My BluetoothHandler class code is below
class BluetoothHandler : NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
// MARK: Properties
var manager: CBCentralManager!
var targetPeripheral: CBPeripheral!
// MARK: Shared Instance
static let sharedInstance = BluetoothHandler()
private override init() {
super.init()
self.startManager()
print("Bluetooth initialised")
}
// MARK: Functions
func startManager() {
manager = CBCentralManager(delegate: self, queue: nil)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
var consoleMessage = ""
switch (central.state) {
case.poweredOn:
consoleMessage = "BLE is powered on"
manager.scanForPeripherals(withServices: nil, options: nil)
case.poweredOff:
consoleMessage = "BLE is powered off"
case.resetting:
consoleMessage = "BLE Resetting"
case.unknown:
consoleMessage = "BLE state unknown"
case.unsupported:
consoleMessage = "Device not supported by BLE"
case.unauthorized:
consoleMessage = "BLE not authorised"
}
print("\(consoleMessage)")
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if (peripheral.name == "Nordic_Blinky") {
print("\(String(describing: peripheral.name)) found at \(RSSI)")
self.stopScan()
if targetPeripheral != peripheral {
targetPeripheral = peripheral
targetPeripheral!.delegate = self
manager.connect(targetPeripheral, options: nil)
print("Connect request sent")
}
}
else if (peripheral.name != nil) {
print("\(String(describing: peripheral.name))")
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("Connected to \(peripheral)")
peripheral.delegate = self
peripheral.discoverServices(nil)
print("Discovering services")
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Any) {
print("Connection failed", error)
}
func centralManager(_ central: CBCentralManager, didDisconnect peripheral: CBPeripheral) {
if self.targetPeripheral != nil {
self.targetPeripheral!.delegate = nil
self.targetPeripheral = nil
}
print("Connection disconnected")
self.startManager()
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
print("Services -- \(peripheral.services) and Error -- \(error)")
if let services = peripheral.services {
for service in services {
peripheral.discoverCharacteristics(nil, for: service)
}
}
}
Try this: swift 3
If your peripheral(BLE device) have services then print in log.
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
if let services = peripheral.services as [CBService]!{
for service in services{
peripheral.discoverCharacteristics(nil, for: service)
}
}
print("==>",peripheral.services!)
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
for newChar: CBCharacteristic in service.characteristics!{
print(newChar)
}
}
Using CoreBluetooth on iOS(10.3) i'm unable to read a characteristics value when bonded(paired?) to a custom BLE HID Device. If I remain unbonded I can read the characteristic fine.
The BLE device implements HID over GATT(HOGP) and an additional service with a single notify characteristic. This additional service/characteristic is what I'm trying to read, not the HID over GATT data which I know is filtered out.
It appears that didDiscoverServices never gets called. I do see didConnect being called.
I'm able to accomplish this on Android so I don't think it's an issue with the BLE device.
Relevant code below:
func centralManagerDidUpdateState(_ central: CBCentralManager) {
var showAlert = true
var message = ""
switch central.state {
case .poweredOff:
message = NSLocalizedString("Bluetooth on this device is currently powered off.", comment: "")
case .unsupported:
message = NSLocalizedString("This device does not support Bluetooth Low Energy.", comment: "")
case .unauthorized:
message = NSLocalizedString("This app is not authorized to use Bluetooth Low Energy.", comment: "")
case .resetting:
message = NSLocalizedString("The BLE Manager is resetting; a state update is pending.", comment: "")
case .unknown:
message = NSLocalizedString("The state of the BLE Manager is unknown.", comment: "")
case .poweredOn:
showAlert = false
message = NSLocalizedString("Bluetooth LE is turned on and ready for communication.", comment: "")
let lastPeripherals = centralManager.retrieveConnectedPeripherals(withServices: [CBUUID(string: Device.DeviceServiceUUID)])
print("count: \(lastPeripherals.count)")
if lastPeripherals.count > 0{
let device = lastPeripherals.last!;
connectingPeripheral = device;
centralManager.connect(connectingPeripheral, options: nil)
} else {
centralManager.scanForPeripherals(withServices: [CBUUID(string: "7340")], options: nil)
}
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if let peripheralName = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
if peripheralName == deviceName {
// save a reference to the device
device= peripheral
device!.delegate = self
// Request a connection to the peripheral
centralManager.connect(device!, options: nil)
}
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.discoverServices(nil)
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
if error != nil {
print("****** DISCONNECTION DETAILS: \(error!.localizedDescription)")
}
device = nil
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
if error != nil {
print("ERROR DISCOVERING SERVICES: \(error?.localizedDescription)")
return
}
if let services = peripheral.services {
for service in services {
print("DISCOVERED SERVICE: \(service)")
if (service.uuid == CBUUID(string: Device.DeviceServiceUUID)) {
peripheral.discoverCharacteristics(nil, for: service)
}
}
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
if error != nil {
print("ERROR DISCOVERING CHARACTERISTICS: \(error?.localizedDescription)")
return
}
if let characteristics = service.characteristics {
for characteristic in characteristics {
// Message Data Characteristic
if characteristic.uuid == CBUUID(string: Device.MessageCharacteristicUUID) {
// Enable the message notifications
messageCharacteristic = characteristic
device?.setNotifyValue(true, for: characteristic)
}
}
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if error != nil {
print("ERROR ON UPDATING VALUE FOR CHARACTERISTIC: \(characteristic) - \(error?.localizedDescription)")
return
}
// extract the data from the characteristic's value property and display the value based on the characteristic type
if let dataBytes = characteristic.value {
if characteristic.uuid == CBUUID(string: Device.MessageCharacteristicUUID) {
displayMessage(dataBytes)
}
}
}