自動水やり装置の製作

投稿 2012/08/21 (火) 午前 10:28 | リビング | hotall

いつの間にか、我が家のベランダで育てている鉢植えが増えてしまいました。そんな折、盆休みで家を留守にすることとなり、水やりをどうしようかと悩んでいました。
そこで、自動的に水やりをする装置を考えました。

この文書は個人的なものであり、読者による改造を推奨するものではなく、内容について一切の責任を負いかねます。
ユーザーによる改造はメーカーの保証・修理が受けられなくなります。



まず、装置の仕様、設計指針ですが
(1) 水やりは日に1回とし、最初に設定した時刻に、設定した時間だけ行うようにします。
(2) 部品はできるだけ手に入りやすいものとします。
(3) 構造はできるだけ簡単にします。


水やりは電動ポンプで行います。
低電圧で安価、手に入れやすい電動ポンプとして左の灯油ポンプを選びました。

ポンプはこのように、手押しポンプと同じ形をしています。

電源は単三電池2本です。

電池ホルダを外します。

制御基板を取り出します。
このポンプは自動停止機能が付いていて、出口ホース側に付いているセンサからの信号を受け、この基板で制御しています。

さて仕組みが分かったところで、回路を設計します。
モータードライブはもともとの制御基板の回路を使用します。
当初、自動停止用のセンサを入り口側に付けて、水が無くなった時に自動的に停止すること考えていましたがセンサを取り出すときに壊してしまい、この機能は実装しませんでした。
制御用のマイコンとしてPIC12F675を使用します。
入出力として設定用のスイッチと状態表示用のLEDを接続します。
今回追加する制御回路の電源は単3電池x2(3V)とし、元のモーター用の電源と別にします。
回路図は下記の通りです。






出口ホースについているセンサを取り除くため、まず、出口ホースを外します。
ホースは圧着されているので、ドライバなどを使い力づくで外します。
ホースを傷つける危険がありますが、その時は、テープで塞ぎます。

センサケーブルを切断します。
ケーブルを引き抜くことも可能ですが、そうすると水漏れの原因となりますので、今回は内側のケーブルは残しておきます。
ホースの先端のセンサはねじ止めされているので、これを外します。

制御基板側のセンサケーブルを切断します。

出口ホースを取りつけます。

回路図に従い小さな基板に実装します。
この基板は強力両面テープで元の制御基板に固定します。

ポンプ本体の正面に穴を開け、スイッチとLEDを取りつけます。
背面に穴を開け、今回追加した基板から制御用電源コードを引き出し、電池ボックスを取りつけます。幸いポンプには電池ボックスを挟める場所があったので、ここに取りつけました。

さて、プログラムを作成します。
今回は処理も軽いので、すべてタイマー割り込み処理内で行います。
割り込みは内部クロックを元に定周期に発生しますので、これを利用して時刻を計測します。
ところが内部クロックは精度が±1%と良くありません。
計算上±15分/日の範囲で誤差が発生します。
今回私が使用したCPUでは-8分/日の誤差がありました。
この誤差はプログラム内の時刻カウンタ最大値で調整します。
-8分/日の誤差の場合、基準値0x069780に対し、
(24*60+8)*60*1/0.2=434400=0x06A0E0
となります。

;**********************************************************************
;
; ファイル名 : Watering.asm
; 作成日付  : 2012/08/06
; バージョン : 1.0.0
;
; 作成者   : Yasuaki Nojiri
;
; Copyright(C) 2012 by Yasuaki Nojiri
; kanagawa japan
; All rights reserved.
;
;**********************************************************************
;
; 生成条件 :
; CPU : PIC 12F675
; CLK : 内部クロック(4MHz)
; Port
; 0 DIN -
; 1 DOUT モーター(0:ON / 1:OFF)
; 2 DOUT LED(0:ON / 1:OFF)
; 3 DIN -
; 4 DIN 水位センサ(0:正常水位 / 1:低水位)[将来仕様]
; 5 DIN SW(0:ON / 1:OFF)
;
;
;**********************************************************************
;
; 説明 :
; このプログラムは日に一回、自動的に水やりをするものです。
; SWをONにするとその間だけモーターがONになります。
; SWをOFFにするとモーターがOFFになり、以降、24時間周期で同じ動作が繰り返されます。
; 再度SWをONにすると、以前に記憶された動作はクリアされ新たな動作が記憶されます。
; 電源をOFFにすると、記憶されていた動作は消去されます。
; (将来仕様)低水位の場合、モーターをOFFにします。
;
; 内部動作の説明:
; TIMERは1/125秒周期でオーバーフローし、割り込みが発生します。
; この割り込み処理内で、すべての処理が行われます。
; SWの入力はチャタリング防止の為に、0.4sの不感時間帯を設けます。
; モータがONの時にLEDは点灯します。
; モータがOFFでON/OFF時刻が設定されている場合は3.2秒周期でLEDが点滅します。
;
;
;
;
;**********************************************************************
list p=12f675 ; プロセッサの定義
#include ; プロセッサ固有変数の定義

