部品 · 読了まで17分

雨センサーとArduinoのガイド:配線、コード、自動で閉まる窓

Arduinoで雨センサーを使う方法:FC-37/YL-83の配線、AOとDOの出力、しきい値の設定、腐食の防止、ブザーのアラーム、サーボで閉まる窓、ESP32とPicoのコードを解説します。

雨センサーを使うと、Arduinoが雨粒を検知して「雨が降り始めた」ことをミリ秒単位で知らせてくれます。手に入るセンサーの中でも特に安く、遊んで楽しいセンサーの一つです。水を受ける検知パッドと、LM393コンパレーターを載せた小さな基板でできていて、Arduinoへはわずか4本の線でつなぎます。洗濯物が濡れる前に取り込むよう知らせるリマインダー、雨が降り始めると自動で閉まる窓やオーニング(日よけ)、雨の日には水やりを休む庭のシステム、小さな気象ステーションも、すべてこのモジュールから始まります。

このガイドでは、FC-37、YL-83、MH-RDの名前で売られている雨センサーモジュールの中身、アナログ出力(AO)とデジタル出力(DO)の違い、ポテンショメーターでしきい値を設定する方法、そして最も見落とされがちなテーマであるパッドの腐食を防ぐ方法を学びます。そのあと、雨の強さをレベルで知らせるArduinoの雨アラーム、サーボモーターで窓を自動で閉める装置、ESP32によるスマホへの通知、Raspberry Pi Pico用のMicroPythonの例をステップごとに作ります。

LM393制御基板付きの雨センサーモジュール(FC-37 / YL-83)
雨センサーキット:左はくし形のパターンが互い違いに並んだYL-83の検知パッド、右はLM393コンパレーター、感度調整用のポテンショメーター、PWR LEDとDO LEDを載せた制御基板で、その間を接続ケーブルがつないでいます。

雨センサーとは?そのしくみ

検知パッドには、2つのくしの歯のように互い違いに並び、決して触れ合わない2本の導電パターンがあります。パッドが乾いているとき、パターン間の抵抗はメガオーム単位で、回路は実質的に開いた状態です。雨粒が2本のパターンにまたがって落ちると、水がその間に橋を架け、抵抗が下がります。落ちる雨粒が多いほど、またパッドの濡れる面積が広いほど、抵抗は低くなります。制御基板では、パッドが抵抗(ほとんどのモデルで10 kΩ)と組み合わさって分圧回路を作ります。パッドが乾いているとAOピンの電圧は電源電圧(VCC)に近く、パッドが濡れるほど下がります。抵抗の値によって出力がどう変わるかは、分圧計算ツールで試せます。

基板上のLM393は、2つの電圧を比べるコンパレーターICです。一方の入力にはパッドからの電圧が、もう一方にはポテンショメーターで設定する基準電圧が入ります。パッドの電圧が基準電圧を下回った瞬間、つまり十分な水が検知されたとき、DO出力がLOWになり、基板上のDO LEDが点灯します。LM393の出力はオープンコレクタですが、基板上のプルアップ抵抗のおかげで、雨が降っていないときDOはHIGHのままです。もう1つのLED(PWR)は、モジュールに電源が入っていることを示すだけです。

項目値
検知パッド約5.4 × 4 cm、FR4基板上に互い違いのパターン、ほとんどのモデルでニッケルめっき
制御基板約3 × 1.5 cm、LM393コンパレーター
電源電圧3.3〜5 V
電流約8 mA(5 Vで両方のLEDが点灯した状態)
アナログ出力(AO)0 VからVCCの間、パッドが濡れると下がる
デジタル出力(DO)乾いているときHIGH、しきい値を超えるとLOW
表示PWR LED(電源)とDO LED(雨を検知)
調整感度調整用のポテンショメーター(変わるのはDOのしきい値だけ)
接続パッド側に2ピン(向きは関係なし)、Arduino側に4ピン:VCC、GND、DO、AO

FC-37、YL-83、MH-RDに違いはある?

結論から言うと、違いはありません。FC-37、YL-83、MH-RDは、同じ回路にメーカーごとに付けられた名前です。どれも検知パッドとLM393制御基板でできていて、同じコードで動きます。違いは主にパッドの大きさ、パターンの細かな違い、めっきの品質です。制御基板は土壌水分センサーで使われるものとほぼ同じなので、2つのモジュールの基板は互いに交換できます。パッドと基板の間の2本のピンにはプラスもマイナスもないので、ケーブルをどちら向きに挿しても問題ありません。時間がたってパッドが劣化したら、パッドだけを交換すれば済みます。

アナログ出力(AO)とデジタル出力(DO)の違い

どちらの出力を使うかはプロジェクトによります。DOが伝えるのは「雨が降っている」か「降っていない」かだけです。しきい値は基板上のポテンショメーターで設定し、Arduino側ではdigitalRead()を1回呼ぶだけで済みます。一方AOは、パッドがどれだけ濡れているかを0〜1023の数値で返すので、霧雨と土砂降りを区別したり、コードでしきい値を変えたり、連続した読み取り値を見て雨が降り始めた・やんだと判断したりできます。両方の出力を同時に使うこともできます。

項目DO(デジタル)AO(アナログ)
わかること雨が降っている/降っていないパッドの濡れ具合(Unoでは0〜1023)
しきい値の設定基板上のポテンショメーターコードで自由に
Arduinoのピン任意のデジタルピンアナログピン(A0〜A5)
雨の強さの区別できないできる(霧雨、雨、土砂降り)
割り込みでの利用できるできない
向いている用途シンプルなアラーム、起動(ウェイクアップ)信号レベルの測定、ヒステリシスを使った自動化

雨センサーの配線:Arduino Uno、ESP32、Pico

