iOS Bluetooth Low Energy (BLE) not discovering TX characteristic - ios

I am using an an arduino feather BLE board and trying to create an iOS app that can send data to the board over BLE. I can connect but I can't get access to the txCharacteristic
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
guard let characteristics = service.characteristics else {
return
}
for characteristic in characteristics {
if characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse) {
writableCharacteristic = characteristic
}
// if(characteristic.uuid == CBUUID(string: "6e400002-b5a3-f393-e0a9-e50e24dcca9e"))
// {
// txCharacteristic = characteristic
// }
var txUUID = CBUUID(string: "6e400002-b5a3-f393-e0a9-e50e24dcca9e")
let temp = characteristic.uuid.uuidString;
switch characteristic.uuid {
case txUUID:
txCharacteristic = characteristic;
break;
default:
break;
}
peripheral.setNotifyValue(true, for: characteristic)
}
}
This code works, but only discovers the following UUIDs:
temp String "00001532-1212-EFDE-1523-785FEABCD123"
temp String "00001531-1212-EFDE-1523-785FEABCD123"
temp String "00001534-1212-EFDE-1523-785FEABCD123"
I have figured out that these UUID's are DFU UUIDs. How can I discover the txCharacteristic instead?
Added for more information how I am calling discoverCharacteristics():
extension SimpleBluetoothIO: CBPeripheralDelegate {
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
guard let services = peripheral.services else {
return
}
targetService = services.first
if let service = services.first {
targetService = service
peripheral.discoverCharacteristics(nil, for: service)
}
}

I was able to figure this out, by reading info at this website:
https://learn.adafruit.com/getting-started-with-the-nrf8001-bluefruit-le-breakout/adding-app-support
Particularly this part (notice that UART is the service and TX and RX are characteristics of the service):
UART Service UUID: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E
TX Characteristic UUID: 6E400002-B5A3-F393-E0A9-E50E24DCCA9E
RX Characteristic UUID: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E
So I needed to call peripheral.discoverServices() with the UART Service ID. In my example below self.serviceUUID = "6e400001-b5a3-f393-e0a9-e50e24dcca9e"
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
//peripheral.discoverServices(nil)
//peripheral.discoverServices([CBUUID(string: "6e400001-b5a3-f393-e0a9-e50e24dcca9e")])
// Disover peripheral with service UUID that was given to us during initialization (self.serviceUUID)
peripheral.discoverServices([CBUUID(string: self.serviceUUID)])
}
Then in didDiscoverCharacteristicsFor service I needed to look for the TX Characteristic ID:
// Did Discover Characteristics
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
guard let characteristics = service.characteristics else {
return
}
for characteristic in characteristics {
if characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse) {
writableCharacteristic = characteristic
}
// TX Characteristic UUID: 6E400002-B5A3-F393-E0A9-E50E24DCCA9E
// https://learn.adafruit.com/getting-started-with-the-nrf8001-bluefruit-le-breakout/adding-app-support
let txUUID = CBUUID(string: "6e400002-b5a3-f393-e0a9-e50e24dcca9e")
switch characteristic.uuid {
case txUUID:
txCharacteristic = characteristic;
break;
default:
break;
}
peripheral.setNotifyValue(true, for: characteristic)
}
}

Related

Reading long attributed value from bluetooth characteristics

public func peripheral(_ peripheral: CBPeripheral,
didDiscoverCharacteristicsFor
service: CBService, error: Error?) {
if (service.uuid.uuidString == "FFA1") {
for characteristic in service.characteristics! {
peripheral.setNotifyValue(true, for: characteristic)
if (characteristic.uuid.uuidString == "FFF3") {
peripheral.readValue(for: characteristic)
print("Found Device Name Characteristic")
print("\(characteristic)")
if let value = characteristic.value {
print("data: \(value as NSData)")
}
}
}
}
}
I am able to read only 22 bytes of data from the above code. But the peripheral device is sending 128 bytes of data. I wanted to read the 128 bytes data at once instead of reading it by chunks.

BLE shows connected even when 'didDisconnectPeripheral' is already called

