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

Arduinoで『自動ON/OFFタイマー付きLEDライト』を作る

ホーム前へ次へ
Arduinoって?

Arduinoで『自動ON/OFFタイマー付きLEDライト』を作る

Arduinoで『自動ON/OFFタイマー付きLEDライト』を作る

Arduino/LEDストリップライト/RTCモジュールを使ったライト自動ON/OFFタイマー回路
2018/02/04

 Arduinoを使って『ON/OFFタイマー付きLEDライト』を自作してみるページ。

 例えば、17:00に点灯、午前0:00に消灯するライト。

 まる1日や数日間、留守にする際などの防犯対策の一環として、規則正しい生活をしている場合、要介護など、電気のON/OFFがままならない状況にある場合、また、夜、明かりが灯っている自宅への帰宅だけでなく、眠る前に電気を消すのすら面倒な!?一人暮らしや家族にとって嬉しいかもしれないオートタイマーライト。

 ただ、ここでは、室内照明にLEDテープ(ストリップ)ライトを、タイマーにRTCモジュールを使い、これをON/OFFさせるものとします。

 Arduinoボードには、Arduino Nano 5V 16MHz互換機 350円、LEDには、12V 5m SMD3238 600球 単色LEDストリップライト411円、仮に1m使うとして約82円、タイマーにRTC DS1302 115円、バックアップ電池にCR2032 54円、昇圧モジュールにMT3608(5個449円)、整流ダイオードに1N4004(50個147円)、ハイパワーMOSFETにIRFZ34N(5個208円)、ブレッドボード、ジャンパワイヤ、1/4Wカーボン抵抗220Ωなど多めに見積もっても約300円をAmazonマーケットプレイスで、LED放熱兼固定用アルミアングル 0.8mmx1.2mmx1.2mmx2000m 343円、1mとして約172円を近所のホームセンターで買うとして、締めて約1203円程度。

 写真では、ケーブル付きDCプラグと端子付きDCジャックも使っていますが、他から一時的に持ってきた関係で、そのまま使っているだけ、使用したLEDストリップライト(単色)もケーブル出しなので昇圧モジュールに直結することで省略可能。

 また、パワーMOSFET、整流ダイオード、抵抗の回路を、見づらいながら、写真下の黒い170穴ミニブレッドボードに載せていますが、これも他から一時的に持ってきたこと、回路の確認、複製が容易だからというだけでNanoが載った400穴ブレッドボードにまとめて組み込むことも可能。

 回路は、LEDテープによる誘導イルミでも使わせて頂いたMOSFET Motor Example画像の回路でモータ部をLEDストリップライト+昇圧モジュールに替えただけ。

 これでも機能することは確認済みなのですが、とりあえずにも140型DCモータでの検証でもいけたのだから、たぶん大丈夫だろうという程度でAmazonで買える極々限られた候補の中から選び、データシートにも目を通していないハイパワーMOSFETと手持ちのものを使った整流ダイオードの選定は、かなり適当。

#include <MyRealTimeClock.h>
 
MyRealTimeClock myRTC(6, 7, 8);
const int led = 13;
 
void setup() {
// Serial.begin(9600);
 pinMode(led, OUTPUT);
// myRTC.setDS1302Time(00, 24, 11, 12 , 04, 02, 2018);
}
 
void loop() {
 myRTC.updateTime();
 
 if(myRTC.hours == 17 && myRTC.minutes == 00 && myRTC.seconds == 00) {
  digitalWrite(led, HIGH);
 }
 if(myRTC.hours == 23 && myRTC.minutes == 59 && myRTC.seconds == 59) {
  digitalWrite(led, LOW);
 }
/*
 Serial.print("Current Date / Time: ");
 Serial.print(myRTC.dayofmonth);
 Serial.print("/");
 Serial.print(myRTC.month);
 Serial.print("/");
 Serial.print(myRTC.year);
 Serial.print(" ");
 Serial.print(myRTC.hours);
 Serial.print(":");
 Serial.print(myRTC.minutes);
 Serial.print(":");
 Serial.println(myRTC.seconds);
*/ 
}

 sleep(スケッチは『鳥よけ装置』参照)を入れていないし、より正確を期すためのNTPによる時刻補正(Arduino IDEサンプルスケッチNTPClient参照)も取り込んではいませんが、ラフスケッチとしては、動作確認でも使わせて頂いたThe DS1302 Real Time Clock on Arduinoのinclude行のみ修正したものを流用、あとはLEDを点灯・消灯させるだけ。

 RTCモジュール(DS1302/DS1307はそのまま、DS3231は要加工)にボタン電池CR2032を入れ、myRTC.setDS1302Time()関数で一度、RTCモジュール上の日付時刻を設定すべくArduinoボードにアップロード、その後、当該行をコメントアウトし、再度、スケッチをArduinoボードにアップロードすれば、設定した時間を保持してくれますていることがわかります

