Edited at

iOS SwiftでBLEのサンプルを動かしてみる

More than 1 year has passed since last update.


はじめに

BLEとの接続について触れてみたいと思い、

「iOS × BLE Core Bluetoothプログラミング」のサンプル(Heart Rate)を

テキストに沿って実装してみました。


基礎知識

Core BluetoothフレームワークというBLEを実装したデバイスとの通信をするクラス郡が用意されている。

(iOS5以上サポート)

用語
説明

セントラル
データを提供される側(クライアント側のイメージ)

ペリフェラル
データを提供する側(サーバー側のイメージ)


■Core Bluetoothプログラミングガイドから引用

スクリーンショット 2017-05-24 18.53.52.png

今回は、セントラル側だけの話をします。

ここからは、セントラルがペリフェラルからデータを定期的に取得する流れを

下記に説明します。


ペリフェラルからデータを取得までのフロー

①セントラルマネージャーを起動する。

②ペリフェラルを検出する。

③ペリフェラルと接続する。

④ペリフェラルのサービスを検出する。

⑤サービス特性を検出する。

⑥サービス特性の値を取得する。


■Core Bluetoothプログラミングガイドから引用

サービスとサービス特性の関係性は、下図のとおりです。

スクリーンショット 2017-05-24 18.55.09.png


主な登場人物

用語
説明

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を利用しています。


ViewController.swift

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と違い、用語等知らない点が多いため、少しずつ学習していきたいと思います。


参考

Core Bluetooth with Swift (ObjCのおまけ付き)