気の向くままに辿るIT/ICT/IoT
webzoit.net
IoT・電子工作

Arduino/LCD1602/RTCでSleep/表示・バックライトON/OFF SW付き時計を作る

ウェブ造ホーム前へ次へ
サイト内検索
カスタム検索
Arduinoって?

Arduino/LCD1602/RTCでSleep/表示・バックライトON/OFF SW付き時計を作る

Arduino/LCD1602/RTCでSleep/表示・バックライトON/OFF SW付き時計を作る

画面表示・バックライトON/OFF・スリープ機能付きArduino/RTC DS1302/LCD 1602時計
2019/04/09

 Arduinoと液晶ディスプレイLCD1602を使ってPower-downモードによるスリープ、ウォッチドックタイマー(WDT)による復帰、物理的な液晶表示のON/OFF、バックライトのON/OFFスイッチ付きデジタル時計を自作してみるページ。

画面表示ON/バックライトOFF

 画面表示ON/バックライトOFF。

画面表示OFF/バックライトOFF

 画面表示OFF/バックライトOFF。

画面表示OFF/バックライトON

 画面表示OFF/バックライトON。

画面表示ON/バックライトON(失敗)

 画面表示ON/バックライトON...見えない...

画面表示ON/バックライトON

 これもピンぼけしてますが、画面表示ON/バックライトONの場合、実際はこんな感じです...。

 バックライト点けた場合、どうやったら、うまく撮影できるんでしょうか...。

 で、併せてArduinoで最もDeepなSleep、SLEEP_MODE_PWR_DOWNもしている、と。

今回使ったもの

 Arduinoボードは、Uno系なら何を使っても同じ方法でいけますし、miniUSBケーブルは100均にもあり、RTCモジュールはDS1307やDS3231/3232などでも、というか3択なら、ボタン電池CR2302を使う場合、パターンカットなど対処を要すものの、アラーム機能付きなど高機能で完成度が高くバグのないDS3231が推奨っぽい、スイッチは、ロッカスイッチでもトグルスイッチでも他でも可です。

 スリープ機能が働くのは、ICチップであってArduinoのような開発ボードは、他に電力を消費するものが搭載されているため、電力消費を激減させたい場合には、5V版ではなく、3.3V版、もっと言えば開発ボードではなく、ATMega328PなどのIC単体での実装が効果的です。

 検証回路としては、ザッとAmazon/Amazonマーケットプレイス相場でArduino Nano 300円前後、LCD1602+I2Cモジュールは、300円前後、DS1302は今や高めで300円前後(DS1307の方が150円前後と割安っぽく、最も推奨されるDS3231も300円前後)、ロッカスイッチは100円前後、ミニブレッドボードも100円前後、ジャンパワイヤ適量20円、ワニグチクリップケーブル20円x4=80円と仮定、CR2302は100均一で単価54円(2個108円)、USBケーブルは100均一で108円とすると1300円前後、Aliexpressでこの6〜8割程度といったところでしょうか。

GAOHO 新しい 1PC 8-Bit マイクロコントローラ、 マイクロコントローラATmega328P-PU

KKHMF UNO R3開発ボード USBケーブル付属 Arduinoと互換

KKHMF Mini USB Nano V3.0 ATmega328P CH340G 5V 16M マイクロコントローラーボード モジュールArduinoと互換

EasyWordMall Arduino Pro Mini互換ボード Atmega328 5V 16MHz

Rasbeeオリジナル FT232RL互換 3.3V/5V FTDI/USB/TTL変換アダプタ

HiLetgo RTC時計モジュール DS3231 AT24C32 IIC

EasyWordMall Arduino AVR、ARM、PIC SMD用 RTC DS1302 リアル タイム クロック モジュール

EasyWordMall DS1307 RTC 7クロックモジュール小型RTC I2C 24C32メモリArduino用

SNOWINSPRING 1602 16 x 2シリアルHD44780文字LCDボードディスプレイ青地に白色5V IIC / I2Cシリアルインタフェースアダプタモジュール付き Arduino用

SODIAL(R)2pcs x ON-OFF 2ポジションSPST2ピンスナップロッカーのスイッチ16A/250V 20A/125V AC

Rasbee 400穴 ブレッドボード 8.5*5.5cm 1個