errorlevel -302 ; エラー:302を無視

__CONFIG _CP_ON & _CPD_ON & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT

;*************************************************
; 定数の定義
;*************************************************
; TIMER0用定数
TIMER0_INIT EQU d'131' ; TIMER0初期値(256 - 125)

; SW用定数
SW1_PORT EQU GPIO ; SW入力ポート
SW1_MASK EQU b'00110000' ; SWビットマスク
SW_BIT EQU 5 ; SWビット位置

; IOビット位置
LEVEL_BIT EQU 4 ; 水位ビット位置
MOTOR_BIT EQU 1 ; モータービット位置
LED_BIT EQU 2 ; LEDビット位置

; 時刻カウンタ最大値(1count=200ms)
; 基準値:1/0.2*60*60*24=0x069780
; 内部クロックには誤差があるのでこの数値で調整します。
TIME_COUNT_3 EQU 0x06 ; 0.2*256*256秒単位
TIME_COUNT_2 EQU 0x97 ; 0.2*256秒単位
TIME_COUNT_1 EQU 0x80 ; 0.2秒単位

;*************************************************
; 変数の定義
;*************************************************
; レジスタの退避域
cblock 0x5C
w_temp ; ワーキングレジスタの退避域
status_temp ; ステータスレジスタの退避域
pclath_temp ; プログラムバンクレジスタの退避域
fsr_temp ; 間接アドレスの退避域
endc

; SW作業領域
cblock 0x030
sw1_cur ; SW現在値
sw1_last ; SW前回値
sw1_proc ; SW処理値
sw1_wait ; SWチャタリング待ちカウンタ

endc

; 出力作業領域
cblock 0x038
cur_out ; 現在出力データ
endc

; 時刻カウンタ
cblock 0x040
cur_time_0 ; 現在時刻(1/125秒)
cur_time_1 ; 現在時刻(25/125秒)
cur_time_2 ; 現在時刻(25*256/125秒)
cur_time_3 ; 現在時刻(25*256*256/125秒)
on_time_1 ; ON設定時刻(25/125秒)
on_time_2 ; ON設定時刻(25*256/125秒)
on_time_3 ; ON設定時刻(25*256*256/125秒)
off_time_1 ; OFF設定時刻(25/125秒)
off_time_2 ; OFF設定時刻(25*256/125秒)
off_time_3 ; OFF設定時刻(25*256*256/125秒)
time_setting ; ON/OFF時刻設定中
endc

;*************************************************
; リセットベクタ
;*************************************************
ORG 0x000 ; リセットベクタアドレス
goto main ; メインルーチンへジャンプ

;*************************************************
; 割込みベクタ
;*************************************************
ORG 0x004 ; 割込みベクタアドレス
; レジスタのバックアップ
movwf w_temp ; Wレジスタの退避
swapf STATUS,w ; ステータスレジスタの内容をWレジスタに設定
clrf STATUS ; バンク0の選択
movwf status_temp ; ステータスレジスタの退避
movf PCLATH,w ; PCLATHレジスタの内容をWレジスタに設定
movwf pclath_temp ; PCLATHレジスタの退避
movf FSR, w ; 間接アドレスレジスタの内容をWレジスタに設定
movwf fsr_temp ; 間接アドレスレジスタの退避

; TIMER0割込要因を調べる
btfss INTCON,T0IF ; オーバーフローしている場合
goto tmr0_int_then_end ; 以下の処理を行う
;

; SWの定周期処理
; SWのチャタリング待ちのチェック
movf sw1_wait, w ; チャタリング待ちカウンタが
sublw 0 ; 0と
btfss STATUS, Z ; 等しくない場合
decf sw1_wait, f ; デクリメント
btfss STATUS, Z ; チャタリング待ちカウンタが0なら
goto sw1_change_then_end ; 次の処理を行う
; SWチャタリング待ち状態でない場合
movf sw1_cur, w ; 現在値の値を
movwf sw1_last ; 前回値に設定
movf SW1_PORT, w ; SW1入力ポートの値の
andlw SW1_MASK ; 対象ビット値を
movwf sw1_cur ; 現在値領域に設定
; SWの入力値を調べる
movf sw1_cur,w ; 現在値の値が
subwf sw1_last,w ; 前回値と
btfsc STATUS, Z ; 異なる場合
goto sw1_change_then_end ; 以下の処理を行う
movlw d'50' ; 初期値:50(50/125秒=0.4秒)を
movwf sw1_wait ; チャタリング待ちカウンタに設定
;
;
sw1_change_then_end


