Zenn
🎹

M5StackでロータリーエンコーダをBluetoothキーボード化する

2025/03/08に公開2

はじめに

機械学習のラベル付け(アノテーション)を楽にするために、ロータリーエンコーダをBluetoothキーボードとして使ってみました。

以前、Raspberry Pi Picoでロータリーエンコーダを読み出し、シリアル信号として出力する記事を書きました。
ただ、やはり無線の方が使いやすく、かつキーボードとして認識される方が様々なデバイス(iPadなど)と接続できるので、今回はM5Stackを使ってBluetoothキーボード化してみました。

https://zenn.dev/yuyakato/articles/8c148a11a8bbb7

動作中の様子

動作中の様子は以下の通りです。
ロータリーエンコーダを回したり内蔵ボタンA〜Cを押下すると、接続されたデバイスにキーが送出されます。

スケッチ

今回作成したスケッチは以下の通りです。いくつかポイントを列挙しておきます。

  • 手元にあったM5Stack Grayを使っています
  • BleKeyboardライブラリを使ってBluetoothキーボード化を実現しています
  • M5KBDの部分がBluetoothキーボードの名前として表示されます
  • KEY_NUM_0などの定数はBleKeyboard.hで定義されているはずですが、なぜか参照できずエラーとなったため、即値で記載していま
  • ロータリーエンコーダの読み込み用クラスRotaryEncoderは自作しました
    • ESP32Encoderライブラリも試してみましたが、チャタリングの影響か期待通りに動作しませんでした
    • そのため、今回は愚直に処理することにしました
    • ただし、10ミリ秒の待ち時間を挟んでいるので、高速に回すとカウント漏れが発生します
  • ロータリーエンコーダは2つ接続されています
    • ロータリエンコーダ0は、2ピン、5ピンに接続されています
    • ロータリエンコーダ1は、16ピン、17ピンに接続されています
    • それぞれ内蔵プルアップ抵抗を使っています
  • ロータリーエンコーダは2パルス/1クリックの仕様なので、カウンタの下位1ビットが0の場合だけキーを送出しています
  • 以下のキーが送出されるようになっています
    • 内蔵Aボタン: Aキー
    • 内蔵Bボタン: Bキー
    • 内蔵Cボタン: Cキー
    • ロータリエンコーダ0(時計回り/反時計回り): テンキーの0/1
    • ロータリエンコーダ1(時計回り/反時計回り): テンキーの2/3
  • 以下を内蔵ディスプレイに表示しています
    • 起動してからの経過ミリ秒
    • Bluetoothキーボードとして接続されているかどうか
    • ロータリーエンコーダ0の位置
    • ロータリーエンコーダ1の位置
#include <M5Stack.h>
#include <BleKeyboard.h>

class RotaryEncoder {
public:
  int position;
  bool cw;
  bool ccw;
protected:
  int pin_a;
  int pin_b;
  bool last_pin_a_value;

public:
  RotaryEncoder(const int pin_a, const int pin_b) {
    this->pin_a = pin_a;
    this->pin_b = pin_b;
    this->position = 0;
  }
  void begin() {
    pinMode(pin_a, INPUT_PULLUP);
    pinMode(pin_b, INPUT_PULLUP);
    this->last_pin_a_value = digitalRead(this->pin_a) == HIGH;
  }
  void update() {
    const bool pin_a_value = digitalRead(this->pin_a) == HIGH;
    const bool pin_b_value = digitalRead(this->pin_b) == HIGH;
    this->cw = false;
    this->ccw = false;
    if (pin_a_value != this->last_pin_a_value) {
      if (pin_b_value != pin_a_value) {
        this->position += 1;
        this->cw = true;
      } else {
        this->position -= 1;
        this->ccw = true;
      }
    }
    this->last_pin_a_value = pin_a_value;
  }
};

BleKeyboard bleKeyboard("M5KBD", "M5Stack");
RotaryEncoder re0(2, 5);
RotaryEncoder re1(16, 17);

void setup() {
  M5.begin();
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setTextColor(WHITE, BLACK);
  M5.Lcd.setTextSize(2);
  M5.Lcd.setCursor(0, 0);
  M5.Lcd.printf("M5KBD\n");

  bleKeyboard.begin();
  re0.begin();
  re1.begin();
}

void loop() {
  M5.update();
  re0.update();
  re1.update();

  if (re0.cw || re0.ccw || re1.cw || re1.ccw) {
    M5.Lcd.setCursor(0, 0);
    M5.Lcd.printf("Time: %d\n", millis());
    M5.Lcd.printf("Keyboard: %s\n", (bleKeyboard.isConnected() ? "connected" : "disconnected"));
    M5.Lcd.printf("re0: %d   \n", re0.position);
    M5.Lcd.printf("re1: %d   \n", re1.position);
  }

  if (M5.BtnA.wasPressed()) {
    if (bleKeyboard.isConnected()) {
      bleKeyboard.write('a');
    }
  }
  if (M5.BtnB.wasPressed()) {
    if (bleKeyboard.isConnected()) {
      bleKeyboard.write('b');
    }
  }
  if (M5.BtnC.wasPressed()) {
    if (bleKeyboard.isConnected()) {
      bleKeyboard.write('c');
    }
  }

  if (bleKeyboard.isConnected()) {
    if ((re0.position & 1) == 0) {
      if (re0.cw) {
        bleKeyboard.write(0xEA);  // KEY_NUM_0
      } else if (re0.ccw) {
        bleKeyboard.write(0xE1);  // KEY_NUM_1
      }
    }
    if ((re1.position & 1) == 0) {
      if (re1.cw) {
        bleKeyboard.write(0xE2);  // KEY_NUM_2
      } else if (re1.ccw) {
        bleKeyboard.write(0xE3);  // KEY_NUM_3
      }
    }
  }

  delay(10);
}

おわりに

BleKeyboardライブラリを使うことで、ロータリーエンコーダを簡単にBluetoothキーボード化することができました。
ロータリーエンコーダをあと3つとボタンをいくつか接続することで、ラベル付けを高速化するツールを作る予定です。

本記事が何らかの参考になれば幸いです。

Discussion

Yuya KatoYuya Kato

やっぱりチャタリング防止は必要でした。対策のために0.01uFのデバウンスコンデンサを入れました。また改訂版の記事を書きたいと思います。

Yuya KatoYuya Kato

M5Stack(と言うかESP32)のGPIO2、GPIO5ピンは、ブートストラップにも使われるピンでした。そのため、ロータリーエンコーダの端子状態によっては書き込みモードに遷移できなくなるので注意が必要です。

M5Stack、自由に使えるピンが案外少ないですね。ESP32開発ボードを使うか・・・。

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