既に 多くの言語・技術でズンドコキヨシ が試みられていますが、Bluetooth Low Energy (以下BLE) 版はまだないみたいなので、やってみました。
ズンドコキヨシ with Bluetooth Low Energy pic.twitter.com/dFRVwUjiqv
— Tsutsumi Shuichi (@shu223) 2016年4月1日
ズンドコキヨシカルチャーの発祥ツイート:
Javaの講義、試験が「自作関数を作り記述しなさい」って問題だったから
— てくも (@kumiromilk) 2016年3月9日
「ズン」「ドコ」のいずれかをランダムで出力し続けて「ズン」「ズン」「ズン」「ズン」「ドコ」の配列が出たら「キ・ヨ・シ!」って出力した後終了って関数作ったら満点で単位貰ってた
ちなみに iOS / Core Bluetooth です。 Swift バージョンは 2.2
##GATT
ズンドコキヨシGATTを以下のように定義しました。(各UUID はuuidgen
で生成)
###Services
####ズンドコキヨシService
- UUID: B8A18F4F-2A2B-4084-AC2D-1ABC289EF05D
- Characteristics
- ズンドコCharacteristic
- キヨシCharacteristic
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
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バイト)
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 ドコ
で更新します。
func publishservice () {
// キャラクタリスティックを作成
ズンドコCharacteristic = Characteristic.ズンドコ.mutableCharacteristic()
キヨシCharacteristic = Characteristic.キヨシ.mutableCharacteristic()
// サービスを作成
let service = Service.ズンドコキヨシ.mutableService()
service.characteristics = [ズンドコCharacteristic, キヨシCharacteristic]
// サービスを Peripheral Managerに追加
peripheralManager.addService(service)
}
@IBAction func zundokoBtnTapped(sender: UIButton) {
let data = ズンドコ.randomData()
ズンドコCharacteristic.value = data
// 更新を通知
peripheralManager.updateValue(
data,
forCharacteristic: ズンドコCharacteristic,
onSubscribedCentrals: nil)
}
##キヨシCentral
BLEで送信されてきた ズン
or ドコ
を受け取り、画面に表示 します。具体的には、Peripheral側からのズンドコCharacteristicの更新通知を受け取り、画面を更新します。
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()
}
}
直近5件が [ズン, ズン, ズン, ズン, ドコ]
になると 「キ・ヨ・シ!」をBLEで送信 します。具体的には、Peripheral側のキヨシCharacteristicに値を書き込みます。
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への書き込み要求を受けたら、画面に「キ・ヨ・シ!」を表示 します。
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)
}
##デモ
2台のiPhoneを用意し、それぞれにCentral, Peripheralをインストールします。
やってみた動画:
ズンドコキヨシ with Bluetooth Low Energy pic.twitter.com/dFRVwUjiqv
— Tsutsumi Shuichi (@shu223) 2016年4月1日
##参考書籍
下記書籍のサンプルを流用しました。
ソシム
売り上げランキング: 48,410
今回は「スキャン」や「アドバタイズ」等の話は割愛しましたが、本書4章以降にかなりわかりやすく書いてあります。以下に紹介記事を書いたので興味があればご参照ください。
##関連記事
- Core Bluetooth with Swift (ObjCのおまけ付き) - Qiita
- Apple製開発ツール「Bluetooth Explorer」でBLEデバイスのGATT仕様を確認する - Qiita
- Core Bluetoothのハマりどころ逆引き辞典 - Qiita
-
「キ・ヨ・シ!」と文字列を送るのはBLEの思想的にマッチしないのでとりあえず1バイトの値を送る、という仕様にしてます。 ↩