はじめに
BLEとの接続について触れてみたいと思い、
「iOS × BLE Core Bluetoothプログラミング」のサンプル(Heart Rate)を
テキストに沿って実装してみました。
基礎知識
Core BluetoothフレームワークというBLEを実装したデバイスとの通信をするクラス郡が用意されている。
(iOS5以上サポート)
用語 | 説明 |
---|---|
セントラル | データを提供される側(クライアント側のイメージ) |
ペリフェラル | データを提供する側(サーバー側のイメージ) |
■Core Bluetoothプログラミングガイドから引用
今回は、セントラル側だけの話をします。
ここからは、セントラルがペリフェラルからデータを定期的に取得する流れを
下記に説明します。
ペリフェラルからデータを取得までのフロー
①セントラルマネージャーを起動する。
②ペリフェラルを検出する。
③ペリフェラルと接続する。
④ペリフェラルのサービスを検出する。
⑤サービス特性を検出する。
⑥サービス特性の値を取得する。
■Core Bluetoothプログラミングガイドから引用
サービスとサービス特性の関係性は、下図のとおりです。
主な登場人物
用語 | 説明 |
---|---|
CBCentralManager | セントラル側のクラス |
CBPeripheral | ペリフェラル側のクラス |
CBService | サービスのクラス |
CBCharacteristic | サービスの特性 |
CBCentralManagerDelegate | 電源のON/OFFの検出、ペリフェラルの検出/接続 |
CBPeripheralDelegate | サービスの検出、サービス特性の取得 |
データを取得までのフローとソースコードの関係性
①セントラルマネージャーを起動する。
centralManager = CBCentralManager(delegate: self, queue: nil)
②ペリフェラルを検出する。
電源がONになるのを待って、スキャンする。
let services: [CBUUID] = [serviceUUID]
centralManager?.scanForPeripherals(withServices: services,
options: nil)
ちなみに、電源のONは、
CBCentralManagerDelegateのcentralManagerDidUpdateStateメソッドで通知される。
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
//電源ONのとき
case CBManagerState.poweredOn:
break
default:
break
}
}
③ペリフェラルと接続する。
ペリフェラルを検出したら、検出したペリフェラルに接続する。
central.connect(peripheral, options: nil)
ちなみに、ペリフェラルの検出は、CBCentralManagerDelegateの下記のメソッドで通知される。
func centralManager(_ central: CBCentralManager,
didDiscover peripheral: CBPeripheral,
advertisementData: [String : Any],
rssi RSSI: NSNumber) {
}
④ペリフェラルのサービスを検出する。
接続されたら、接続したペリフェラルのサービスを検出する。
peripheral.discoverServices([serviceUUID])
ちなみに、接続されると、CBCentralManagerDelegateの下記のメソッドで通知される。
func centralManager(_ central: CBCentralManager,
didConnect peripheral: CBPeripheral) {
}
⑤サービス特性を検出する。
サービスを検出したら、サービスの特性を検出する。
peripheral.discoverCharacteristics([charcteristicUUID],
for: (peripheral.services?.first)!)
ちなみに、サービスが検出されると、CBPeripheralDelegateの下記のメソッドが呼ばれる。
func peripheral(_ peripheral: CBPeripheral,
didDiscoverServices error: Error?) {
}
⑥サービス特性の値を取得する。
サービス特性が発見されると、CBPeripheralDelegateの下記のメソッドが呼ばれる。
func peripheral(_ peripheral: CBPeripheral,
didDiscoverCharacteristicsFor service: CBService,
error: Error?) {
}
データが更新されると、CBPeripheralDelegateの下記のメソッドが呼ばれる
func peripheral(_ peripheral: CBPeripheral,
didUpdateValueFor characteristic: CBCharacteristic,
error: Error?) {
// characteristic.valueから値を取得する(Date型)
}
サンプル
心拍数モニターアプリを写経し、Swift3に変換してみました。
セントラル側は、本アプリです。
ペリフェラル側は、Light BlueのHeart Rateを利用しています。
import UIKit
import CoreBluetooth
//Central : 本アプリ
//Peripheral : Light Blue
final class ViewController: UIViewController {
//GATTサービス(Heart Rate) https://www.bluetooth.com/ja-jp/specifications/gatt/services
let kServiveUUIDHeartRate = "0x180D"
//Attribute Types (UUIDs)
let kCharacteristcUUIDHeartRateMeasurement = "0x2A37"
var centralManager: CBCentralManager!
var peripheral: CBPeripheral!
var serviceUUID : CBUUID!
var charcteristicUUID: CBUUID!
override func viewDidLoad() {
super.viewDidLoad()
setup()
}
/// セントラルマネージャー、UUIDの初期化
private func setup() {
centralManager = CBCentralManager(delegate: self, queue: nil)
serviceUUID = CBUUID(string: kServiveUUIDHeartRate)
charcteristicUUID = CBUUID(string: kCharacteristcUUIDHeartRateMeasurement)
}
}
//MARK : - CBCentralManagerDelegate
extension ViewController: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
//電源ONを待って、スキャンする
case CBManagerState.poweredOn:
let services: [CBUUID] = [serviceUUID]
centralManager?.scanForPeripherals(withServices: services,
options: nil)
default:
break
}
}
/// ペリフェラルを発見すると呼ばれる
func centralManager(_ central: CBCentralManager,
didDiscover peripheral: CBPeripheral,
advertisementData: [String : Any],
rssi RSSI: NSNumber) {
self.peripheral = peripheral
centralManager?.stopScan()
//接続開始
central.connect(peripheral, options: nil)
}
/// 接続されると呼ばれる
func centralManager(_ central: CBCentralManager,
didConnect peripheral: CBPeripheral) {
peripheral.delegate = self
peripheral.discoverServices([serviceUUID])
}
}
//MARK : - CBPeripheralDelegate
extension ViewController: CBPeripheralDelegate {
/// サービス発見時に呼ばれる
func peripheral(_ peripheral: CBPeripheral,
didDiscoverServices error: Error?) {
if error != nil {
print(error.debugDescription)
return
}
//キャリアクタリスティク探索開始
peripheral.discoverCharacteristics([charcteristicUUID],
for: (peripheral.services?.first)!)
}
/// キャリアクタリスティク発見時に呼ばれる
func peripheral(_ peripheral: CBPeripheral,
didDiscoverCharacteristicsFor service: CBService,
error: Error?) {
if error != nil {
print(error.debugDescription)
return
}
peripheral.setNotifyValue(true,
for: (service.characteristics?.first)!)
}
/// データ更新時に呼ばれる
func peripheral(_ peripheral: CBPeripheral,
didUpdateValueFor characteristic: CBCharacteristic,
error: Error?) {
if error != nil {
print(error.debugDescription)
return
}
updateWithData(data: characteristic.value!)
}
private func updateWithData(data : Data) {
print(#function)
let reportData = data.withUnsafeBytes {
[UInt8](UnsafeBufferPointer(start: $0, count: data.count))
}
/// Format Bitが0 or 1
if (reportData.first != nil) && 0x01 == 0 {
print("BPM: \(reportData.last!)")
} else {
print("BPM : \(CFSwapInt16LittleToHost(UInt16(reportData.last!)))")
}
}
}
まとめ
今回は、BLEとの接続の感触をみるために、セントラルのみを触ってみました。
HTTP/HTTPSと違い、用語等知らない点が多いため、少しずつ学習していきたいと思います。