HiLetgo 400穴 ブレッドボード 8.5*5.5cm 5個セット

HiLetgo 400穴 ブレッドボード 8.5*5.5cm 10個セット

Rasbee SY-170 ミニブレッドボード カラフルブレッドボード 5個

ELEGOO 120pcs多色デュポンワイヤー、arduino用、オス-メス オス-オス メス ?メス ブレッドボードジャンパーワイヤー

HiLetgo ジャンパーワイヤー(オス-オス) 20cmx40本 5セット

Rasbee ジャンパーワイヤー(オス-オス) 各種長さx65本

HiLetgo ベークライトDIYプロトタイプボード PCBユニバーサルブレッドボード 5x7cm(10枚)

 LCD1602/LCD2004などのキャラクタ液晶は、個別に買ってはんだ付けでも構いませんが、何れにしても配線4本で済むI2Cモジュール付きがなんと言っても楽で嵩張らず簡単です。

回路

ArduinoLCD1602
I2C
DS1302液晶SWBacklight SW備考
SDA(A4)SDA---
SCL(A5)SCL--
4---他極はGNDへ
5---
6-CLK--
7DAT
8RST
5VVCC
GND

 表のスペース節約を図った結果、スイッチをSW、バックライトをBacklightと表記しました。

 スイッチの接続は少し悩んだ結果○印にしました。

 尚、表示とバックライトのON/OFFスイッチありきで考えたため、後述のスケッチのウォッチドックタイマー4秒との兼ね合いで切り替えから、若干タイムラグが生じます。

 むしろ、タイムラグはありますが、スリープ・復帰とこれらのON/OFFスイッチを併用できます、というか、そうしたくて結果、こうなりました。

 電源は、ArduinoボードをUSB接続するか、電池なら+/-をVIN/GNDに接続します。

 電池の場合、GNDは他とは別のピンからとります。

スケッチ・ライブラリ

#include <LiquidCrystal_PCF8574.h>
#include <MyRealTimeClock.h>
#include <avr/sleep.h>                  // スリープライブラリ
#include <avr/wdt.h>                    // ウォッチドッグタイマー ライブラリ
 
LiquidCrystal_PCF8574 lcd(0x27); // lcd1602 yellow black
//LiquidCrystal_PCF8574 lcd(0x3F); // lcd2004 yellow black
 
MyRealTimeClock myRTC(6, 7, 8);
 
char daysOfTheWeek[7][12] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
 
const int dsw = 4; // Display ON/OFF Switch
const int bsw = 5; // Backlight ON/OFF Switch
 
volatile int wdt_cycle = 0;           // 必要ならコメントアウトを削除
// 0=16ms, 1=32ms, 2=64ms, 3=128ms, 4=250ms, 5=500ms
// 6=1sec, 7=2sec, 8=4sec, 9=8sec
int wdt_time_num = 8;
// 60sec/4sec=15
int const_num = 15;
 
void setup() {
  Serial.begin(9600);
  pinMode(dsw, INPUT_PULLUP);
  pinMode(bsw, INPUT_PULLUP);
//  myRTC.setDS1302Time(00, 15, 10, 0 , 07, 04, 2019);
  lcd.begin(16, 2);
  lcd.print("DS1302 Clock");
  Serial.println("DS1302 Clock");
  lcd.setCursor(0,1);
  lcd.print("Start ...");
  Serial.println("Start ...");
  delay(2000);
  clockDisp();
}
 
void loop() {
  sw_trig();
  // delayWDT2(8):4sec*15=60sec
  if (wdt_cycle == const_num) {
    clockDisp();
    wdt_cycle = 0;
  }
  delayWDT2(wdt_time_num);
}
 
void delayWDT2(unsigned long t) {       // パワーダウンモードでdelayを実行
  Serial.flush();                       // シリアルバッファが空になるまで待つ
  delayWDT_setup(t);                    // ウォッチドッグタイマー割り込み条件設定
 
  // ADCを停止(消費電流 147→27μA)
  ADCSRA &= ~(1 << ADEN);
 
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);  // パワーダウンモード指定
  sleep_enable();
 
  // BODを停止(消費電流 27→6.5μA)
  MCUCR |= (1 << BODSE) | (1 << BODS);   // MCUCRのBODSとBODSEに1をセット
  MCUCR = (MCUCR & ~(1 << BODSE)) | (1 << BODS);  // すぐに(4クロック以内)BODSSEを0, BODSを1に設定
 
  asm("sleep");                         // 3クロック以内にスリープ sleep_mode();では間に合わなかった
 
  sleep_disable();                      // WDTがタイムアップでここから動作再開
  ADCSRA |= (1 << ADEN);                // ADCの電源をON(BODはハードウエアで自動再開される)
}
 
