BLE(HID)機器をEPS32に接続する

色々なキーボード、マウスをPC-98に接続しようという企画(?)も終盤です。PS/2、USBは何となく出来上がりました。

残りはBluetoothになります。やる気になった理由は

  1. EPS32にはBluetooth(Classic・BLE)がサポートされてる
  2. USBHostSheildが不要
  3. USBの時に作ったガジェットをそのまま流用できる

まぁ、1,2は後付け理由ですw。一番大きいのは3ですね!もうコテ握ってMiniDinを結線するのは老眼(初期と信じてる)にはつらい…。やりたくない。歯科拡大鏡が必要なレベル。

Bluetoothには3.0までのBluetooth Classic、それ以降のBLE(Bluetooth Low Energy)があります。この二つには互換性がなく別規格と思っていいみたいです。この4.0以降がすべてBLEというわけではないのですが、最近発売されているマウス、キーボードは、ほとんどBLEです。

EPS32はClassic、BLE両方に対応しています。最新のデバイスに対応すればよいですのでBLEで決め打ちしていきたいと思います。というかClassic使うにはESP-IDF使わんとダメっぽいからめんどくさいです。使うのはNimBLEというライブラリを用いて接続していきます。ESP32をマウス、キーボード化する例はたくさんあるのですが、ESP32にHIDデバイスの接続例がなかなかありません。下のサイトは一番詳しく説明されているサイトではないかと思います。ひな形となるコードはMoke Nakamuraさんのサイトより参考にしてすすめました。

この中にあるNimbleTest.inoを実行できるようにしておきましょう。そして、ダイソーに走ってBLEのシャッターを買いましょう。まずはサンプルを確実に動かせることが理解への早道です。僕?もちろん無駄に買いそろえてますw。しばらくカチカチ遊べば準備完了です

最初にまずGithubを張り付けておきます。ちなみに今回からVSCode + PlatformIOで開発しています。Arduino IDE 2.0のビルトが遅すぎでやる気が削がれていたので変えました。

なんで…今まで変えなかったんだろ…VSCode + PlatformIOサイコー!

開発が捗ります。時間を返してほしいです!

コードを追いながら読んでくださるとありがたいです。


BLE HIDとEPS32の接続

まずはBLEの基礎知識として、下のサイトを読んでおくのがいいと思います。

ペリフェラル、セントラル、アドバタイズ、サービス、キャラクタリスティックなどBLE用語がありますので理解していないと訳が分かりません。理解に努めましょう!

流れとしては

  1. セントラル(ESP32)がペリフェラル(マウスorキーボード)からのアドバタイズ受信
  2. HIDの接続したいマウスキーボードを判断し接続
  3. キャラクタリスティックのNotify属性(キーボード打った時やマウスを動かした時のデータ通知)のコールバック処理
  4. キーボードパーサーでNotify属性のデータ処理とPC-98へ送信
  5. マウスパーサーでNotify属性のデータ処理とPC-98へ送信

です。一つずつ追ってみます


セントラル(ESP32)がペリフェラル(マウスorキーボード)からのアドバタイズ受信

今回利用するBluetoothマウスは

キーボードは

この検証しか使う予定はないのでメルカリで777円だったかで購入したものです(後で後悔します)

まずはこの機器の電源を投入してESP32でアドバタイズの様子を確認してみます。NimbleTest.inoのシリアル出力をみてみます。ずらずら出てきますが不要なものは消してあります。

アドレスってのはペリフェラルのMacアドレス。UUIDはサービスIDです。サービスIDは割り当てが決まっているものもあり、調べてみると0xfef3とか0xfe50はGoogle Incとなってますので関係ないデバイスです。キーボードとかマウスはHIDサービスですので0x1812です。

出力を見るとマウス、キーボードの電源をいれた直後の状態。つまり接続待機(ペアリングではなく)です。サービスIDに0x1812はないですね。キーボードであろう「BT WORD」デバイス名らしいものがわかりますが、マウスはデバイス名、アドレス、サービスがわからなけれは何なのかすらもわかりません。

ちなみに言ってしまうとdd:77:54:7e:b2:98がキーボード、00:a8:2c:08:00がマウスです。このMacアドレスを紐づけてマウス、キーボードして検知・保存する必要があるわけです。いろいろな解説サイトをみてみましたがこれをペアリングといのかどうなのかはわかりませんがペアリングと思ってますw。

