今回のゴール
M5StackとiPhoneの間でBluetooth LE通信する。
今回作るプロジェクトは、下記の通り。
- M5Stackのボタンを押すと液晶を赤・黄・青に変わる
- iPhoneも同じように3色のボタンをつけてボタンを押すと色が変わる
- BluetoothLEで相互に接続し、M5Stackで色を変化させると連動してiPhoneの画面も変わる。その逆もできるようにする
って感じです。
BluetoothLEの基礎知識
簡単ですが、BluetoothLEでは以下のことを理解しておくと便利です。
- デバイスの役割として、ペリフェラルとセントラルがある
- ペリフェラルは、発信する側 → 親機
- セントラルは、受信する側 → 子機
- GATT → 通信のベースとなるプロファイル
- write、read、notifyの3つがある
- writeは、セントラル→ペリフェラルにデータを渡す
- readは、セントラル→ペリフェラルに読み込みデータを要求する
- notifyは、ペリフェラル→セントラルに通知する
BluetoothLEのサービスUUIDを取得する
Online UUID Generatorで3つのUUIDを作成します。
Bulk Version 1 UUID Generation で How Many? を 3 と入力して GENERATE してください。
生成された3つのUUIDはあとで使いますので控えてください。(赤枠のところ)
M5StackのBluetoothLE周りの解説
今回は、M5Stackをペリフェラルとして、writeとnotifyを使います。
まずUUIDを3つ定義します。
- サービスUUID→writeとnotifyが使えますよとiPhoneに通知するために使います
- write→iPhoneからのデータを受診するために使います。
- notify→iPhoneにデータを送るために使います。
setup()
時に初期化するに必要なinitBLE()
にまとめてあります。
loop()
で必要な処理はloopBLE()
にまとめています。
ボタンを押した時にloopLCDcolor()
内に実装されている下記コードでiPhoneにデータを送信しています。
if (deviceConnected) {
char sendMessage[10];
lastColor.toCharArray(sendMessage, 10);
pNotifyCharacteristic->setValue(sendMessage);
pNotifyCharacteristic->notify();
}
iOSアプリ側の実装
まずM5Stackを検索するためにペリフェラルスキャンをします。
まずCoreBluetoothのCentralManagerのインスタンス取得します。
centralManager = CBCentralManager(delegate: self, queue: nil, options: nil)
Bluetoothデバイスの電源など入るとfunc centralManagerDidUpdateState(_ central: CBCentralManager)
が呼ばれます。
central.state
が.poweredOn
ならばペリフェラルスキャンを開始します。
centralManager.scanForPeripherals(withServices: nil, options: nil)
ペリフェラルを発見するとfunc centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber)
が呼ばれます。
M5Stackで設定したローカルネームと一致するか確認し一緒ならばペリフェラルスキャンを停止し、そのペリフェラルと接続します。
centralManager.stopScan()
centralManager.connect(connectToPeripheral, options: nil)
ペリフェラルを接続完了するとfunc centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral)
を呼ばれるので対応しているサービスを取得します。
connectToPeripheral.delegate = self
connectToPeripheral.discoverServices(nil)
サービスを取得できるとfunc peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?)
が呼ばれます。1サービスごとに呼ばれます。
サービスがnotifyの場合、M5Stackからデータが送信されるので待ち受けします。
connectToPeripheral.setNotifyValue(true, for: characteristic)
実際に、M5Stackからデータが送られてくるとfunc peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?)
が呼ばれます。
最後に
ベタ書きコードで恥ずかしいですが、M5Stack(ESP32にも使える)とiOSデバイスでBluetoothLE通信しているサンプルがなかなか見つけづらいので参考になればと思います。