I am trying to disconnect from a BLE device (raspberry-pi3), the delegates for disconnection are being triggered and upon checking explicitly if the peripheral is disconnected it shows that the peripheral is indeed disconnected. But, when I see in the settings app, I still see the device is connected.
Following is what I am trying:
Setup CBCentralManager as I come to the page.
func setupBLE() {
lblStatus.text = "Establising connection to the Scrubport"
self.manager = CBCentralManager(delegate: self, queue: .main)
}
Handle any state change if any
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
self.dismissAlert()
self.showLoader()
self.timer = Timer.scheduledTimer(withTimeInterval: 30, repeats: false, block: { _ in
// No device found. Stop scanning.
})
self.manager?.scanForPeripherals(withServices: [SERVICE_UUID])
return
}
else if central.state == .unauthorized {
// Permission handlers here:
return
}
// Handle all other cases.
}
Upon find the device try connecting
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
self.peripheral = peripheral
connect()
}
func connect() {
guard let peripheral = peripheral else {
// Making sure we got the peripheral.
return
}
lblStatus.text = "Connecting..."
self.manager?.connect(peripheral)
}
Upon connection discover the service
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
lblStatus.text = "Connected, preparing device for transfer..."
self.timer.invalidate()
central.stopScan()
peripheral.delegate = self
peripheral.discoverServices([SERVICE_UUID])
}
Find the characteristics next
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
if let services = peripheral.services {
for service in services {
if service.uuid == SERVICE_UUID {
peripheral.discoverCharacteristics([CHARACTERISTICS_UUID], for: service)
}
}
}
}
Request notification
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
if let characteristics = service.characteristics {
for characteristic in characteristics {
if characteristic.uuid == self.CHARACTERISTICS_UUID {
self.characteristic = characteristic
peripheral.setNotifyValue(true, for: characteristic)
}
}
}
}
Upon getting an update for notification change I do the write and it is all successful.
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
if characteristic.isNotifying {
self.stopLoader()
lblStatus.text = "Notifying now.."
} else {
print("Un-notified")
print("Trying disconnection")
manager?.cancelPeripheralConnection(peripheral)
}
}
At the end I want to disconnect, or handle if app is closed or pressed back mid way
Trying clean up using following code:
fileprivate func cleanup() {
print("CLEANING")
guard let manager = manager else {
return
}
print("Check scan.")
if manager.isScanning {
print("Stopping scan.")
manager.stopScan()
}
// Don't do anything if we're not connected
// self.discoveredPeripheral.isConnected is deprecated
print("Checking peripheral connection")
guard peripheral?.state == .connected else {
print("No peripheral connected.")
return
}
// See if we are subscribed to a characteristic on the peripheral
print("Checking services")
guard let services = peripheral?.services else {
print("No services connection found.")
cancelPeripheralConnection()
return
}
print("Looping services")
for service in services {
print("Checking characteristics")
guard let characteristics = service.characteristics else {
print("No characteristics")
continue
}
print("Looping characteristics")
for characteristic in characteristics {
print("Comparing characteristics UUID is notifying")
if characteristic.uuid.isEqual(CHARACTERISTICS_UUID) && characteristic.isNotifying {
print("Un-notifying")
peripheral?.setNotifyValue(false, for: characteristic)
} else {
print("Nope not the one.", characteristic.isNotifying)
}
}
}
}
fileprivate func cancelPeripheralConnection() {
print("Remove peripheral connection")
guard let manager = manager, let peripheral = peripheral else {
print("Manager or peripheral not found!")
return
}
print("Cancelling peripheral connection.")
// If we've got this far, we're connected, but we're not subscribed, so we just disconnect
manager.cancelPeripheralConnection(peripheral)
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
print("SERVICE INFO: didDisconnectPeripheral", peripheral.name ?? "Unknown")
print("Was there an error?", error ?? "No error")
self.peripheral = nil
let peripherals = central.retrieveConnectedPeripherals(withServices: [])
let _peri = central.retrievePeripherals(withIdentifiers: [peripheral.identifier])
_peri.forEach { per in
print(per.state == .connected, "connected")
print(per.state == .connecting, "connecting")
print(per.state == .disconnected, "disconnected")
print(per.state == .disconnecting, "disconnecting")
}
print(peripherals)
}
Following are the logs that get printed on cleanup():
CLEANING
Check scan.
Checking peripheral connection
Checking services
Looping services
Checking characteristics
Looping characteristics
Comparing characteristics UUID is notifying
Un-notifying
Un-notified
Trying disconnection
SERVICE INFO: didDisconnectPeripheral raspberrypi-cm3
Was there an error? No error
false connected
false connecting
true disconnected
false disconnecting
[]
But this what I see in the settings app after clean up.