マウス、キーボードにはペアリングのボタンがあると思います。それを押してみましょう。

とログ出力されます。今度はわかりやすいですね。サービスIDは0x1812でHID、マウスは「ELECOM shellpha」、キーボードは「BT WORD」となっています。しかし、Macアドレスが微妙に変わってます。ペアリングボタンを押すたびにMacアドレスが変わっているようでセキュリティ上の問題でしょうね。今どきのデバイスはMacアドレスはランダム化されており追跡できないようなってますし完全にユニークな識別子として利用はできないです。しかし、ペアリングを押さないで電源入れただけの待機状態だとMacアドレスは変更されませんのでペアリング時のMacアドレスを覚えておいて再接続していると予測できます。

Macアドレスの他にも恒久的に保存しておく情報があるので、int値を保存しておく汎用関数も作っておきます(後ほど説明)。LittileFSをつかっていきます。

せんせー!SPIFFSじゃだめなんですか?

SPIFFSは、とっくにDeprecatedになりました!次のひと~!

使用感はLittileFSも同じですので調べてください。

冗長な感じで、もーちょい簡潔に書ける気もしますが、これでESP32の電源が切れてもペアリングしたMacアドレスやint値は保持されます。

次に、ペアリングしたときに「LECOM shellpha」なのか、「BT WORD」なのか、はたまた関係のないHIDデバイスなのかを判断しアドレスを保存しておく処理を行うようにします。まずはアドバタイズ取得したときのコールバック処理です。接続の部分に少しかかわってくる部分もあります。


HIDの接続したいマウスキーボードを判断し接続

アドバタイズ時にMacアドレスが既知であれば接続、一致しなければHIDデバイスの中から名前が一致するデバイスを探し接続という流れです。接続処理自体はloop()内でdoConnectの値をみてconnectToServer()で行います。connectToServer()ではクライアント(ペリフェラル)と接続したときのコールバック処理を指定しています。抜粋です。

初回ペアリング(ペアリングは初回だけなので馬から落馬感…)の時にはMacアドレスを保存します。これで一度ペアリングすれば次からはマウス・キーボードの電源を入れたり、スリープから復帰すれば、すぐ接続できます。

接続の際重要な設定部分はupdateConnParams(x,x,x,x)です。これは通知の更新間隔やスリープのタイムアウト設定なんですね。onConnParamsUpdateRequestで要求された設定でupdateConnParamsしないと正しくデバイスが動きません。マウスだと正しい分解能で操作できなかったり、キーボードだと接続自体が不安定でした。

いろいろ試してみると、初回ペアリング時(ペアリングボタンを押したとき)にはconnectToServer()で接続した瞬間コールバックでonConnParamsUpdateRequestが、エレコムマウス、3COINキーボード共に呼ばれました。2回目接続以降ではonConnParamsUpdateRequestのコールバックは、エレコムマウスでは呼ばれるんですが、3COINのキーボードは呼ばれないんですね。機材によって異なる挙動をしました。

2回目以降の接続時にupdateConnParamsを正しく送るには初回ペアリング時のonConnParamsUpdateRequestで要求されるitvl_min,itvl_max,latency,timeoutを保存しておく必要があるわけです。というわけで上のlittleFSでloadValue()、saveValue()を使ってマウス、キーボードそれぞれの値を保存しておきます。2回目以降は保存値を用いてupdateConnParamsを送ればOKとなるわけです。

onAuthenticationCompleteでもitvl,latency,timeoutが送られてきていまして、その値を使おうかとも思ったんですが、onConnParamsUpdateRequestと違う値もあるし、itvl_min,itvl_maxはどうやって計算するのかも不明なので無視してます。

いろいろ説明しましたが、事前にわかってないとダメなものはデバイス名のみということになります。ここはハードコーディングで問題ないと思いますw

ここまで接続の話でした。次はBLE機器からの情報処理とマウス・キーボードパーサーのデータ引き渡しと解析となります。記事を改めます。

投稿者 まる

Twitter : @dinagon Instagram : @d_dinagon フォロバします!!最近、ESP/Arduinoいじりすぎでアプリ開発が進んでない。歯医者なのに歯のことはあまり触れませんw