Arduino Uno R4 WiFiで結露検出|露点計算で“見えないリスク”を可視化する

設備まわりや窓際、ケース内部では、温度差と湿度の条件がそろうと結露が発生します。 見た目ではまだ濡れていなくても、空気の状態によっては「結露が発生しやすい状態」になっていることがあります。

今回は、Arduino Uno R4 WiFi を使い、「エレクトロニクスを簡単に」をテーマに温湿度と表面温度から結露リスクを判定し、LCD表示とLED通知を行うPoCを作ってみました。 ポイントは、単に湿度を見るのではなく、露点温度表面温度を比較して判定することです。露点は、空気を冷やしたときに相対湿度100%に達する温度で、表面温度が露点以下になると結露が起きやすくなります。
National Weather Service

今回は低電圧のPoCです。商用電源機器や実設備を直接制御する構成ではありません。実運用へ進める場合は、安全設計や絶縁、保護回路を十分に検討してください。

目次

まず「結露」をどう考えるか

結露とは、簡単に言うと空気中の水分が、冷えた表面で水滴になる現象です。
ここで大事なのは、空気がどれだけ湿っているかだけではなく、その空気に触れている表面がどれだけ冷たいかです。

たとえば室内の湿度がそれほど高くなくても、

  • 金属ケースの表面だけ冷えている

  • 窓ガラスが外気で冷やされている

  • 機器の一部が局所的に冷えている

といった条件が重なると、その表面だけ結露することがあります。

そのため今回は、以下の2つを別々に測定します。

  • 空気の状態
    → 気温、湿度、露点

  • 対象表面の状態
    → 表面温度

そして、表面温度が露点に近いか、下回っているかを見て判定します。

構成

使用した主な部品は以下です。

  • Arduino Uno R4 WiFi

  • DHT11系 温湿度センサー

  • サーミスタ (10kΩ NTC)

  • I2C LCD 1602

  • 赤LED/緑LED

  • 抵抗、ブレッドボード、ジャンパーワイヤ

仕組み

今回の判定ロジックはシンプルです。

  1. 温湿度センサーで気温湿度を取得

  2. その値から露点温度を計算

  3. サーミスタで表面温度を取得

  4. 表面温度と露点を比較して判定

判定ルールは次のようにしました。

  • 表面温度 <= 露点結露状態

  • 表面温度 <= 露点+1℃結露リスク

  • それ以外 → 通常

DHT11系センサーは手軽な反面、高精度用途向けではないため、PoCでは、検出の遅れを防ぐために、やや早めに反応するよう余裕幅を持たせています。DHT11は3.3~5.5V 動作で、測定間隔は2秒以上が推奨されています。
DHT11 Datashee

Arduino Uno R4 WiFi を使う理由

Arduino Uno R4 WiFiは、Renesas RA4M1 (48MHz Arm Cortex-M4、256KB Flash、32KB SRAM) を搭載し、さらにESP32-S3 により Wi-Fi / Bluetooth に対応しています。 ローカル表示だけのPoCから、将来的なMQTT送信やWebダッシュボード連携まで広げやすいのが魅力です。
Arduino Documentation

開発環境は PlatformIO + VS Codeを使えます。ボード設定は board = uno_r4_wifi、プラットフォームは renesas-ra です。
PlatformIO
RA4M1-48MHz Arm Cortex-M4とLCDコントローラおよびHMI用静電容量式タッチセンサ搭載32ビットマイクロコントローラ| Renesas

配線

今回は、温湿度センサー・表面温度用サーミスタ・I2C LCD・LEDを Arduino Uno R4 WiFiに接続します。

前提
・DHT11 は3ピンのモジュール品 (VCC/GND/DATA) を想定
・LCD は I2C バックパック付き 1602を想定
・サーミスタは10k NTC を想定
・LED には電流制限抵抗を入れます

部品

端子

接続先

補足

DHT11モジュール

VCC

5V

モジュール前提

DHT11モジュール

GND

GND

DHT11モジュール

DATA

D2

3ピンモジュールならそのまま接続しやすい

I2C LCD 1602

VCC

5V

I2C バックパック付きLCD想定

I2C LCD 1602

GND

GND

I2C LCD 1602

SDA

SDA

Uno R4 WiFi のSDA ピンへ

I2C LCD 1602

SCL

SCL

Uno R4 WiFi のSCL ピンへ

サーミスタ (10kΩ NTC)

片側

5V

分圧回路を構成

サーミスタ (10kΩ NTC)

もう片側

A010k抵抗の接続点

表面温度を測る入力点

固定抵抗 (10kΩ)

片側

A0 とサーミスタの接続点