モジュールのピン/部品Arduino UnoESP32Raspberry Pi Pico
VCCD7(測定中だけ電源を入れる場合)または5VGPIO 25または3V3GP15または3V3(OUT)
GNDGNDGNDGND
AOA0GPIO 34(ADC1)GP26(ADC0)
DOD2任意(例:GPIO 27)GP14
アクティブブザー(+)D8任意GP13
サーボの信号線D9任意GP16
ESP32とRaspberry Pi Picoのピンが耐えられるのは最大3.3 Vです。これらのボードの5V、VIN、VBUSからモジュールに電源を供給すると、パッドが乾いているときにAOとDOの出力が5 Vまで上がり、入力ピンを壊すおそれがあります。モジュールには3.3 VかGPIOピン経由で電源を供給してください。ESP32では、AOを必ずADC1のピン(GPIO 32〜39)のいずれかにつないでください。ADC2のピンはWi-Fiがオンの間は読み取れません。

表でVCCが5Vではなくデジタルピンにつながっていることに気づいたかもしれません。モジュールが5 Vで消費する電流は約8 mAしかないので、Arduinoのピンで十分に供給できます(推奨される上限はピン1本あたり20 mA)。こうすれば、測定する数ミリ秒の間だけモジュールをオンにし、それ以外の時間はオフにしておけます。これがなぜそれほど重要なのかは、腐食のセクションで説明します。Picoのピンは初期設定で駆動電流が4 mAになっています。モジュールが3.3 Vで消費するのは数mAだけなので通常はこれで足りますが、読み取り値が安定しない場合は、代わりに3V3(OUT)ピンからモジュールに電源を供給できます。

感度の設定:ポテンショメーターでしきい値を調整する

  1. パッドを拭いて乾かし、モジュールに電源を入れます。PWR LEDが点灯し、DO LEDは消灯しているはずです。
  2. 小さなドライバーでポテンショメーターをゆっくり回し、DO LEDが点灯するところを探します。パッドが乾いているのにLEDがすでに点灯している場合は、逆方向に回してください。
  3. LEDが消えるちょうどその位置まで戻します。ここが、パッドが乾いた状態でDOがHIGHのままになる、最も感度の高い設定です。
  4. 同じ方向にもう少し回して余裕を持たせます。そうしないと、湿った空気や露でもアラームが作動することがあります。
  5. 指からパッドに水を2〜3滴落とします。DO LEDが点灯するはずです。点灯しない場合は、感度を少し上げてください。
  6. パッドを乾かし、LEDが消えるまでにどれくらいかかるかを観察します。これで、システムが「雨がやんだ」と判断するまでにどれくらい待つべきかの目安がわかります。
水道水でテストする場合は、結果をうのみにしないでください。水道水には溶けたミネラルが含まれているため、通常は雨水より数倍電気を通しやすくなっています。水道水では完璧に動くしきい値でも、実際の霧雨には反応が遅れることがあります。最も正確な設定は、最初の雨の日にシリアルモニタで実際の値を読み取って決めることです。それまでは、蛇口の下にかざすより、霧吹きで細かい水滴をパッドに吹きかけるほうが、ずっと本物の雨に近い状態を再現できます。

ステップごとに:Arduinoで最初の読み取り

  1. モジュールのVCCをD7に、GNDをGNDに、AOをA0に、DOをD2につなぎます。2線式のケーブルでパッドを基板に接続します。
  2. 下のコードをArduino IDEで開き、ボードとしてArduino Unoを選んで書き込みます。
  3. シリアルモニタを9600ボーで開き、パッドが乾いた状態のAOの値を記録します。
  4. パッドに水を数滴たらし、さらに量を増やして、そのつどAOの値を書き留めます。
  5. パッドが完全に濡れたときの値も記録します。これらの値が、次のスケッチで使うDRYとWETの定数になります。
C++ (Arduino IDE)
1// Yağmur sensörü: AO ve DO okuma / Rain sensor: reading AO and DO
2const int SENSOR_POWER = 7;   // modülün VCC'si bu pinden beslenir / module VCC is fed from this pin
3const int RAIN_AO = A0;       // analog çıkış / analog output
4const int RAIN_DO = 2;        // dijital çıkış / digital output
5
6void setup() {
7  Serial.begin(9600);
8  pinMode(SENSOR_POWER, OUTPUT);
9  digitalWrite(SENSOR_POWER, LOW);  // başlangıçta kapalı / off at start-up
10  pinMode(RAIN_DO, INPUT);
11}
12
13void loop() {
14  digitalWrite(SENSOR_POWER, HIGH);             // modülü aç / power the module
15  delay(50);                                    // değerin oturmasını bekle / let the reading settle
16  int analogValue = analogRead(RAIN_AO);        // 0-1023: kuruyken yüksek / high when dry
17  bool raining = digitalRead(RAIN_DO) == LOW;   // yağmurda DO = LOW / DO goes LOW in rain
18  digitalWrite(SENSOR_POWER, LOW);              // modülü kapat / power it off
19
20  Serial.print(F("AO: "));
21  Serial.print(analogValue);
22  Serial.print(F("  DO: "));
23  Serial.println(raining ? F("YAGMUR / RAIN") : F("KURU / DRY"));
24  delay(2000);                                  // 2 sn'de bir ölç / measure every 2 s
25}

コードは測定の前にD7をHIGHにしてモジュールに電源を入れ、基板上のコンデンサが充電されて読み取り値が安定するまで50 ms待ち、AOとDOを読み取ってから、すぐにモジュールの電源を切ります。こうすることで、2秒ごとのループのうちモジュールに電源が入っているのは50 msだけ、つまり時間の2.5%になります。雨のときDOはLOWになるので、条件はdigitalRead(RAIN_DO) == LOWと書きました。表示するメッセージのトルコ語の単語は、一部のターミナルで特殊文字が文字化けするため、特殊文字を使わずに書いています。