(実用品への実装、運用時は、当該作業は1度だけですが、当然、検証時などif文の条件となる時間を変更したい場合は、スケッチを編集後、再度アップロードする必要があります。)

 検証時には、if文の時間を変更すれば、数秒や数十秒といった短時間で動作確認できますし、setup()/loop()関数内でそれぞれコメントアウトしてあるシリアル操作部を有効にすれば、シリアルモニタ上でRTCモジュール+バックアップ電池が保持している時間の確認も可能です。

 今回は、スケッチ上で決め打ちで時間指定していますが、作りようによっては、物理スイッチやタッチパネル、スマホなどで時間を指定できるようにすることもできるでしょう。

[2018/02/13]

 と思いましたが、さて、スリープを...と考えた時、DS1302によるこの方法だとディープスリープモードからの割り込みがかけられない、かと言ってアラーム機能はない模様、DS1307にはタイマーらしきものがあるようですが、時間指定、特にON、OFFの2段階はできそうもない...ことに気づきました。

 やっぱり、DS3231を使わないとダメか...。

#include <DS3232RTC.h>    // http://github.com/JChristensen/DS3232RTC
#include <Streaming.h>    // http://arduiniana.org/libraries/streaming/
#include <avr/sleep.h>
#include <avr/power.h>
 
const int trigerPin = 2;
const int ledPin = 13;
const uint8_t alarmInput(trigerPin);
bool alarm_state = false;
 
const int on_hour = 19;
const int on_minutes = 28;
const int on_seconds = 40;
const int off_hour = 19;
const int off_minutes = 30;
const int off_seconds = 0; // 今回のようにAlarm2とした場合は使えないので固定
 
void wakeUpNow(){
 Serial.println("Wake up!");
 
 alarm_state = true;
 
 // デバグ用
 int val = digitalRead(ledPin);
 Serial.print("val = ");
 Serial.println(val);
 Serial.print("hour = ");
 Serial.println(hour());
 Serial.print("minute = ");
 Serial.println(minute());
 Serial.print("second = ");
 Serial.println(second());
 // デバグ用
 
// if (hour() == off_hour) {
// if (minute() == off_minutes && second() == off_seconds) {
 if (minute() == off_minutes || digitalRead(ledPin)) {
  digitalWrite(ledPin, LOW);
// } else {
// } else if(hour() == on_hour) {
// } else if(minute() == on_minutes && second() == on_seconds) {
 } else if(minute() == on_minutes || !digitalRead(ledPin)) {
  digitalWrite(ledPin, HIGH);
 }
 
/*
 // LEDのON/OFFで評価するとスケッチアップや電源ONのタイミングによっては挙動が逆に...
 if (digitalRead(ledPin)) {
  digitalWrite(ledPin, LOW);
 } else {
  digitalWrite(ledPin, HIGH);
 }
*/
}
 
void sleepNow(){
 Serial.println("Sleep...");
 delay(10);
 set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 sleep_enable(); //スリープの有効化
 
 // アラームの割り込み時の信号はLOWなので第3引数は、FALLING
 attachInterrupt(digitalPinToInterrupt(alarmInput), wakeUpNow, FALLING);
 sleep_mode(); //指定したモードでスリープを開始
 sleep_disable(); //割り込み時、スリープから復帰
 detachInterrupt(0); //割り込み処理解除
}
 
