雨の降り始めを検出するセンサーを ESP32 で作ってみました.
はじめに
以前 『ESP32 の ULP を活用したワイヤレス雨量計』を作りましたが,ししおどしの仕組みで計測する仕組みなので,ある程度雨が本降りになるまで雨を検出できません.
そこで,ESP32 のタッチセンサー機能を活用して雨滴そのものを検出するセンサーを作ることにしました.
原理
ESP32 には静電容量式のタッチセンサー IF が搭載されています.本来は次のようにして使います.
ESP32 の動作としては,一定期間に特定の電圧になるまで充放電を繰り返させ,そのサイクル数をカウントして出力します.静電容量が増えると充放電にかかる時間が長くなり,一定期間に充放電できる回数が減ります.そのため,カウント数をモニタすることで静電容量の変化が読み取れます.
タッチセンサー IF の先に雨滴で静電容量が変化する雨滴センサーを接続すれば,雨の検出を行うことができます.
必要な部品
AliExpress で入手するもの
- Single PCB board raindrop module
心臓部となる雨滴を検出するセンサーです.安っぽいので,ちゃんと長期間機能するか心配だったのですが,問題なさそうです.
10枚もいらないと思いますが,送料込みで400円ほどなので,お値打ちです.
Amazon で入手するもの
- コニシ 強力補修テープ ボンド ストームガード クリヤー
雨滴センサーを保護するシートです.使わなくてもセンシングはできますが,ある程度耐久性を確保したい場合,なんらかのカバーをセンサーの上に配置した方が良いです.
- 信越 一般工業用RTVゴム 100g 透明 KE45-100TM
シリコーンゴムです.センサーは長期間屋外に設置することになるので,これを使って隙間を埋めておきます.
回路
回路は簡単.ESP32 の GPIO14 は TOUCH_PAD_NUM6 に対応しているので,この先に雨滴センサーを接続します.雨滴センサーの残った方の端子は GND に接続します.
私の場合,『太陽電池と電気二重層コンデンサによる ESP32 駆動』で作ったものに組み込んだので,全体の見た目は次のような形になりました.
ソフト
ESP32 のソフト全体は github に esp32_raindrop という名前で上げてあります.
タッチセンサー関連の処理は下記の部分になります.
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 32 33 34 |
#define TOUCH_SAMPLE 5 int cmp_touch(const uint16_t *a, const uint16_t *b) { if (*a < *b) { return -1; } else if (*a == *b) { return 0; } else { return 1; } } void touchpad_init() { ESP_ERROR_CHECK(touch_pad_init()); ESP_ERROR_CHECK(touch_pad_config(TOUCH_PIN, 0)); ESP_ERROR_CHECK(touch_pad_set_voltage(TOUCH_HVOLT_2V7, TOUCH_LVOLT_0V5, TOUCH_HVOLT_ATTEN_1V)); ESP_ERROR_CHECK(touch_pad_filter_start(10)); } uint16_t touchpad_sense() { uint16_t touch_list[TOUCH_SAMPLE]; for (uint32_t i = 0; i < TOUCH_SAMPLE; i++) { ESP_ERROR_CHECK(touch_pad_read_filtered(TOUCH_PIN, touch_list + i)); } qsort(touch_list, TOUCH_SAMPLE, sizeof(uint16_t), (int (*)(const void *, const void *))cmp_touch); return touch_list[TOUCH_SAMPLE >> 1]; } |
touchpad_init()
で初期化を行い,touchpad_sense()
でセンシングを行っています.設定としては,10ms の期間で 0.5V と 2.7V の間を充放電させるようにしています.割とノイズが入るので,安定した結果を得るために5回計測を行って中央値を採用しています.
出力例
センサーの出力は下記のような感じになります.晴れているときは 400 近くあるのですが,雨が降ってくると 350 未満になります.
立ち下がっている部分を拡大すると次のようになります.●印が実際にセンサーが出力したポイントで,1分間隔になっています.これを見ると雨が降り始めてから5分もたたないうちに出力値が大きく変わっていることがわかります.
なお,静電容量は雨だけでなく温度や埃でも多少変動しますが,これまで半年以上様子を見た感じでは雨ほどの大きな変化はなく,あまりに気にしなくて良さそうです.
過去7ヶ月分のデータは次のような感じでした.
LINE による通知
雨の降り始めを検出できたので,次はスマホに通知してみます.
センシングしたデータは InfluxDB に突っ込んでいるので,REST API でデータを取り出し,雨の降り始めていれば LINE Notify で通知することにします.また,せっかくなので通知の際に気象庁の雨雲レーダの情報も添付しようと思います.
コード全体は次のようになります.
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
#!/usr/bin/env python3 # -*- coding: utf-8 -*- from influxdb import InfluxDBClient import requests from datetime import datetime from config import LINE_NOTIFY_TOKEN from pathlib import Path import os import urllib.request RADAR_MAP_URL_BASE = 'http://www.jma.go.jp/jp/radnowc/imgs/radar/211/' LINE_NOTIFY_API_ENDPOINT = 'https://notify-api.line.me/api/notify' INFLUXDB_ADDR = '192.168.2.20' INFLUXDB_PORT = 8086 INFLUXDB_DB = 'sensor' # NOTE: センサからは最長で 30 分間間隔でデータが登録されるので, # 余裕を見て過去 1 時間分を取得 INFLUXDB_QUERY = """ SELECT MEAN("touchpad") FROM "sensor.esp32" WHERE ("hostname" = \'ESP32-raindrop\') AND time >= now() - 1h GROUP BY time(2m) FILL(previous) ORDER by time asc """ WET_THRESHOLD = 370 NOTIFY_FLAG_FILE = '.notify' NOTIFY_INTERVAL = 7200 def radar_map_url(): return RADAR_MAP_URL_BASE + \ datetime.today().strftime('%Y%m%d%H') \ + '{:02d}'.format(datetime.today().minute // 5 * 5) \ + '-00.png' def notify_flag_file(): return os.path.join(os.path.dirname(__file__), NOTIFY_FLAG_FILE) def line_notify(message, image): payload = { 'message': message } headers = { 'Authorization': 'Bearer ' + LINE_NOTIFY_TOKEN } files = { } try: files = { 'imageFile': image } except: pass res = requests.post(LINE_NOTIFY_API_ENDPOINT, data=payload, headers=headers, files=files) Path(notify_flag_file()).touch() def check_soil_wet(): client = InfluxDBClient(host=INFLUXDB_ADDR, port=INFLUXDB_PORT, database=INFLUXDB_DB) result = client.query(INFLUXDB_QUERY) thresh_below = 0 status_list = list(map(lambda x: x['mean'], result.get_points())) thresh_below = 0 thresh_above = 0 for val in status_list: if (val is None): # NOTE: データが登録されていない期間は None continue elif (val < WET_THRESHOLD): thresh_below += 1 elif (val > WET_THRESHOLD): thresh_above += 1 thresh_below = 0 # NOTE: reset count # DEBUG # print('{} (below, above) = ({}, {})'.format(datetime.now(), thresh_below, thresh_above)) # print(status_list) return (thresh_below > 0) and (thresh_above > 0) def check_already_notified(): notified_datetime = datetime.fromtimestamp(os.stat(notify_flag_file()).st_mtime) elapsed_time = (datetime.now() - notified_datetime).total_seconds() return elapsed_time > NOTIFY_INTERVAL if check_soil_wet() and check_already_notified(): line_notify('\U00002614' + '️雨が降り始めました!', urllib.request.urlopen(radar_map_url())) |
InfluxDB の場合,データが無い期間は None が帰ってくるので,そこだけ気をつけてやれば難しくはないと思います.
このスクリプトを CRON で定期的に動かしておけば,雨が降り始めた際に,次のような通知が来ます.
応用編
雨滴センサーの出力は,雨滴センサーの上に水滴があるかどうかで変わるので,雨が降り止んでもまだ乾ききらない間はずっと小さい値を出力してくれます.
そこで,以前作成した『Raspberry Pi による「本格」水やりシステム (ソフト編)』の制御にこの特性を活用することで,無駄な水やりを省くようにしています.
まとめ
ESP32 のタッチセンサー IF を活用すると簡単に雨の降り始めを検出することができます.ESP32 の低消費電力を活用して太陽電池駆動にすれば,メンテナンスフリーで運用できるのでお勧めです.
コメント
お願いします。
売ってくださいませー