Getting CBATTErrorDomain Code=6 The request is not supported error while writing value to peripheral

I created my custom peripheral to write data to peripheral and tried to write data from my central.
when write value function is executed i am getting request is not supported error.
Here is my code.Hope you understand my problem.
Looking for the solution to fix.
Thanks in advance.
connectedPeripheral?.writeValue(data, for: characteristic, type: .withResponse)
Setup up my custom BLEPeripheral and startAdvertising in Peripheral
// MARK: CBPeripheralManagerDelegate
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
if peripheral.state == .poweredOn {
setup()
} else {
print("peripheral is not available: \(peripheral.state.rawValue)")
}
}
func peripheralManager(_ peripheral: CBPeripheralManager, didAdd service: CBService, error: Error?) {
if let error = error {
print("Could not add service: \(error.localizedDescription)")
} else {
print("peripheral added service. Start advertising")
let advertisementData: [String: Any] = [
CBAdvertisementDataServiceUUIDsKey: [CBUUID(string: BLEIdentifiers.serviceIdentifier)],
CBAdvertisementDataLocalNameKey: "BLE Sensor" // This key will not be transmitted when app is backgrounded
]
manager.startAdvertising(advertisementData)
}
}
func peripheralManagerDidStartAdvertising(_ peripheral: CBPeripheralManager, error: Error?) {
if let error = error {
print("Could not start advertising: \(error.localizedDescription)")
} else {
print("peripheral started advertising")
}
}
func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveRead request: CBATTRequest) {
print("Did receive read request: \(request)")
if !request.characteristic.uuid.isEqual(characteristic.uuid) {
peripheral.respond(to: request, withResult: .requestNotSupported)
} else {
guard let value = characteristic.value else {
peripheral.respond(to: request, withResult: .invalidAttributeValueLength)
return
}
if request.offset > value.count {
peripheral.respond(to: request, withResult: .invalidOffset)
} else {
request.value = value.subdata(in: request.offset..<value.count-request.offset)
peripheral.respond(to: request, withResult: .success)
}
}
}
func setup() {
let characteristicUUID = CBUUID(string: BLEIdentifiers.characteristicIdentifier)
characteristic = CBMutableCharacteristic(type: characteristicUUID, properties: [.read, .write,.notify], value: nil, permissions: [.readable,.writeable])
let descriptor = CBMutableDescriptor(type: CBUUID(string: CBUUIDCharacteristicUserDescriptionString), value: "BLESensor prototype")
characteristic.descriptors = [descriptor]
let serviceUUID = CBUUID(string: BLEIdentifiers.serviceIdentifier)
let service = CBMutableService(type: serviceUUID, primary: true)
service.characteristics = [characteristic]
manager.add(service)
}
Writing data to Peripheral in Central
func writeDataToPeripheral(data: Data){
if let characteristics = ctService?.characteristics {
for characteristic in characteristics {
if characteristic.uuid == CBUUID(string: BLEIdentifiers.characteristicIdentifier) {
if characteristic.properties.contains(.write) {
connectedPeripheral?.writeValue(data, for: characteristic, type: .withResponse)
}
}
}
}
}
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
guard error == nil else {
print("Error discovering didWriteValueFor: error", error.debugDescription)
//Getting Error Domain=CBATTErrorDomain Code=6 "The request is not supported."
return
}
print("Message sent")
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
if let error = error {
print("peripheral failed to discover services: \(error.localizedDescription)")
} else {
peripheral.services?.forEach({ (service) in
print("service discovered: \(service)")
peripheral.discoverCharacteristics([CBUUID(string: BLEIdentifiers.characteristicIdentifier)], for: service)
})
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
if let error = error {
print("NSA A peripheral failed to discover characteristics: \(error.localizedDescription)")
} else {
ctService = service
service.characteristics?.forEach({ (characteristic) in
print("NSA A characteristic discovered: \(characteristic)")
if characteristic.uuid == CBUUID(string: BLEIdentifiers.characteristicIdentifier) {
// keep a reference to this characteristic so we can write to it
writeCharacteristic = characteristic
}
if characteristic.properties.contains(.read) {
peripheral.readValue(for: characteristic)
}
peripheral.discoverDescriptors(for: characteristic)
})
}
}
In order to support writes, you must implement the didReceiveWrite method in your CBPeripheralManagerDelegate.
Since you don't have this method, you get a "not supported" response to your write request.