void setup() {
 Serial.begin(9600);
 
 pinMode(trigerPin, INPUT_PULLUP);
 
 // アラームフラグ、アラーム割り込みフラグの初期化
 RTC.alarm(ALARM_1);
 RTC.alarm(ALARM_2);
 RTC.alarmInterrupt(ALARM_1, false);
 RTC.alarmInterrupt(ALARM_2, false);
 // 割り込み用のINT/SQWピン使用時は、方形(矩形)波出力を無効にする必要がある
 RTC.squareWave(SQWAVE_NONE);
 
 // RTCモジュールで時刻を同期
 setSyncProvider(RTC.get);
 if (timeStatus() != timeSet)
  Serial.println("Unable to sync with the RTC");  // RTCで同期不能
 else
  Serial.println("RTC has set the system time");  // RTCでシステム時刻を設定中
 Serial.flush();
 
/*
 // 初回のスケッチアップロード時の日付時刻を設定、初回のみコメント外し有効にする
 // RTC時間設定 例) 2018/02/13 19:28:20
 tmElements_t tm;
 tm.Hour = 19;
 tm.Minute = 28;
 tm.Second = 20;
 tm.Day = 13;
 tm.Month = 2;
 tm.Year = 2018 - 1970; // tmElements_t.Yearの初期値は1970から始まるものとして算出される
 RTC.write(tm);   // tm構造体からRTCを設定
*/
 
 // 例) アラーム1を19:28:40に設定
 //RTC.setAlarm(ALM1_MATCH_HOURS, 40, 28, 19, 0);
 RTC.setAlarm(ALM1_MATCH_DATE, on_seconds, on_minutes, on_hour, 0);
 // アラーム1の有効化
 RTC.alarmInterrupt(ALARM_1, true);
 
 // 例) アラーム2を19:29に設定
 //RTC.setAlarm(ALM2_MATCH_HOURS, 00, 29, 19, 0);
 RTC.setAlarm(ALM2_MATCH_DATE, off_seconds, off_minutes, off_hour, 0);
 // アラーム2の有効化
 RTC.alarmInterrupt(ALARM_2, true);
 
 // シリアルモニタに日付時刻を表示
 digitalClockDisplay();
}
 
void loop() {
 // 経過日付時刻表示と必要に応じて割り込みフラグを初期化
 if (alarm_state) {
  alarm_state = false;
  digitalClockDisplay();
  RTC.alarm(ALARM_1);
  RTC.alarm(ALARM_2);
 }
 sleepNow(); // スリープ
}
 
void digitalClockDisplay(void)
{
 setSyncProvider(RTC.get);
 Serial.print(year());
 Serial.print('/');
 Serial.print(month());
 Serial.print('/');
 Serial.print(day());
 Serial.print(' ');
 printDigits(hour());
 Serial.print(':');
 printDigits(minute());
 Serial.print(':');
 printDigits(second());
 Serial.println();
 Serial.flush();
}
 