; 現在時刻の更新
incf cur_time_0,f ; 現在時刻(1/125秒)のインクリメント
movf cur_time_0,w ; 現在時刻(1/125秒)の値が
sublw d'25' ; 25と
btfss STATUS,Z ; 等しい場合
goto cur_time_changed_end ; 以下の処理を行う
clrf cur_time_0 ; 現在時刻(1/125秒)をクリア
incfsz cur_time_1,f ; 現在時刻(25/125秒)のインクリメントし、0の場合
goto cur_time_count_end ; 次の処理を行う
incfsz cur_time_2,f ; 現在時刻(25*256/125秒)のインクリメント、0の場合
goto cur_time_count_end ; 次の処理を行う
incf cur_time_3,f ; 現在時刻(25*256*256/125秒)のインクリメント
cur_time_count_end
; 1日周期のリセット(周期:24*560*60*25/128=0x069780)
movf cur_time_3, w ; 現在時刻(25*256*256/125秒)が
sublw TIME_COUNT_3 ; 0x06と
btfss STATUS, Z ; 等しい場合
goto cur_time_reset_end ; 以下の処理を行う
movf cur_time_2, w ; 現在時刻(25*256/125秒)が
sublw TIME_COUNT_2 ; 0x97と
btfss STATUS, Z ; 等しい場合
goto cur_time_reset_end ; 以下の処理を行う
movf cur_time_1, w ; 現在時刻(25/125秒)が
sublw TIME_COUNT_1 ; 0x80と
btfss STATUS, Z ; 等しい場合
goto cur_time_reset_end ; 以下の処理を行う
clrf cur_time_1 ; 現在時刻(25/125秒)をクリア
clrf cur_time_2 ; 現在時刻(25*256/125秒)をクリア
clrf cur_time_3 ; 現在時刻(25*256*256/125秒)をクリア
cur_time_reset_end

; モーターとLEDの制御
call control_sub ; サブルーチンの呼び出し

cur_time_changed_end
; TIMER0割込の後処理
movlw TIMER0_INIT ; タイマー初期値を
movwf TMR0 ; タイマカウンタに設定
bcf INTCON,T0IF ; オーバーフローフラグのクリア
tmr0_int_then_end

; レジスタの復帰
movf fsr_temp, w ; 退避した間接アドレスレジスタの内容をWレジスタに設定
movwf FSR ; 間接アドレスレジスタを復帰
movf pclath_temp, w ; 退避したPCLATHレジスタの内容をWレジスタに設定
movwf PCLATH ; PCLATHレジスタの内容を復帰
swapf status_temp, w ; 退避したステータスレジスタの内容をWレジスタに設定
movwf STATUS ; ステータスレジスタの内容を復帰
swapf w_temp, f ;
swapf w_temp, w ; Wレジスタの内容を復帰
retfie ; 割込みからの復帰


;*************************************************
; メインルーチン
;*************************************************
main
call proc_init_sub ; プロセッサ初期化

main_loop
goto main_loop ; メインルーチンでは何もしない

;
;*************************************************
; プロセッサ初期化サブルーチン
;*************************************************
proc_init_sub
; オプションレジスタの設定
bsf STATUS, RP0 ; バンク1
movlw b'10000101' ; (7) :プルアップ :1(なし)
; (6) :割込みエッジ :0(未使用)
; (5) :TMR0クロックソース:0(内部クロック)
; (4) :TMR0エッジ :0(未使用)
; (3) :プリスケーラ割当 :0(TMR0)
; (2-0):プリスケーラレート:101(1/64)
movwf OPTION_REG ; オプションレジスタに設定
bcf STATUS, RP0 ; バンク0

; IOポートの設定
; GPIOの初期化
bcf STATUS, RP0 ; バンク0
clrf GPIO ; GPIOの初期化
movlw b'00000111' ; コンパレータを
movwf CMCON ; 無効に設定
bsf STATUS, RP0 ; バンク1
clrf ANSEL ; すべてデジタル入力とする
movlw b'00111001' ; ポート(--IIIOOI)の
movwf TRISIO ; 入出力設定
bcf STATUS, RP0 ; バンク0


