水槽冷却ファンをラズパイで制御したい

RaspberryPi

筆者の子が飼っているウーパールーパー(3代目)。

5-8年は生きるとされているが、初代、2代共に単独飼育で約4年と世間で言われている寿命よりは短命であった。3代目はペア。当初、両方オスだと思っていて黒い方を「月之進」ピンクの方を「日衛門」と名付けたのであるが、実は日衛門がメスであることがわかった。というのも約1年経過して日衛門が卵を産んだのである(そこで判明)。

大量に卵を産んで大量に孵ったが、半年後に生き残ったのはたった一匹。その一匹は別の水槽にて飼育中なのであるが、その水槽の管理もしなければならないし、卵から孵った個体なのでぜひとも長生きしていただきたい。

過去に短命であったその原因の一つは夏場の水温であろうと考えた。朝、ペットボトルを凍らせた物を水槽に入れて出かけるなどしてもその一瞬だけで午後には水温が30℃近い。エアコンを入れても水温はそうは下がらない。

世の中には水槽冷却ファンなるものが売られていることを知る。

  • ただのファン
  • 風量をコントロールできるもの(温度をコントロールできるわけではない)
  • 温度センサー(といっても原始的なサーモスタット)である程度の温度制御ができるもの
  • 電子的な温度センサーがあって設定した温度になるようにCPUが風量を制御するもの
  • ペルチェ式クーラー
  • チラー式やコンプレッサー式など本格的なもの、、、etc

さまざまである。価格帯もそれぞれであるが、最も安価なただのPC用のCPUファン然としたものが数千円であり、非常に馬鹿馬鹿しい。そんなものはどこの家庭のジャンク箱にいくつか転がっているわけで、電源を直結すればただのファンとしてはすぐにでも稼働できる。

仮にそういったものがなくても百均にでも行けば簡易な扇風機を安価に入手することができるので、似たようなものは作れる。しかし、それではイマイチ面白くない、、、というわけで、ある程度の温度制御可能な水槽冷却ファンを作ることはできないものかと考える。

と、傍にRaspberry Pi Zero W(以下ラズパイ)が余っているので、これを使うことにした。実は当初、ESP32-DevKitでやっていたのであるが、玄関に出入り監視用のUSBカメラを備えたラズパイを稼働させていたのと、元々玄関に水槽が置いてあるのでそのまま機能追加すれば良かったのでプログラムを移植した。従って、ESP32でももちろん可能。

一番上はESP32、そしてどこのご家庭にもあるラズパイZero W、下は3Bである。3Bで色々やって、安定したらZeroに持っていく、そういうやり方でプログラムを開発することが多い。4とか5も出てきているんだけど高いし入手がまだ不安定なんだよね。それに、そんなにスペックいらないのよ、ESP32でも十分なくらいで。まぁ今から買うならZero2とか3B+とかでいいかと。

まず、用意すべきは温度センサー。防水であることを条件にするとステンレスパッケージ加工されたDS18B20一択。旧ダラスセミコンダクタ、マキシムを経て現・アナログデバイセズ社製の高精度デジタル温度センサでインターフェースは1-Wire、温度測定範囲は-55℃~+125℃と十分。しかも安価。DS18B20を使うにはプルアップする必要があるので4.7kΩ抵抗を一本用意。それにしても、1-Wireの他のデバイスってあるのかな。

そして、FANはジャンク箱にある12Vのやつ。12Vで駆動するのでリレーを介してドライブする。念の為フォトカプラ(図ではシャープのPC817になっているが、手持ちの東芝のTLP521-1を使用、どっちでも良い)でアイソレーション。

トランジスタ(図ではUTCのDTC114ELになっているが、ありがちな2SC1815と抵抗2本でももちろん構わない、その場合抵抗は10kΩ2本が別途必要)でリレー(図ではオムロンG6Aになっているが、実際にはもっと小さいパナソニックATQ209を使用)をドライブする、実にオーソドックス。

そんな感じで回路図を引いてみて、万能基板に製作した。インジケーター用にLEDを設け、現在の温度を知るためのリクエストSWもケースに取り付けた。

ラズパイはまず、/boot/config.txt に下記の行を追加。

dtoverlay=w1-gpio

で、再起動して、

pi@pi1:~ $ lsmod|grep w1
w1_therm               17732  0
w1_gpio                 3165  0
wire                   32902  2 w1_gpio,w1_therm