void printDigits(int digits)
{
 // 日付時刻1桁時、1桁めに0を設定
 if (digits < 10)
  Serial.print('0');
 Serial.print(digits);
}

 というわけで改めて買うとして今、唯一、手持ちのDS3231を積んだRTCモジュールZS-042は、以前作った温湿度計付き時計に使ったものなので、そこから持ってくることにしました。

 回路は、前述のものとほとんど同じでRTCモジュールをDS1302からDS3231に差し替え、DS3231搭載モジュールのVCC/GND/SDA/SCL/SQWをArduino Nano互換機の5V/GND/A4/A5/D2に接続するだけ(Uno系で割り込みに使えるピンはD2/D3、内D2を使いました)。

 JChristensen/DS3232RTCライブラリをダウンロード、本ライブラリのサンプルスケッチalarm_ex7.inoとMega用ですが、このライブラリを使っているというArduinoでDS3231 RTCモジュールを使った定期処理のスケッチを拝借、一部変更したものと、前述(前リンク)の鳥よけ回路で参照させて頂いたスリープ機能を盛り合わせたラフスケッチがこれです。

 ただ、分単位などの短時間設定による検証時には、そんなことにはならないでしょうが、スリープから復帰した際のif文をLEDテープライトのON/OFF状態で評価しており、アラーム1とアラーム2の区別がつかず、スケッチアップロードのタイミングによっては挙動が逆になる電源投入時間を考慮(ON/OFFの場合、ON前かつOFF後に)する必要が出てくるため、時間で評価する方がベターでしょう。

 と...放置するのも何なので若干、ごちゃごちゃしますが、スケッチを追加編集して、時間で評価してみたところ、期待通りの結果を得ることができました。

 注意点として、この例のように分単位で評価する場合、デバグ用時刻表示がRTC設定時間に、attachInterrupt()、もしくは、alarmInput()との兼ね合いからなのか、秒指定可能なアラーム1も分単位(0秒)の直前でスリープから復帰するようでスリープ復帰時における時間評価の際には、アラーム設定よりマイナス1分しておかないと、うまくいきませんでした。(これからやってみるが1時間単位でも同じかも?)...現時点ではLEDのON/OFF評価にして挙動が正しくなるタイミングでスケッチをアップするのが無難かも。

 んー...なぜか、スリープ復帰初回の時刻が、RTC設定日付になってしまうため、評価できない...全く解決策にはなっていないものの、後に改善策を見出す、きっかけとするためにも経過を残し、時間とLED消灯を条件とした合作にしておくことにします。(挙動の逆転電源投入タイミングを考慮する必要を回避するため、1回めをON、2回めをOFFとした場合、1回めのアラーム時刻より前かつ2回めのアラーム時刻の後にスケッチのアップロード電源投入を行なうものとします。)

 ライトには、LEDテープライトを使うわけですが、先日作ったばかりのデスク下にある棚用の照明と同様にLEDテープライト+アルミアングル+カプトンテープで補強したものを使う予定。

 と思いましたが、デスク下用のライトは作り直すことにし、差し替えたので、そのアルミアングル約34cm、LEDテープライト36球 約30cmを、ここで使うことにしました。

 検証時に少しハマったのは、モジュール仕様なので当然なのですが、RTC.setAlarm()関数は、DS3232RTC.h上では、引数に[秒]を入れても入れなくてもよいようになっていますが、アラーム2については、秒指定しても割り込みが入らず、分指定をすると割り込みできたこと。

 あ、日付・時刻などでアラーム設定している場合、運用するなら、他のRTCライブラリ同様、RTCモジュールにバックアップ電池(CR2032を使う場合、DS1302/DS1307は、そのまま、DS3231はパターンカットなど要加工)をセット、RTC時刻を設定してスケッチをアップロード、スケッチのRTC時刻設定箇所をコメントアウト後、再度、スケッチをアップロードしておかないとダメだ。

 1度も電源を落とさないなら不要ですが、運用場所への移動時など、1度でも電源を落とすことがあるなら、コメントアウトしておかないと再度電源を投入した際、以前アップロードした時点でスケッチ上に設定してあったRTC時刻に再設定されてしまい、日付・時刻などでアラーム設定している場合は、その分、誤差が出て、たいていの場合、アラーム設定の意味がなくなってしまうため。

 というわけでスリープ初回復帰時にRTC設定時間になってしまう問題は解決...。

[2018/02/17]

 ん...?期待しない時間に点いたり消えたりする...。

 何か勘違いしてる?もしかしてALM1_MATCH_HOURS/ALM2_MATCH_HOURSだとX時Y分じゃなくてX時間Y分後になっちゃうとか!?それをやるなら、ALM1_MATCH_DATE/ALM2_MATCH_DATE!?違うよね...HOURSでいけるよね?いやいや、やっぱり、DATEか...。

 だとすると、LEDのON/OFF状態での評価はいらなくなって、ちゃんと指定時刻でON/OFFしてくれる...なら全て解決ですが、とりあえず、スケッチを修正しつつ、試してみよう...。

