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と手持ちのものを使った整流ダイオードの選定は、かなり適当。
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モジュール+バックアップ電池が保持している時間の確認も可能です。
今回は、スケッチ上で決め打ちで時間指定していますが、作りようによっては、物理スイッチやタッチパネル、スマホなどで時間を指定できるようにすることもできるでしょう。
と思いましたが、さて、スリープを...と考えた時、DS1302によるこの方法だとディープスリープモードからの割り込みがかけられない、かと言ってアラーム機能はない模様、DS1307にはタイマーらしきものがあるようですが、時間指定、特にON、OFFの2段階はできそうもない...ことに気づきました。
やっぱり、DS3231を使わないとダメか...。
というわけで改めて買うとして今、唯一、手持ちの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設定時間になってしまう問題は解決...。
ん...?期待しない時間に点いたり消えたりする...。
何か勘違いしてる?もしかしてALM1_MATCH_HOURS/ALM2_MATCH_HOURSだとX時Y分じゃなくてX時間Y分後になっちゃうとか!?それをやるなら、ALM1_MATCH_DATE/ALM2_MATCH_DATE!?違うよね...HOURSでいけるよね?いやいや、やっぱり、DATEか...。
だとすると、LEDのON/OFF状態での評価はいらなくなって、ちゃんと指定時刻でON/OFFしてくれる...なら全て解決ですが、とりあえず、スケッチを修正しつつ、試してみよう...。
と思いきや...先に機能するアラーム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時]自体は、アラーム設定と同じ時間になるかも...?)
他のDS3231/DS3232ライブラリでも同様なのかは、未確認ですが、もう疲れたので、タイミングを見計らって電源を投入することにし、LEDの点灯/消灯状態を条件として評価しておくことに...。
ん...?アラーム(割り込み)が効かない(失敗する)ことがある...?
たいていは、うまくいくのですが、たまに点灯していないことがあり、原因がわからず困っていました。
調べてみるとDS3231の入力電圧は、推奨3.3Vとのこと...何も考えず、5Vかけていた...。
3.3Vに変更してテスト中...。
これはこれとしてHIGH/LOWの判断が微妙なケースがあるのか、状況は変わらないっぽい。
仕方ないのでLEDの状態を示すboolean型フラグに変更...でもよいですが、敢えてor条件として追加し、検証中。
HIGH/LOWが微妙とかいう話ではなく、コンセントかな...?
う、違った...大丈夫だと思っていた位置のコンセントでも点灯しないことがあった...一体、なぜ...。
え...、ON時間にオフになり、OFF時間にONになることがあった...。
if(RTC.alarm(ALARM_1)){}とかが使えれば、こんなことにはならないと思うが...なぜか、使えないし...。
仕方ないから、loop()内で苦肉の策を講じてみた...。
なんでだ...、何か勘違いしているのかな...謎すぎる...。
うっかり、すっかり、遠回りしてしまいましたが、自作スリープ機能付きArduino/LCD1602時計に乗じて、省電力化にあたっては、(DS3232でもDS1302でもなく)DS1307を使い、Arduinoで最も節電効果が高いスリープモードSLEEP_MODE_PWR_DOWN、復帰には、ウォッチドックタイマー/WDTを使うことにしました。
前述のようにどれを使ってもよいのですが、DS1307を使ったのは、単にDS1302は他で使ってしまった、RTCモジュールのタイマーを使う必要がないならDS3231/DS3232である必要もないなと思ったからです。
当時、WDTを自身の勘違いから侮っていたのですが、間違いであることに気づいた次第です。
スケッチはこちらで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充電器っと...。