Zenn
📡

Raspberry Pi Pico 2 WでロータリーエンコーダをBluetoothキーボード化する

2025/03/12に公開
1

はじめに

以前、「Raspberry Pi Picoでロータリーエンコーダの回転を検出する」、「M5StackでロータリーエンコーダをBluetoothキーボード化する」という記事を書きました。
先日、スイッチサイエンスから「Raspberry Pi Pico 2 W」が発売され、入手できましたので、これらの記事の内容をアップデートし、Raspberry Pi Pico 2 WでロータリーエンコーダをBluetoothキーボード化してみました。

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

https://zenn.dev/yuyakato/articles/d960a0b2722d7d

Raspberry Pi Pico 2 Wとは?

簡単に言えば「Raspberry Pi Pico 2」のWi-Fi/Bluetoothモジュール内蔵版、あるいは「Raspberry Pi Pico W」の進化版です。

詳しくはスイッチサイエンスのサイトなどを参照ください。なお、私が購入したのはピンヘッダ実装済みの「Raspberry Pi Pico 2 WH
」です。

https://www.switch-science.com/products/10053

https://www.switch-science.com/products/10258

Amazonでも買えますが、いつの間にか値段が上がっていて割高です。(送料込みですが)

https://www.amazon.co.jp/exec/obidos/ASIN/B0DP48PLGF/x7edimv35x-22/

動作中の様子

動作中の様子は以下の通りです。
ロータリーエンコーダを回すと、テンキーの0〜3キーが送出されます。
写真ではロータリーエンコーダが4つ写っていますが、現時点ではまだ2つしか接続されていません。

スケッチ

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

  • Raspberry Pi Pico版ではMicroPythonを使っていましたが、今回はArduino環境を使っています
  • Raspberry Pi Pico版、M5Stack版では定期的にロータリーエンコーダのA/B相を読み込んで処理していましたが、割り込みベースに変更しました
    • 定期的な読み込みでは、高速に回転させたときにカウント漏れが発生してしまったため
  • M5StackからRaspberry Pi Picoに戻した理由は、M5Stackは自由に使えるGPIOが思いの外少なかったため
    • Raspberry Pi PicoはGPIOが沢山あるのが良いですね
  • ロータリーエンコーダは2つ接続されています
    • ロータリエンコーダ0は、GPIO0、GPIO1に接続されています
    • ロータリエンコーダ1は、GPIO2、GPIO3に接続されています
    • GPIO0〜3の内蔵プルアップ抵抗を使っています
    • GPIO0〜3にチャタリング防止のためのデカップリングコンデンサ(0.01uF)を挿入しています
    • それなりにチャタリングが発生するため、割り込みベースで処理するためにはデカップリングコンデンサは必須です
  • 以下のキーが送出されるようになっています
    • ロータリエンコーダ0(時計回り/反時計回り): テンキーの0/1
    • ロータリエンコーダ1(時計回り/反時計回り): テンキーの2/3
  • 割り込みハンドラの登録には、ラムダ式を使っています
    • Arduinoでもラムダ式は使えるのですね(まあC++なので当然かもですが)
#include <KeyboardBLE.h>

class RotaryEncoder {
public:
  int8_t pin_a;
  int8_t pin_b;
  int16_t state;
  int16_t position;
public:
  RotaryEncoder(const int8_t pin_a, const int8_t pin_b) {
    this->pin_a = pin_a;
    this->pin_b = pin_b;
    this->state = 0;
    this->position = 0;
  }
  void begin(void (*handler)(void)) {
    pinMode(pin_a, INPUT_PULLUP);
    pinMode(pin_b, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(pin_a), handler, CHANGE);
    attachInterrupt(digitalPinToInterrupt(pin_b), handler, CHANGE);
  }
  void update() {
    this->state = this->state << 4 | digitalRead(this->pin_a) << 1 | digitalRead(this->pin_b);
    if (this->state == 0x1023) {
      this->position++;
    } else if (this->state == 0x2013) {
      this->position--;
    }
  }
};

class RotaryEncoderEvent {
public:
  RotaryEncoder *rotaryEncoder;
  int16_t previousPosition;
  int16_t position;
  bool cw;
  bool ccw;
  bool changed;
public:
  RotaryEncoderEvent(RotaryEncoder *rotaryEncoder) {
    this->rotaryEncoder = rotaryEncoder;
    this->previousPosition = 0;
  }
  void begin() {
    this->previousPosition = this->rotaryEncoder->position;
  }
  void update() {
    this->position = this->rotaryEncoder->position;
    this->cw = false;
    this->ccw = false;
    this->changed = false;
    if (this->position > this->previousPosition) {
      this->cw = true;
      this->changed = true;
    } else if (this->position < this->previousPosition) {
      this->ccw = true;
      this->changed = true;
    }
    this->previousPosition = this->position;
  }
};

RotaryEncoder re0(0, 1);
RotaryEncoder re1(2, 3);
RotaryEncoderEvent re0Event(&re0);
RotaryEncoderEvent re1Event(&re1);
bool sendingKeys = true;

void sendKeyIf(const bool condition, const uint8_t key) {
  if (condition && sendingKeys) {
    KeyboardBLE.press(key);
    delay(10);
    KeyboardBLE.releaseAll();
  }
}

void setup() {
  Serial.begin(9600);
  re0.begin([] {
    re0.update();
  });
  re1.begin([] {
    re1.update();
  });
  re0Event.begin();
  re1Event.begin();
  KeyboardBLE.begin("RasPiPicoKeyboard");
  delay(5000);
}

void loop() {
  re0Event.update();
  re1Event.update();

  if (re0Event.changed) {
    Serial.printf("re0: %d %s\n", re0Event.position, (re0Event.cw ? "cw" : "ccw"));
  }
  if (re1Event.changed) {
    Serial.printf("re1: %d %s\n", re1Event.position, (re1Event.cw ? "cw" : "ccw"));
  }

  sendKeyIf(re0Event.cw, KEY_KP_0);
  sendKeyIf(re0Event.ccw, KEY_KP_1);
  sendKeyIf(re1Event.cw, KEY_KP_2);
  sendKeyIf(re1Event.ccw, KEY_KP_3);

  delay(10);
}

おわりに

いやー、Raspberry Pi Pico 2 W、良いですね。
今後も色々と作っていきたいと思います。

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

1

Discussion

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