サーミスタと分圧を構成

固定抵抗 (10kΩ)

もう片側

GND

緑LED

アノード

D6 経由で220~330Ω抵抗

抵抗を直列に入れる

緑LED

カソード

GND

赤LED

アノード

D7 経由で220~330Ω抵抗

抵抗を直列に入れる

赤LED

カソード

GND

サーミスタまわりの配線イメージ

サーミスタは単独ではなく、固定抵抗との分圧回路にして A0 へ入れます。今回のコードは10kΩサーミスタ+10kΩ固定抵抗を前提にしています。


5V
 |
[サーミスタ 10kΩ]
 |
 +------> A0
 |
[固定抵抗 10kΩ]
 |
GND

このA0に入る中点電圧を analogRead() で読み取り、抵抗値を逆算して表面温度ppを求めます。
元の表では「サーミスタ→A0」とだけ書かれていましたが、実際には分圧回路が必要です。

プログラム

以下は、結露判定と表示・LED通知を行う最小構成のサンプルです。

cpp

#include <Arduino.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <DHT.h>
#include <math.h>

#define DHTPIN 2
#define DHTTYPE DHT11

const int thermistorPin = A0;
const int greenLedPin = 6;
const int redLedPin = 7;

LiquidCrystal_I2C lcd(0x27, 16, 2);
DHT dht(DHTPIN, DHTTYPE);

const float SERIES_RESISTOR = 10000.0;
const float THERMISTOR_NOMINAL = 10000.0;
const float TEMPERATURE_NOMINAL = 25.0;
const float B_COEFFICIENT = 3950.0;
const float riskMarginC = 1.0;

unsigned long lastRead = 0;
const unsigned long readInterval = 2500;

enum State { SAFE, RISK, WET, ERROR_STATE };
State state = ERROR_STATE;

float readSurfaceTempC() {
  int adc = analogRead(thermistorPin);
  if (adc <= 0) adc = 1;
  if (adc >= 1023) adc = 1022;
  float resistance = SERIES_RESISTOR * (1023.0 / adc - 1.0);
  float steinhart = resistance / THERMISTOR_NOMINAL;
  steinhart = log(steinhart);
  steinhart /= B_COEFFICIENT;
  steinhart += 1.0 / (TEMPERATURE_NOMINAL + 273.15);
  steinhart = 1.0 / steinhart;
  return steinhart - 273.15;
}

float calcDewPoint(float t, float h) {
  const float a = 17.62;
  const float b = 243.12;
  float gamma = log(h / 100.0) + (a * t) / (b + t);
  return (b * gamma) / (a - gamma);
}

void setup() {
  Serial.begin(115200);
  pinMode(greenLedPin, OUTPUT);
  pinMode(redLedPin, OUTPUT);
  lcd.init();
  lcd.backlight();
  dht.begin();
}

void loop() {
  if (millis() - lastRead < readInterval) return;
  lastRead = millis();

  float airTemp = dht.readTemperature();
  float humidity = dht.readHumidity();
  float surfaceTemp = readSurfaceTempC();

  if (isnan(airTemp) || isnan(humidity) || humidity <= 0 || humidity > 100) {
    state = ERROR_STATE;
  } else {
    float dewPoint = calcDewPoint(airTemp, humidity);
    if (surfaceTemp <= dewPoint) state = WET;
    else if (surfaceTemp <= dewPoint + riskMarginC) state = RISK;
    else state = SAFE;

    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("A:"); lcd.print(airTemp, 0);
    lcd.print("C H:"); lcd.print(humidity, 0); lcd.print("%");

    lcd.setCursor(0, 1);
    lcd.print("S:"); lcd.print(surfaceTemp, 0);
    lcd.print(" DP:"); lcd.print(dewPoint, 0);

    digitalWrite(greenLedPin, state == SAFE);
    digitalWrite(redLedPin, state == RISK || state == WET);
  }

  if (state == ERROR_STATE) {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Sensor Error");
    lcd.setCursor(0, 1);
    lcd.print("Check wiring");
  }
}
ini

[env:uno_r4_wifi]
platform = renesas-ra
board = uno_r4_wifi
framework = arduino
monitor_speed = 115200
lib_deps =
  adafruit/DHT sensor library
  marcoschwartz/LiquidCrystal_I2C

LCDの表示内容

このサンプルでは、LCD1行目に空気の状態、2行目に表面温度と露点を表示します。

通常時の表示例

A:25C H:60%
S:18 DP:16

それぞれの意味は次の通りです。

  • AAir Temperature
    空気の温度です。この例では25℃。

  • HHumidity
    相対湿度です。この例では60%。

  • SSurface Temperature
    サーミスタで測った表面温度です。この例では18℃。

  • DPDew Point
    温度と湿度から計算した露点温度です。この例では16℃。

