google-site-verification: google3bd66dd162ef54c7.html
fc2ブログ

Arduinoを使ったアクアリウム照明コントローラー(ソフト解説編)

 連載しているアクアリウム照明作りの話はこれがたぶん最終回。今回はソフト解説編です。

▼ミニ水草水槽(ポットアクアリウム)
ポットアクアリウム
 白に赤色LEDの光を多めに入れた状態です。

 先にお断りしておきますが、このソフトは以前作った物を寄せ集め、そこを出発点にしてあれこれ修正追加して作った物です。そんなことで、同じような機能の関数が複数あったり、グローバル変数が異常に多かったりします。これを公開するのはちょっと恥ずかしいのですが、未来の自分に対する伝言の意味を兼ねて記事にします。

 プログラムは1000行くらいあるので記事中にリストは記載しません。プログラムはこちら→20171029AquaLightCont.txt

 コンパイラの警告を「全て」にすると、EEPROMのライブラリの使い方にワーニングが出ています。「初期値」でコンパイルするとエラーは出ないので致命的なエラーは無いと思います。なお、コンパイル後に出るメモリーサイズなどの情報は下記です(Arduini IDE 1.8.1を使用)。かなりメモリーを使っていますがまだ余裕があると思います(たぶん)。

最大32256バイトのフラッシュメモリのうち、スケッチが18960バイト(58%)を使っています。
最大2048バイトのRAMのうち、グローバル変数が949バイト(46%)を使っていて、ローカル変数で1099バイト使うことができます。


 以下ポイントと思われる点を解説していきます。

◆RTCとの同期
 以前作ったソーラータイマースイッチではCPUを深いスリープに入れ、ピン割り込み(int0)で復旧させることでプログラムをタイマーに同期させていました。こうやれば省エネになるのですが、残念なことにPWMが止まってしまうのでLEDを調光することが出来ません。これではダメなので、ピンの状態をポーリングで検出する方式に変更しました。waitPin2Low()がその関数です。

◆明るさの制御
 一日の秒の値で各種動作タイミングを決定しています。なお秒の値は0時0分0秒から始まる連番です。値の最大は86339(24*3600-1)なので int(16ビット)の範囲を超えるのでlongで表現しています。

◆日の出、日の入り時刻の計算
 calcT1T2()関数で行っています。この関数については、以前の記事で詳しく解説していますが、日付と経緯度からその地点の日の出/日の入り時刻を計算するプログラムです。なお、東京の経緯度を使って計算しているので、これを変更したい場合は、float Longitude = 139.7414; float Latitude = 35.6581; の部分を修正します。
 まあそこまでやらなくても、日の出・日の入り時刻のオフセット値をボタン操作で設定出来るので、そちらを使った方が簡単だと思います。

◆照明明るさの設定プログラム
 ここがこのソフトの中心になる部分です。いろいろ方法を考えたのですが、照明の明るさ、変化速度、タイミングなどを柔軟に定義したかったので、関数の形で定義することにしました。そんなことで、ここを理解するにはArduino というか、C言語の基礎知識が必要です。

 照明プログラムは下記に示す LedContProgram()で定義しています。点灯パターンを修正したい場合はここを変更します。なお、このプログラムは見つけやすいように、プログラムリストの最後に置いてあります。