[2018/02/18]

 と思いきや...先に機能するアラーム1を時間で評価しようとチェックしてみると電源投入時間になってしまい、電源投入時間が限定されてしまう...これなら、まだ、タイミングの制約はあるとはいえ、電源投入時間に幅があるLED点灯/消灯を評価した方がマシ...。

 ライブラリの説明にあるif(RTC.alarm(ALARM_1)){}を使うとloop{}内のアラームリセットの有効・無効に関わらず、アラームのリセットが先にかかってしまうようで条件文内の処理を1行も実行できない上に[Wake up]の[Wa]でシリアルモニタ上の表示が止まり、後のif(RTC.alarm(ALARM_2)){}のSerial文は表示されないので評価すらされていない模様...。

 LEDの点灯タイミングでdigitalWriteでledPinをHIGHにし、RTC.alarm(ALARM_1);とすると点灯はしますが、その後のelse文(digitalWrite(ledPin, LOW);やRTC.alarm(ALARM_2);)が評価されないようでLEDがOFFにならない...。

 尚、時間、分(、秒)の指定は、ALM1_MATCH_HOURS/ALM2_MATCH_HOURSでよさ気ですが、少なくともアラーム2の結果を見る限り、アラームが機能した時点の[時間]は、設定通りですが、アラーム時間をジャストX時0分とした場合、アラーム復帰時として評価する時間は、やはり、[分]同様、早めに復帰するようでアラーム設定時間マイナス1時となりました。([分]指定時刻によっては[X時]自体は、アラーム設定と同じ時間になるかも...?)

#include <DS3232RTC.h>    // http://github.com/JChristensen/DS3232RTC
#include <Streaming.h>    // http://arduiniana.org/libraries/streaming/
#include <avr/sleep.h>
#include <avr/power.h>
 
const int trigerPin = 2;
const int ledPin = 13;
const uint8_t alarmInput(trigerPin);
bool alarm_state = false;
 
const int on_hour = 17;
const int on_minutes = 0;
const int on_seconds = 0;
const int off_hour = 23;
const int off_minutes = 59;
const int off_seconds = 0; // 今回のようにAlarm2とした場合は使えないので固定
 
void wakeUpNow(){
 Serial.println("Wake up!");
 
 alarm_state = true;
 
 // デバグ用
 int val = digitalRead(ledPin);
 Serial.print("val = ");
 Serial.println(val);
 Serial.print("hour = ");
 Serial.println(hour());
 Serial.print("minute = ");
 Serial.println(minute());
 Serial.print("second = ");
 Serial.println(second());
 // デバグ用
 
 if (digitalRead(ledPin)) {
  digitalWrite(ledPin, LOW);
 } else {
  digitalWrite(ledPin, HIGH);
 }
}
 
void sleepNow(){
 Serial.println("Sleep...");
 delay(12);
 set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 sleep_enable(); //スリープの有効化
 
 // アラームの割り込み時の信号はLOWなので第3引数は、FALLING
 attachInterrupt(digitalPinToInterrupt(alarmInput), wakeUpNow, FALLING);
 sleep_mode(); //指定したモードでスリープを開始
 sleep_disable(); //割り込み時、スリープから復帰
 detachInterrupt(0); //割り込み処理解除
}
 
void setup() {
 Serial.begin(9600);
 
 pinMode(trigerPin, INPUT_PULLUP);
 
 // アラームフラグ、アラーム割り込みフラグの初期化
 RTC.alarm(ALARM_1);
 RTC.alarm(ALARM_2);
 RTC.alarmInterrupt(ALARM_1, false);
 RTC.alarmInterrupt(ALARM_2, false);
 // 割り込み用のINT/SQWピン使用時は、方形(矩形)波出力を無効にする必要がある
 RTC.squareWave(SQWAVE_NONE);
 
 // RTCモジュールで時刻を同期
 setSyncProvider(RTC.get);
 if (timeStatus() != timeSet)
  Serial.println("Unable to sync with the RTC");  // RTCで同期不能
 else
  Serial.println("RTC has set the system time");  // RTCでシステム時刻を設定中
 Serial.flush();
 
/*
 // 初回のスケッチアップロード時の日付時刻を設定、初回のみコメント外し有効にする
 // RTC時間設定 例) 2018/02/13 19:28:20
 tmElements_t tm;
 tm.Hour = 19;
 tm.Minute = 28;
 tm.Second = 20;
 tm.Day = 13;
 tm.Month = 2;
 tm.Year = 2018 - 1970; // tmElements_t.Yearの初期値は1970から始まるものとして算出される
 RTC.write(tm);   // tm構造体からRTCを設定
*/
 
 RTC.setAlarm(ALM1_MATCH_HOURS, on_seconds, on_minutes, on_hour, 0);
 // アラーム1の有効化
 RTC.alarmInterrupt(ALARM_1, true);
 
 RTC.setAlarm(ALM2_MATCH_HOURS, off_seconds, off_minutes, off_hour, 0);
 // アラーム2の有効化
 RTC.alarmInterrupt(ALARM_2, true);
 
 // シリアルモニタに日付時刻を表示
 digitalClockDisplay();
}
 
