Raspberry Pi Picoでロータリーエンコーダの回転を検出する
はじめに
機械学習のラベル付け(アノテーション)を行っている時など「位置/数値を微妙に調整したい」というシチュエーションは少なくないと思います。
キーボード、マウス、タッチパネルを使ったインターフェースも良いですが、場合によっては物理的な入力装置を使った方が便利で直感的なことがあります。また、軽くて配置が自由であれば手首にも優しいです。
今回はそんな場合に使えるように、Raspberry Pi Picoでロータリーエンコーダの回転を検出してみました。
ロータリーエンコーダとは?
回転数や回転角度、回転位置などを検出するセンサーの1種で、様々な種類があります。
今回は、かなり昔に秋月電子通商で購入した2相出力、インクリメンタルタイプ、24クリックのものを使用しました。
随分前に購入したものなので型番は定かではありませんが、以下が近いと思います。
Amazonで売っているもので言えば、以下が近そうです。(実物は未確認です)
Raspberry Pi Picoとは?
一言で言えば「Pythonが動く安価なマイコンボード」です。解説記事は沢山あるので、ここでは割愛します。
今回は初代のRaspberry Pi Picoを使用しました。Pico 2、Pico Wなどではありません。
全体像
ロータリーエンコーダとRaspberry Pi Picoを接続した全体像は下図の通りです。
配線図
配線は単純で、ロータリーエンコーダのA/B/Cを、Raspberry PiのGP0/GP1/GNDにそれぞれ接続するだけです。
Raspberry Pi Picoにはプログラマブルなプルアップ抵抗が内蔵されているため、外部に抵抗は不要です。
ロータリーエンコーダ (A)---(緑)---(1) GP0 Raspberry Pi Pico
ロータリーエンコーダ (B)---(黄)---(2) GP1 Raspberry Pi Pico
ロータリーエンコーダ (C)---(黒)---(3) GND Raspberry Pi Pico
Pythonコード
今回はMicroPythonを使用しました。
複数のロータリーエンコーダを接続する予定なのでRotaryEncoder
としてクラス化しています。
Pin(x, Pin.IN, Pin.PULL_UP)
のx
を修正することで、任意のGPIOピンに接続することができます。
今回はチャタリングについては特に考慮していませんが、簡単に試した限りでは問題なく動作しているようです。
気になる方はスリープを長めにしたり、デバウンス処理を入れるのが良いかと思います。
from machine import Pin
import utime
import sys
import time
import json
class RotaryEncoder:
def __init__(self, pin_a, pin_b):
self.pin_a = pin_a
self.pin_b = pin_b
self.position = 0
self.last_pin_a = pin_a.value()
def read(self):
current_pin_a = self.pin_a.value()
current_pin_b = self.pin_b.value()
direction = None
if current_pin_a != self.last_pin_a:
if current_pin_b != current_pin_a:
self.position += 1
direction = "CW"
else:
self.position -= 1
direction = "CCW"
self.last_pin_a = current_pin_a
return (direction, self.position)
re0 = RotaryEncoder(Pin(0, Pin.IN, Pin.PULL_UP), Pin(1, Pin.IN, Pin.PULL_UP))
print(json.dumps({
"time": time.ticks_ms() / 1000,
"event": "init",
}))
while True:
re0_state = re0.read()
if re0_state[0] is not None:
print(json.dumps({
"time": time.ticks_ms() / 1000,
"event": "change",
"state": {
"re0": {"direction": re0_state[0], "position": re0_state[1]},
},
}))
utime.sleep(0.001)
動作例
時計回り(CW: Clockwise)に2クリック、反時計回り(CCW: Counter Clockwise)に3クリック回転した時の出力は以下の通りです。
1クリックで2回出力されているのは、使用しているロータリーエンコーダが2パルス/1クリックのためです。
状態が標準出力に出力され、JSON Lines形式になっています。フィールドの意味は以下の通りです。
-
time
: 起動してからの経過秒数 -
event
: イベントの種類(init
、change
) -
state
: ロータリーエンコーダの状態-
direction
: 回転方向(CW
、CCW
) -
position
: 現在の位置(累積値)
-
{"time": 140.595, "event": "init"}
{"time": 142.165, "event": "change", "state": {"re0": {"direction": "CW", "position": 1}}}
{"time": 142.202, "event": "change", "state": {"re0": {"direction": "CW", "position": 2}}}
{"time": 142.779, "event": "change", "state": {"re0": {"direction": "CW", "position": 3}}}
{"time": 142.82, "event": "change", "state": {"re0": {"direction": "CW", "position": 4}}}
{"time": 144.206, "event": "change", "state": {"re0": {"direction": "CCW", "position": 3}}}
{"time": 144.27, "event": "change", "state": {"re0": {"direction": "CCW", "position": 2}}}
{"time": 144.931, "event": "change", "state": {"re0": {"direction": "CCW", "position": 1}}}
{"time": 144.974, "event": "change", "state": {"re0": {"direction": "CCW", "position": 0}}}
{"time": 145.614, "event": "change", "state": {"re0": {"direction": "CCW", "position": -1}}}
{"time": 145.652, "event": "change", "state": {"re0": {"direction": "CCW", "position": -2}}}
おわりに
Raspberry Pi Picoを使うことで、とても簡単にロータリーエンコーダの回転を検出することができました。
Pythonでサクッと書けるのはとても良いですね。しかも1000円以下でUSBに対応しているとは、良い時代です。
今後は、複数のロータリーエンコーダを組み合わせて、ラベル付けのための入力装置を作りたいと思っています。
Discussion