void LedContProgram(unsigned long x, int e ) {  // 明るさ変化パターン設定プログラム
// if文:イベント開始時刻(累積秒)。RedSetとWhiteSetの引数は下記
// 第一引数:明るさ設定値(0.003から1.0の値を設定)。
// 第二引数:明るさ遷移時間(秒)明るさは等比級数で変化
// 第三引数:実行(Execute)フラグ。ゼロならスロープ計算無し(高速実行される)
const float p = 0.75; // LED電流調整係数(1.0以下の値を入れる)

// 夜明け
if (x == (FixOnTime - 5UL) * 60UL) { // small ON 時刻の5分前
RedSet(0.10, 60, e); // 1分スロープで赤だけ10%点灯(真っ赤な朝焼け)
}
if (x == FixOnTime * 60UL ) { // small ON 時刻
RedSet(0.003, 300, e); // 5分スロープで赤消灯
WhiteSet(0.07, 60, e); // 1分スロープで白7%(夜明け前)
}

// 日の出から日中まで(3時間かけて増光)
// 日照の変化パターンに似せるため1時間置きに目標値を設定(指数関数で一気に変えてはダメ)
if (x == (mNumT1 + T1offset) * 60UL ) { // 日の出時刻なら
RedSet(0.45 * p, 3600, e); // 1時間スロープで赤45%
WhiteSet(0.45 * p, 3600, e); // 1時間スロープで白45%
}
if (x == (mNumT1 + T1offset + 60) * 60UL ) { // 日の出時刻 +1Hrなら
RedSet(0.83 * p, 3600, e); // 1時間スロープで赤83%
WhiteSet(0.83 * p, 3600, e); // 1時間スロープで白83%
}
if (x == (mNumT1 + T1offset + 120) * 60UL ) { // 日の出時刻 +2Hrなら
RedSet(1.0 * p, 3600, e); // 1時間スロープで赤100%点灯
WhiteSet(1.0 * p, 3600, e); // 1時間スロープで白100%点灯
}

// 日中から夕暮れ(3時間かけて減光)日没後はスモールライト点灯
if (x == (mNumT2 + T2offset - 180) * 60UL ) { // 日没時刻 3Hr前なら
RedSet(0.83 * p, 3600, e); // 1時間スロープで赤83%
WhiteSet(0.83 * p, 3600, e); // 1時間スロープで白83%
}
if (x == (mNumT2 + T2offset - 120) * 60UL ) { // 日没時刻 2Hr前なら
RedSet(0.45 * p, 3600, e); // 1時間スロープで赤45%
WhiteSet(0.45 * p, 3600, e); // 1時間スロープで白45%
}
if (x == (mNumT2 + T2offset - 60) * 60UL ) { // 日没時刻 1Hr前なら
RedSet(0.003, 3600, e); // 1時間スロープで赤消灯
WhiteSet(0.07, 3600, e); // 1時間スロープで白だけ7%点灯に移行(夕暮れ)
}

// 消灯前の演出
if (x == FixOffTime * 60UL) { // small OFF 時刻
RedSet(0.10, 300, e); // 5分スロープで赤10%点灯(夕焼け開始)
}
if (x == (FixOffTime + 10UL) * 60UL) { // small OFF 時刻10分後(真っ赤な夕焼け開始)
WhiteSet(0.003, 600, e); // 10分スロープで白消灯し、真っ赤にする(日没20分後)
}
if (x == (FixOffTime + 25UL) * 60UL) { // small OFF 時刻25分後
RedSet(0.003, 300, e); // 5分スロープで赤消灯(夜間)
}
}
 if文がいっぱいありますが、秒の値を見ていて、一致したら必要な処理を行うようになっています。プログラムのメインルーチンは1秒に一回処理を行いますが、その中で毎回この関数に飛んできて必要な設定を行うという仕掛けです。

 ここで呼んでいるWhiteSet() とRedSet()関数でLEDの明るさを決定するパラメーターを修正します。説明はリスト中のコメントに記載した通りですが、新しい明るさとその明るさに達するまでの秒数を指定しています。
 実際のLEDの明るさ(PWM出力の値)はWhiteLedOut()とRedLedOut()の関数で設定しており、ここでは目標値と変化率を反映した値を決めてPWM出力を行うようになっています。

  const float p = 0.75; というのはLEDの明るさを制限するために入れたもので、明るさを75%に制限しています。これは私の使用しているLEDライトの制限で、LEDの温度が上がり過ぎないためにこういう値を入れました。本当は最大(p=1.0)で使いたいところです。もしこのプログラムを使われる方がいらっしゃったら、この値はご自分の環境に合わせる必要があります。

◆液晶画面を使った値の入力プログラム
 このプログラムではキャラクタ液晶の画面とプッシュボタンを使った操作でいろいろなパラメーターの入力を行っています。その機能を実現するためのプログラムもがいろいろありすぎて、統一されていないのがこのプログラムの一番残念なところです。具体的には、lcdInput(), lcdInputSNRN(), lcdInputNNN, setV() とこれらに付随して出てくるlcdDispで始まる関数たちです。
 別の関数になったのは、小数点付きとか符号付き表示にしたいなど、個別の事情があるのですが、うまい吸収方法が見つからなかったたので、同じような内容の関数が乱立してプログラムの行数が多くなってしまったのが一番残念な点です。

◆まとめ
 これでArduinoを使ったアクアリウム照明コントローラー作りの一連の話はおしまい(のはず)です。

 これまでArduinoを使っていろいろな物を作ってきましたが、今回の物はこれまでと少し方向性が違っていて、見ていて綺麗だし、癒し効果もあって面白いです。あと、ここまでの機能を持った水槽用照明用のコントローラーはちょっと世の中に無い気がするので、作って良かったです。

 (今頃になって)アクアリウムライトについて調べてみると、赤、青、白のLEDを使うものが多いようです。確かPWMのピンはまだ余っていたと思うので、三色混合のプログラムを作るのも面白いかも知れません。
カレンダー
09 | 2017/10 | 11
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31 - - - -
プロフィール

ラジオペンチ

Author:ラジオペンチ
電子工作を中心としたブログです。たまに近所(東京都稲城市)の話題など。60過ぎて視力や器用さの衰えを感じつつ日々挑戦!
コメントを入れる時にメールアドレスの記入は不要です。なお、非公開コメントは受け付けていません。
記事の内容のご利用は読者の自己責任でお願いします。

記事が気に入ったらクリックを!
最新記事
カテゴリ
最新コメント
リンク
FC2カウンター
検索フォーム
月別アーカイブ
RSSリンクの表示
QRコード
QRコード