CBPeripheral services are not rediscovered properly after change

I have an iOS Swift 5 project on which CBCentralManager is used to connect to a BLE device. There is a write CBCharacteristic which should be used to write a certain value from the iOS device and after a successful write, the device changes its services adding a new service into its GATT table. CoreBluetooth seems to not recognize the updated services. The BLE device is paired with iOS.
Things tried and what happened:
If you don't implement didModifyServices from CBPeripheralDelegate, CoreBluetooth complains that the services changed but you don't have a method to handle these changes
If you implement didModifyServices, it never gets called and the above message never gets shown
Tried manually rediscovering services after a successful write but the CBPerpiheral returns the old service only, despite the BLE device actually having another one now in the GATT table, as if CoreBluetooth caches the old services and doesn't actually rediscover them
Tried disconnecting and discarding the CBPeripheral instance, in order to rediscover it and reconnect - in this case, the peripheral never gets rediscovered again after disconnecting.
Any ideas what could be possibly done to be able to rediscover a new service?
Relevant CoreBluetooth code:
enum BluetoothStateChange
{
case unknown
case unsupported
case unauthorized
case poweredOff
}
class BluetoothCentralService: NSObject
{
var onStateChange: ((BluetoothStateChange) -> Void)?
var onServicesScannedPeripheralsUpdate: ((_ peripherals: [UUID]) -> Void)?
var onCharacteristicsDiscovered: ((_ peripheral: UUID, _ service: CBUUID, _ characteristics: [CBUUID]) -> Void)?
var onCharacteristicWriteFinished: ((_ peripheral: UUID, _ characteristic: CBUUID, _ isSuccessful: Bool) -> Void)?
var onCharacteristicReadFinished: ((_ peripheral: UUID, _ characteristic: CBUUID, _ value: Data?) -> Void)?
private var connectingPeripherals = Set<CBPeripheral>()
private var connectedPeripherals = Set<CBPeripheral>()
private var servicesDiscoveredPeripherals = Set<CBPeripheral>()
private var ignoredPeripherals = Set<CBPeripheral>()
private let central: CBCentralManager
private let logger: LoggerProtocol
var rememberedDeviceUUID: UUID?
init(central: CBCentralManager, logger: LoggerProtocol, rememberedDeviceUUID: UUID? = nil)
{
self.central = central
self.logger = logger
self.rememberedDeviceUUID = rememberedDeviceUUID
super.init()
central.delegate = self
}
func ignore(_ uuid: UUID)
{
guard
let peripheral = connectedPeripherals.first(where: { $0.identifier == uuid })
?? servicesDiscoveredPeripherals.first(where: { $0.identifier == uuid })
else {
return
}
ignoredPeripherals.insert(peripheral)
servicesDiscoveredPeripherals.remove(peripheral)
central.cancelPeripheralConnection(peripheral)
}
func services(for uuid: UUID) -> [CBUUID]
{
return servicesDiscoveredPeripherals.first { $0.identifier == uuid }?.services?.map { $0.uuid } ?? []
}
func discoverCharacteristics(for peripheralUuid: UUID, service uuid: CBUUID)
{
guard
let peripheral = servicesDiscoveredPeripherals.first(where: { $0.identifier == peripheralUuid }),
let service = peripheral.service(with: uuid)
else {
return
}
peripheral.discoverCharacteristics(nil, for: service)
}
func write(
to peripheralUuid: UUID,
service serviceUuid: CBUUID,
characteristic characteristicUuid: CBUUID,
value: Data,
type: CBCharacteristicWriteType
){
guard
let peripheral = servicesDiscoveredPeripherals.first(where: { $0.identifier == peripheralUuid }),
let service = peripheral.service(with: serviceUuid),
let characteristic = service.characteristics?.first(where: { $0.uuid == characteristicUuid })
else {
return
}
peripheral.writeValue(value, for: characteristic, type: type)
}
func read(from peripheralUuid: UUID, service serviceUuid: CBUUID, characteristic characteristicUuid: CBUUID)
{
guard
let peripheral = servicesDiscoveredPeripherals.first(where: { $0.identifier == peripheralUuid }),
let service = peripheral.service(with: serviceUuid),
let characteristic = service.characteristics?.first(where: { $0.uuid == characteristicUuid })
else {
return
}
peripheral.readValue(for: characteristic)
}
}
extension BluetoothCentralService: CBCentralManagerDelegate
{
func centralManagerDidUpdateState(_ central: CBCentralManager)
{
switch central.state
{
case .resetting:
break
case .unknown:
onStateChange?(.unknown)
case .unsupported:
onStateChange?(.unsupported)
case .unauthorized:
onStateChange?(.unauthorized)
case .poweredOff:
onStateChange?(.poweredOff)
connectedPeripherals = []
servicesDiscoveredPeripherals = []
ignoredPeripherals = []
onServicesScannedPeripheralsUpdate?([])
case .poweredOn:
scanRemembered()
default:
logger.log(.warning, "\(#function): Unhandled state type.")
}
}
private func scanRemembered()
{
guard
let uuid = rememberedDeviceUUID,
let peripheral = central.retrievePeripherals(withIdentifiers: [uuid]).first
else {
scanConnected(); return
}
connect(peripheral)
}
private func scanConnected()
{
let connected = central.retrieveConnectedPeripherals(withServices: [])
connectedPeripherals.formUnion(connected)
connectedPeripherals.forEach { $0.discoverServices(nil) }
scanNew()
}
private func scanNew()
{
central.scanForPeripherals(withServices: nil)
}
private func connect(_ peripheral: CBPeripheral)
{
guard
!ignoredPeripherals.contains(peripheral),
!connectingPeripherals.contains(peripheral),
!connectedPeripherals.contains(peripheral),
!servicesDiscoveredPeripherals.contains(peripheral)
else {
return
}
connectingPeripherals.insert(peripheral)
peripheral.delegate = self
central.connect(peripheral)
}
func centralManager(
_ central: CBCentralManager,
didDiscover peripheral: CBPeripheral,
advertisementData: [String : Any],
rssi RSSI: NSNumber
){
guard
!ignoredPeripherals.contains(peripheral),
!connectingPeripherals.contains(peripheral),
!connectedPeripherals.contains(peripheral),
!servicesDiscoveredPeripherals.contains(peripheral)
else {
return
}
connect(peripheral)
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral)
{
connectingPeripherals.remove(peripheral)
connectedPeripherals.insert(peripheral)
peripheral.discoverServices(nil)
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?)
{
connectedPeripherals.remove(peripheral)
}
}
extension BluetoothCentralService: CBPeripheralDelegate
{
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?)
{
guard error == nil else { return }
servicesDiscoveredPeripherals.insert(peripheral)
connectedPeripherals.remove(peripheral)
onServicesScannedPeripheralsUpdate?(servicesDiscoveredPeripherals.map { $0.identifier })
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?)
{
onCharacteristicsDiscovered?(
peripheral.identifier,
service.uuid,
service.characteristics?.compactMap { $0.uuid } ?? []
)
}
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?)
{
onCharacteristicWriteFinished?(peripheral.identifier, characteristic.uuid, error == nil)
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?)
{
onCharacteristicReadFinished?(peripheral.identifier, characteristic.uuid, characteristic.value)
}
func peripheral(_ peripheral: CBPeripheral, didModifyServices invalidatedServices: [CBService])
{
// This never gets called if it's actually implemented here!
}
}