; タイマーの初期化
clrwdt ; ウォッチドックタイマの初期化
movlw TIMER0_INIT ; タイマー初期値を
movwf TMR0 ; タイマカウンタに設定
bcf INTCON,T0IF ; オーバーフローフラグのクリア

; 変数の初期化
clrf sw1_cur ; SW現在値
clrf sw1_last ; SW前回値
clrf sw1_proc ; SW処理値
clrf sw1_wait ; SWチャタリング待ちカウンタ
clrf cur_out ; 現在出力データ
clrf cur_time_0 ; 現在時刻(1/125秒)
clrf cur_time_1 ; 現在時刻(25/125秒)
clrf cur_time_2 ; 現在時刻(25*256/125秒)
clrf cur_time_3 ; 現在時刻(25*256*256/125秒)
clrf on_time_1 ; ON設定時刻(25/125秒)
clrf on_time_2 ; ON設定時刻(25*256/125秒)
clrf on_time_3 ; ON設定時刻(25*256*256/125秒)
clrf off_time_1 ; OFF設定期間(25/125秒)
clrf off_time_2 ; OFF設定期間(25*256/125秒)
clrf off_time_3 ; OFF設定期間(25*256*256/125秒)
clrf time_setting ; ON/OFF時刻設定中

; LEDをOFFに設定
bsf GPIO, LED_BIT ; GPIOの該当ビットをクリア

; モーターをOFFに設定
bsf GPIO, MOTOR_BIT ; GPIOの該当ビットをセット
bsf cur_out, MOTOR_BIT ; 現在出力データをセット

; 割込みの設定
movlw b'10100000' ; 割込み制御値:グローバルとTMR0を有効を
movwf INTCON ; 割込み制御レジスタに設定

return

;*************************************************
; 制御サブルーチン
;*************************************************
control_sub
; SWの処理
; SWの現在値を調べる
movf sw1_cur,w ; 現在の値が
subwf sw1_proc,w ; 処理値と
btfsc STATUS,Z ; 異なる場合
goto sw1change_then_end ; 以下の処理を行う

btfsc sw1_cur, SW_BIT ; SWがonの場合、次の処理を行う
goto sw1_on_end ; 以下の処理を行う
; モーターをONに設定
bcf GPIO, MOTOR_BIT ; GPIOの該当ビットをクリア
bcf cur_out, MOTOR_BIT ; 現在出力データをクリア
; ON設定時刻を設定
movf cur_time_1,w ; 現在時刻(25/125秒)の値を
movwf on_time_1 ; ON設定時刻(25/125秒)に設定
movf cur_time_2,w ; 現在時刻(25*256/125秒)の値を
movwf on_time_2 ; ON設定時刻(25*256/125秒)に設定
movf cur_time_3,w ; 現在時刻(25*256*256/125秒)の値を
movwf on_time_3 ; ON設定時刻(25*256*256/125秒)に設定
movlw 0xFF ; 0xFFを
movwf time_setting ; on/off時刻設定中をセット
goto sw1_off_end
sw1_on_end

movf time_setting,w ; on/off時刻設定中が
sublw 0xFF ; 0xFFに
btfss STATUS,Z ; 設定されている場合
goto time_setting_end ; 以下の処理を行う
; 設定状態の場合
; モーターをOFFに設定
bsf GPIO, MOTOR_BIT ; GPIOの該当ビットをセット
bsf cur_out, MOTOR_BIT ; 現在出力データをセット
; OFF設定時刻を設定
movf cur_time_1,w ; 現在時刻(25/125秒)の値を
movwf off_time_1 ; OFF設定時刻(25/125秒)に設定
movf cur_time_2,w ; 現在時刻(25*256/125秒)の値を
movwf off_time_2 ; OFF設定時刻(25*256/125秒)に設定
movf cur_time_3,w ; 現在時刻(25*256*256/125秒)の値を
movwf off_time_3 ; OFF設定時刻(25*256*256/125秒)に設定
clrf time_setting ; on/off時刻設定中をクリア
goto sw1_off_end
time_setting_end
sw1_off_end

movf sw1_cur, w ; 現在の値を
movwf sw1_proc ; 処理値に設定
goto time_normal_end
sw1change_then_end

