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

M5StickC PLUSとロータリーエンコーダの配線
M5StickC PLUSとロータリーエンコーダの配線

この記事では、ロータリーエンコーダをArduinoで使う方法をご説明します。ここではA相・B相の2相出力の汎用ロータリーエンコーダを使用しました。

実は、ロータリーエンコーダを使っていざプログラミングしようとするとかなり大変です。そのため、ここではArduinoライブラリを使用しました。

なお、ArduinoボードはArduino互換機であるESP32 PICOが搭載されたM5StickC PLUSを使用しました。M5StickC PLUSは、バッテリーやLCD他いろいろなセンサがパッケージされたM5Stackシリーズ製品の一つです。一般のArduinoと同じようにArduino IDEで開発できます。

Arduino互換機であれば本記事の内容で動作すると思いますので、ぜひご参考になさってみてください。また、M5StickC PLUSの使い方はM5StickC PLUSでArduinoをはじめよう!をご参考になさってみてください。

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

M5StickC PLUSとロータリーエンコーダの配線
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
A0110
B0011

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

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

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

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

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

それでは、実際に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の指定は、こちらの公式サイトで説明されているようですが、いまいちよく分かりませんでした。

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;
    }
}

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

記事に関するご質問などがあればTwitterへお返事ください。
この記事で紹介した商品
M5Stackのオススメ参考書
M5Stack製品
M5StickCで使えるHat
関連記事