パッドの状態AOの目安(Uno、5 V)DO(調整済みの基板)
乾いていてきれい1000〜1023HIGH
数滴750〜950設定によりHIGHまたはLOW
霧雨、パッドの一部が濡れている500〜750LOW
強い雨300〜500LOW
完全に濡れている、水道水150〜350LOW

これらの数値はおおよその目安にすぎません。パッドの大きさ、めっきの状態、水の純度、パッド上のほこりによって、値は目に見えて変わります。同じモジュールでも、パッドを数か月屋外に置くと値がずれていきます。そのため次のスケッチでは、生の値をそのまま使うのではなく、自分で測った乾いたとき(DRY)と濡れたとき(WET)の値を使って、0〜100の濡れ具合(%)に変換します。こうすれば、しきい値に手を付けずに、この2つの定数を更新するだけで済みます。

Arduinoの雨アラーム:雨の強さのレベルとブザー

次は、測定を役に立つアラームにしましょう。コードはAOを8回続けて読み取って平均し、その結果をmap()で濡れ具合(%)に変換して、乾燥、霧雨、雨、土砂降りの4つのレベルのいずれかに分類します。雨が強くなると、アクティブブザーがレベルの番号と同じ回数だけ鳴ります。霧雨なら1回、雨なら2回、土砂降りなら3回です。ブザーのプラス側の足をD8に、マイナス側の足をGNDにつなぎます。アクティブブザーとパッシブブザーの違いはブザーとは?で紹介しています。

C++ (Arduino IDE)
1// Yağmur şiddeti seviyeleri ve sesli alarm / Rain intensity levels and audible alarm
2const int SENSOR_POWER = 7;     // modülün VCC'si / module VCC
3const int RAIN_AO = A0;         // analog çıkış / analog output
4const int BUZZER = 8;           // aktif buzzer / active buzzer
5const int DRY = 1015;           // kuru panel değeri / dry pad reading (kendin ölç / measure yours)
6const int WET = 350;            // tamamen ıslak panel / fully wet pad (kendin ölç / measure yours)
7
8const char* const LEVEL_NAMES[] = {"Kuru / Dry", "Ciseleme / Drizzle", "Yagmur / Rain", "Saganak / Downpour"};
9int lastLevel = 0;
10
11int readWetness() {
12  digitalWrite(SENSOR_POWER, HIGH);
13  delay(50);
14  long sum = 0;
15  for (int i = 0; i < 8; i++) sum += analogRead(RAIN_AO);  // 8 ölçümün ortalaması / average of 8 readings
16  digitalWrite(SENSOR_POWER, LOW);
17  return constrain(map(sum / 8, DRY, WET, 0, 100), 0, 100);  // % ıslaklık / % wetness
18}
19
20void beep(int count) {
21  for (int i = 0; i < count; i++) {
22    digitalWrite(BUZZER, HIGH);
23    delay(150);
24    digitalWrite(BUZZER, LOW);
25    delay(150);
26  }
27}
28
29void setup() {
30  Serial.begin(9600);
31  pinMode(SENSOR_POWER, OUTPUT);
32  pinMode(BUZZER, OUTPUT);
33}
34
35void loop() {
36  int wet = readWetness();
37  int level;
38  if (wet < 10) level = 0;        // kuru / dry
39  else if (wet < 40) level = 1;   // çiseleme / drizzle
40  else if (wet < 75) level = 2;   // yağmur / rain
41  else level = 3;                 // sağanak / downpour
42
43  Serial.print(F("Islaklik / Wetness: %"));
44  Serial.print(wet);
45  Serial.print(F("  "));
46  Serial.println(LEVEL_NAMES[level]);
47
48  if (level > lastLevel) beep(level);  // şiddet artınca seviye kadar bip / beep once per level when it gets heavier
49  lastLevel = level;
50  delay(3000);                         // 3 sn'de bir ölç / measure every 3 s
51}

ここでの%は、パッドのどれだけの面積が濡れているかを示すもので、気象の専門家が使う1時間あたりのミリ単位の降水量ではありません。それでも実用上はとても役に立ちます。弱い霧雨はパターンのごく一部にしか橋を架けませんが、土砂降りではほぼ全面が覆われます。レベルの境目(10、40、75)は、自分の観察に合わせて変えられます。ブザーはレベルが上がったときだけ鳴るので、雨が同じレベルで続いている間に3秒ごとにビープ音が鳴ることはありません。

腐食:パッドはなぜさびるのか、どうすれば長持ちするのか

雨センサーの最大の問題は、時間とともに劣化することです。パッドに直流(DC)をかけ続けると、パターン間の水が電解液の役割を果たし、電気分解が始まります。プラス側につながったパターンの金属が少しずつ溶け出し、ニッケルめっきが薄くなり、その下の銅が黒ずんだり緑色になったりします。その結果、乾いたパッドの抵抗が下がり、読み取り値がずれ、最終的にはパターンが切れてしまいます。常に5 Vがかかった状態で屋外に置いたパッドは、数週間で目に見えて劣化することがあります。よい知らせもあります。劣化はパッドに電源が入っている時間に比例し、その時間は自分でコントロールできます。

  • 測定中だけ電源を入れる:VCCをデジタルピンから供給し、1回の読み取りにつき50 msだけモジュールをオンにします。2秒ごとに測定するシステムなら、パッドに電源が入っているのは時間の2.5%で、劣化の進み方を約40分の1に抑えられます。
  • 測定の回数を減らす:雨の状態は数秒では変わりません。窓や通知のプロジェクトなら、5〜30秒に1回の読み取りで十分です。30秒ごとに測定するシステムでは、パッドに電源が入っているのは時間のわずか約0.2%です。
  • 極性を入れ替える(上級):パッドを2本のデジタルピンに直接つなぎ、読み取りのたびにどちらの端をHIGHにするかを切り替えると、劣化が2本のパターンに均等に分散されます。この方法では制御基板を使わず、抵抗で分圧回路を自分で組み、それぞれの向きで読み取りを行います。
  • パッドをきれいに保つ:ほこり、花粉、鳥のふんは導電性を高め、乾いたときの読み取り値を下げます。ときどきイソプロピルアルコールと柔らかいブラシでパッドを掃除してください。紙やすりや研磨スポンジはめっきを傷つけます。
  • パッドではなく基板を保護する:制御基板は防水ケースに入れるか、防湿コーティング(コンフォーマルコーティング)を施します。パッドは決してニス、シリコン、テープで覆わないでください。水がパターンに触れる必要があります。
  • 予備のパッドを用意する:パッドは安い消耗品です。読み取り値が目に見えてずれたり、パターンが黒ずんだりしたら、パッドを交換してDRYとWETを測り直すだけです。