movf time_setting,w ; on/off時刻設定中が
sublw 0xFF ; 0xffに
btfsc STATUS,Z ; 設定されていない場合
goto time_normal_end ; 以下の処理を行う
; 通常状態の場合
movf cur_time_1,w ; 現在時刻(25/125秒)の値が
subwf on_time_1,w ; ON設定時刻(25/125秒)と
btfss STATUS,Z ; 等しい場合
goto cur_on_time_end ; 以下の処理を行う
movf cur_time_2,w ; 現在時刻(25*256/125秒)の値が
subwf on_time_2,w ; ON設定時刻(25*256/125秒)と
btfss STATUS,Z ; 等しい場合
goto cur_on_time_end ; 以下の処理を行う
movf cur_time_3,w ; 現在時刻(25*256*256/125秒)の値が
subwf on_time_3,w ; ON設定時刻(25*256*256/125秒)と
btfss STATUS,Z ; 等しい場合
goto cur_on_time_end ; 以下の処理を行う
; モーターをONに設定
bcf GPIO, MOTOR_BIT ; GPIOの該当ビットをクリア
bcf cur_out, MOTOR_BIT ; 現在出力データをクリア
goto time_normal_end
cur_on_time_end
; OFF設定時刻と比較
movf cur_time_1,w ; 現在時刻(25/125秒)の値が
subwf off_time_1,w ; OFF設定時刻(25/125秒)と
btfss STATUS,Z ; 等しい場合
goto cur_off_time_end ; 以下の処理を行う
movf cur_time_2,w ; 現在時刻(25*256/125秒)の値が
subwf off_time_2,w ; OFF設定時刻(25*256/125秒)と
btfss STATUS,Z ; 等しい場合
goto cur_off_time_end ; 以下の処理を行う
movf cur_time_3,w ; 現在時刻(25*256*256/125秒)の値が
subwf off_time_3,w ; OFF設定時刻(25*256*256/125秒)と
btfss STATUS,Z ; 等しい場合
goto cur_off_time_end ; 以下の処理を行う
; モーターをOFFに設定
bsf GPIO, MOTOR_BIT ; GPIOの該当ビットをセット
bsf cur_out, MOTOR_BIT ; 現在出力データをセット
cur_off_time_end
; 水位のチェック
; btfsc sw1_cur, LEVEL_BIT ; 低水位の場合
; モーターをOFFに設定
; bsf GPIO, MOTOR_BIT ; GPIOの該当ビットをセット
time_normal_end

; LEDの制御
btfsc cur_out, MOTOR_BIT ; モーターがonの場合、次の処理を行う
goto led_on_end ; 以下の処理を行う
; LEDをONに設定
bcf GPIO, LED_BIT ; GPIOの該当ビットをセット
goto led_end
led_on_end
; モーターがOFFの場合、設定時刻の有無をチェック
movf on_time_1,w ; ON設定時刻(25/125秒)の値が
subwf off_time_1,w ; OFF設定時刻(25/125秒)と
btfss STATUS,Z ; 等しい場合
goto led_blink ; 以下の処理を行う
movf on_time_2,w ; ON設定時刻(25*256/125秒)の値が
subwf off_time_2,w ; OFF設定時刻(25*256/125秒)と
btfss STATUS,Z ; 等しい場合
goto led_blink ; 以下の処理を行う
movf on_time_3,w ; ON設定時刻(25*256*256/125秒)の値が
subwf off_time_3,w ; OFF設定時刻(25*256*256/125秒)と
btfsc STATUS,Z ; 等しくない場合(設定時刻がある場合)
goto led_end ; 以下の処理を行う
led_blink
; LEDの点滅
movf cur_time_1,w ; 現在時刻(25/125秒)の値が
andlw 0x0F ; 1/16周期(16*25/125s)に
btfss STATUS,Z ; 等しい場合
goto led_off ; 以下の処理を行う
; LEDをONに設定
bcf GPIO, LED_BIT ; GPIOの該当ビットをセット
goto led_end
led_off
; LEDをOFFに設定
bsf GPIO, LED_BIT ; GPIOの該当ビットをクリア
led_end

return

;*************************************************

END ; プログラムの終わり

出口ホースの先端はウレタンを切り抜いたものを取り付けます。
これに配水ホース用の穴を開けます。

出口ホースに取り付けます。
分岐は最終的にコック付の三又にしました。

配水ホースを取り付けて完成です。
配水ホースは安価な熱帯魚用のエアホースを利用しました。
配水ホースの先は洗濯ばさみを紐でくくりつけ、鉢にクリップします。

水をやりたい時刻にやりたい時間だけスイッチを押して学習させます。
あとは24時間周期で自動的に同じことが繰り返されます。

留守中に使うことを考えていましたが、現在は、日に2回必要な植物で1回分をこれで水やりしています。楽ができますし、やり忘れが防げて便利です。


« PrevNext »

コメント

コメントはありません。

コメントする