Skip to content

Instantly share code, notes, and snippets.

@UniversalSuperBox
Created September 3, 2019 20:47
Show Gist options
  • Save UniversalSuperBox/4240dca31984afda7028a7c3d522cde5 to your computer and use it in GitHub Desktop.
Save UniversalSuperBox/4240dca31984afda7028a7c3d522cde5 to your computer and use it in GitHub Desktop.
Find and print out lots of information about nearby BLE devices in Swift
//Copyright 2019 Dalton Durst
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
// This file demostrates how to discover and connect to BLE devices, then
// discover services, included services, characteristics, and descriptors.
import CoreBluetooth
// Add a Bluetooth UUID here to connect to a device.
// The UUID of every detected device is output as 'identifier' when this file
// is run with IDENTIFIER = nil.
let IDENTIFIER: String? = nil
/**
Handles many CBPeripheral events by printing their relevant info to console.
Check the full documentation of CBPerhipheralDelegate to find out more about
each method.
*/
class DebugCBPeripheralDelegate: NSObject, CBPeripheralDelegate {
override init() {
super.init()
print("Created new DebugCBPeripheralDelegate")
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
if let err = error {
print(err)
return
}
if let services = peripheral.services {
print("Found the services \(services) on \(peripheral).")
for service in services {
peripheral.discoverIncludedServices(nil, for: service)
peripheral.discoverCharacteristics(nil, for: service)
}
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverIncludedServicesFor service: CBService, error: Error?) {
if let err = error {
print(err)
return
}
if let includedServices = service.includedServices {
print("Service \(service) includes services: \(includedServices)")
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
if let err = error {
print(err)
return
}
if let characteristics = service.characteristics {
print("Service \(service) includes characteristics: \(characteristics)")
for characteristic in characteristics {
peripheral.discoverDescriptors(for: characteristic)
}
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverDescriptorsFor characteristic: CBCharacteristic, error: Error?) {
if let err = error {
print(err)
return
}
if let descriptors = characteristic.descriptors {
print("Characteristic \(characteristic) has descriptors \(descriptors)")
}
}
func peripheralDidUpdateName(_ peripheral: CBPeripheral) {
print("\(peripheral)'s name changed.")
}
func peripheral(_ peripheral: CBPeripheral, didReadRSSI RSSI: NSNumber, error: Error?) {
print("\(peripheral)'s RSSI has changed to \(RSSI)")
}
}
/**
Communicates with the CoreBluetooth Central manager, asking it to search for
local BLE devices and acting on the results of those searches.
*/
class BLEFinder: NSObject, CBCentralManagerDelegate {
override init() {
super.init()
centralManager = CBCentralManager(delegate: self, queue: nil)
}
/**
Scanning for local peripherals can only happen when centralManager.state
is poweredOn, so we make sure that's the case before we start.
This is invoked whenever the manager's state changes, such as when it
becomes unauthorized or powers off. We'll only begin scanning when it
turns on, though.
*/
func centralManagerDidUpdateState(_ central: CBCentralManager) {
print("state: \(central.state.rawValue)")
if centralManager.state == CBManagerState.poweredOn {
centralManager.scanForPeripherals(withServices: nil, options: nil)
}
}
/**
This gets invoked whenever the manager find a peripheral during a scan.
The time varies, but this will keep printing the same information over
and over again. Take note of how the data changes over time, this can be
particularly useful to understanding BLE. For example, notice how the
first scan of a device may return 'nil' for the name, but subsequent
scans return the name correctly.
If IDENTIFIER is set at the top of this file and we find a peripheral
whose UUID matches it, we connect to that peripheral and stop scanning.
*/
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
print("Found peripheral \(peripheral) with RSSI \(RSSI) and advertising the following data: \(advertisementData).")
// .name != nil is mainly to distinguish the device we're connecting to
// in the console output. If it was omitted, the code should function
// almost identically.
if peripheral.identifier.uuidString == IDENTIFIER && peripheral.name != nil {
connectTo(peripheral: peripheral)
}
}
/**
This gets invoked when the manager connects to a peripheral.
Since we're just trying to get information about the device we're
investigating, we'll start searching for services on it.
*/
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("Successfully connected to \(peripheral). Now I'll find its services.")
// We need to keep strong references to the delegate.
// If we don't, it'll get deallocated and we won't receive the data
// we need.
self.peripheralDelegate = DebugCBPeripheralDelegate()
self.peripheral = peripheral
peripheral.delegate = self.peripheralDelegate!
peripheral.discoverServices(nil)
}
func connectTo(peripheral: CBPeripheral) {
print("Connecting to \(peripheral)")
self.peripheral = peripheral
// We stop scanning to reduce the output to the console. Scanning can
// stay on while you connect if you prefer.
centralManager.stopScan()
centralManager.connect(peripheral, options: nil)
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
if let err = error {
print("Failed to connect to perhipheral \(peripheral): \(err)")
}
}
var centralManager: CBCentralManager!
var peripheral: CBPeripheral?
var peripheralDelegate: CBPeripheralDelegate?
}
var finder = BLEFinder()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment