⌨️

BLEキーボードをつくって、iPadを制御しよう

2022/05/01に公開約6,500字

ESP32を使って、BLEキーボードを作りました。
iPadを制御します↓。

https://twitter.com/tw_kotatu/status/1520682833630871552

関連記事

bleマウスも作りました。

https://zenn.dev/kotaproj/articles/esp32_blemouse

😟困ったこと

iPadで電子書籍を読むことが多いのですが、持っていると疲れます。
また、ページをめくるのにスワイプ動作操作がいるため、面倒です。
楽な姿勢で、ページめくりができないかというのが悩みでした。

💡やったこと

  1. 利用者は、ジョイスティック/スイッチを操作する
  • スイッチには、複数ある
  • ※ジョイスティック/スイッチの概要は後述
  1. マイコンボード(ESP32)は、スイッチ状態を監視している
  • スイッチの変化に応じて、BLEキーボードコマンドを送信する
  1. iPadは、キーボードコマンドを受け制御される

今回は、ESP32を使用して作成しました。

仕様検討

電子書籍のページめくりがメインなので、ページめくりのために、矢印が必要になります。
また、Kindleの立ち上げもできるようにします。

以下としました。

デバイス 動作 iPad側への送信
🕹X軸 ←操作 キーボード Arrow Left
🕹X軸 →操作 キーボード Arrow Right
🕹Y軸 ↑操作 キーボード Arrow Up
🕹Y軸 ↓操作 キーボード Arrow Down
🕹スイッチ1 押下 Kindleアプリの起動
スイッチ2 押下 ホーム画面
スイッチ3 押下 キーボード Arrow Left
スイッチ4 押下 キーボード Arrow Right

🔧パーツ一覧

機材名 備考
ESP32評価ボード ESP32-WROVER 開発ボード/ESP32-WROOM 開発ボードのどちらでも可
🕹ボード ジョイスティックおよびスイッチセット - Amazonで購入
タクトスイッチ 3つ使用

接続図

MPU側でプルアップ設定を行うため、抵抗は不要です。

💻環境

VScode - PlatformIOを使用します。
PlatformaIOの環境構築は、↓にまとめておきました。

https://zenn.dev/kotaproj/articles/esp32_vscode_pio

ライブラリ - ESP32 BLE Keyboard

ESP32をBLEキーボードにするために、下記のライブラリを使用します。

https://github.com/T-vK/ESP32-BLE-Keyboard

インストール手順は、以下となります。

  • PlatformIO - Librariesを選択し、ESP32-BLE-Keyboardを検索
  • ESP32-BLE-Keyboardを選択
  • "Add to Project"を選択
  • プロジェクトを指定 - 今回は、"ipadBleKbd"というプロジェクトを指定
  • "Add"を選択
  • platformio.iniに下記が追加される

lib_deps = t-vk/ESP32 BLE Keyboard@^0.3.2

これで、インクルードできるようになります。

#include <BleKeyboard.h>

📝手順

以下を記載します。

  • 筐体の作成
  • ソフト設計/コード

筐体の作成

3Dプリンタで筐体を作成しました。
親指で、🕹を制御できるように上に配置しました。
スイッチは、側面に配置しました。

https://zenn.dev/kotaproj/articles/a70464d8cd3540

ソフト設計/コード

最初はシングルタスクで書いたのですが、後々拡張していきたいと思い、マルチタスクで書き直しました。
FreeRTOSを使用しています。

タスク構成

タスク名 役割
スイッチ監視タイマー スイッチのDOWN/UPの変化を監視する
ジョイスティック監視タイマー ジョイスティックの変化を監視する
メッセージ管理タスク スイッチ/ジョイスティックのイベントをキーボード管理に通知
キーボード管理タスク メッセージ管理から受けたイベントをBleキーボード制御を送信

コード関連

コード自体は、githubにアップしてあります。
ポイントを記載します。

bleキーボードライブラリ

初期化は以下のように行います。

#include <BleKeyboard.h>

static BleKeyboard s_xBleKbd;

s_xBleKbd.begin();

beginの後で、ペアリングできる状態になります。
iPadからペアリングを実施できます。

接続しているかどうかは以下で判定できます。

if (s_xBleKbd.isConnected())
{
    // !!!接続中!!!
}

キーボードの送信する場合は、以下となります。

// 矢印←を送信
s_xBleKbd.write(KEY_LEFT_ARROW);

// Commandキー(Windowsキー)を押しながら、"h"を送信
s_xBleKbd.press(KEY_LEFT_GUI);
delay(10);
s_xBleKbd.print("h");
delay(10);
s_xBleKbd.release(KEY_LEFT_GUI);
delay(50);

Kindleの起動は以下となります。
速すぎる送信は、iPad側で取りこぼします。
実機動作を見ながら、delay関数を入れています。

KbdTask.cpp
/**
 * @brief kindle appの起動
 */
static void vProcSw_StartKindle(void)
{
    // ->home (Cmd + h)
    s_xBleKbd.press(KEY_LEFT_GUI);
    delay(10);
    s_xBleKbd.print("h"); //文字を送信
    delay(10);
    s_xBleKbd.release(KEY_LEFT_GUI);
    delay(50);

    // ->command (Cmd + Space)
    s_xBleKbd.press(KEY_LEFT_GUI);
    delay(50);
    s_xBleKbd.print(" "); // space
    delay(50);
    s_xBleKbd.release(KEY_LEFT_GUI);
    delay(50);

    // Erase the string with backspace
    for (int i = 0; i < 10; i++)
    {
        s_xBleKbd.write(KEY_BACKSPACE);
        delay(50);
    }

    // input - "kindle"
    delay(500);
    s_xBleKbd.print("kindle");
    delay(50);

    // input - "enter"
    s_xBleKbd.write(KEY_RETURN);
    delay(50);

    return;
}

ジョイスティック監視(/スイッチ監視)

ジョイスティック監視を例に説明します。
25msec毎に、コールされるタイマーを使用しました。
xTimerCreate()/xTimerReset()は、FreeRTOS - APIとなります。

ApiJs.cpp
#define JS_CHECK_TIME (25) // JoyStickチェック間隔:25msec
...
static TimerHandle_t s_xTimerJs = NULL;
...

ErType_t xInitJs(void)
{
    ...
    // text name, timer period, auto-reload, number of times, callback
    s_xTimerJs = xTimerCreate(cName, JS_CHECK_TIME, pdTRUE, (void *)0, vTimerCallbackJs);
    if (NULL == s_xTimerJs)
    {
        Serial.printf("%s : error - xTimerCreate\n", __func__);
        return ER_FAIL;
    }

    if (pdTRUE != xTimerReset(s_xTimerJs, JS_CHECK_TIME))
    {
        Serial.printf("%s : error - xTimerReset\n", __func__);
        return ER_FAIL;
    }

下記の関数が25msec毎にコールされます。

ApiJs.cpp
static void vTimerCallbackJs( TimerHandle_t xTimer)
{
    int32_t x = analogRead(JS_ASGN_AXIS_X);
    int32_t y = analogRead(JS_ASGN_AXIS_Y);
    ...

x, yには、0-4095の値で取得されます。
今回の接続だと以下のような値が取得されます。

ジョイスティック状態 x値 y値
Idle 2048付近 2048付近
左[←] 0付近 2048付近
右[→] 4095付近 2048付近
上[↑] 2048付近 4095付近
下[↓] 2048付近 0付近

https://qiita.com/kotaproj/items/c191883a15cfc5ec27c0

https://qiita.com/kotaproj/items/e9b2f56a89ff0a6a3d3a

コード全体

https://github.com/kotaproj/esp32_kbd

さいごに

久しぶりにライフハック的なものが作れてよかったです。
デバッカなしで、RTOSのデバックはなかなか大変でした。
同じ仕組みでBleマウスもつくったので、後日紹介できればと思います。

参考URL

https://www.freertos.org/

https://github.com/T-vK/ESP32-BLE-Keyboard
GitHubで編集を提案

Discussion

ログインするとコメントできます