確認のため、lsmod コマンドでドライバが存在することを確認。

w1_gpioがない場合は、/boot/config.txtを再確認。w1_thermがない場合は配線が間違っている。設定・配線が間違ってなければ温度センサの数だけ/sys/bus/w1/devices/の下に28-で始まるフォルダができる。28-以降の文字列(アドレス)はそのセンサー固有のもので、複数個接続したときはこれで判別する。

pi@pi1:~ $ ls /sys/bus/w1/devices/
28-3c34f648b137  28-3ce5f6485d38  w1_bus_master1

これらの中のw1_slaveファイルに温度を1000倍した数値が入る。例えば、

pi@pi1:/sys/bus/w1/devices/28-3c34f648b137 $ more w1_slave 
8b 01 55 05 7f a5 a5 66 0f : crc=0f YES
8b 01 55 05 7f a5 a5 66 0f t=24687

この、t=24687が24.687℃を表しているわけである。どっちがどうかわからん場合、どっちかのセンサーを握っておけば36℃前後まで上がるはずだから、それで判別すれば良い。で、2つのセンサーでそれぞれ2つの水温を測り、ある温度を超えたらFANを回して水面に風を当てて水温を下げ、下がったら停止するというプログラムを組む。

from gpiozero import OutputDevice, Button
from time import sleep
from datetime import datetime
from w1thermsensor import W1ThermSensor, Sensor, errors
import requests

FAN1 = OutputDevice(17)
FAN2 = OutputDevice(27)
RQSW = Button(12)  # RQSW

sensor_1 = W1ThermSensor(sensor_type=Sensor.DS18B20, sensor_id='3ce5f6485d38')
sensor_2 = W1ThermSensor(sensor_type=Sensor.DS18B20, sensor_id='3c34f648b137')

temp_1 = 0  # 水槽小初期化
temp_2 = 0  # 水槽大初期化

host = 'https://notify-api.line.me/api/notify'
token = 'YourToken'
headers = {'Authorization': f'Bearer {token}'}

def send(message):
    payload = {'message': message}
    requests.post(host, data=payload, headers=headers)

old_state_1 = False
new_state_1 = False
old_state_2 = False
new_state_2 = False

try:
    while True:
        try:
            temp_1 = sensor_1.get_temperature()
            print('水槽小 {0:.1f}℃'.format(temp_1))

            if temp_1 > 25:
                FAN1.on()
                new_state_1 = True
                if old_state_1 == False and new_state_1 == True:
                    print('FAN1 ON')
                    send('\n' + '水槽小 {0:.1f}℃'.format(temp_1) + '\n' + 'FAN Worked!')
                    old_state_1 = new_state_1

            if temp_1 < 23.5:
                FAN1.off()
                new_state_1 = False
                if old_state_1 == True and new_state_1 == False:
                    print('FAN1 OFF')
                    send('\n' + '水槽小 {0:.1f}℃'.format(temp_1) + '\n' + 'FAN Stopped!')
                    old_state_1 = new_state_1
        except errors.SensorNotReadyError:
            print('水槽小センサーがまだ準備できていません。')

        try:
            temp_2 = sensor_2.get_temperature()
            print('水槽大 {0:.1f}℃'.format(temp_2))

            if temp_2 > 25:
                FAN2.on()
                new_state_2 = True
                if old_state_2 == False and new_state_2 == True:
                    print('FAN2 ON')
                    send('\n' + '水槽大 {0:.1f}℃'.format(temp_2) + '\n' + 'FAN Worked!')
                    old_state_2 = new_state_2

            if temp_2 < 23.5:
                FAN2.off()
                new_state_2 = False
                if old_state_2 == True and new_state_2 == False:
                    print('FAN2 OFF')
                    send('\n' + '水槽大 {0:.1f}℃'.format(temp_2) + '\n' + 'FAN Stopped!')
                    old_state_2 = new_state_2
        except errors.SensorNotReadyError:
            print('水槽大センサーがまだ準備できていません。')
        
        # 現在の日時を取得
        current_time = datetime.now()

        # 時、分、秒をそれぞれ取得
        current_h = current_time.hour
        current_m = current_time.minute
        current_s = current_time.second

        if int(current_h) in [6, 8, 10, 12, 14, 16, 18, 20, 22]:
            if int(current_m) == 43:
                if 0 <= int(current_s) <= 5:
                    print(current_s)
                    send('\n' + '水槽大 {0:.1f}℃'.format(temp_2) + '\n' + '水槽小 {0:.1f}℃'.format(temp_1))
                    sleep(5)

        if RQSW.is_pressed:
            send('\n' + '水槽大 {0:.1f}℃'.format(temp_2) + '\n' + '水槽小 {0:.1f}℃'.format(temp_1))
            sleep(5)