iOS 11 Core Bluetooth restoration not working

I have an iOS app that uses Core Bluetooth to connect to a V.BTTN smart button. Everything worked perfectly fine in iOS 10, but since iOS 11 has come out the restoration process seems to break.
When I launch my app, with a previously paired button, the OS does in fact call the centralManager:willRestoreState, and the accompanying dictionary includes a pointer to a CBPeripheral object that has a status of connected. Just as it did back in iOS 10. However, the problem I am running into is when I call the discoverServices on the peripheral I am never returned any services in the peripheral:didDiscoverServices method. In fact that method, nor any other method, is called at all.
I have been searching all of the internet and have found people having similar issues with button pairing an connections, but those have generally been issues with improper lifecycle management of the peripheral object. I believe I have everything setup correct and at a loss. Does anyone have any idea what could be going on here?
import Foundation
import CoreBluetooth
class SmartButtonManager: NSObject {
//MARK: - Singleton
static let sharedManager = SmartButtonManager()
//MARK: - Properties
var discoveredDevices: [CBPeripheral] {
get {
return perfs
}
}
fileprivate(set) var isBluetoothOn = false
//MARK: - Private Constants & Variables
fileprivate var connectedButtonUUID: String?
fileprivate let queue = DispatchQueue(label: "V.BTTN", qos: .background, attributes: .concurrent, autoreleaseFrequency: .inherit, target: nil)
fileprivate var manager: CBCentralManager?
fileprivate var perfs = [CBPeripheral]()
fileprivate var connectedPerf: CBPeripheral?
fileprivate var isButtonReady = false
//MARK: - Initialization
override init() {
super.init()
}
//MARK: - Configure
func configure(withLaunchOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) {
// create new central manager
manager = CBCentralManager(delegate: self, queue: queue, options: [CBCentralManagerOptionRestoreIdentifierKey: managerIdentifier])
}
}
//MARK: - V.BTTN
extension SmartButtonManager: CBCentralManagerDelegate, CBPeripheralDelegate {
func scanForVbttn() {
perfs.removeAll()
manager?.scanForPeripherals(withServices: services, options: nil)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOff:
print("[centralManagerDidUpdateState] CB BLE hardware is powered off")
perfs.removeAll()
isBluetoothOn = false
case .poweredOn:
print("[centralManagerDidUpdateState] CB BLE hardware is powered on. Start scanning for peripherals")
isBluetoothOn = true
case .unauthorized:
print("[centralManagerDidUpdateState] CB BLE hardware is not authorized")
case .unsupported:
print("[centralManagerDidUpdateState] CB BLE hardware is not supported")
isBluetoothOn = false
default:
print("[centralManagerDidUpdateState] CB BLE hardware state is unknown")
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
print("[centralManager:didDiscover peripheral:advertisementData] CB BLE did discover peripheral with advertisement data: \(advertisementData)")
guard let perfName = advertisementData[CBAdvertisementDataLocalNameKey] as? String else {
print("[centralManager:didDiscover peripheral:advertisementData] peripheral name is unknown")
return
}
if perfName.contains("V.ALRT") {
peripheral.delegate = self
perfs.append(peripheral)
if connectedButtonUUID == perfName {
connect(peripheral: peripheral)
}
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("[central:didConnect peripheral:] CB BLE hardware did connect")
handleDidConnect(toPeripheral: peripheral)
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
print("[central:didFailToConnect error:] CB BLE peripheral did failed to connect with error: \(String(describing: error))")
connectedPerf = nil
isButtonReady = false
connectedButtonUUID = nil
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
print("[central:didDisconnectPeripheral peripheral:] CB BLE peripheral did disconnect")
connectedPerf = nil
isButtonReady = false
connectedButtonUUID = nil
}
func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) {
print("[central:willRestoreState dict:] CB BLE hardware will restore state")
print("\(dict)")
guard let ps = dict[CBCentralManagerRestoredStatePeripheralsKey] as? [CBPeripheral] else {
print("[central:willRestoreState dict:] No perfs to restore")
return
}
print("[central:willRestoreState dict:] Will restore perfs")
perfs = ps
print("[central:willRestoreState dict:] Attempt to reconnect to V.BTTN")
print("[central:willRestoreState dict:] perfs \(perfs)")
for p in perfs {
if p.name == connectedButtonUUID {
print("[central:willRestoreState dict:] Connect to perf \(p)")
handleDidConnect(toPeripheral: p)
break
}
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
guard let services = peripheral.services else {
print("[peripheral:didDiscoverServices error:] CB BLE peripheral unable to discover services")
return
}
print("[peripheral:didDiscoverServices error:] BLE peripheral did discover services")
for s in services {
print("[peripheral:didDiscoverServices error:] CB BLE Service \(s.description)")
peripheral.discoverCharacteristics(nil, for: s)
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
print("[peripheral:didDiscoverCharacteristicsFor service:] CB BLE did discover characteristics for service \(service.uuid.description)")
if compareCBUUID(uuid1: service.uuid, uuid2: CBUUID(string: BLE_VSN_GATT_SERVICE_UUID)) {
guard let characteristics = service.characteristics else {
return
}
for aChar in characteristics {
// write the verification key
if aChar.uuid.isEqual(CBUUID(string: BLE_VERIFICATION_SERVICE_UUID)) {
self.writeVerificationKey(forPeripheral: peripheral, characteristic: aChar)
self.enable(peripheral: peripheral)
break
}
}
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
//NOTE: This code has been omitted as there is little need to see this
}
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
print("[peripheral:didWriteValueFor characteristic:] characteristic: \(characteristic.uuid)")
if let e = error {
print("[peripheral:didWriteValueFor characteristic:] error: \(e)")
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
if !characteristic.isNotifying {
print("[peripheral:didUpdateNotificationStateFor:][Characteristic is not notifiying so cancel the connection]")
manager?.cancelPeripheralConnection(peripheral)
}
}
//MARK: - Helpers
fileprivate func writeVerificationKey(forPeripheral peripheral: CBPeripheral, characteristic: CBCharacteristic) {
print("[peripheral:didDiscoverCharacteristicsFor service:] Write verification key")
let data = NSData(bytes: [0x80,0xBE,0xF5,0xAC,0xFF] as [UInt8], length: 5)
peripheral.writeValue(data as Data, for: characteristic, type: CBCharacteristicWriteType.withResponse)
}
fileprivate func handleDidConnect(toPeripheral peripheral: CBPeripheral) {
if let n = peripheral.name {
connectedButtonUUID = n
}
connectedPerf = peripheral
peripheral.delegate = self
peripheral.discoverServices(nil)
isButtonReady = true
}
fileprivate func enable(peripheral: CBPeripheral) {
guard let services = peripheral.services else {
return
}
for service: CBService in services {
if compareCBUUID(uuid1: service.uuid, uuid2: CBUUID(string: BLE_VSN_GATT_SERVICE_UUID)) {
guard let characteristics = service.characteristics else {
return
}
for aChar in characteristics {
if aChar.uuid.isEqual(CBUUID(string: BLE_KEYPRESS_DETECTION_UUID)) {
// enable button
print("[peripheral:didDiscoverCharacteristicsFor service:] Enable short press")
let data = NSData(bytes: [0x02] as [UInt8], length: 1)
peripheral.writeValue(data as Data, for: aChar, type: CBCharacteristicWriteType.withResponse);
} else if aChar.uuid.isEqual(CBUUID(string: BLE_SILENT_NORMAL_MODE)) {
// enable button
print("[peripheral:didDiscoverCharacteristicsFor service:] Enable normal mode")
let data = NSData(bytes: [0x00] as [UInt8], length: 1)
peripheral.writeValue(data as Data, for: aChar, type: CBCharacteristicWriteType.withResponse);
} else if aChar.uuid.isEqual(CBUUID(string: BLE_FALL_KEYPRESS_DETECTION_UUID)) {
// enable fall detection
print("[peripheral:didDiscoverCharacteristicsFor service:] Enable fall detection")
peripheral.setNotifyValue(true, for: aChar)
}
}
}
}
checkBatteryLevel()
}
func disconnectVbttn() {
guard let peripheral = connectedPerf else {
return
}
setVbttnToStealthMode()
manager?.cancelPeripheralConnection(peripheral)
isButtonReady = false
}
fileprivate func compareCBUUID(uuid1: CBUUID, uuid2: CBUUID) -> Bool {
if (uuid1.data as NSData).isEqual(to: uuid2.data) {
return true
}
return false
}
func stopScanning() {
manager?.stopScan()
}
}

Resources