📡
Raspberry Pi Pico 2 WでロータリーエンコーダをBluetoothキーボード化する
はじめに
以前、「Raspberry Pi Picoでロータリーエンコーダの回転を検出する」、「M5StackでロータリーエンコーダをBluetoothキーボード化する」という記事を書きました。
先日、スイッチサイエンスから「Raspberry Pi Pico 2 W」が発売され、入手できましたので、これらの記事の内容をアップデートし、Raspberry Pi Pico 2 WでロータリーエンコーダをBluetoothキーボード化してみました。
Raspberry Pi Pico 2 Wとは?
簡単に言えば「Raspberry Pi Pico 2」のWi-Fi/Bluetoothモジュール内蔵版、あるいは「Raspberry Pi Pico W」の進化版です。
詳しくはスイッチサイエンスのサイトなどを参照ください。なお、私が購入したのはピンヘッダ実装済みの「Raspberry Pi Pico 2 WH
」です。
Amazonでも買えますが、いつの間にか値段が上がっていて割高です。(送料込みですが)
動作中の様子
動作中の様子は以下の通りです。
ロータリーエンコーダを回すと、テンキーの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、良いですね。
今後も色々と作っていきたいと思います。
本記事が何らかの参考になれば幸いです。
Discussion