Created
May 3, 2018 16:40
-
-
Save erica/68670079a4c2799a88ba18521f76a40f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// BTHelper.swift | |
// Buzz | |
// | |
// Created by Erica Sadun on 4/17/18. | |
// Copyright © 2018 Erica Sadun. All rights reserved. | |
// | |
import Foundation | |
import CoreBluetooth | |
public class BTHelper: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { | |
// BLE Access | |
var centralManager: CBCentralManager | |
override init() { | |
self.centralManager = CBCentralManager(delegate: nil, queue: nil) | |
super.init() | |
self.centralManager.delegate = self | |
} | |
@objc | |
public func shutOff() { | |
if let target = targetPeripheral { | |
centralManager.cancelPeripheralConnection(target) | |
} | |
} | |
@objc | |
public func startUp() { | |
if let target = targetPeripheral { | |
centralManager.connect(target, options: [:]) | |
} | |
} | |
// Required. Invoked when the central manager’s state is updated. | |
public func centralManagerDidUpdateState(_ manager: CBCentralManager) { | |
switch manager.state { | |
case .poweredOff: | |
print("BLE has powered off") | |
centralManager.stopScan() | |
case .poweredOn: | |
print("BLE is now powered on") | |
centralManager.scanForPeripherals(withServices: nil, options: nil) | |
case .resetting: print("BLE is resetting") | |
case .unauthorized: print("Unauthorized BLE state") | |
case .unknown: print("Unknown BLE state") | |
case .unsupported: print("This platform does not support BLE") | |
} | |
} | |
var targetPeripheral: CBPeripheral? = nil | |
// Invoked when the central manager discovers a peripheral while scanning. | |
public func centralManager(_ manager: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData advertisement: [String : Any], rssi: NSNumber) { | |
guard let name = peripheral.name | |
else { print("Skipping unnamed service"); return } | |
guard name.hasPrefix("MI") | |
else { print("Skipping \"\(name)\" service"); return } | |
// RSSI is Received Signal Strength Indicator | |
print("Found \"\(name)\" peripheral (RSSI: \(rssi))") | |
print("Advertisement data:", advertisement, "\n") | |
// Attempt connection and service scan | |
print("Scan stopped.\n") | |
print("Attempting to connect to \(name)\n") | |
centralManager.stopScan() | |
targetPeripheral = peripheral | |
targetPeripheral?.delegate = self | |
centralManager.connect(peripheral, options: nil) | |
} | |
// Invoked when a connection is successfully created with a peripheral. | |
public func centralManager(_ manager: CBCentralManager, didConnect peripheral: CBPeripheral) { | |
if let name = peripheral.name { | |
print("\"\(name)\" has connected.") | |
print("Requesting service discovery.\n") | |
peripheral.discoverServices(nil) | |
} | |
} | |
// Invoked when you discover the peripheral’s available services. | |
public func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { | |
if let services = peripheral.services { | |
for service in services { | |
peripheral.discoverCharacteristics(nil, for: service) | |
} | |
} | |
} | |
var vibrationCharacteristic: CBCharacteristic? = nil | |
// Invoked when you discover the characteristics of a specified service. | |
public func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { | |
guard let characteristics = service.characteristics | |
else { print("Unable to retrieve service characteristics"); return } | |
for characteristic in characteristics { | |
let name = characteristic.uuid.uuidString | |
switch name { | |
case "2A06": | |
print("Available: Vibration") | |
vibrationCharacteristic = characteristic | |
let note = Notification(name: Notification.Name(rawValue: name)) | |
NotificationCenter.default.post(note) | |
default: | |
break | |
} | |
} | |
} | |
// Invoked when you write data to a characteristic’s value. | |
public func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) { | |
print("Peripheral did write characteristic value for " + characteristic.uuid.uuidString) | |
} | |
// Invoked when you retrieve a specified characteristic’s value, or when the peripheral device notifies your app that the characteristic’s value has changed. | |
public func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { | |
print("Peripheral did update characteristic value for " + characteristic.uuid.uuidString) | |
guard let data = characteristic.value | |
else { print("missing updated value"); return } | |
print(data.count, "bytes received") | |
switch characteristic.uuid.uuidString { | |
case "FF0F": | |
func evaluatePairing(_ data: Data) { | |
let result = data.withUnsafeBytes({ (ptr: UnsafePointer<Int16>) in | |
ptr.withMemoryRebound(to: Int16.self, capacity: 1) { pointer in | |
return pointer.pointee | |
} | |
}) | |
print("Pairing result:", result) | |
} | |
evaluatePairing(data) | |
default: | |
break | |
} | |
} | |
// External vibration consumption | |
/// Available vibration patterns | |
// - SeeAlso: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.alert_level.xml | |
public enum Pattern { case low, high } | |
public func vibrate(degree: Pattern) { | |
guard let characteristic = vibrationCharacteristic | |
else { return } | |
switch degree { | |
case .low: | |
targetPeripheral?.writeValue(Data([0x1]), for: characteristic, type: .withoutResponse) | |
case .high: | |
targetPeripheral?.writeValue(Data([0x2]), for: characteristic, type: .withoutResponse) | |
} | |
self.perform(#selector(shutOff), with: nil, afterDelay: 2.0) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment