押しボタンの代わりにロータリーエンコーダーを使用
今までの記事。Arduino UNO とRTCモジュールとキャラクター液晶モジュールで時計を作成。現場でPCが無くても時刻変更ができるように進めてきた。
最後にタクトスイッチで数値変更するのをロータリーエンコーダーで変更できるようにしようと思う。
ロータリーエンコーダーとは簡単に言うと回転方向や回転角度を拾うものだ。
昔からあるものとすれば、工業用の装置でモーターで位置決めが必要な時に使う。
モーターの軸に等角度で穴の開いた円盤をつけて、その穴を馬蹄形の光電センサーでOFFとONを拾う。角度差をつけて二個のセンサーを付けると、そのOFF/ONの組み合わせで回転方向や回転の角速度がわかる仕組みだ。
でも今回使用するのは形状はボリュームみたいなもの。右や左に回すとクリック感があるので、それでそれぞれの方向に何回クリックしたか信号を出すもの。
アマゾンで購入すると単価が高いっちゃ高いけど、送料梱包費度と思えば仕方がない。
県外のパーツショップに行く時間と交通費を考えれば仕方が無い。
つまみは二個で1000円とかすごいぼったくりだ。でも秋月電子さんで数十円だが送料が500円かかる。何かのついでじゃないと勿体ない。
軸は押し込むとA接点のスイッチになっていて、ばねで戻って指を離せばOFFになる。
ここが気に入った。
動かしてみる
ライブラリはここの物を使用する。
スケッチに書き込むのに一番スッキリしていそうだったから。
僕の購入したローターリーエンコーダーは手前に三つピンが出ていて、奥側に二つピンが出ている。
3ピンの方は左右をArduino UNO のA2番とA3番にそれぞれ接続。真ん中はGNDへ。
2ピンの方は片側は4番に入れた。
ロータリーエンコーダーをそのままブレッドボードに差すと、不安定でぐらつき入力が入らない時がある。
そのためはんだ付けでリード線を付けた。
つまみは3Dプリンターで作成。秋月電子さんで一個数十円で販売しているが送料が500円かかる。何かのついでに買えばよい。
ライブラリのサンプル例を試す
Arduino IDEからサンプル例の「polling」を書き込んで試してみる。
エンコーダーを左右に回してクリック感を感じると、回転方向がシリアルモニターに表示される。
サンプル例を少し触って、液晶モジュールに表示してみる。
#include <LiquidCrystal_I2C.h>
#include <Rotary.h>
LiquidCrystal_I2C lcd(0x27, 16, 4);
Rotary r = Rotary(2, 3);
int xx = 10;
void setup() {
Serial.begin(115200);
lcd.init();
r.begin(true);
lcd.clear();
lcd.backlight();
}
void loop() {
lcd.setCursor(0, 0);
lcd.clear();
lcd.print(xx);
unsigned char result = r.process();
if (result == DIR_NONE) {
// do nothing
}
else if (result == DIR_CW) {
xx++;
}
else if (result == DIR_CCW) {
xx--;
}
//lcd.setCursor(0, 0);
//lcd.print(xx);;
//Serial.println(xx);
}
まず数字の10をスタート地点にしてエンコーダーを左右に回すと数値が増減する。
ちなみにCWは正転(時計回り)CCWは反転(反時計回り)です。
スケッチを合体させる
#include <Rotary.h>//エンコーダー
#include <DS3232RTC.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#if defined(ARDUINO) && ARDUINO >= 100
#define printByte(args) write(args);
#else
#define printByte(args) print(args,BYTE);
#endif
DS3232RTC myRTC;
LiquidCrystal_I2C lcd(0x27, 20, 4); // set the LCD address to 0x27 for a 16 chars and 2 line display
Rotary rotary = Rotary(2, 3);
char rtcDate[40];//年月日時分秒を収納する文字列
int entB = 4; // EnterボタンのピンNo.(エンコーダーの接点を使用
int selB = 0; // SelectボタンのピンNo.
int yy;
int mon;
int dd;
int hh;
int mm;
int xx;
void updateClock() {
setTime(hh, mm, 0, dd, mon, 2000 + yy); //時、分、秒、日、月、年の順番に入力
myRTC.set(now());
lcd.clear();
}
// 時計の時刻合わせ
void clockSet() {
int n, nen, tsuki, dayCount;
lcd.clear();
lcd.setCursor(0, 0);
//lcd.print("Clock set mode"); // モード入りメッセージ
//lcd.printByte(0xc8); lcd.printByte(0xba); lcd.printByte(0xa6); lcd.printByte(0xd5); lcd.printByte(0xd9); lcd.printByte(0xbc);
lcd.printByte(0xbc); lcd.printByte(0xde); lcd.printByte(0xba); lcd.printByte(0xb8); //時刻設定モードカタカナ表示
lcd.printByte(0xcd); lcd.printByte(0xdd); lcd.printByte(0xba); lcd.printByte(0xb3);
while (digitalRead(entB) == LOW) { // Clock EnterスイッチがOFFになるまで待つ
}
delay(30);
lcd.setCursor(0, 0);
lcd.print("SEL "); lcd.printByte(0xbd); lcd.printByte(0xb3); lcd.printByte(0xc1);
lcd.print(" ENT "); lcd.printByte(0xc2); lcd.printByte(0xb7); lcd.printByte(0xde); lcd.printByte(0xcd);
lcd.setCursor(0, 1);
sprintf(rtcDate, "%4d/%02d/%02d %02d:%02d:%02d",
year(), month(), day(), hour(), minute(), second());
lcd.print(rtcDate);
//Serial.println(rtcDate);
//データ位置とmin,max値を指定して数値設定プログラムを呼ぶ
setV(3, 23, 33); // 年(2023〜2033年まで)
yy = xx;
setV(6, 1, 12); // 月
mon = xx;
// 日は月の大小と閏年を反映して呼ぶ
nen = (rtcDate[2] & 0x0f) * 10 + (rtcDate[3] & 0x0f);
tsuki = (rtcDate[5] & 0x0f) * 10 + (rtcDate[6] & 0x0f);
dayCount = 31;
if (tsuki == 4 | tsuki == 6 | tsuki == 9 | tsuki == 11) {
dayCount = 30;
}
if (tsuki == 2) { // 2月の処理
dayCount = 28;
if ((nen % 4) == 0) {
dayCount = 29; // うるう年
}
}
setV(9, 1, dayCount); // 日
dd = xx;
setV(12, 0, 23); // 時
hh = xx;
setV(15, 0, 59); // 分
mm = xx;
lcd.noCursor();
updateClock(); // RTCに日時を設定
}
// set ボタンが押される毎にrtcDateの指定位置の値をインクリメント、液晶を更新
// ent ボタンが押されたら戻る
void setV(int p, int minV, int maxV) { // p:ポインタ、minV:最小値、maxV:最大値
int yy;
int mon;
int dd;
int hh;
int mim;
//int xx;
char upper;
char lower;
lcd.setCursor(p, 1);// 液晶のカーソルを移動
lcd.cursor();// カーソル表示
//Serial.print("p = ");
//Serial.println(p);
xx = (rtcDate[p - 1] & 0x0f) * 10 + (rtcDate[p] & 0x0f);
while (digitalRead(entB) == LOW) { // entBがOFFになるまで待つ
delay(30);
}
//Serial.println("push");
while (digitalRead(entB) == HIGH) { // entBがNOでなければ以下の処理行う
lcd.setCursor(p, 1);
lcd.cursor();// カーソル表示
unsigned char result = rotary.process();
if (result == DIR_NONE) {
// do nothing
}
else if (result == DIR_CW) { //エンコーダー正転
xx++;
}
else if (result == DIR_CCW) { //エンコーダー反転
xx--;
}
upper = (xx / 10) | 0x30; // 上位をASCIIへ変換
lower = ( xx % 10) | 0x30; // 下位をASCIIへ変換
lcd.setCursor(p - 1, 1 );
lcd.print(upper); // 表示更新
lcd.print(lower);
if (xx >= maxV + 1) { // 上限超えてたらゼロに戻す
xx = minV;
}
if (xx <= minV - 1) { // 上限超えてたらゼロに戻す
xx = maxV;
}
}
delay(30);
Serial.println(xx);
//upper = (xx / 10) | 0x30; // 上位をASCIIへ変換
//lower = ( xx % 10) | 0x30; // 下位をASCIIへ変換
//lcd.setCursor(p - 1, 1 );
//lcd.print(upper); // 表示更新
//lcd.print(lower);
rtcDate[p - 1] = upper; // データ文字列更新
rtcDate[p] = lower;
while (digitalRead(selB) == LOW) { // selBが離されるまで待つ
}
delay(30);
//delay(30);
//lcd.noCursor(); // 液晶のカーソルを消す
}
void setup() {
Serial.begin(115200);
myRTC.begin();
rotary.begin(true);//エンコーダーセット
setSyncProvider(myRTC.get); // the function to get the time from the RTC
if (timeStatus() != timeSet)
Serial.println("Unable to sync with the RTC");
else
Serial.println("RTC has set the system time");
lcd.init();
lcd.backlight();
digitalWrite(6, HIGH);
Serial.print("ready");
// オープンドレインなのでプルアップ pinMode(13, OUTPUT); // 動作表示LED
pinMode(entB, INPUT); // ClockEnterボタン
pinMode(selB, INPUT); // ClockSelectボタン
pinMode(selB, INPUT); // ClockSelectボタン
digitalWrite(entB, HIGH); // プルアップ
digitalWrite(selB, HIGH); // プルアップ
delay(50);
lcd.clear();
if (digitalRead(entB) == LOW) {// Enterボタンが押されていたら
clockSet(); // RTCの時刻を合わせる
}
}
void loop() {
sprintf(rtcDate, "%4d/%02d/%02d %02d:%02d:%02d",
year(), month(), day(), hour(), minute(), second());
//年月日時分を年以外二桁表示
lcd.setCursor(0, 0);
lcd.print(rtcDate);
//lcd.print(hour());lcd.print(":");lcd.print(minute());
delay(500);
lcd.setCursor(13, 0); //セミコロンがフリッカして何となく時計が動いている雰囲気
lcd.print(" ");
delay(500);
}
エンコーダーのつまみを押し込んだ状態で電源ONあるいはリセットさせると、時刻変更のサブルーチンに入る。
カーソルが年の一番最後の桁に表示されるが、エンコーダーを回すと増減する。
設定最大値を超えると、設定最小値の数値となる。逆転させても同様。分表示なんかは0から59まで有るからちまちま増方向でタクトスイッチ押すより楽だとおもう。
動画を撮影。
頑張って自分で作ったように書いているが、全部ネット上の情報の継ぎ接ぎである。
ネットで公開してくださる皆さん、ありがとうございます。
ライブラリを覗いたり、データシートを読むのはまだまだこれから。初心者のみなさん頑張りましょう。
宿題
キャラクター液晶モジュールではなくグラフィック液晶モジュールを使用してみようと思う。せっかくなんでアナログ時計で。
御来訪いただき有難うございます
皆様には余り縁の無い話ですが、胃がん癌治療中でもうすぐ手術予定です。
ステージ4なので手術後の生存率はデータ上では癌の中でも良くないです。
糖尿病を併発してやれやれな感じですが、時間の許す限りものづくりしてみたいです。
癌治療中がどんな感じなのかはこちら