サーボモーターで窓やオーニングを自動で閉める

雨センサーの最も人気のある使い道は、雨が降り始めると窓、オーニング、温室の屋根を自動で閉めるシステムです。SG90サーボモーターを使えば模型で作れます。ここでは2つのルールが重要です。1つ目は確認です。雨粒が1滴落ちたり虫がパッドに止まったりしただけで窓が閉まってはいけないので、閉める前に濡れた読み取り値が3回続くのを待ちます。2つ目はヒステリシスです。雨がやんでもパッドはすぐには乾かず、値はしきい値の付近を行ったり来たりします。窓が何度も開いたり閉まったりしないように、「雨」と「乾燥」に別々のしきい値を使い、パッドが10分間途切れずに乾いた状態を保ってから窓を開けます。サーボの配線と角度の制御についてはサーボモーターとは?をご覧ください。

SG90サーボモーター
SG90サーボモーター
アクティブブザー
アクティブブザー
C++ (Arduino IDE)
1// Yağmurda pencereyi servo ile kapatan sistem / Closes a window with a servo when it rains
2#include <Servo.h>
3
4const int SENSOR_POWER = 7;     // modülün VCC'si / module VCC
5const int RAIN_AO = A0;         // analog çıkış / analog output
6const int BUZZER = 8;           // aktif buzzer / active buzzer
7const int SERVO_PIN = 9;        // servo sinyal kablosu / servo signal wire
8
9const int DRY = 1015;           // kuru panel / dry pad (kendin ölç / measure yours)
10const int WET = 350;            // ıslak panel / wet pad (kendin ölç / measure yours)
11const int RAIN_ON = 25;         // %25 ve üstü: yağıyor / 25% and above: raining
12const int RAIN_OFF = 8;         // %8 ve altı: panel kurudu / 8% and below: pad is dry
13const int CONFIRM_COUNT = 3;    // art arda 3 ıslak ölçüm / 3 wet readings in a row
14const unsigned long SAMPLE_MS = 2000UL;   // 2 sn'de bir ölç / measure every 2 s
15const unsigned long DRY_MS = 600000UL;    // 10 dk kuru kalmalı / must stay dry for 10 min
16
17const int OPEN_ANGLE = 90;      // pencere açık / window open
18const int CLOSED_ANGLE = 0;     // pencere kapalı / window closed
19
20Servo windowServo;
21bool closed = false;
22int wetCount = 0;
23unsigned long lastSample = 0;
24unsigned long drySince = 0;
25
26int readWetness() {
27  digitalWrite(SENSOR_POWER, HIGH);
28  delay(50);                                              // değerin oturmasını bekle / let it settle
29  int raw = analogRead(RAIN_AO);
30  digitalWrite(SENSOR_POWER, LOW);
31  return constrain(map(raw, DRY, WET, 0, 100), 0, 100);  // % ıslaklık / % wetness
32}
33
34void beep(int count) {
35  for (int i = 0; i < count; i++) {
36    digitalWrite(BUZZER, HIGH);
37    delay(150);
38    digitalWrite(BUZZER, LOW);
39    delay(150);
40  }
41}
42
43void setup() {
44  Serial.begin(9600);
45  pinMode(SENSOR_POWER, OUTPUT);
46  pinMode(BUZZER, OUTPUT);
47  windowServo.write(OPEN_ANGLE);   // attach'tan önce açı ver, servo zıplamasın / set the angle before attach to avoid a jump
48  windowServo.attach(SERVO_PIN);
49}
50
51void loop() {
52  unsigned long now = millis();
53  if (now - lastSample < SAMPLE_MS) return;   // ölçüm zamanı gelmedi / not time to measure yet
54  lastSample = now;
55
56  int wet = readWetness();
57  Serial.print(F("Islaklik / Wetness: %"));
58  Serial.println(wet);
59
60  if (wet >= RAIN_ON) {
61    drySince = now;                            // kuruma sayacını sıfırla / reset the drying timer
62    if (wetCount < CONFIRM_COUNT) wetCount++;
63    if (!closed && wetCount >= CONFIRM_COUNT) {
64      closed = true;
65      beep(3);
66      windowServo.write(CLOSED_ANGLE);         // pencereyi kapat / close the window
67      Serial.println(F("Yagmur: pencere kapandi / Rain: window closed"));
68    }
69  } else {
70    wetCount = 0;
71    if (wet > RAIN_OFF) {
72      drySince = now;                          // ara bölge kuru sayılmaz / the middle zone does not count as dry
73    } else if (closed && now - drySince >= DRY_MS) {
74      closed = false;
75      windowServo.write(OPEN_ANGLE);           // 10 dk kuruysa aç / open after 10 dry minutes
76      Serial.println(F("Kuru: pencere acildi / Dry: window opened"));
77    }
78  }
79}
定数役割初期値
DRY / WET自分で測った、乾いたパッドと濡れたパッドの読み取り値1015 / 350
RAIN_ONこの%以上で「雨」とみなす25%
RAIN_OFFこの%以下で「乾燥」とみなす8%
CONFIRM_COUNT窓を閉めるのに必要な、濡れた読み取り値の連続回数3(約6秒)
DRY_MS窓を開けるまでに、パッドが乾いた状態を保つ必要がある時間10分
OPEN_ANGLE / CLOSED_ANGLE開いた位置と閉じた位置のサーボの角度90° / 0°
SG90は動いている間に数百mAの電流を消費することがあります。サーボを複数使う場合やMG996Rのような大きなサーボを使う場合は、Arduinoの5Vピンではなく別の5 V電源から供給し、GNDを共通にしてください。本物の窓やオーニングにはサーボでは力不足です。ギア付きモーターかリニアアクチュエーター、両端のリミットスイッチ、挟み込み防止の対策が必要です。このコードは、機構を制御するロジックを学び、模型で試すためのものです。

