流れ
まず、CoreBluetooth.frameworkを追加する。
import CoreBluetooth
CBPeripheralManagerを生成。
peripheralManager = CBPeripheralManager(delegate: self, queue: nil)
stateが変化したらdelegateメソッドが呼ばれるので.poweredOn
であることを確認できれば
Managerの準備は完了。
public func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager){
switch (peripheral.state){
case .poweredOn:
print("PeripheralManager state is ok")
ready = true
default:
print("PeripheralManager state is ng:", peripheral.state)
ready = false
}
}
Characteristicを作成。CBCharacteristicProperties.read.union(CBCharacteristicProperties.notify)
で、
Centralが読みにくることも、通知を受け取ることもできるようにし、CBAttributePermissions.readable
でreadのみ許可する。
このvalueをnilにしておかないと、キャッシュされあとで変更できなくなる。
characteristic = CBMutableCharacteristic(
type: CHARACTERISTIC_UUID,
properties: CBCharacteristicProperties.read.union(CBCharacteristicProperties.notify),
value:nil,
permissions:CBAttributePermissions.readable)
このCharacteristicのServiceを作成し、Managerに登録する。
let service = CBMutableService(type: SERVICE_UUID, primary: true)
service.characteristics = [characteristic]
peripheralManager!.add(service)
ready = true
public func peripheralManager(_ peripheral: CBPeripheralManager, didAdd service: CBService, error: Error?){
if(error != nil){
print("Add Service error:", error)
}else{
print("Add Service ok")
}
}
ServiceをAdvertiseする。
peripheral.startAdvertising([
CBAdvertisementDataLocalNameKey: LOCAL_NAME,
CBAdvertisementDataServiceUUIDsKey: [SERVICE_UUID]
])
public func peripheralManagerDidStartAdvertising(_ peripheral: CBPeripheralManager, error: Error?){
if(error != nil){
print("Start Advertising error:", error)
}else{
print("Start Advertising ok")
}
}
通知要求してきたCentralに向けて通知する。onSubscribedCentrals: nil
で全てのCentralに送る。
peripheralManager!.updateValue(d, for: characteristic, onSubscribedCentrals: nil)
読みにきたときのdelegateメソッド。
public func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveRead request: CBATTRequest){
if (request.characteristic.uuid.isEqual(self.characteristic.uuid)) {
if let value = self.characteristic.value{
if (request.offset > value.count) {
peripheral.respond(to: request, withResult: CBATTError.invalidOffset)
print("Read fail: invalid offset")
return;
}
}
request.value = self.characteristic.value?.subdata(
in: Range(uncheckedBounds: (request.offset, (self.characteristic.value?.count)! - request.offset))
)
peripheral.respond(to: request, withResult: CBATTError.success)
print("Read success")
}else{
print("Read fail: wrong characteristic uuid:", request.characteristic.uuid)
}
}
全体
エラーハンドリングは適当だが、とりあえず動くものができた。
import CoreBluetooth
class BLEPeripheral : NSObject, CBPeripheralManagerDelegate {
let CHARACTERISTIC_UUID = CBUUID(string:"5F83E23F-BCA1-42B3-B6F2-EA82BE46A93D")
let SERVICE_UUID = CBUUID(string:"BF9CB85F-620C-4A67-BDD2-1A64213F74CA")
let LOCAL_NAME = "my-ble"
private var peripheralManager : CBPeripheralManager?
private var characteristic: CBMutableCharacteristic
private var ready = false
public override init(){
characteristic = CBMutableCharacteristic(
type: CHARACTERISTIC_UUID,
properties: CBCharacteristicProperties.read.union(CBCharacteristicProperties.notify),
value:nil,
permissions:CBAttributePermissions.readable)
super.init()
peripheralManager = CBPeripheralManager(delegate: self, queue: nil)
}
public func update(_ data: Data?) -> Bool {
if(ready){
if let d = data{
characteristic.value = d
return peripheralManager!.updateValue(d, for: characteristic, onSubscribedCentrals: nil)
}else{
print("data is null")
}
}else{
print("not ready")
}
return false
}
/*
CBPeripheralManagerDelegate
*/
public func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager){
switch (peripheral.state){
case .poweredOn:
print("PeripheralManager state is ok")
let service = CBMutableService(type: SERVICE_UUID, primary: true)
service.characteristics = [characteristic]
peripheralManager!.add(service)
ready = true
default:
print("PeripheralManager state is ng:", peripheral.state)
ready = false
}
}
public func peripheralManager(_ peripheral: CBPeripheralManager, didAdd service: CBService, error: Error?){
if(error != nil){
print("Add Service error:", error)
}else{
print("Add Service ok")
peripheral.startAdvertising([
CBAdvertisementDataLocalNameKey: LOCAL_NAME,
CBAdvertisementDataServiceUUIDsKey: [SERVICE_UUID]
])
}
}
public func peripheralManagerDidStartAdvertising(_ peripheral: CBPeripheralManager, error: Error?){
if(error != nil){
print("Start Advertising error:", error)
}else{
print("Start Advertising ok")
}
}
public func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveRead request: CBATTRequest){
var value: Data?
switch request.characteristic.uuid {
case characteristic.uuid:
value = characteristic.value
default: break
}
if let v = value{
if (request.offset > v.count) {
peripheral.respond(to: request, withResult: CBATTError.invalidOffset)
print("Read fail: invalid offset")
return;
}
request.value = v.subdata(
in: Range(uncheckedBounds: (request.offset, v.count - request.offset))
)
peripheral.respond(to: request, withResult: CBATTError.success)
print("Read success")
}else{
print("Read fail: wrong characteristic uuid:", request.characteristic.uuid)
}
}
public func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic){
print("Subscribe to", characteristic.uuid)
}
}
現在の時間を1秒ごとに更新して通知がくることを確認した。
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.update), userInfo: nil, repeats: true)
timer!.fire()
public func update(){
let now = String(format: "%.0f", arguments: [Date().timeIntervalSince1970])
peripheral.update(now.data(using: String.Encoding.utf8))
}