void delayWDT_setup(unsigned int ii) {  // ウォッチドッグタイマーをセット。
  // 引数はWDTCSRにセットするWDP0-WDP3の値。設定値と動作時間は概略下記
  // 0=16ms, 1=32ms, 2=64ms, 3=128ms, 4=250ms, 5=500ms
  // 6=1sec, 7=2sec, 8=4sec, 9=8sec
  byte bb;
  if (ii > 9 ) {                        // 変な値を排除
    ii = 9;
  }
  bb = ii & 7;                          // 下位3ビットをbbに
  if (ii > 7) {                         // 7以上(7.8,9)なら
    bb |= (1 << 5);                     // bbの5ビット目(WDP3)を1にする
  }
  bb |= ( 1 << WDCE );
 
  MCUSR &= ~(1 << WDRF);                // MCU Status Reg. Watchdog Reset Flag ->0
  // start timed sequence
  WDTCSR |= (1 << WDCE) | (1 << WDE);   // ウォッチドッグ変更許可(WDCEは4サイクルで自動リセット)
  // set new watchdog timeout value
  WDTCSR = bb;                          // 制御レジスタを設定
  WDTCSR |= _BV(WDIE);
}
 
ISR(WDT_vect) {                         // WDTがタイムアップした時に実行される処理
  wdt_cycle++;                      // 必要ならコメントアウトを外す
/*
  Serial.print("wdt_cycle = ");
  Serial.println(wdt_cycle);
*/
}
 
void sw_trig() {
  if(digitalRead(dsw) == LOW) {
    lcd.display();
  } else {
    lcd.noDisplay();
  }
  if(digitalRead(bsw) == LOW) {
    lcd.setBacklight(HIGH);
  } else {
    lcd.setBacklight(LOW);
  }
}
 
void clockDisp() {
  myRTC.updateTime();
  lcd.clear();
  lcd.print(myRTC.year);
  lcd.print("/");
  if(myRTC.month < 10) lcd.print("0");
  lcd.print(myRTC.month);
  lcd.print("/");
  if(myRTC.dayofmonth < 10) lcd.print("0");
  lcd.print(myRTC.dayofmonth);
  lcd.print("(");
  lcd.print(daysOfTheWeek[myRTC.dayofweek]);
  lcd.print(")");
 
  lcd.noBlink();
  lcd.setCursor(0,1);
  lcd.print("Time: ");
  if(myRTC.hours < 10) lcd.print("0");
  lcd.print(myRTC.hours);
  lcd.print(":");
  if(myRTC.minutes < 10) lcd.print("0");
  lcd.print(myRTC.minutes);
/*
    lcd.print(":");
    if(myRTC.seconds < 10) lcd.print("0");
    lcd.print(myRTC.seconds);
    delay(1000);
*/
}

 ライブラリには、次のものを使わせて頂きました。

 スケッチのスリープと復帰部分に関しては、ArduinoのCPUをスリープさせて消費電流を減らす関数 delayWDT2をそのまま使わせて頂きました。

 秒表示はしない前提で1分ごとに時計を更新表示させるべく、ウォッチドックタイマーの時間は、計算しやすい4秒としました。

 尚、delay()は使わなかったものの、ズレが生じる可能性は無視しました。

 2つのスイッチON/OFF状態用にsw_trig()、時計表示用にclockDisp()関数を作り、前者をloop()先頭、後者を初期表示の為、setup()に、また、WDT復帰時、かつ、1分ごとに表示のため、loop()内で実行させています。

 1分ごとの(4秒で復帰のウォッチドックタイマーを15回カウント)チェックをloop()内で行なうことで4秒ごとながら常時、スイッチ操作が効くという算段です。

 もちろん、操作を4秒にピタッと合わせる必要はなく、切り替え後のタイムラグとして現れます。

 MyRealTimeClockライブラリの場合、画面表示のONには、display()、OFFには、noDisplay()が、バックライトのON/OFFには、setBacklight(HIGH)/setBacklight(LOW)が用意されています。

 ちなみにsetBacklight()の引数には、0-255の値を入れることを想定しているようで255/0でも良いのですが、途中の値には、あまり意味がなさそうなのでHIGH/LOWで良いかと。

 ところでRTCモジュールにボタン電池を入れ、日付時刻を設定後、アップロード、更に日付時刻設定行をコメントアウト後、改めてアップロードしておかないと電源を入れるたび、スケッチ上の設定時の日付時刻から開始されてしまうので要注意です。

