Help us understand the problem. What is going on with this article?

M5StackとiOS端末と通信してみた

今回のゴール

M5StackとiPhoneの間でBluetooth LE通信する。
今回作るプロジェクトは、下記の通り。

  • M5Stackのボタンを押すと液晶を赤・黄・青に変わる
  • iPhoneも同じように3色のボタンをつけてボタンを押すと色が変わる
  • BluetoothLEで相互に接続し、M5Stackで色を変化させると連動してiPhoneの画面も変わる。その逆もできるようにする

って感じです。

ソースはGithubに公開されています。

demo.gif

BluetoothLEの基礎知識

簡単ですが、BluetoothLEでは以下のことを理解しておくと便利です。

  • デバイスの役割として、ペリフェラルとセントラルがある
  • ペリフェラルは、発信する側 → 親機
  • セントラルは、受診する側 → 子機
  • GATT → 通信のベースとなるプロファイル
  • write、read、notifyの3つがある
  • writeは、セントラル→ペリフェラルにデータを渡す
  • readは、セントラル→ペリフェラルに読み込みデータを要求する
  • notifyは、ペリフェラル→セントラルに通知する

BluetoothLEのサービスUUIDを取得する

Online UUID Generatorで3つのUUIDを作成します。
Bulk Version 1 UUID GenerationHow Many?3 と入力して GENERATE してください。
スクリーンショット 2019-04-10 22.47.42.png
生成された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通信しているサンプルがなかなか見つけづらいので参考になればと思います。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away