ESP32でスマホに雨の通知を送る

外出中に雨が降り始めたことを知るには、ESP32が使えます。下のコードは、雨が降り始めるとTelegramで「洗濯物を取り込んで」というメッセージを送り、パッドが15分間乾いた状態が続くと「雨がやみました」というメッセージを送ります。ライブラリマネージャからUniversalTelegramBotとArduinoJsonのライブラリをインストールする必要があります。ボットの作り方とチャットIDの調べ方は、ESP32のTelegramボットでステップごとに説明しています。

C++ (Arduino IDE)
1// ESP32 yağmur bildirimi (Telegram) / ESP32 rain notification (Telegram)
2#include <WiFi.h>
3#include <WiFiClientSecure.h>
4#include <UniversalTelegramBot.h>        // ArduinoJson kütüphanesi de gerekir / also needs the ArduinoJson library
5
6const char* WIFI_SSID = "WIFI_ADI";      // Wi-Fi ağının adı / your Wi-Fi name
7const char* WIFI_PASS = "WIFI_SIFRESI";  // Wi-Fi şifren / your Wi-Fi password
8#define BOT_TOKEN "1234567890:ABCdefGhIJKlmNoPQRstuVWXyz"  // BotFather'dan / from BotFather
9#define CHAT_ID "123456789"              // senin chat ID'n / your chat ID
10
11const int SENSOR_POWER = 25;             // modülün VCC'si (3,3 V) / module VCC (3.3 V)
12const int RAIN_AO = 34;                  // ADC1 pini, Wi-Fi ile çalışır / ADC1 pin, works with Wi-Fi
13const int DRY = 4095;                    // kuru panel / dry pad (kendin ölç / measure yours)
14const int WET = 1500;                    // ıslak panel / wet pad (kendin ölç / measure yours)
15const int RAIN_ON = 25;                  // %25 ve üstü: yağıyor / 25% and above: raining
16const int RAIN_OFF = 8;                  // %8 ve altı: kuru / 8% and below: dry
17const unsigned long SAMPLE_MS = 5000UL;  // 5 sn'de bir ölç / measure every 5 s
18const unsigned long DRY_MS = 900000UL;   // 15 dk kuru: yağmur bitti / 15 dry minutes: rain is over
19
20WiFiClientSecure client;
21UniversalTelegramBot bot(BOT_TOKEN, client);
22
23bool raining = false;
24int wetCount = 0;
25unsigned long lastSample = 0;
26unsigned long drySince = 0;
27
28int readWetness() {
29  digitalWrite(SENSOR_POWER, HIGH);
30  delay(50);                             // değerin oturmasını bekle / let it settle
31  int raw = analogRead(RAIN_AO);         // 0-4095
32  digitalWrite(SENSOR_POWER, LOW);
33  return constrain(map(raw, DRY, WET, 0, 100), 0, 100);
34}
35
36void connectWiFi() {
37  WiFi.mode(WIFI_STA);
38  WiFi.begin(WIFI_SSID, WIFI_PASS);
39  unsigned long start = millis();
40  while (WiFi.status() != WL_CONNECTED && millis() - start < 15000) {  // en fazla 15 sn bekle / wait up to 15 s
41    delay(500);
42  }
43}
44
45void notify(const String& text) {
46  if (WiFi.status() != WL_CONNECTED) connectWiFi();  // bağlantı koptuysa yeniden bağlan / reconnect if dropped
47  if (WiFi.status() == WL_CONNECTED) {
48    bot.sendMessage(CHAT_ID, text, "");
49  }
50}
51
52void setup() {
53  Serial.begin(115200);
54  pinMode(SENSOR_POWER, OUTPUT);
55  digitalWrite(SENSOR_POWER, LOW);
56  client.setCACert(TELEGRAM_CERTIFICATE_ROOT);  // api.telegram.org kök sertifikası / root certificate
57  connectWiFi();
58  notify("Yagmur sensoru devrede / Rain sensor is online");
59}
60
61void loop() {
62  unsigned long now = millis();
63  if (now - lastSample < SAMPLE_MS) return;
64  lastSample = now;
65
66  int wet = readWetness();
67  Serial.printf("Islaklik / Wetness: %%%d\n", wet);
68
69  if (!raining) {
70    wetCount = (wet >= RAIN_ON) ? wetCount + 1 : 0;
71    if (wetCount >= 2) {                 // art arda 2 ıslak ölçüm (~10 sn) / 2 wet readings in a row (~10 s)
72      raining = true;
73      drySince = now;
74      String msg = "Yagmur basladi, camasirlari topla! / It's raining, bring in the laundry! %";
75      msg += wet;
76      notify(msg);
77    }
78  } else {
79    if (wet > RAIN_OFF) {
80      drySince = now;                    // hâlâ ıslak / still wet
81    } else if (now - drySince >= DRY_MS) {
82      raining = false;
83      wetCount = 0;
84      notify("Yagmur durdu / The rain has stopped");
85    }
86  }
87}

