SwiftでCorebluetoothを使ってKonashiのLEDをチカチカさせる

iPhone6Sを買ったしSwiftでいろいろと遊んでます。
今回は以前買ったKonashi v2をKonashiSDKを使わないでCorebluetoothでLチカさせてみました。 こちらの本"iOSxBLE Core Bluetoothプログラミング"で学びながら作ってます。

開発環境
開発環境 : XCode7.2
使用機器 : iPhone6S, iOS9.2
Konashi : Konashi v2(koshian)

アプリケーションが実行する処理の流れ

  1. アプリに配置したスキャンボタンを押して、周辺のペリフェラル(Konashiとか)を検索。
  2. ペリフェラルを発見したら接続。
  3. ペリフェラルと接続できたら、ペリフェラルが提供するサービスを検索。
  4. 提供しているサービスを見つけたら、サービスに含まれるキャラクタリスティックを検索。
  5. 目的のキャラクタリスティックを発見したら、そのキャラクタリスティックをプロパティに保持しておく。
  6. アプリに配置したスイッチボタンでキャラクタリスティックの値を書き換える。
  7. キャラクタリスティックの値が変更されるとLEDが点灯/消灯します。
//
//  ViewController.swift
//  Corebluetooth LED Blinking Example
//
//  Created by miokato on 1/20/16.
//  Copyright © 2016 miok. All rights reserved.
//

import UIKit
import CoreBluetooth

class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate {

    var isScanning: Bool = false
    var isBlink: Bool = false
    var centralManager: CBCentralManager!
    var peripheral: CBPeripheral!
    var settingCharacteristic: CBCharacteristic!
    var outputCharacteristic: CBCharacteristic!
        
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.centralManager = CBCentralManager(delegate: self, queue: nil)
    }
    
    // ===============================================================================
    // MARK: Actions
    
    // スキャン
    @IBAction func scanBtnTapped(sender: UIButton) {
        if !isScanning {
            isScanning = true
            self.centralManager.scanForPeripheralsWithServices(nil, options: nil)
            sender.setTitle("Stop Scan", forState: UIControlState.Normal)
        }
        else {
            isScanning = false
            self.centralManager.stopScan()
            sender.setTitle("Start Scan", forState: UIControlState.Normal)
        }
    }

    
    // LEDスイッチ
    @IBAction func writeValue(sender: UIButton) {
        if self.settingCharacteristic == nil || self.outputCharacteristic == nil {
            print("konashi is not ready!")
            return
        }
        var value: CUnsignedChar
        
        if !isBlink {
            isBlink = true
            sender.setTitle("Turn Off", forState: UIControlState.Normal)
            // LED2を光らせる
            value = 0x01 << 1
            
        }
        else {
            isBlink = false
            sender.setTitle("Turn On", forState: UIControlState.Normal)
            // LED2を消す
            value = 0x00 << 1
        }
        let data: NSData = NSData(bytes: &value, length: 1)
        
        
        self.peripheral.writeValue(data,
            forCharacteristic: self.settingCharacteristic,
            type: CBCharacteristicWriteType.WithoutResponse)
        
        self.peripheral.writeValue(data,
            forCharacteristic: self.outputCharacteristic,
            type: CBCharacteristicWriteType.WithoutResponse)
    }
    
    // ===============================================================================
    // MARK: CBCentral Manager Delegate
    
    // セントラルマネージャの状態変化があると呼ばれる
    func centralManagerDidUpdateState(central: CBCentralManager) {
        print("state: \(central.state)")
    }
    
    // ペリフェラルが見つかると呼ばれる
    func centralManager(central: CBCentralManager,
        didDiscoverPeripheral peripheral: CBPeripheral,
        advertisementData: [String : AnyObject],
        RSSI: NSNumber)
    {
        print("発見したBLEデバイス: \(peripheral)\n")
        
        if peripheral.name?.hasPrefix("konashi") == true {
            self.peripheral = peripheral
            // 接続開始
            self.centralManager.connectPeripheral(self.peripheral, options: nil)
        }

    }
    
    // ペリフェラルに接続したら呼ばれる
    func centralManager(central: CBCentralManager,
        didConnectPeripheral peripheral: CBPeripheral)
    {
        print("接続成功")
        
        // サービス検索結果を受け取るデリゲートをセット
        peripheral.delegate = self
        // サービス検索開始
        peripheral.discoverServices(nil)
    }
    
    // ペリフェラルへの接続が失敗すると呼ばれる
    func centralManager(central: CBCentralManager,
        didFailToConnectPeripheral peripheral: CBPeripheral,
        error: NSError?)
    {
        print("接続失敗...")
    }
    
    // ===============================================================================
    // MARK: CBPeripheralDelegate
    
    // サービス発見したら呼ばれる
    func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?) {

        if error != nil {
            print("エラー: \(error)")
            return
        }
        
        if peripheral.services?.count <= 0 {
            print("no services")
            return
        }
        
        let services = peripheral.services!
        print("\(services.count)個のサービスを発見しました。\n\(services)\n")
        
        for service in services {
            peripheral.discoverCharacteristics(nil , forService: service)
        }

    }
    
    // キャラクタリスティックを取得したら呼ばれる
    func peripheral(peripheral: CBPeripheral,
        didDiscoverCharacteristicsForService service: CBService,
        error: NSError?)
    {
        if error != nil {
            print("エラー: \(error)")
            return
        }
        
        if service.characteristics?.count <= 0 {
            print("no characteristics")
            return
        }
        
        let characteristics = service.characteristics!
        print("\(characteristics.count)個のキャラクタリスティックを発見しました。\n\(characteristics)\n")
        
        for characteristic in characteristics {

            if characteristic.UUID.isEqual(CBUUID(string: "229B3000-03FB-40DA-98A7-B0DEF65C2D4B")) {
                self.settingCharacteristic = characteristic
                print("KONASHI_PIO_SETTING_UUID を発見")
            } else if characteristic.UUID.isEqual(CBUUID(string: "229B3002-03FB-40DA-98A7-B0DEF65C2D4B")) {
                self.outputCharacteristic = characteristic
                print("KONASHI_PIO_OUTPUT_UUID を発見")
            }
        }
    }
    
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}