電池駆動検証

 2019/04/09 21:30〜 使用済み単3アルカリ電池x4(電圧5.55V)、液晶表示・バックライト共にONで使用開始。

 2019/04/14 21:30 使用済み単3アルカリ電池x4(電圧4.15V)、バックライトは明るいものの、液晶表示は、微かになり始めたので終了。

 5日とは、意外なほど短かったですね...ただ、電池電圧4.54V、LCD、DS1307、スイッチ部で残電圧3.1〜3.3V程度、もっといけそうなもんですし、鳥よけは電池残圧2V程度までで1ヶ月ほど持つんですが...、なぜ...。

 鳥よけは外部割り込み、この時計は4秒毎のWDTという違いによるのか、そもそも電池電圧4V台で随所で3V前半まで電圧降下、バックライトはともかく、そもそもLCDの入力電圧は5Vなんでしょうし、それだと不足ということなんでしょうね...。

 となるとICチップATMega328Pで実装してみるか、USB充電器で運用ですかね...。

備考

 ウォッチドックタイマーは最大8秒まで対応で8秒設定が最も省電力、今回は4秒なので最大の効果は発揮できないのでしょうが、どれくらいのものなんでしょう...。

 電流も0.01A以上を計測できるUSB電圧電流計だと比較どころか、常時、バックライトOFFで0.00A、ONで0.01Aという結果しか得ることができませんでした。

 ちなみにDS1302に使ったボタン電池も使用済みで残電圧2.8Vだったものを使っていますが、時刻保持もしてくれますし、機能はしており、検証でもこれを入れたままにしてあります。

[2019/04/14]

 単3エネループx4本で丸5日でした。

ケーシングしてみたArduino/RTC DS1302/LCD 1602時計
[2019/04/27]

 ユニバーサル基板にはんだ付け、表示、バックライトのON/OFFには、トグルスイッチを使い、100均セリアのトレカケースに入れてみました。

 LCDの配線が微妙に短いこともあり、ボルト止めするか否かは、思案中...。

 今更ながら、[Time:]なんて要らないですし、37センサーキット温湿度計DHT11の他に3つも入っていて余ってる温度計モジュールを入れるのもありか...収まらないか...、モジュールだと。

ケーシングしてみたArduino/RTC DS1302/LCD 1602時計の裏面

 配線は、盛りはんだしたり、新品もありますが、今回は、結構たまってる芯抜けしたりしたジャンパワイヤなどを使いました。

 今回使ったArduino Nanoボードは、ユニバーサル基板にメスのピンヘッダをハンダ付けして挿しました。

 尚、ユニバーサル基板は、そのままでは入らなかったので一辺だけ端っこをカッターで筋を入れてからラジオペンチで折り(割り)ました。

 ちなみに電子工作を初めて2年ちょい、ブレッドボード版を以て完成としたものは、そこそこありますが、ユニバーサル基板は、数カ月前、自作スマートスピーカー用にラズパイboot/reboot/shutdownボタンで使ったのが初めて、これが2つめです。

 ケースの底、内側には、基板のクッション代わりというか、配線外れ防止、底面密着回避の為にグルーガンで要所にグルーを盛るかしておくのも良いかなと。

 何度か電源を入れ直したりすると曜日が飛び、再アップしないと表示されなくなったりするのは、DS1307のご愛嬌(バグ)ってことでしょうか、Arduino公式サイトにも書いてあった通り、DS1302/DS1307ではなく、やっぱり、DS3231にしておく方が賢明そうです。

ウェブ造ホーム前へ次へ