Objective-Cをまだよく理解できてない初心者の私が、SwiftでBLE接続するまでの情報をまとめてみました。
個人メモに近いので間違いがあればご容赦くださいませ。
実行環境
- Mac OS X Yosemite 10.7.5
- Xcode version 6.0.1
- iPhone6plus + iOS 8.1.2
前提知識
BLE(Bluetooth Low Energy)の仕組みについて基本だけでも理解する必要があります。
私はApple公式ドキュメント Core Bluetooth プログラミングガイド が非常にわかりやすかったです。
まずはひと通り目を通して以下のキーワードを理解するとよいです。
- セントラルとペリフェラル
- サービスとキャラクタリスティック
- アドバタイズ
やりたかったこと
- mbed-HRM1017にBLE接続してLED点灯(Lチカ)
→mbed HRM1017側はwrite用のサービス"FFF0"、キャラクタリスティック"FFF1"と仮定。mbed側のプログラムは対象外。
概要説明
iOSアプリのおおまかな流れは以下になります。
1-1. CentralManagerの起動
1-2. CentralManager状態の受信
2-1. Peripheral探索開始
2-2. Peripheral探索結果の受信(複数あれば複数回)
3-1. 指定したPeripheralへ接続開始
3-2. Peripheralへの接続結果の受信
4-1. 利用可能Serviceの探索開始
4-2. Service探索結果の受信
5-1. 利用可能Characteristicの探索開始
5-2. Characterristic探索結果の受信
----ここからは用途に応じて----
<Writeの場合>
6-1. 指定データの書き込み
6-2. 書き込み結果の受信
<Notifyの場合>
7-1. 指定データの書き込み
7-2. 書き込み結果の受信
ソースコード
- StoryBoardで作成したためViewControllerのソースコードのみ記載します。
- あくまで流れを理解するためのサンプルです、コピペはオススメしません。
- 引数にnilを設定するなどApple非推奨の内容も含まれます、ご注意を。
import UIKit
import CoreBluetooth
class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate {
var centralManager: CBCentralManager!
private var peripheralArray = [CBPeripheral]()
private var serviceArray = [CBService]()
private var characteristicArray = [CBCharacteristic]()
@IBOutlet weak var scanStatus: UILabel!
@IBOutlet weak var scanStatusText: UITextView!
@IBOutlet weak var connectStatus: UILabel!
@IBOutlet weak var sendText: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// ボタン押下でCoreBluetoothを起動
@IBAction func pushStartBtn(sender: AnyObject) {
// 1-1. CentralManagerの起動
self.centralManager = CBCentralManager(delegate: self, queue: nil, options: nil)
}
// 1-2. CentralManager状態の受信
func centralManagerDidUpdateState(central: CBCentralManager!) {
switch (central.state) {
case .PoweredOff:
println("BLE PoweredOff")
case .PoweredOn:
println("BLE PoweredOn")
// 2-1. Peripheral探索開始
central.scanForPeripheralsWithServices(nil, options: nil)
/* ↑の第1引数はnilは非推奨。
該当サービスのCBUUIDオブジェクトの配列が望ましい */
case .Resetting:
println("BLE Resetting")
case .Unauthorized:
println("BLE Unauthorized")
case .Unknown:
println("BLE Unknown")
case .Unsupported:
println("BLE Unsupported")
}
}
// 2-2. Peripheral探索結果の受信(複数あれば複数回)
func centralManager(central: CBCentralManager!,
didDiscoverPeripheral peripheral: CBPeripheral!,
advertisementData: [NSObject : AnyObject]!,
RSSI: NSNumber!){
println("name: \(peripheral.name)")
println("UUID: \(peripheral.identifier.UUIDString)")
println("advertisementData: \(advertisementData)")
println("RSSI: \(RSSI)")
// 配列に追加
self.peripheralArray.append(peripheral as CBPeripheral)
}
// リストから該当Peripheralを選択し接続を開始
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// 省電力のためスキャンを停止
self.centralManager.stopScan()
// 3-1. 指定したPeripheralへ接続開始
self.centralManager.connectPeripheral(peripheralArray[indexPath.row], options: nil)
}
// 3-2. Peripheralへの接続結果の受信(成功時)
func centralManager(central: CBCentralManager!,
didConnectPeripheral peripheral: CBPeripheral!)
{
println("connection success!")
// デリゲートの設定
peripheral.delegate = self
// 今回はサービスを"FFF0"に指定
let UUID = CBUUID(string: "FFF0")
// 4-1. 利用可能Serviceの探索開始
peripheral.discoverServices([UUID])
/* ↑の引数はnilは非推奨。*/
}
// 3-2. Peripheralへの接続結果の受信(失敗時)
func centralManager(central: CBCentralManager!,
didFailToConnectPeripheral peripheral: CBPeripheral!,
error: NSError!)
{
println("connection failed")
}
// 4-2. Service探索結果の受信
func peripheral(peripheral: CBPeripheral!, didDiscoverServices error: NSError!) {
if (error != nil) {
println("error: \(error)")
return
}
for service in peripheral.services
{
self.serviceArray.append(service as CBService)
// 5-1. 利用可能Characteristicの探索開始
peripheral.discoverCharacteristics(nil, forService:service as CBService)
/* ↑の第1引数はnilは非推奨。*/
}
}
// 5-2. Characterristic探索結果の受信
func peripheral(peripheral: CBPeripheral!,
didDiscoverCharacteristicsForService service: CBService!,
error: NSError!)
{
if (error != nil) {
println("error: \(error)")
return
}
for characteristic in service.characteristics {
self.characteristicArray.append(characteristic as CBCharacteristic)
}
}
// ボタン押下で書き込み開始
@IBAction func pushWriteBtn(sender: AnyObject) {
// 書き込むデータを仮で設定
var value: UInt = 0x0000FF80808080808000
let data: NSData = NSData(bytes: &value, length: 10)
// 6-1. 指定データの書き込み
[peripheral .writeValue(data, forCharacteristic: characteristicArray[0], type: CBCharacteristicWriteType.WithResponse)]
}
// 6-2. 書き込み結果の受信
func peripheral(peripheral: CBPeripheral!,
didWriteValueForCharacteristic characteristic: CBCharacteristic!,
error: NSError!)
{
if (error != nil) {
println("Write...error: \(error)")
return
}
println("Write success!")
}
}
感想
- サンプルは多数出回っていますが基本はどれも同じ流れです。
- 自分が躓いたのは受信したPeripheralやサービスを保存しておく必要がある点です。これさえ抑えておけば後はどうにかなるかなと。