ESP32でロータリーエンコーダーを使用して、LEDの明るさ調整をしてみたいと思います。LEDの明るさの制御には、PWMを使用します。
ESP32とロータリーエンコーダーの接続
ESP32とロータリーエンコーダー、LED、LCDモジュールを接続していきます。
準備するもの
今回使用するものは、以下になります。
- ESP32
- ブレッドボード
- ジャンパーワイヤー
- ロータリーエンコーダー(ノンクリックタイプ)
- ロータリーエンコーダーDIP化基板
- 小型ボリューム用つまみ
- LED
- 抵抗(220Ω)
ロータリーエンコーダー、DIP化基板、小型ボリューム用つまみは、秋月電子通商様から購入しました。
ロータリーエンコーダーをDIP化基板に半田付けします。
接続
部品を以下のように接続していきます。
プログラム作成
ロータリーエンコーダーを使用して、LEDの明るさを調整するプログラムを作成していきます。
ロータリーエンコーダーのライブラリの追加
ロータリーエンコーダーを使用するためのライブラリをインストールします。
Arduino IDEを起動して、メニューの「ツール」→「ライブラリを管理」を選択します。
表示した「ライブラリマネージャ」のテキストボックスに「rotaryencoder」を入力します。
複数候補が表示されるので、「RotaryEncoder by Matthias Hertel」を探してインストールします。
ロータリーエンコーダーとPWM制御
ライブラリのインストールが完了したらプログラムを作成していきます。
処理概要は、以下になります。
- ロータリーエンコーダーを回して、LEDの明るさ調整を行う。
- ロータリーエンコーダーを正転させるとLEDが明るくなる。
- ロータリーエンコーダーを逆回転させるとLEDが暗くなる。
- LEDの明るさ調整は、PWM制御で行う。
- LCDモジュールとシリアル通信で、現在LEDに設定している値と ロータリーエンコーダーの回転方向を表示する。
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <RotaryEncoder.h>
const int LED = 5; // LED接続ピン
const int PWM_CH = 0; // PWMチャンネル
// LCDモジュール用設定
const unsigned int ADDRESS = 0x27;
const int CHARS_NUM = 16;
const int LINES_NUM = 2;
LiquidCrystal_I2C lcd(ADDRESS, CHARS_NUM, LINES_NUM);
// ロータリーエンコーダー用設定
const int PINA = 13; // ロータリーエンコーダーA入力
const int PINB = 14; // ロータリーエンコーダーB入力
const int ROTARYMIN = 0; // ロータリーエンコーダー最小値
const int ROTARYMAX = 255; // ロータリーエンコーダー最大値
int lastPos = -1; // ロータリーエンコーダー前回値
int encoderCount = 0; // ロータリーエンコーダー現在値
// ロータリーエンコーダークラス変数
RotaryEncoder encoder(PINA, PINB, RotaryEncoder::LatchMode::TWO03);
// ロータリーエンコーダー制御
void encoderProc() {
encoder.tick();
int newPos = encoder.getPosition();
if (newPos < ROTARYMIN) {
encoder.setPosition(ROTARYMIN);
newPos = ROTARYMIN;
} else if (newPos > ROTARYMAX) {
encoder.setPosition(ROTARYMAX);
newPos = ROTARYMAX;
}
if (lastPos != newPos) {
encoderCount = newPos;
ledcWrite(PWM_CH, encoderCount);
dispValue(encoderCount, (int)(encoder.getDirection()));
lastPos = newPos;
}
}
// LCD、シリアル表示処理
void dispValue(int val, int dir) {
char disp[8];
memset(disp, 0x00, sizeof(disp));
sprintf(disp, "%03d", val);
lcd.setCursor(0, 0);
lcd.print("value: ");
lcd.print(disp);
Serial.print("pos:");
Serial.print(disp);
lcd.setCursor(0, 1);
lcd.print("dir: ");
memset(disp, 0x00, sizeof(disp));
sprintf(disp, "%2d", dir);
lcd.print(disp);
Serial.print(" dir:");
Serial.println(disp);
}
void setup() {
pinMode(LED, OUTPUT);
ledcSetup(PWM_CH, 1000, 8);
ledcAttachPin(LED, 0);
pinMode(PINA, INPUT_PULLUP);
pinMode(PINB, INPUT_PULLUP);
lcd.init();
lcd.backlight();
dispValue(0, 0);
encoder.setPosition(0);
Serial.begin(115200);
delay(1000);
Serial.println("Start");
}
void loop() {
encoderProc();
}
プログラムについて簡単に説明していきます。
1~2行目で読み込んでいるヘッダファイルは、LCDモジュール(液晶)を使用するためのものです。
3行目で読み込んでいる「RotaryEncoder.h」が、ロータリーエンコーダーを使用するためのヘッダファイルになります。
15~25行目が、ロータリーエンコーダーに関する設定になります。
const int PINA = 13; // ロータリーエンコーダーA入力
const int PINB = 14; // ロータリーエンコーダーB入力
const int ROTARYMIN = 0; // ロータリーエンコーダー最小値
const int ROTARYMAX = 255; // ロータリーエンコーダー最大値
int lastPos = -1; // ロータリーエンコーダー前回値
int encoderCount = 0; // ロータリーエンコーダー現在値
// ロータリーエンコーダークラス変数
RotaryEncoder encoder(PINA, PINB, RotaryEncoder::LatchMode::TWO03);
15~16行目で、ロータリーエンコーダーからの入力ピンを設定しています。
18~19行目は、PWM制御を255段階で行うため、最小値に0、最大値に255を設定しています。
25行目で、ロータリーエンコーダークラスの変数を定義しています。第1、第2引数にロータリーエンコーダーからの入力ピン番号を指定します。第3引数は、ラッチモードとなっていましたが、違いがわからないためサンプルスケッチと同様の設定にしておきました。
次に初期化処理「setup()」関数の説明になります。
void setup() {
pinMode(LED, OUTPUT);
ledcSetup(PWM_CH, 1000, 8);
ledcAttachPin(LED, 0);
pinMode(PINA, INPUT_PULLUP);
pinMode(PINB, INPUT_PULLUP);
lcd.init();
lcd.backlight();
dispValue(0, 0);
encoder.setPosition(0);
Serial.begin(115200);
delay(1000);
Serial.println("Start");
}
77~79行目で、LEDのPWM制御の設定を行っています。
77行目で、LEDピン(5番)を出力用に設定しています。
78行目で、PWMの制御設定を行っています。第1引数で利用するPWMのチャンネル、第2引数でPWMの基本周波数、第3引数でデューティー比を表すビット数を設定しています。
79行目で、LEDピン(5番)にPWMのチャンネル0を割り当てています。チャンネルは、0~15を選択できます。
84~86行目は、LCDモジュールの初期設定になります。
88行目で、ロータリーエンコーダーの開始値を設定しています。
次に、ロータリーエンコーダーの制御とLEDの明るさを設定している「encoderProc()」関数の処理について説明いたします。
void encoderProc() {
encoder.tick();
int newPos = encoder.getPosition();
if (newPos < ROTARYMIN) {
encoder.setPosition(ROTARYMIN);
newPos = ROTARYMIN;
} else if (newPos > ROTARYMAX) {
encoder.setPosition(ROTARYMAX);
newPos = ROTARYMAX;
}
if (lastPos != newPos) {
encoderCount = newPos;
ledcWrite(PWM_CH, encoderCount);
dispValue(encoderCount, (int)(encoder.getDirection()));
lastPos = newPos;
}
}
「encoderProc()」関数は、「loop()」関数で呼び出されている唯一の関数になります。
29行目の「encoder.tick()」関数を呼び出すことで、ロータリーエンコーダーからの入力値A、Bを取得して、内部で保持する値の増減を行っているようです。正回転をすると「+1」、逆回転をすると「-1」になります。
31行目で、ロータリーエンコーダークラス内で保持している現在値を取得します。
33~40行目で、最小値~最大値の範囲内に値を収めるように制御しています。取得値が最小値より小さい、または、最大値より大きい場合は、「encoder.setPosition()」でロータリーエンコーダークラス内で保持している値を範囲内に収まるように変更しています。
45行目で、LEDの明るさ調整のためのPWM出力設定を行っています。78行目で、デューティー比を表すビット数を8に設定しているため、0~255の値が設定できます。
以上がプログラムの簡単な説明になります。
動作確認
実際に動作を確認してみます。
つまみを左に回すと正回転、右に回すと逆回転になります。
左に回していくと、LEDが点灯してだんだんと明るくなっていきます。また、右に回していくとだんだんと暗くなっていきます。
シリアルモニタやLCDモジュールでも値が確認できています。
左に回すと「1」で正回転、右に回すと「-1」で逆回転が確認できました。
まとめ
ロータリーエンコーダーを使用して、LEDの明るさ調整を行ってみました。
最初にロータリーエンコーダーの制御を自分で作ってみたのですが、取得値が飛び飛びになってしまい、なかなかうまくいきませんでした。
ライブラリを使用して実装したところ、正常に値が取得できていて、自分の力不足を痛感しているところです。
再度どこかのタイミングで、ロータリーエンコーダーの制御プログラムに挑戦していきたいと思います。
【参考図書】