except KeyboardInterrupt:
    pass

適当な名前、fan.pyとか付けて保存し、crontabに登録、再起動したら勝手に実行するようにした。

GPIOをコントロールするにはRPi.GPIOが有名であり当初はそれで記述したのであるが既に開発停止らしいのでRPi.GPIOで出来ることは全て可能とされるgpiozeroにて書き換えた。

機能的には水槽小と水槽大、2つの水温を測り、25℃を上回ったらそれぞれの水槽に設置したFANが回り、23.5℃を下回るまで回り続け、下回ったら止まる。かつ、稼働時と停止時にそれぞれLINEで通知する。通知するソフトを実装するよりそういうものに乗っかった方が早い。

LINE Notify
LINE Notify allows you to send web notifications from GitHub, IFTTT, Mackerel, and more directly to your LINE chats.

LINE NotifyはLINEアカウントを持っていれば誰でも無料で使える。マイページ、アクセストークンの発行でトークンは必ずどこかにコピーしておく。一度閉じると再度表示されないのである。で、

token = 'YourToken'

ここに書いておくと

こんな感じにLINEが飛んでくるというわけだ。

また定時的に(6時から22時まで2時間おき)各時43分になったら水温を通知する(この画像だと16:43がそれ)。なぜ43分かというと、たまたまそのあたりの時間が何も通知が来ないからである(例えば毎時0分などではいろいろな通知が来る)。寝ているであろう23時から朝5時までは黙ってて欲しいので、6時から22時にした。しかし、FANの稼働と停止時は通知するようにはした。狙い通り、25.1℃で大小それぞれのFANが稼働して、23.4℃で停止の通知が来る。

そしてプッシュSW(RQ)を付けているが、これを押下した時にも温度を測定して通知するようにした。これで任意に「今何度かな?」が出来るわけである。

水温はなるべく高温にならないほうがいいと考え、最初は25℃を超えたらON、25℃を下回ったらOFFするようにしたのであるが、そうするとON/OFFを繰り返してしまう。そう、少しでも25℃を超えたらON、下回ったらOFFするので水温は25℃前後をキープできるのであるが、FANの稼働状況としては非常に鬱陶しくなる。また、通知も鬱陶しい。従って、閾値を設けてある程度下がってからまた上がるまでの時間を稼ぐようにしたかった。実験の結果、停止温度は23.5℃にしたのである。

本当にジャンク箱にあった12VのFANで、こちらは水槽大、2個並列で動かす。アクリル板で綺麗な蓋を作りたいのであるが時間がないので段プラで。FANは4線式であったが回転数検知や回転数制御は不要なので、12VとGNDだけを使用する。消費電流は一個あたり0.5Aなので、計1Aである。なので、リレーを介してこれまたノートPC用のジャンクACアダプターから12Vを供給しているわけであるが、このアダプターは3Aあるので容量的には十分である。

Raspberry Pi Zero W - ヘッダー ハンダ付け済み - ラズベリー・パイ ゼロ W ワイヤレス

Raspberry Pi Zero W – ヘッダー ハンダ付け済み – ラズベリー・パイ ゼロ W ワイヤレス

Raspberry Pi Zero 2 W ラズベリーパイ Zero W(2代)日本技適取得 RAM容量 512MB CPU速度1GHz クアッドコア 64ビット Arm CPUタイプ Cortex-A53 コンピュータ

Raspberry Pi Zero 2 W ラズベリーパイ Zero W(2代)日本技適取得 RAM容量 512MB CPU速度1GHz クアッドコア 64ビット Arm CPUタイプ Cortex-A53 コンピュータ

SupLucktar DS18B20防水デジタル温度温度センサープローブ1M 2M 3M 5M 10M 15M - 5M

SupLucktar DS18B20防水デジタル温度温度センサープローブ1M 2M 3M 5M 10M 15M – 5M

コメント

タイトルとURLをコピーしました