つまりこの例では、
・空気温度:25℃
・湿度:60%
・表面温度:18℃
・露点:16℃

となっていて、表面温度18℃は露点16℃より高いので、まだ結露状態ではありません。

エラー時の表示

センサー値が読めなかった場合は、LCDに次のように表示します。


Sensor Error
Check wiring

この表示になったときは、まず以下を確認します。

・DHT11 の VCC / GND / DATA 配線
・サーミスタの分圧回路
・LCD の I2C 配線
・GND の共通接続
・センサーの電源電圧

LEDが光る条件

LED は状態をざっくり見分けるための表示です。
このサンプルでは、緑LEDが通常、赤LEDが注意または結露を表します。

緑LEDが点灯するとき

緑LEDは、通常状態(SAFE)のときに点灯します。
条件は次の通りです。

表面温度 > 露点 + 1℃

つまり、表面温度にまだ十分な余裕があり、すぐには結露しにくい状態です。

例:
・露点 16℃
・表面温度 20℃
この場合は20 > 17なのでSAFEとなり、緑LEDが点灯します。

赤LEDが点灯するとき

赤LEDは、結露リスク(RISK)または結露状態(WET)のときに点灯します。

1.結露リスク(RISK)

条件:
表面温度 <= 露点 + 1℃
かつ
表面温度 > 露点

これは、まだ露点は下回っていないが、かなり近づいてる状態です。

例:
・露点 16℃
・表面温度 16.5℃
この場合、表面温度は露点+1℃以内に入っているため、赤LEDが点灯します。

2. 結露状態(WET)

条件:
表面温度 <= 露点

これは、表面温度が露点以下になっており、結露が起きやすい、またはすでに起きていてもおかしくない状態です。

例:
・露点 16℃
・表面温度 15℃
この場合はWETと判定され、赤LEDが点灯します。

プログラムのポイント

  • DHT11で空気の温度・湿度を取得

  • サーミスタで表面温度を取得

  • 露点を計算して結露のしやすさを判定

  • 通常時は緑LED

  • リスク時・結露時は赤LED

  • LCDに気温・湿度・表面温度・露点を表示

  • エラー時はLCDにセンサー異常メッセージを表示

単に湿度だけを見るより、露点と表面温度の差を見た方が、結露らしい振る舞いを追いやすくなります。
NationalWeather Service

サーミスタを手で温めたとき
サーミスタを冷えたペットボトルにくっつけたとき

改善案

今回のPoCを発展させるなら、次のような改善が考えられます。

  • LCDの2行目に SAFE / RISK / WET を文字で表示する

  • シリアルモニタにも判定結果を出してデバッグしやすくする

  • より高精度な温湿度センサーへ変更する

  • 閾値にヒステリシスを入れて判定の揺れを減らす

  • Wi-Fiでログを送信して履歴管理する

  • 複数箇所の温湿度・表面温度をまとめて監視する

  • 実際の水滴検知センサーを追加して二重判定にする

  • 無線連携として、Renesasは Wi-Fi 6/ Bluetooth LE対応のRA6シリーズも展開している。
    より低消費電力・高機能な常時接続デバイスへ広げていく方向性を考えるうえで参考になります。
    WiFi/BLE-MCU (RA6シリーズ)
    WiFi/BLEモジュール (RRQ61051)

まとめ

今回は、Arduino Uno R4 WiFi を使って結露検出を行うPoCを作ってみました。
構成としてはシンプルですが、

  • 温度計を読む

  • 表面温度を読む

  • 露点を計算する

  • 結露リスクを判定する

  • LCDとLEDで状態を知らせる

という流れを通して、結露を見える化して早めに対処する仕組みを試せます。

特に今回のサンプルでは、

  • LCDで数値の中身を確認できる

  • LEDで状態をひと目で把握できる

という形にしているため、まずはPoCとして動かしやすい構成です。

まずはローカルで動かし、そこからログ化や遠隔監視へ広げていくと面白いテーマだと思います。

参考リンク

Arduino UNO R4 WiFi 公式ドキュメント
PlatformIO: Arduino Uno R4 WiFi ボード情報
DHT11 Datasheet
Dew Point vs. Humidity | National Weather Service
RA4M1-48MHz Arm Cortex-M4とLCDコントローラおよびHMI用静電容量式タッチセンサ搭載32ビットマイクロコントローラ | Renesas
WiFi/BLE-MCU (RA6シリーズ)
WiFi/BLEモジュール (RRQ61051)