void loop() {
 // 経過日付時刻表示と必要に応じて割り込みフラグを初期化
 if (alarm_state) {
  alarm_state = false;
  digitalClockDisplay();
  RTC.alarm(ALARM_1);
  RTC.alarm(ALARM_2);
 }
 sleepNow(); // スリープ
}
 
void digitalClockDisplay(void)
{
 setSyncProvider(RTC.get);
 Serial.print(year());
 Serial.print('/');
 Serial.print(month());
 Serial.print('/');
 Serial.print(day());
 Serial.print(' ');
 printDigits(hour());
 Serial.print(':');
 printDigits(minute());
 Serial.print(':');
 printDigits(second());
 Serial.println();
 Serial.flush();
}
 
void printDigits(int digits)
{
 // 日付時刻1桁時、1桁めに0を設定
 if (digits < 10)
  Serial.print('0');
 Serial.print(digits);
}

 他のDS3231/DS3232ライブラリでも同様なのかは、未確認ですが、もう疲れたので、タイミングを見計らって電源を投入することにし、LEDの点灯/消灯状態を条件として評価しておくことに...。

[2018/03/16]

 ん...?アラーム(割り込み)が効かない(失敗する)ことがある...?

 たいていは、うまくいくのですが、たまに点灯していないことがあり、原因がわからず困っていました。

 調べてみるとDS3231の入力電圧は、推奨3.3Vとのこと...何も考えず、5Vかけていた...。

 3.3Vに変更してテスト中...。

#include <DS3232RTC.h>    // http://github.com/JChristensen/DS3232RTC
#include <Streaming.h>    // http://arduiniana.org/libraries/streaming/
#include <avr/sleep.h>
#include <avr/power.h>
 
const int trigerPin = 2;
const int ledPin = 13;
const uint8_t alarmInput(trigerPin);
bool alarm_state = false;
bool led_state = false;
 
const int on_hour = 17;
const int on_minutes = 0;
const int on_seconds = 0;
const int off_hour = 23;
const int off_minutes = 59;
const int off_seconds = 0; // 今回のようにAlarm2とした場合は使えないので固定
 
void wakeUpNow(){
 Serial.println("Wake up!");
 
 alarm_state = true;
 
 // デバグ用
 int val = digitalRead(ledPin);
 Serial.print("val = ");
 Serial.println(val);
 Serial.print("hour = ");
 Serial.println(hour());
 Serial.print("minute = ");
 Serial.println(minute());
 Serial.print("second = ");
 Serial.println(second());
 // デバグ用
 
 if (digitalRead(ledPin) || led_state) {
  digitalWrite(ledPin, LOW);
 } else if (!digitalRead(ledPin) || !led_state) {
  digitalWrite(ledPin, HIGH);
 }
}
 
void sleepNow(){
 Serial.println("Sleep...");
 delay(12);
 set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 sleep_enable(); //スリープの有効化
 
 // アラームの割り込み時の信号はLOWなので第3引数は、FALLING
 attachInterrupt(digitalPinToInterrupt(alarmInput), wakeUpNow, FALLING);
 sleep_mode(); //指定したモードでスリープを開始
 sleep_disable(); //割り込み時、スリープから復帰
 detachInterrupt(0); //割り込み処理解除
}
 
