サイトロゴ

M5StickC PLUSとロータリーエンコーダ

著者画像
Toshihiko Arai

この記事では、ロータリーエンコーダをArduinoで使う方法をご説明します。ここではA相・B相の2相出力の汎用ロータリーエンコーダを使用しました。 実は、ロータリーエンコーダを使っていざプログラミングしようとするとかなり大変です。そのため、ここではArduinoライブラリを使用しました。 なお、ArduinoボードはArduino互換機であるESP32 PICOが搭載されたM5StickC PLUSを使用しました。M5StickC PLUSは、バッテリーやLCD他いろいろなセンサがパッケージされたM5Stackシリーズ製品の一つです。一般のArduinoと同じようにArduino IDEで開発できます。 Arduino互換機であれば本記事の内容で動作します。また、M5StickC PLUSの使い方は をご覧ください。

ロータリーエンコーダの使い方

汎用のロータリーエンコーダには固定ピン以外に3つの端子があります。真ん中の端子はArduinoのGNDへ接続します。両端のピンが出力ピンです。出力は1か0のデジタル信号になります。これら2つの出力ピンは、必ずプルアップさせてください。抵抗でプルアップする場合は、10kΩを介して3V3の電圧へ繋ぎます。また、内蔵プルアップ抵抗が使える場合は pinMode(RE_A, INPUT_PULLUP) でプルアップします。M5StickC PLUSには内蔵プルアップが設定されますので、今回はそれを使用します。

ちなみにAmazonでよく売られているスイッチ付きのロータリーエンコーダですが、こちらもA相・B相ありますから、この記事で行う方法と同じ扱いで大丈夫です。

ロータリーエンコーダを回転させるとステップごとに出力は表のように変化します。

Step #1 #2 #3 #4
A 0 1 1 0
B 0 0 1 1

A相とB相が1/4周期ずれることで、どちらに回転されたかプログラムで判断が可能になってます。ただし、冒頭にも述べたとおり実際にプログラムするとかなりややこしいです。優れたライブラリがありましたので、今回はこちらを利用します。 https://github.com/mathertel/RotaryEncoder

GitHubからzipファイルをダウンロードして解答し、Arduinoのlibrariesディレクトリへ移して使ってください。または、srcにあるRotaryEncoder.hRotaryEncoder.cppだけを.inoファイルのあるディレクトリへ配置しても動きます。

RotaryEncoder.cppを覗けば、ロータリーエンコーダの処理の大変さが実感できるでしょう。

Arduinodえロータリーエンコーダを使ってみよう

それでは、実際にArduino(M5StickC PLUS)を使ってロータリーエンコーダを使ってみましょう。

ピンの状態を監視するには割り込み処理を使用します。こちらがその設定です。

attachInterrupt(digitalPinToInterrupt(RE_A), checkPosition, CHANGE);
attachInterrupt(digitalPinToInterrupt(RE_B), checkPosition, CHANGE);

指定したピンの状態が変化すればcheckPosition関数が呼ばれます。状態変化の指定には他にも次のようなものがあります。

項目 動作
LOW ピンがLOWのとき発生
CHANGE ピンの状態が変化したときに発生
RISING ピンの状態がLOWからHIGHに変わったときに発生
FALLING ピンの状態がHIGHからLOWに変わったときに発生

また、割り込み処理の関数checkPositionIRAM_ATTRで指定してます。IRAM_ATTRを使用すると、コンパイルされたコードはがESP32の内部RAM領域に配置され、割り込み処理を高速化できます。ただし、ESP32以外のArduinoボードでは使えない場合があります。その際はIRAM_ATTRの記述を削除してください。

new RotaryEncoder(RE_A, RE_B, RotaryEncoder::LatchMode::TWO03); でRotaryEncoderクラスの初期化を行います。LatchModeの指定は、こちらの公式サイトで説明されてますが、いまいちよく分かりませんでした。 http://www.mathertel.de/Arduino/RotaryEncoderLibrary.aspx

loop関数内のencoder->tick()は、出力ピンの状態を観察してます。出力に変化があればクラス内部でポジションを加算・減算させているようです。 現在のポジション値は、encoder->getPosition()で簡単に取得できます。

#include <M5StickCPlus.h>
#include <RotaryEncoder.h>

#define RE_A 0
#define RE_B 26

RotaryEncoder* encoder = nullptr;

IRAM_ATTR void checkPosition() { // ビルドに失敗する場合はIRAM_ATTRを削除してください
    encoder->tick(); // just call tick() to check the state.
}



void setup() {
    M5.begin();
    M5.Axp.ScreenBreath(9);
    M5.Lcd.setRotation(3);
    M5.Lcd.fillScreen(WHITE);
    M5.Lcd.setTextColor(BLACK);

    pinMode(RE_A, INPUT_PULLUP);
    pinMode(RE_B, INPUT_PULLUP);


    attachInterrupt(digitalPinToInterrupt(RE_A), checkPosition, CHANGE);
    attachInterrupt(digitalPinToInterrupt(RE_B), checkPosition, CHANGE);


    encoder = new RotaryEncoder(RE_A, RE_B, RotaryEncoder::LatchMode::TWO03);
    M5.Lcd.fillScreen(WHITE);
    M5.Lcd.setCursor(0, 10);
    M5.Lcd.setTextSize(2);
    M5.Lcd.println("Setup is complete! Rotate the rotary encoder!");

}


void loop() {

    static int pos = 0;

    encoder->tick(); // just call tick() to check the state.

    int newPos = encoder->getPosition();
    if (pos != newPos) {
        M5.Lcd.fillScreen(WHITE);
        M5.Lcd.setCursor(0, 10);
        M5.Lcd.setTextSize(4);
        M5.Lcd.printf(" pos:%d\n", newPos);
        M5.Lcd.printf(" dir:%d\n", (int)(encoder->getDirection()));
        pos = newPos;
    }
}

ロータリーエンコーダは、ポテンショメータ(可変抵抗)よりも微細な値を読み取ることができますので、ステッピングモータと組み合わせたりして正確に制御する場合に使えます。

関連記事