ESP32のADCは12ビットなので値は0〜4095の範囲になり、モジュールを3.3 Vで動かすと、乾いたパッドは通常4095にとても近い値になります。それでもDRYとWETの値は自分で測ってください。メッセージの送信には1〜2秒かかることがあるため、測定間隔は5秒にしました。また、同じにわか雨でメッセージが次々に送られないように、rainingフラグを使っています。電池でシステムを動かしたい場合は、読み取りの合間にESP32をディープスリープさせることができ、Home Assistantを使っているならMQTTでデータを送れます。

Raspberry Pi PicoとMicroPythonで雨センサーを使う

Raspberry Pi Picoでも考え方は同じです。モジュールにはGP15経由で3.3 Vの電源を供給し、AOをGP26に、DOをGP14に、アクティブブザーをGP13に、サーボの信号線をGP16につなぎます。PicoのADCは、read_u16()で0〜65535の値を返します。下のコードは、雨を検知するとビープ音を3回鳴らし、サーボでオーニングを収納します。パッドが10分間乾いた状態が続くと、オーニングを再び開きます。

MicroPython
1# Raspberry Pi Pico yağmur sensörü + servo tente / Raspberry Pi Pico rain sensor + servo awning
2from machine import Pin, ADC, PWM
3import time
4
5power = Pin(15, Pin.OUT, value=0)   # modülün VCC'si (3,3 V) / module VCC (3.3 V)
6rain_ao = ADC(26)                   # GP26 = ADC0, AO çıkışı / AO output
7rain_do = Pin(14, Pin.IN)           # DO: yağmurda 0 / DO: 0 in rain
8buzzer = Pin(13, Pin.OUT, value=0)  # aktif buzzer / active buzzer
9servo = PWM(Pin(16))
10servo.freq(50)                      # servolar 50 Hz ister / servos expect 50 Hz
11
12DRY, WET = 65000, 24000             # kendi değerlerinle değiştir / replace with your readings
13RAIN_ON, RAIN_OFF = 25, 8           # % eşikler / % thresholds
14DRY_MS = 10 * 60 * 1000             # 10 dk kuru kalmalı / must stay dry for 10 min
15OPEN_ANGLE, CLOSED_ANGLE = 90, 0
16
17def set_angle(deg):
18    us = 544 + deg * (2400 - 544) // 180    # Arduino Servo ile aynı aralık / same range as Arduino Servo
19    servo.duty_u16(us * 65535 // 20000)     # 20 ms periyodun oranı / share of the 20 ms period
20
21def read_sensor():
22    power.value(1)
23    time.sleep_ms(50)                       # değerin oturmasını bekle / let it settle
24    raw = rain_ao.read_u16()                # 0-65535
25    do_wet = rain_do.value() == 0
26    power.value(0)
27    pct = (DRY - raw) * 100 // (DRY - WET)
28    return max(0, min(100, pct)), do_wet
29
30def beep(count):
31    for _ in range(count):
32        buzzer.value(1)
33        time.sleep_ms(150)
34        buzzer.value(0)
35        time.sleep_ms(150)
36
37closed = False
38wet_count = 0
39dry_since = time.ticks_ms()
40set_angle(OPEN_ANGLE)                       # tente açık başlar / awning starts open
41
42while True:
43    wet, do_wet = read_sensor()
44    print("Islaklik / Wetness: %", wet, " DO:", "YAGMUR / RAIN" if do_wet else "KURU / DRY")
45    now = time.ticks_ms()
46    if wet >= RAIN_ON:
47        dry_since = now                     # kuruma sayacını sıfırla / reset the drying timer
48        wet_count += 1
49        if not closed and wet_count >= 3:   # art arda 3 ıslak ölçüm / 3 wet readings in a row
50            closed = True
51            beep(3)
52            set_angle(CLOSED_ANGLE)         # tenteyi kapat / retract the awning
53    else:
54        wet_count = 0
55        if wet > RAIN_OFF:
56            dry_since = now
57        elif closed and time.ticks_diff(now, dry_since) >= DRY_MS:
58            closed = False
59            set_angle(OPEN_ANGLE)           # 10 dk kuruysa aç / open after 10 dry minutes
60    time.sleep(2)

MicroPythonには組み込みのサーボクラスがないので、角度をパルス幅に自分で変換します。サーボは、20 msごとに届く約0.5〜2.5 msのパルスで位置が決まります。set_angle()関数は0〜180度を、ArduinoのServoライブラリと同じ544〜2400マイクロ秒の範囲に対応させ、その時間をduty_u16()用の0〜65535の比率に変換します。Thonnyでコードをmain.pyとしてPicoに保存すると、Picoに電源が入るたびに自動で実行されます。

屋外への設置:正しい配置のコツ

  • パッドを斜めに取り付ける:20〜45度傾けると水滴が流れ落ち、雨がやんだあとパッドが早く乾きます。パッドが水平だと水がたまり、システムが何時間も「雨」と言い続けることがあります。
  • 空が開けた場所を選ぶ:軒下、木の下、水がしたたるエアコンの下にはパッドを置かないでください。このような場所では、本物の雨を見逃すうえ、思わぬしずくで誤作動します。
  • 電子部品は屋内に置く:屋外に出すのはパッドだけにします。制御基板とArduinoは防水ケースに入れ、ケーブルの引き込み口はケーブルグランドかシリコンでふさぎます。
  • ケーブルは短くする:パッドと基板の間のケーブルは数メートルまで延長できますが、それより長いとノイズが増えます。読み取り値を平均すると、この揺らぎを抑えられます。
  • 露や霧に注意する:涼しい夜には、パッドに結露した水分で小さな濡れ具合の値が出ることがあります。RAIN_ONのしきい値を少し高めに設定し、連続した読み取りで確認すれば、こうした誤報のほとんどを防げます。
  • 雪の検知は遅い:乾いた雪は電気を通さないので、センサーが反応するのは雪が溶けて水になってからです。寒い地域では、このセンサーを信頼できるのは雨に対してだけと考えてください。

雨センサーと土壌水分センサー・水位センサーの違い

センサー何を測る?出力主な用途
雨センサー(FC-37、YL-83)表面に落ちた水滴AOとDO雨アラーム、窓やオーニングの自動化
土壌水分センサー土の中の水分AOとDO(同じLM393基板)植物の水やり
水位センサー(平行パターン)容器内の水の高さアナログ(S)タンクや水槽の水位
転倒ます型雨量計降水量(mm)1回傾くごとに1パルス気象ステーション
同じLM393制御基板を使う土壌水分センサー
同じLM393制御基板を使う土壌水分センサー
水位センサー
水位センサー

雨センサーは「どれだけ雨が降ったか」という問いには答えられません。それには、気象ステーションで使われる転倒ます型雨量計が必要です。じょうごから入った水が小さなますを満たし、一定量になるとますが傾きます(一般的な気象ステーションキットでは、1回の転倒が0.2794 mmの雨に相当します)。そして傾くたびに、磁石がリードスイッチを一瞬閉じます。Arduinoはこのパルスを割り込みで数え、その回数にますの値を掛けて、降水量をミリメートル単位で求めます。2つのセンサーを一緒に使うのも理にかなっています。雨センサーは雨が降り始めたことをすぐに知らせ、雨量計は合計を記録します。容器にたまる水の高さを測りたい場合は、水位センサーのほうが適しています。

雨センサーのプロジェクトアイデア

  • 洗濯物アラート:雨が降り始めるとブザーが鳴り、LEDが点灯します。ESP32を使えばスマホにもメッセージが届きます。
  • スマート温室の屋根:雨のときはサーボで屋根の換気窓を閉め、温度に応じて換気のために開けます。詳しくはスマート温室プロジェクトをご覧ください。
  • 雨の日は水やりを休む:土壌水分センサーで作った自動水やりシステムが、雨の間にポンプを動かさないようにします。
  • ミニ気象ステーション:BME280で測った温度、湿度、気圧と一緒に、雨の状態をOLEDディスプレイに表示します。
  • ワイパーの模型:雨が強くなるほどサーボを速く振って、車の自動ワイパーをまねる楽しいプロジェクトです。
  • スマートホームのシナリオ:雨が降り始めると窓を閉め、オーニングを収納し、通知してくれるシステムについては、Arduinoのスマートホームプロジェクトのガイドをご覧ください。

よくある間違いと対処法

症状考えられる原因対処法
AOが常に1023のまま変わらないパッドのケーブルが外れているか断線している。AOではなくDOをA0につないでいるパッドと基板の間の2本の線とA0の接続を確認する
パッドが乾いているのにAOがとても低いパッドが汚れているかまだ濡れている。何かがパターン間をつないでいる。モジュールに電源が入る前に読み取っているパッドを掃除して乾かす。コードでは電源を入れて待ってから読み取る
DOがまったく変わらないポテンショメーターが一方の端いっぱいまで回っている感度調整のステップを最初からやり直す
晴れているのに雨アラームが鳴る露、霧、パッドの汚れ、または感度が高すぎるしきい値RAIN_ONを上げ、連続した読み取りで確認し、パッドを掃除する
雨がやんだのに、システムがまだ「雨」と表示するパッドが水平に取り付けられていて乾くのが遅いパッドを斜めに取り付け、RAIN_OFFとDRY_MSを調整する
数週間で読み取り値がずれ、パターンが黒ずんできた常時通電による腐食ピンからモジュールに電源を供給し、測定回数を減らし、パッドを交換する
ESP32でWi-Fiがオンになると読み取り値がおかしくなるAOがADC2のピンにつながっているAOをGPIO 32〜39のピンに移す
サーボががたつく、またはArduinoがリセットするサーボの電流で5Vラインの電圧が下がっているサーボを別の5 V電源から供給してGNDを共通にし、電源に470 µFのコンデンサを並列に入れる
雨を待たずにシステムをテストするいちばん簡単な方法は、濡れた指か湿らせたティッシュでパッドのパターンをなぞることです。より実際に近いテストをするなら、30〜40 cm離れたところから霧吹きでパッドに何回か吹きかけます。濡れ具合(%)が上がり、パッドが乾くにつれてゆっくり下がっていく様子を、シリアルモニタで観察できます。

雨センサーは安価ですが、分圧回路、コンパレーター、ヒステリシス、そして腐食のような現実の問題を一度に学べる、学びの多いモジュールです。パッドには測定中だけ電源を入れること、しきい値は自分の読み取り値から決めることを覚えておけば、長く活躍してくれます。BlueGraysアプリには、ブザー、サーボモーター、スマート水やりステーション、ESP32のTelegramアラームなど、この記事の部品を使った回路図とコード付きのステップごとのプロジェクトがあり、そこに雨センサーを簡単に追加できます。

よくある質問

雨センサーはどのように動作しますか?

検知パッド上の互い違いに並んだ2本の導電パターンは、乾いているときは互いに絶縁されています。雨粒がパターン間に橋を架けると、パッドの抵抗が下がり、制御基板上の分圧回路の出力(AO)が下がります。LM393コンパレーターはこの電圧をポテンショメーターで設定したしきい値と比較し、しきい値を超えるとDO出力をLOWにします。

雨センサーで降水量を測れますか?

いいえ。雨センサーが示すのは、パッドのどれだけの面積が濡れているかです。雨が降り始めたことやおおよその強さを知るには十分ですが、ミリメートル単位の降水量はわかりません。雨の量を測るには、傾くたびにリードスイッチでパルスを出す転倒ます型雨量計を使います。

雨センサーはなぜ腐食するのですか?どれくらい持ちますか?

パッドに直流をかけ続けると、パターン間の水の中で電気分解が起こり、プラス側のパターンの金属が溶け出します。常に5 Vがかかった状態で屋外に置いたパッドは、数週間で目に見えて劣化することがあります。デジタルピンからモジュールに電源を供給し、1回の読み取りにつき数ミリ秒だけオンにすれば、パッドはずっと長持ちし、何か月も問題なく動くことも珍しくありません。

雨が降ると、雨センサーの出力はLOWとHIGHのどちらになりますか?

一般的なLM393モジュールでは、乾いているときDO出力はHIGHです。パッド上の水がポテンショメーターで設定したしきい値を超えるとLOWになり、DO LEDが点灯します。AO出力は乾いているときに最も高く、パッドが濡れるにつれて下がります。Arduino Unoでは、乾いたパッドの値は通常1000を超えます。

雨センサーはESP32や3.3 Vで使えますか?

はい、モジュールは3.3〜5 Vで動作します。ESP32とRaspberry Pi Picoでは、AOとDOの出力も3.3 Vを超えないように、3.3 Vから電源を供給してください。ESP32では、Wi-Fiがオンの間も動作し続けるADC1のピン(GPIO 32〜39)のいずれかにAOをつないでください。

雨センサーは雪、露、霧を検知できますか?

乾いた雪は電気を通さないので、センサーが雪を検知するのは溶けてからです。一方、露や濃い霧はパッドに薄い水分の膜を残すことがあり、小さな濡れ具合の値が出て、ときには誤報の原因になります。これを防ぐには、しきい値を少し高めにし、連続した複数の読み取りでアラームを確認し、パッドを斜めに取り付けてください。

全プロジェクトを見る
ブザーの使い方
#13初級

ブザーの使い方

呼び鈴やアラームがどうやって音を出すのか、気になったことはありませんか?Arduinoが小さなブザーを短い間隔で鳴らし、音を出す、止める、を繰り返します。電気で音が生まれる仕組みを学び、メロディーを演奏するプロジェクトへの第一歩を踏み出せます。

4.7(89)
161
DHT11で温度と湿度を測定
#55中級

DHT11で温度と湿度を測定

部屋の温度は何度で、空気は湿っているでしょうか、それとも乾燥しているでしょうか?DHT11センサーが温度と湿度を測定し、Arduinoが2行のLCDディスプレイに値を表示して、数秒ごとに更新します。センサーに異常があればエラーメッセージが表示されます。自分だけの小さな気象観測装置を作る第一歩です。

4.7(47)
73
サーボモーターの使い方
#35初級
プレミアム

サーボモーターの使い方

小型サーボモーターのアームが、一方の端、中央、もう一方の端の3つの位置へ順番に回り、それぞれの位置で2秒間止まります。Arduinoが保持する角度をモーターに指示すると、モーターはその位置まで動いて止まります。ロボットアームや自動開閉するふたが動く仕組みを学べます。

4.7(48)
67
スマート水やりステーション
#83上級
プレミアム

スマート水やりステーション

旅行中も植物が枯れないよう、自動で水やりをするシステムです。土壌水分センサーで土の湿り具合を測定し、乾燥するとArduinoが給水ポンプを数秒間動かして、青いライトを点灯させます。水分が適切なときは緑のライトが点灯します。土がひどく乾燥すると、赤いライトが点滅し、ブザーが鳴って知らせます。

5.0(42)
57
炎センサーの使い方
#50中級
プレミアム

炎センサーの使い方

キッチンや作業場で、人より先に火に気づく小さな見張り役です。炎センサーは火が放つ目に見えない光を検出します。異常がないときは緑色のLEDが点灯し、炎を検出すると赤色のLEDが点灯してブザーが鳴ります。このシンプルな回路で火災感知器の基本的な仕組みを学べますが、実際の感知器の代わりにはなりません。

4.6(33)
44
ESP32動体検知アラーム(PIR + ブザー + LED + Telegram)
#145上級
プレミアム

ESP32動体検知アラーム(PIR + ブザー + LED + Telegram)

PIRセンサーが動きを検知するとブザーとLEDを作動させ、同時にTelegramで持ち主に即座に通知するスマート防犯アラームです。アラームが繰り返される頻度を抑えるため、30秒のクールダウン(待ち時間)を設けています。この仕組みはPIRの検知が正しいかを確認したり、誤検知をなくしたりするものではありません。家の防犯、ガレージの監視、倉庫や作業部屋などに使えます。ESP32-CAMなら写真付きの通知を追加でき、通常のESP32 DevKit V1ではテキストで通知します。

0.0(0)
9
ESP32 HTTPClient+天気API(LCD付き)
#133上級
プレミアム

ESP32 HTTPClient+天気API(LCD付き)

このプロジェクトでは、ESP32でHTTPClient.hを使い、インターネット経由で天気API(wttr.in)をREST APIとして呼び出します。返されたJSONレスポンスをArduinoJson v7で解析し、都市名、気温、湿度、天気、風の情報をI2C LCDディスプレイにトルコ語と英語で表示します。wttr.inは無料で、APIキーも不要です。都市名を入力するだけで、その都市の現在の天気を取得できます。30分ごとに自動更新し、表示画面ごとに異なるLEDの光り方で状態を知らせます。

4.7(13)
20