ズンドコキヨシ with Bluetooth Low Energy

既に [多くの言語・技術でズンドコキヨシ]((http://qiita.com/shunsugai@github/items/971a15461de29563bf90)) が試みられていますが、Bluetooth Low Energy (以下BLE) 版はまだないみたいなので、やってみました。


<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">ズンドコキヨシ with Bluetooth Low Energy <a href="https://t.co/dFRVwUjiqv">pic.twitter.com/dFRVwUjiqv</a></p>&mdash; Tsutsumi Shuichi (@shu223) <a href="https://twitter.com/shu223/status/715946579585314820">2016年4月1日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>


ズンドコキヨシカルチャーの発祥ツイート：

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">Javaの講義、試験が「自作関数を作り記述しなさい」って問題だったから<br>「ズン」「ドコ」のいずれかをランダムで出力し続けて「ズン」「ズン」「ズン」「ズン」「ドコ」の配列が出たら「キ・ヨ・シ！」って出力した後終了って関数作ったら満点で単位貰ってた</p>&mdash; てくも (@kumiromilk) <a href="https://twitter.com/kumiromilk/status/707437861881180160">2016年3月9日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>


ちなみに iOS / Core Bluetooth です。 Swift バージョンは 2.2


##GATT

ズンドコキヨシGATTを以下のように定義しました。（各UUID は`uuidgen` で生成）

###Services

####ズンドコキヨシService

- UUID: B8A18F4F-2A2B-4084-AC2D-1ABC289EF05D
- Characteristics
  - ズンドコCharacteristic
  - キヨシCharacteristic

```swift:GATT.swift
enum Service : Int {
    case ズンドコキヨシ
    
    func UUID() -> CBUUID {
        switch self {
        case .ズンドコキヨシ:
            return CBUUID(string: "B8A18F4F-2A2B-4084-AC2D-1ABC289EF05D")
        }
    }
    
    func mutableService() -> CBMutableService {
        return CBMutableService(type: UUID(), primary: true)
    }
}
```

###Characteristics

####ズンドコCharacteristic

- UUID: C2F0882D-2440-40F4-A4A9-95BE43B89EAE
- Properties: Read, Notify
- value: `ズン` or `ドコ`

####キヨシCharacteristic

- UUID: C2F0882E-2440-40F4-A4A9-95BE43B89EAE
- Properties: Write
- value: 0x00 [^1]

[^1]: 「キ・ヨ・シ！」と文字列を送るのはBLEの思想的にマッチしないのでとりあえず1バイトの値を送る、という仕様にしてます。


```swift:GATT.swift
enum Characteristic : Int {
    case ズンドコ
    case キヨシ
    
    func UUID() -> CBUUID {
        switch self {
        case .ズンドコ:
            return CBUUID(string: "C2F0882D-2440-40F4-A4A9-95BE43B89EAE")
        case .キヨシ:
            return CBUUID(string: "C2F0882E-2440-40F4-A4A9-95BE43B89EAE")
        }
    }
    
    private func properties() -> CBCharacteristicProperties {
        switch self {
        case .ズンドコ:
            return [CBCharacteristicProperties.Notify, CBCharacteristicProperties.Read]
        case .キヨシ:
            return [CBCharacteristicProperties.Write]
        }
    }
    
    private func permissions() -> CBAttributePermissions {
        switch self {
        case .ズンドコ:
            return [CBAttributePermissions.Readable]
        case .キヨシ:
            return [CBAttributePermissions.Writeable]
        }
    }
    
    func mutableCharacteristic() -> CBMutableCharacteristic {
        return CBMutableCharacteristic(
            type: UUID(),
            properties: properties(),
            value: nil,
            permissions: permissions())
    }
}
```

##`ズン` と `ドコ`

ズンドコCharacteristicでやり取りする値。実体は `UInt8` （1バイト）

```swift:Zundoko.swift
enum ズンドコ: UInt8 {
    case ズン
    case ドコ

    private func data() -> NSData {
        return NSData(bytes: [self], length: 1)
    }
    
    static func randomData() -> NSData {
        return ズンドコ(rawValue: UInt8(arc4random_uniform(2)))!.data()
    }
    
    init?(data: NSData) {
        var bytes = [UInt8](count: data.length, repeatedValue: 0)
        data.getBytes(&bytes, length: data.length)
        guard let value = bytes.first, zundoko = ズンドコ(rawValue: value) else { return nil }
        self = zundoko
    }
}
```

##ズンドコPeripheral

ボタンを押すと、 **`ズン` or `ドコ` をランダムに生成してBLEで送信** します。具体的には、Zundoko Characteristic の値を `ズン` or `ドコ` で更新します。

```swift:Peripheral.swift
func publishservice () {
    // キャラクタリスティックを作成
    ズンドコCharacteristic = Characteristic.ズンドコ.mutableCharacteristic()
    キヨシCharacteristic   = Characteristic.キヨシ.mutableCharacteristic()
    
    // サービスを作成
    let service = Service.ズンドコキヨシ.mutableService()
    service.characteristics = [ズンドコCharacteristic, キヨシCharacteristic]
    
    // サービスを Peripheral Managerに追加
    peripheralManager.addService(service)
}
```

```swift:Peripoheral.swift
@IBAction func zundokoBtnTapped(sender: UIButton) {
    let data = ズンドコ.randomData()
    ズンドコCharacteristic.value = data

    // 更新を通知
    peripheralManager.updateValue(
        data,
        forCharacteristic: ズンドコCharacteristic,
        onSubscribedCentrals: nil)
}
```

<img width="200" alt="IMG_3743.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/3180/ac9adae5-dd8d-b319-3352-b0dca3e34b5a.png">


##キヨシCentral

**BLEで送信されてきた `ズン` or `ドコ` を受け取り、画面に表示** します。具体的には、Peripheral側からのズンドコCharacteristicの更新通知を受け取り、画面を更新します。

```swift:Central.swift
func peripheral(peripheral: CBPeripheral, didUpdateValueForCharacteristic characteristic: CBCharacteristic, error: NSError?)
{
    guard let data = characteristic.value, zundoko = ズンドコ(data: data) else { return }
    zundokos.append(zundoko)

    let recents = zundokos.suffix(5)
    zundokoLabel.showZundokos(recents)
    
    if recents == [.ズン, .ズン, .ズン, .ズン, .ドコ] {
        sayKiyoshi()
    }
}
```

<img width="200" alt="IMG_5899.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/3180/36ffbf29-9561-59ba-197a-6e2b226205e0.png">


直近5件が `[ズン, ズン, ズン, ズン, ドコ]` になると **「キ・ヨ・シ！」をBLEで送信** します。具体的には、Peripheral側のキヨシCharacteristicに値を書き込みます。

```swift:Central.swift
private func sayKiyoshi() {
    guard let peripheral = peripheral, kiyoshiCharacteristic = kiyoshiCharacteristic else { return }

    var value = 0x00
    let data = NSData(bytes: &value, length: 1)
    
    peripheral.writeValue(
        data,
        forCharacteristic: kiyoshiCharacteristic,
        type: CBCharacteristicWriteType.WithResponse)
}
```

##改めてズンドコPeripheral側

Peripheral側ではキヨシCharacteristicへの書き込み要求を受けたら、**画面に「キ・ヨ・シ！」を表示** します。

```swift:Peripheral.swift
func peripheralManager(peripheral: CBPeripheralManager, didReceiveWriteRequests requests: [CBATTRequest])
{
    for request in requests where request.characteristic.UUID == キヨシCharacteristic.UUID {
        キヨシCharacteristic.value = request.value
        kiyoshiLabel.hidden = false
    }
    peripheralManager.respondToRequest(requests[0] , withResult: CBATTError.Success)
}
```

<img width="200" alt="IMG_3742.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/3180/4d457d11-5c58-725b-5c84-dda81d4a1b40.png">


##デモ

2台のiPhoneを用意し、それぞれにCentral, Peripheralをインストールします。

やってみた動画：

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">ズンドコキヨシ with Bluetooth Low Energy <a href="https://t.co/dFRVwUjiqv">pic.twitter.com/dFRVwUjiqv</a></p>&mdash; Tsutsumi Shuichi (@shu223) <a href="https://twitter.com/shu223/status/715946579585314820">2016年4月1日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>



##参考書籍

下記書籍のサンプルを流用しました。

<div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4883379736/22301-22/ref=nosim/" name="amazletlink" target="_blank"><img src="http://ecx.images-amazon.com/images/I/51XxjzdT%2BKL._SL160_.jpg" alt="iOS×BLE Core Bluetoothプログラミング" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4883379736/22301-22/ref=nosim/" name="amazletlink" target="_blank">iOS×BLE Core Bluetoothプログラミング</a><div class="amazlet-powered-date" style="font-size:80%;margin-top:5px;line-height:120%">posted with <a href="http://www.amazlet.com/" title="amazlet" target="_blank">amazlet</a> at 16.04.01</div></div><div class="amazlet-detail">堤 修一 松村 礼央 <br />ソシム <br />売り上げランキング: 48,410<br /></div><div class="amazlet-sub-info" style="float: left;"><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4883379736/22301-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jpで詳細を見る</a></div></div></div><div class="amazlet-footer" style="clear: left"></div></div>

今回は「スキャン」や「アドバタイズ」等の話は割愛しましたが、本書4章以降にかなりわかりやすく書いてあります。以下に紹介記事を書いたので興味があればご参照ください。

- [技術書『iOS×BLE Core Bluetooth プログラミング』の紹介 - Qiita](http://qiita.com/shu223/items/e1c91f176a48475cd2ad)

##関連記事

- [Core Bluetooth with Swift （ObjCのおまけ付き） - Qiita](http://qiita.com/shu223/items/78614325ce25bf7f4379)
- [Apple製開発ツール「Bluetooth Explorer」でBLEデバイスのGATT仕様を確認する - Qiita](http://qiita.com/shu223/items/46dabad41cf2eed67d13)
- [Core Bluetoothのハマりどころ逆引き辞典 - Qiita](http://qiita.com/shu223/items/f67f1b0fb1840cf0bd63)