void setup() {
 Serial.begin(9600);
 
 pinMode(trigerPin, INPUT_PULLUP);
 
 // アラームフラグ、アラーム割り込みフラグの初期化
 RTC.alarm(ALARM_1);
 RTC.alarm(ALARM_2);
 RTC.alarmInterrupt(ALARM_1, false);
 RTC.alarmInterrupt(ALARM_2, false);
 // 割り込み用のINT/SQWピン使用時は、方形(矩形)波出力を無効にする必要がある
 RTC.squareWave(SQWAVE_NONE);
 
 // RTCモジュールで時刻を同期
 setSyncProvider(RTC.get);
 if (timeStatus() != timeSet)
  Serial.println("Unable to sync with the RTC");  // RTCで同期不能
 else
  Serial.println("RTC has set the system time");  // RTCでシステム時刻を設定中
 Serial.flush();
 
/*
 // 初回のスケッチアップロード時の日付時刻を設定、初回のみコメント外し有効にする
 // RTC時間設定 例) 2018/02/13 19:28:20
 tmElements_t tm;
 tm.Hour = 19;
 tm.Minute = 28;
 tm.Second = 20;
 tm.Day = 13;
 tm.Month = 2;
 tm.Year = 2018 - 1970; // tmElements_t.Yearの初期値は1970から始まるものとして算出される
 RTC.write(tm);   // tm構造体からRTCを設定
*/
 
 RTC.setAlarm(ALM1_MATCH_HOURS, on_seconds, on_minutes, on_hour, 0);
 // アラーム1の有効化
 RTC.alarmInterrupt(ALARM_1, true);
 
 RTC.setAlarm(ALM2_MATCH_HOURS, off_seconds, off_minutes, off_hour, 0);
 // アラーム2の有効化
 RTC.alarmInterrupt(ALARM_2, true);
 
 // シリアルモニタに日付時刻を表示
 digitalClockDisplay();
}
 
void loop() {
 // 経過日付時刻表示と必要に応じて割り込みフラグを初期化
 if (alarm_state) {
  alarm_state = false;
  digitalClockDisplay();
  RTC.alarm(ALARM_1);
  RTC.alarm(ALARM_2);
 }
 sleepNow(); // スリープ
}
 
void digitalClockDisplay(void)
{
 setSyncProvider(RTC.get);
 Serial.print(year());
 Serial.print('/');
 Serial.print(month());
 Serial.print('/');
 Serial.print(day());
 Serial.print(' ');
 printDigits(hour());
 Serial.print(':');
 printDigits(minute());
 Serial.print(':');
 printDigits(second());
 Serial.println();
 Serial.flush();
}
 
void printDigits(int digits)
{
 // 日付時刻1桁時、1桁めに0を設定
 if (digits < 10)
  Serial.print('0');
 Serial.print(digits);
}
[2018/03/18]

 これはこれとしてHIGH/LOWの判断が微妙なケースがあるのか、状況は変わらないっぽい。

 仕方ないのでLEDの状態を示すboolean型フラグに変更...でもよいですが、敢えてor条件として追加し、検証中。

[2018/03/21]

 HIGH/LOWが微妙とかいう話ではなく、コンセントかな...?

[2018/03/24]

 う、違った...大丈夫だと思っていた位置のコンセントでも点灯しないことがあった...一体、なぜ...。

...
 
void loop() {
 // 経過日付時刻表示と必要に応じて割り込みフラグを初期化
 if (alarm_state) {
  alarm_state = false;
  digitalClockDisplay();
  RTC.alarm(ALARM_1);
  RTC.alarm(ALARM_2);
  setSyncProvider(RTC.get);
  if (hour() == off_hour && flg_led) {
   digitalWrite(ledPin, LOW);
   flg_led = false;
  } else if(hour() == on_hour && !flg_led){
   digitalWrite(ledPin, HIGH);
   flg_led = true;
  }
 }
 sleepNow(); // スリープ
}
 
...
[2018/03/29]

 え...、ON時間にオフになり、OFF時間にONになることがあった...。

 if(RTC.alarm(ALARM_1)){}とかが使えれば、こんなことにはならないと思うが...なぜか、使えないし...。

 仕方ないから、loop()内で苦肉の策を講じてみた...。

[2018/04/02]

 なんでだ...、何か勘違いしているのかな...謎すぎる...。

 

LEDテープライト12Vによる自動ON/OFFライト
[2019/04/11]

 うっかり、すっかり、遠回りしてしまいましたが、自作スリープ機能付きArduino/LCD1602時計に乗じて、省電力化にあたっては、(DS3232でもDS1302でもなく)DS1307を使い、Arduinoで最も節電効果が高いスリープモードSLEEP_MODE_PWR_DOWN、復帰には、ウォッチドックタイマー/WDTを使うことにしました。

 前述のようにどれを使ってもよいのですが、DS1307を使ったのは、単にDS1302は他で使ってしまった、RTCモジュールのタイマーを使う必要がないならDS3231/DS3232である必要もないなと思ったからです。

 当時、WDTを自身の勘違いから侮っていたのですが、間違いであることに気づいた次第です。

#include <RTClib.h>
#include <avr/sleep.h>                  // スリープライブラリ
#include <avr/wdt.h>                    // ウォッチドッグタイマー ライブラリ
 
RTC_DS1307 rtc;
const int led = 13;
 
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 = 9;
 
int onflg = 0;
 
void setup() {
Serial.begin(9600);
pinMode(led, OUTPUT);
  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }
  if (! rtc.isrunning()) {
    Serial.println("RTC is NOT running!");
    // following line sets the RTC to the date & time this sketch was compiled
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // This line sets the RTC with an explicit date & time, for example to set
    // January 21, 2014 at 3am you would call:
//     rtc.adjust(DateTime(2019, 4, 13, 14, 46, 0));
  }
}
 
void loop() {
  DateTime now = rtc.now();
 
if(now.hour() == 17 && now.minute() == 0 && onflg == 0) {
  digitalWrite(led, HIGH);
  onflg=1;
}
if(now.hour() == 23 && now.minute() == 59 && onflg == 1) {
  digitalWrite(led, LOW);
  onflg=0;
}
/*
Serial.print("Current Date / Time: ");
Serial.print(now.day());
Serial.print("/");
Serial.print(now.month());
Serial.print("/");
Serial.print(now.year());
Serial.print(" ");
Serial.print(now.hour());
Serial.print(":");
Serial.print(now.minute());
Serial.print(":");
Serial.println(now.second());
*/
//  delay(1000);
  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++;                      // 必要ならコメントアウトを外す
}

 スケッチはこちらでRTCライブラリには、Arduino IDEの[ライブラリの管理]からインストールできるAdafruitのRTClib、スリープと復帰機能には、ArduinoのCPUをスリープさせて消費電流を減らす関数 delayWDT2を使わせて頂きました。

 DS1307にコイン電池を入れて時刻調整のrtc.adjust()を設定後、アップロード、更にrtc.adjust()をコメントアウトして改めてアップロードしておかないと電源投入の度にスケッチ上の設定日付時刻から時を刻み始めるので要注意です(RTCモジュール、RTCライブラリ共通)。

 WDT時間との関係でLEDのON/OFFのif文条件の秒(now.second())は、外しました。

 onflgは、必要性は微妙なものの、if条件から秒を除いたことで指定時間の1分間、WDT時間(このスケッチでは8sec)ごとにdigitalWrite()を実行してしまうのは、無駄かなと思って使っています。

 ちなみに、もし、LED点灯・消灯において、このonflgを条件に使い、このスケッチのまま実行した場合、LED点灯前の時刻(17時より前)にスケッチをアップロードしておかないと翌日の17時まで点灯しなくなるので要注意です。

 尚、今回は、Arduinoボードとして5V版Nanoを使いましたが、5Vより、3.3V、更にリンク先にもあるように開発ボードではなく、ATMega328PなどのIC単体を使う方が、電力使用量が少なくなります。

 AC接続USB充電器に接続して運用する予定ですが、興味本位で17:00 ON/23:59 OFFの先のオートライトを電池駆動で試してみることにします。

 04/11 19:00〜 単3エネループx4本 電圧5.25Vで開始。

 04/14 23:30〜 単3エネループx4本 電圧3.99Vで激しく点滅後、消灯。

 丸3日と4時間半...こんなもんですかね、あとは、ATMega328P単体でどのくらいになるかってところでしょうか(自身はやってみる予定ないですが)。

 使うとしたらUSB充電器っと...。

ホーム前へ次へ