第8章 ESP32 DHT11 / DHT22 完整教學:溫溼度感測、Sensor Error 與高溫警報器

發布日期: |更新日期:
文章主圖:第8章 ESP32 DHT11 / DHT22 完整教學:溫溼度感測、Sensor Error 與高溫警報器

前言:讓 ESP32 開始感知環境

前面的 ESP32 教學中,我們已經學會:

GPIO Output
GPIO Input
PWM
ADC
Touch Sensor

這些功能讓 ESP32 可以:

控制 LED
讀取按鍵
調整亮度
讀取類比訊號
感應觸摸

接下來開始進入真正的:

Sensor 感測器應用

最適合入門的環境 Sensor 之一,就是:

DHT11
DHT22

它們可以同時測量:

Temperature
+
Humidity

也就是:

溫度 + 相對濕度

只需要一個 Data GPIO,就可以讓 ESP32 取得兩種環境資訊。

因此非常適合拿來製作:

數位溫溼度計
室內環境監測器
智慧家庭 Sensor
高溫警報器
溫室監控
機房監測
IoT 環境節點
風扇自動控制器

本章將從最基本的:

DHT11 / DHT22
↓
接線
↓
Library
↓
readTemperature()
↓
readHumidity()
↓
Sensor Error
↓
Serial Monitor

一路完成:

ESP32 數位溫溼度計

並進一步製作:

ESP32 高溫警報器

一、DHT11 是什麼?

DHT11 是非常常見的低成本:

Digital Temperature and Humidity Sensor

可以同時測量:

Temperature
Humidity

DHT11 的優點是:

價格低
接線簡單
只需要一條 Data
Arduino / ESP32 Library 很成熟
適合入門教材

Adafruit 提供的 DHT11 規格資料顯示,其典型應用範圍約為:

溫度:
0~50°C
精度約 ±2°C

濕度:
20~80% RH
精度約 ±5% RH

最大取樣頻率約:

1 Hz

也就是最快約每秒讀取一次。

所以 DHT11 很適合:

Arduino / ESP32 入門
一般室內環境監測
教育實驗
非高精度用途

二、DHT22 是什麼?

DHT22 也是:

Temperature + Humidity Sensor

常見型號也包括:

AM2302

和 DHT11 相比,DHT22:

量測範圍更大
解析度更高
溫度精度較好
濕度範圍更廣

Adafruit 提供的資料顯示,DHT22 / AM2302 大致可量測:

Temperature:
-40~80°C
約 ±0.5°C

Humidity:
0~100% RH
約 ±2~5% RH

最大取樣頻率約:

0.5 Hz

也就是:

約每 2 秒讀取一次

因此如果專案對量測範圍與精度要求較高:

DHT22 通常會比 DHT11 更適合。

三、DHT11 與 DHT22 比較

DHT11 與 DHT22 / AM2302 規格比較
項目 DHT11 DHT22 / AM2302
溫度範圍 約 0~50°C 約 -40~80°C
溫度精度 約 ±2°C 約 ±0.5°C
濕度範圍 約 20~80% RH 約 0~100% RH
濕度精度 約 ±5% RH 約 ±2~5% RH
最大取樣頻率 約 1 Hz 約 0.5 Hz
成本 較低 較高
適合用途 教學、一般室內 較完整環境監測

如果只是:

ESP32 入門
+
室內溫溼度

DHT11 就可以。

如果希望:

範圍更廣
精度較好
負溫度量測

則建議選:

DHT22

四、DHT11 / DHT22 是數位感測器

這一點和上一章 ADC 很不一樣。

上一章 LDR 使用:

Analog Voltage
↓
ADC
↓
0~4095

DHT11 / DHT22 則是:

Sensor
↓
Digital Communication
↓
ESP32 GPIO

所以不需要:

analogRead();

而是利用 DHT Library 與 Sensor 通訊。

概念:

ESP32
 │
GPIO
 │
Data
 │
DHT11 / DHT22

DHT Sensor 會把:

Humidity
Temperature
Checksum

經由單一 Data Line 傳回 ESP32。

五、DHT Sensor 為什麼不像普通 digitalRead()?

DHT 的 Data Pin 雖然也是:

HIGH / LOW

訊號,

但它並不是:

digitalRead(DHT_PIN);

一次就可以取得溫度。

DHT Sensor 使用特定的:

Timing Protocol

資料是透過一連串精確時間長度的脈波傳輸。

因此實際程式通常交給:

DHT Library

負責處理。

這也是為什麼初學者不需要自己分析數十個微秒級 Pulse。

Adafruit DHT Library 目前的底層程式會根據不同 DHT 型號產生啟動訊號,再分析 Sensor 回傳的脈波資料。

六、本章使用哪一套 DHT Library?

本章使用:

DHT sensor library by Adafruit

Arduino IDE 可以直接安裝。

Adafruit 官方目前的教學方式仍然是:

Arduino IDE
↓
Library Manager
↓
搜尋 DHT
↓
DHT sensor library by Adafruit

另外還需要:

Adafruit Unified Sensor

作為 Dependency。

七、Arduino IDE 安裝 DHT Library

在 Arduino IDE:

Sketch
↓
Include Library
↓
Manage Libraries

或者直接開啟:

Library Manager

搜尋:

DHT sensor library

找到:

DHT sensor library
by Adafruit

安裝。

接著搜尋:

Adafruit Unified Sensor

再安裝:

Adafruit Unified Sensor

目前 Adafruit 官方 DHT Library 仍明確列出:

Adafruit Unified Sensor Driver 為 Dependency。

八、DHT11 常見腳位

裸的 4 Pin DHT11 / DHT22 通常具有:

VCC
DATA
NC
GND

從 Sensor 正面看,常見排列概念為:

Pin 1
VCC

Pin 2
DATA

Pin 3
NC

Pin 4
GND

但正式接線仍應以:

你手上 Sensor 的 Datasheet / Module 標示

為準。

如果是三腳 Module,

常見標示:

VCC
DATA / OUT / S
GND

而且很多 DHT Module 已經內建 Data Pull-up 電阻。

九、裸 Sensor 為什麼要 Pull-up 電阻?

Adafruit 的 DHT 接線範例建議裸 Sensor 在:

DATA

與:

VCC

之間加入:

約 10KΩ Pull-up

例如:

3.3V
 │
 ├──────── DHT VCC
 │
10KΩ
 │
 └──────── DHT DATA
              │
              └──── ESP32 GPIO

如果使用現成三腳 DHT Module,

模組上通常可能已經有:

Pull-up Resistor

此時通常不用另外再裝一顆。

十、本章 DHT 接線

本章使用:

DHT DATA → GPIO4

ESP32:

3.3V
GPIO4
GND

DHT Module:

VCC
DATA
GND

接線表:

ESP32 與 DHT11 / DHT22 接線表
ESP32 DHT11 / DHT22
3.3V VCC
GPIO4 DATA
GND GND

如果使用裸 4 Pin Sensor:

DATA
↔
VCC

之間加入約:

10KΩ

Pull-up。

ESP32 是 3.3V Logic 系統,因此本教材使用:

3.3V

供電與 Data Pull-up,避免讓 ESP32 GPIO Data Line 被拉到 5V。

十一、第一個 DHT11 程式

程式首先需要:

#include <DHT.h>

然後定義:

#define DHTPIN 4
#define DHTTYPE DHT11

建立 Sensor:

DHT dht(
  DHTPIN,
  DHTTYPE
);

在:

setup()

執行:

dht.begin();

十二、ESP32 DHT11 基本程式

#include <DHT.h>

#define DHTPIN 4
#define DHTTYPE DHT11

DHT dht(
  DHTPIN,
  DHTTYPE
);

void setup()
{
  Serial.begin(115200);

  dht.begin();

  Serial.println(
    "ESP32 DHT11 Start"
  );
}

void loop()
{
  delay(2000);

  float humidity =
    dht.readHumidity();

  float temperature =
    dht.readTemperature();

  Serial.print(
    "Temperature = "
  );

  Serial.print(
    temperature
  );

  Serial.println(
    " °C"
  );

  Serial.print(
    "Humidity = "
  );

  Serial.print(
    humidity
  );

  Serial.println(
    " %"
  );
}

十三、readTemperature()

讀取 Celsius:

float temperature =
  dht.readTemperature();

預設取得:

°C

例如:

Temperature = 27.3 °C

Adafruit DHT Library 目前 readTemperature() 預設以 Celsius 回傳;如果傳入 true 則可轉換成 Fahrenheit。

十四、讀取華氏溫度

也可以:

float fahrenheit =
  dht.readTemperature(true);

例如:

Temperature = 81.1 °F

本系列教材以:

Celsius °C

為主。

十五、readHumidity()

讀取相對濕度:

float humidity =
  dht.readHumidity();

例如:

Humidity = 63.2 %

這裡的 % 指:

Relative Humidity

也就是:

% RH

十六、什麼是 Relative Humidity?

Humidity 通常不是「空氣裡有多少克水」這麼單純。

DHT Sensor 回傳的是:

Relative Humidity

也就是:

相對濕度

例如:

50% RH

代表目前空氣中的水氣含量相對於該溫度下飽和水氣能力的比例約為 50%。

一般環境監控常會看到:

Temperature
27.5°C

Humidity
65%

這兩個值應一起觀察。

十七、DHT11 / DHT22 不能一直瘋狂讀取

這是非常常見的錯誤。

不要寫:

void loop()
{
  float t =
    dht.readTemperature();

  float h =
    dht.readHumidity();
}

讓 ESP32 每秒嘗試讀取成千上萬次。

因為 DHT 是:

Low Sampling Rate Sensor

DHT11 硬體最大約:

1 Hz

DHT22 最大約:

0.5 Hz

Adafruit 官方範例也採用約:

delay(2000);

再進行下一次讀取。

十八、本系列統一使用 2 秒讀取一次

本章不管使用:

DHT11

或:

DHT22

統一使用:

const unsigned long
READ_INTERVAL = 2000;

也就是:

2 秒

原因有兩個。

第一:

DHT22

本來就只適合約每 2 秒更新一次。

第二,目前 Adafruit DHT Library 本身:

MIN_INTERVAL

仍設定為:

2000 ms

用來避免過於頻繁地重新讀取 Sensor。

因此用:

2 seconds

最簡單,也能同時適用 DHT11 與 DHT22。

十九、DHT22 程式如何修改?

如果原本:

#define DHTTYPE DHT11

改成:

#define DHTTYPE DHT22

其他大部分程式可以保持不變。

例如:

#include <DHT.h>

#define DHTPIN 4
#define DHTTYPE DHT22

DHT dht(
  DHTPIN,
  DHTTYPE
);

這就是使用 DHT Library 最大的優點之一。

同一套:

readTemperature();
readHumidity();

可以支援不同 DHT Sensor。

Adafruit 官方範例也是透過切換:

DHTTYPE

來選擇 DHT11 或 DHT22。

二十、什麼是 Sensor Error?

實際使用 DHT 時,可能會碰到:

Failed to read from DHT sensor

原因可能很多:

接線錯誤
Sensor 沒有供電
DATA Pin 錯誤
DHTTYPE 設錯
Pull-up 不正確
讀取 Timing 失敗
線路太長
Sensor 故障
電源不穩

這時:

dht.readTemperature();

或:

dht.readHumidity();

可能回傳:

NaN

二十一、NaN 是什麼?

NaN:

Not a Number

意思就是:

這次沒有取得有效的數值。

所以不能只寫:

Serial.println(
  temperature
);

還要檢查:

isnan();

例如:

if (
  isnan(temperature)
)
{
  Serial.println(
    "Temperature Error"
  );
}

Adafruit 官方 DHT 範例同樣使用 isnan() 判斷 Temperature / Humidity 讀取是否失敗。

二十二、正確的 Sensor Error 判斷

推薦:

float humidity =
  dht.readHumidity();

float temperature =
  dht.readTemperature();


if (
  isnan(humidity)
  ||
  isnan(temperature)
)
{
  Serial.println(
    "Failed to read from DHT sensor!"
  );

  return;
}

如果任何一個:

Temperature
Humidity

無效,

本次資料就不要繼續處理。

二十三、為什麼不要用錯誤資料控制設備?

假設未檢查:

NaN

就直接:

Temperature
↓
Relay
↓
Fan

當 Sensor 失效時,

控制邏輯可能變得不可靠。

因此真正的 Sensor 系統應該是:

Read Sensor
↓
Validate Data
↓
Valid?
├── YES → Process
└── NO  → Error Handling

這是非常重要的工程習慣。

二十四、實作一:ESP32 數位溫溼度計

第一個正式實作:

ESP32 Digital Thermometer & Hygrometer

雖然目前沒有 LCD,

但 Serial Monitor 就是第一個顯示介面。

流程:

ESP32
↓
DHT11 / DHT22
↓
Temperature
Humidity
↓
Sensor Error Check
↓
Serial Monitor

二十五、ESP32 數位溫溼度計完整程式

/*
  第 8 章
  ESP32 DHT11 / DHT22

  實作:
  ESP32 數位溫溼度計

  DHT DATA:
  GPIO4

  Library:
  DHT sensor library by Adafruit

  Dependency:
  Adafruit Unified Sensor
*/


#include <DHT.h>


// -----------------------------
// DHT
// -----------------------------
const uint8_t DHT_PIN = 4;


// DHT11
#define DHT_TYPE DHT11

// 如果使用 DHT22,改成:
// #define DHT_TYPE DHT22


DHT dht(
  DHT_PIN,
  DHT_TYPE
);


// -----------------------------
// Read Interval
// -----------------------------
const unsigned long
READ_INTERVAL = 2000;


unsigned long
lastReadTime = 0;


// -----------------------------
// setup()
// -----------------------------
void setup()
{
  Serial.begin(
    115200
  );


  delay(
    500
  );


  Serial.println();
  Serial.println(
    "ESP32 DHT Temperature"
  );

  Serial.println(
    "& Humidity Monitor"
  );


  dht.begin();


  Serial.println(
    "DHT Sensor Ready"
  );
}


// -----------------------------
// loop()
// -----------------------------
void loop()
{
  unsigned long
  currentTime =
    millis();


  if (
    currentTime
    - lastReadTime
    >= READ_INTERVAL
  )
  {
    lastReadTime =
      currentTime;


    // -------------------------
    // Read Sensor
    // -------------------------
    float humidity =
      dht.readHumidity();


    float temperature =
      dht.readTemperature();


    // -------------------------
    // Sensor Error
    // -------------------------
    if (
      isnan(humidity)
      ||
      isnan(temperature)
    )
    {
      Serial.println(
        "DHT Sensor Error!"
      );

      return;
    }


    // -------------------------
    // Display
    // -------------------------
    Serial.println(
      "--------------------"
    );


    Serial.print(
      "Temperature : "
    );

    Serial.print(
      temperature,
      1
    );

    Serial.println(
      " °C"
    );


    Serial.print(
      "Humidity    : "
    );

    Serial.print(
      humidity,
      1
    );

    Serial.println(
      " %RH"
    );
  }
}

readHumidity()、readTemperature() 與 isnan() 都是 Adafruit 官方 DHT 範例目前採用的基本讀取方式。

二十六、為什麼正式版不用 delay(2000)?

最簡單的範例可以:

delay(2000);

但是本章正式程式改成:

millis();

原因是:

delay(2000)
↓
ESP32 整段停住 2 秒

未來如果加入:

Button
Touch
Wi-Fi
MQTT
Web Server
LED Animation
Relay

就會受到影響。

使用:

millis();

則可以:

每 2 秒讀 DHT

同時:

loop()

仍然可以執行其他工作。

二十七、millis() 讀取架構

基本公式:

if (
  currentTime
  - lastReadTime
  >= READ_INTERVAL
)
{
  lastReadTime =
    currentTime;

  // Read Sensor
}

這種寫法非常重要。

因為後面的 IoT 程式通常變成:

loop()
├── 每 20ms 處理 Button
├── 每 100ms 更新 Display
├── 每 2s 讀 DHT
├── 每 5s 上傳 MQTT
└── 持續處理 Wi-Fi

而不是大量:

delay();

二十八、Serial Monitor 顯示格式

本章建議:

--------------------
Temperature : 27.4 °C
Humidity    : 62.7 %RH

這比:

27.4
62.7

更容易 Debug。

真正工程專案的 Serial Output 最好帶:

名稱
數值
單位
錯誤
狀態

例如:

Temperature : 27.4 °C
Humidity    : 62.7 %RH
Sensor      : OK

二十九、如何顯示 Sensor Status?

可以增加:

Serial.println(
  "Sensor      : OK"
);

如果 Error:

Serial.println(
  "Sensor      : ERROR"
);

這樣未來做:

LCD
OLED
Web Dashboard
MQTT

就可以直接使用相同概念。

三十、DHT11 為什麼看起來數值變化很慢?

因為 DHT11 / DHT22 本來就是:

低速環境 Sensor

Adafruit 也直接形容這類 Sensor 的更新速度較慢,DHT11 約每秒一次,DHT22 約每兩秒一次。

所以它適合:

室內溫度
室內濕度
環境監控

不適合:

高速溫度變化
高速控制回授
快速資料擷取

如果專案要求高速,

應使用其他 Sensor。

三十一、DHT11 與 DHT22 怎麼選?

如果是:

ESP32 入門教材

選:

DHT11

原因:

便宜
容易取得
容易理解

如果是:

比較正式的環境監測

建議:

DHT22

因為:

量測範圍更廣
溫度精度較好
濕度範圍更完整

三十二、DHT Sensor 適合精密量測嗎?

不適合把 DHT11 視為:

實驗室精密溫溼度計

DHT11 本身就是:

Low Cost Sensor

如果專案需要:

高精度
高穩定性
長期校準
工業等級

應該選用更高階 Sensor。

DHT11 / DHT22 更適合:

教育
IoT Demo
一般環境監測
Threshold Control
趨勢觀察

三十三、進階實作:ESP32 高溫警報器

現在把 DHT Sensor 從:

只是顯示數字

升級成:

自動控制系統

加入:

LED
+
Passive Buzzer

流程:

DHT
↓
Temperature
↓
ESP32
↓
Threshold
↓
高溫?
├── NO → Normal
└── YES → LED + Buzzer

三十四、高溫警報器材料

ESP32 DHT 高溫警報器材料清單
元件 數量
ESP32 1
DHT11 或 DHT22 1
LED 1
220Ω~330Ω 電阻 1
Passive Buzzer 1
麵包板 1
杜邦線 若干

本章使用:

DHT DATA
→ GPIO4

Alarm LED
→ GPIO18

Passive Buzzer
→ GPIO23

三十五、高溫警報接線

DHT

ESP32 3.3V
→ DHT VCC

ESP32 GPIO4
→ DHT DATA

ESP32 GND
→ DHT GND

LED

GPIO18
 │
220Ω
 │
LED
 │
GND

Passive Buzzer

概念:

GPIO23
 │
Buzzer
 │
GND

但實際 Buzzer 的:

工作電壓
驅動電流

要先查 Datasheet。

如果需要的電流超過 GPIO 適合直接驅動的範圍,

應改成:

GPIO23
↓
Transistor / Driver
↓
Buzzer

三十六、設定高溫 Threshold

例如:

const float
HIGH_TEMP_THRESHOLD =
  30.0;

代表:

Temperature
>= 30.0°C

啟動警報。

實際應用當然可以改成:

28°C
35°C
40°C
50°C

依設備需求決定。

三十七、最簡單的高溫判斷

if (
  temperature
  >= HIGH_TEMP_THRESHOLD
)
{
  // Alarm
}
else
{
  // Normal
}

例如:

27°C
↓
Normal

30°C
↓
Alarm

35°C
↓
Alarm

三十八、為什麼最好加入 Hysteresis?

假設 Threshold:

30°C

Sensor 在附近變成:

29.9
30.1
29.8
30.2
29.9

那麼 Alarm 就會:

ON
OFF
ON
OFF

一直跳。

解決方式:

Hysteresis

例如:

30°C
→ Alarm ON

28.5°C
→ Alarm OFF

中間:

28.5~30°C

保持原來狀態。

三十九、高溫警報 Hysteresis

設定:

const float
ALARM_ON_TEMP = 30.0;

const float
ALARM_OFF_TEMP = 28.5;

邏輯:

if (
  !alarmState
  &&
  temperature
  >= ALARM_ON_TEMP
)
{
  alarmState = true;
}

以及:

if (
  alarmState
  &&
  temperature
  <= ALARM_OFF_TEMP
)
{
  alarmState = false;
}

這樣會穩定很多。

四十、進階實作:ESP32 高溫警報器完整程式

/*
  第 8 章
  ESP32 DHT11 / DHT22

  進階實作:
  ESP32 高溫警報器

  功能:
  1. DHT 溫度讀取
  2. DHT 濕度讀取
  3. Sensor Error Check
  4. 2 秒非阻塞式讀取
  5. Serial Monitor
  6. LED 高溫警報
  7. Passive Buzzer 警報
  8. Temperature Hysteresis

  GPIO:
  DHT DATA -> GPIO4
  LED      -> GPIO18
  Buzzer   -> GPIO23

  Arduino-ESP32:
  3.x LEDC API
*/


#include <DHT.h>


// =====================================
// DHT
// =====================================
const uint8_t DHT_PIN =
  4;


#define DHT_TYPE DHT11

// 使用 DHT22 時改成:
// #define DHT_TYPE DHT22


DHT dht(
  DHT_PIN,
  DHT_TYPE
);


// =====================================
// Alarm Output
// =====================================
const uint8_t LED_PIN =
  18;

const uint8_t BUZZER_PIN =
  23;


// =====================================
// Temperature Threshold
// =====================================
const float
ALARM_ON_TEMP =
  30.0;


const float
ALARM_OFF_TEMP =
  28.5;


bool alarmState =
  false;


// =====================================
// Sensor Interval
// =====================================
const unsigned long
READ_INTERVAL =
  2000;


unsigned long
lastReadTime =
  0;


// =====================================
// Buzzer Frequency
// =====================================
const uint32_t
BUZZER_FREQ =
  2000;


const uint8_t
BUZZER_RESOLUTION =
  8;


// =====================================
// Alarm Output
// =====================================
void updateAlarm()
{
  if (
    alarmState
  )
  {
    digitalWrite(
      LED_PIN,
      HIGH
    );


    ledcWriteTone(
      BUZZER_PIN,
      2000
    );
  }
  else
  {
    digitalWrite(
      LED_PIN,
      LOW
    );


    ledcWriteTone(
      BUZZER_PIN,
      0
    );
  }
}


// =====================================
// setup()
// =====================================
void setup()
{
  Serial.begin(
    115200
  );


  delay(
    500
  );


  Serial.println();
  Serial.println(
    "ESP32 DHT High"
  );

  Serial.println(
    "Temperature Alarm"
  );


  // -----------------------------
  // DHT
  // -----------------------------
  dht.begin();


  // -----------------------------
  // LED
  // -----------------------------
  pinMode(
    LED_PIN,
    OUTPUT
  );


  digitalWrite(
    LED_PIN,
    LOW
  );


  // -----------------------------
  // Passive Buzzer
  // -----------------------------
  if (!ledcAttach(
        BUZZER_PIN,
        BUZZER_FREQ,
        BUZZER_RESOLUTION))
  {
    Serial.println(
      "Buzzer LEDC Error!"
    );
  }


  ledcWriteTone(
    BUZZER_PIN,
    0
  );


  Serial.println(
    "System Ready"
  );
}


// =====================================
// loop()
// =====================================
void loop()
{
  unsigned long
  currentTime =
    millis();


  // -----------------------------
  // Sensor Interval
  // -----------------------------
  if (
    currentTime
    - lastReadTime
    < READ_INTERVAL
  )
  {
    return;
  }


  lastReadTime =
    currentTime;


  // -----------------------------
  // Read DHT
  // -----------------------------
  float humidity =
    dht.readHumidity();


  float temperature =
    dht.readTemperature();


  // -----------------------------
  // Sensor Error
  // -----------------------------
  if (
    isnan(humidity)
    ||
    isnan(temperature)
  )
  {
    Serial.println(
      "DHT Sensor Error!"
    );


    /*
      Sensor Error 時:
      本例關閉 Alarm Output。

      真正安全相關設備,
      應依 Fail-safe 需求決定
      Sensor Error 要採取什麼動作。
    */

    alarmState =
      false;


    updateAlarm();


    return;
  }


  // -----------------------------
  // Alarm ON
  // -----------------------------
  if (
    !alarmState
    &&
    temperature
    >= ALARM_ON_TEMP
  )
  {
    alarmState =
      true;
  }


  // -----------------------------
  // Alarm OFF
  // -----------------------------
  if (
    alarmState
    &&
    temperature
    <= ALARM_OFF_TEMP
  )
  {
    alarmState =
      false;
  }


  // -----------------------------
  // Update Output
  // -----------------------------
  updateAlarm();


  // -----------------------------
  // Serial Monitor
  // -----------------------------
  Serial.println(
    "-------------------------"
  );


  Serial.print(
    "Temperature : "
  );

  Serial.print(
    temperature,
    1
  );

  Serial.println(
    " °C"
  );


  Serial.print(
    "Humidity    : "
  );

  Serial.print(
    humidity,
    1
  );

  Serial.println(
    " %RH"
  );


  Serial.print(
    "Alarm       : "
  );


  Serial.println(
    alarmState
    ? "HIGH TEMPERATURE"
    : "NORMAL"
  );
}

四十一、高溫警報器執行流程

ESP32 開機:

setup()
↓
DHT begin()
↓
LED Output
↓
Buzzer LEDC
↓
System Ready

接著每:

2 seconds

執行:

DHT
↓
Temperature
Humidity
↓
Valid?

如果:

Sensor Error

則:

Error Handling

如果資料正常:

Temperature
↓
Threshold

四十二、Normal 狀態

例如:

Temperature
= 26.5°C

結果:

LED
OFF

Buzzer
OFF

Alarm
NORMAL

四十三、高溫狀態

例如:

Temperature
= 31.2°C

超過:

30.0°C

因此:

Alarm State
ON

輸出:

LED
ON

Buzzer
2000 Hz

Serial
HIGH TEMPERATURE

四十四、溫度下降時

Alarm 已經 ON,

不會在:

29.9°C

立即關閉。

而要等到:

Temperature
<= 28.5°C

才:

Alarm OFF

這就是:

Hysteresis(補充說明)

可以避免 Alarm 在臨界溫度附近快速切換。

四十五、Sensor Error 時到底應不應該關閉警報?

這取決於專案。

本章教育範例使用:

Sensor Error
↓
Alarm Output OFF

主要方便理解。

但如果是:

機房
鍋爐
工業設備
醫療
安全監控

Sensor Error 本身就可能應該:

觸發 Fault Alarm

例如:

Temperature Alarm
+
Sensor Fault Alarm

甚至:

Sensor Error
↓
進入 Fail-safe Mode

所以不要直接把教材中的 Error 行為套到安全關鍵系統。

四十六、如何把 Sensor Error 獨立成狀態?

更完整的系統可以定義:

STATE_NORMAL
STATE_HIGH_TEMP
STATE_SENSOR_ERROR

例如:

正常
→ Green

高溫
→ Red + Buzzer

Sensor Error
→ Yellow + Different Buzzer

這會比單純:

ON / OFF

更像真正產品。

四十七、常見錯誤:Failed to read from DHT sensor

如果看到:

DHT Sensor Error!

先依序檢查:

1. VCC
2. GND
3. DATA GPIO
4. DHTTYPE
5. Pull-up
6. Library
7. Sensor
8. 讀取間隔

不要第一時間就認為:

ESP32 壞掉。

四十八、常見錯誤:DHTTYPE 設錯

如果硬體是:

DHT11

程式卻:

#define DHT_TYPE DHT22

Sensor Data 解碼方式會不正確。

所以:

DHT11

要:

#define DHT_TYPE DHT11

DHT22:

#define DHT_TYPE DHT22

Adafruit 官方測試程式也是透過這個 Macro 選擇不同 Sensor。

四十九、常見錯誤:一直快速讀 DHT

不要:

loop()
↓
read
↓
read
↓
read
↓
read

Adafruit 目前官方範例仍然在兩次 Measurement 之間:

delay(2000);

而目前 Library 的讀取實作也具有 2000 ms Minimum Interval Cache。

本章建議:

READ_INTERVAL
=
2000 ms

五十、常見錯誤:三腳模組又另外亂加 Pull-up

如果使用:

DHT Module

很多模組 PCB 已經有:

Pull-up Resistor

通常不必另外再加。

如果使用:

裸 DHT Sensor

才應特別確認 Data Pull-up。

最可靠的方法:

看你手上模組的電路圖或 PCB。

五十一、常見錯誤:DHT Data Pull-up 到 5V

ESP32 GPIO 是:

3.3V Logic

因此不要把 DHT Data Line 透過 Pull-up:

拉到 5V

再直接接 ESP32 GPIO。

本章統一:

Sensor VCC
→ 3.3V

Data Pull-up
→ 3.3V

讓 GPIO 電壓保持在適合 ESP32 的邏輯範圍。

五十二、常見錯誤:溫度突然顯示 nan

例如:

Temperature = nan

這不是:

0°C

而是:

無效資料

程式應:

if (
  isnan(temperature)
)

判斷。

不能把 NaN 當真正 Sensor Data。

Adafruit 官方範例同樣會在任一 DHT Read 為 NaN 時直接回報 Failure。

五十三、如何同時讀攝氏與華氏?

float celsius =
  dht.readTemperature();

float fahrenheit =
  dht.readTemperature(true);

然後:

27.5°C
81.5°F

如果你的網站未來要提供國際使用者,

可以同時顯示:

°C / °F

五十四、DHT Library 還能計算 Heat Index

Adafruit DHT Library 另外提供:

computeHeatIndex();

例如:

float heatIndex =
  dht.computeHeatIndex(
    temperature,
    humidity,
    false
  );

其中:

false

代表使用 Celsius。

Adafruit 官方 DHTtester 範例目前也示範 Celsius / Fahrenheit Heat Index 計算。

這可以延伸做:

體感熱度
環境舒適度
高溫警告

但本章主軸仍然先放在:

Temperature
Humidity
Sensor Error

五十五、Serial Monitor 可以加入 Heat Index

例如:

Temperature : 30.5 °C
Humidity    : 75.2 %RH
Heat Index  : 34.7 °C

這比單純:

30.5°C

能提供更多環境資訊。

不過 Heat Index 是經公式計算的衍生數據,

不是 Sensor 直接量到第三個物理量。

五十六、ESP32 + DHT 可以加入 LCD1602 嗎?

可以。

架構:

DHT11 / DHT22
↓
ESP32
↓
Temperature + Humidity
↓
I²C
↓
LCD1602

顯示例如:

TEMP: 27.5 C
HUM : 63.2 %

這就會從:

Serial Monitor

正式變成:

獨立式數位溫溼度計

五十七、ESP32 + DHT + OLED

也可以使用:

SSD1306 OLED

顯示:

Temperature
Humidity
Trend
Alarm
Wi-Fi

例如:

TEMP
27.5 C

HUM
63.2 %

STATUS
NORMAL

五十八、ESP32 + DHT + Relay

前面高溫警報只是:

LED
Buzzer

再進一步:

Temperature
↓
Threshold
↓
Relay
↓
Fan

即可製作:

高溫自動風扇

例如:

>= 30°C
→ Fan ON

<= 28°C
→ Fan OFF

同樣使用:

Hysteresis(補充說明 2)

避免 Relay 一直跳。

五十九、注意 Relay 不要直接驅動大負載

ESP32 GPIO 只能輸出:

Control Signal

真正控制:

風扇
馬達
燈具
加熱器

應使用:

Relay Module
MOSFET
Driver
SSR

並依:

Voltage
Current
Isolation
Safety

正確設計。

六十、ESP32 + DHT + Wi-Fi

ESP32 相較 Arduino UNO 最大的優勢之一就是:

Wi-Fi

因此 DHT 專題下一步非常自然:

DHT11 / DHT22
↓
ESP32
↓
Wi-Fi
↓
Web Server / MQTT / Cloud

就可以建立:

IoT 溫溼度監控器

例如:

Room
↓
DHT22
↓
ESP32
↓
Wi-Fi
↓
MQTT
↓
Dashboard

六十一、ESP32 + DHT + Web Server

後續可以做:

ESP32 IP
↓
Browser
↓
Temperature
Humidity

例如:

ESP32 Environment Monitor

Temperature
27.4 °C

Humidity
62.5 %

Status
NORMAL

這樣手機直接用 Browser 就可以查看。

六十二、ESP32 + DHT + MQTT

進一步:

ESP32
↓
MQTT Broker

發布:

home/room1/temperature

home/room1/humidity

資料:

27.4
62.5

就能與:

Home Assistant
Node-RED
Server
Database
Dashboard

整合。

六十三、ESP32 + DHT + Database

資料流程:

DHT
↓
ESP32
↓
Wi-Fi
↓
API / MQTT
↓
Database

資料表:

Time
Temperature
Humidity

例如:

10:00  26.8  60.2
10:05  27.0  61.0
10:10  27.3  61.5

這樣就可以繪製:

Temperature Trend
Humidity Trend

六十四、ESP32 DHT 專案適合哪些用途?

室內環境監測

ESP32
+
DHT22
+
OLED

高溫警報器

DHT
↓
Threshold
↓
Buzzer

自動風扇

DHT
↓
Temperature
↓
Relay
↓
Fan

智慧溫室

Temperature
Humidity
↓
ESP32
↓
Fan
Pump
Cloud

機房環境監控

DHT22
↓
ESP32
↓
Wi-Fi
↓
Alert

不過正式機房或安全關鍵監測應選擇符合實際可靠度需求的工業級 Sensor,而不應把低成本 DHT 教學模組當唯一安全監控設備。

六十五、DHT11 / DHT22 不適合哪些場合?

不適合:

高精度實驗室量測
快速溫度回授
安全關鍵控制
極端環境
工業認證量測

DHT11 尤其屬於:

Low-cost Sensor

比較適合:

學習
IoT Prototype
一般環境趨勢
Threshold

DHT22 雖然精度較好,

但仍應依專案要求選擇 Sensor。

六十六、FAQ:ESP32 DHT11 / DHT22 常見問題

Q1:ESP32 可以使用 DHT11 嗎?

可以。

DHT11 使用單一 Digital Data Line,可以搭配 Adafruit DHT Library 由 ESP32 讀取 Temperature 與 Humidity。

Q2:ESP32 可以使用 DHT22 嗎?

可以。

程式只需要將:

#define DHT_TYPE DHT11

改成:

#define DHT_TYPE DHT22

即可使用相同的 readTemperature() 與 readHumidity() API。

Q3:DHT11 與 DHT22 哪個比較好?

DHT22:

溫度範圍更廣
濕度範圍更廣
溫度精度較佳

DHT11:

價格較低
適合基礎教學

Q4:ESP32 DHT 要多久讀一次?

本教材建議:

2000 ms

也就是:

2 秒

Adafruit 官方 DHTtester 目前也是等待約兩秒再進行下一次 Measurement,而目前 Library 內部的 Minimum Interval 也是 2000 ms。

Q5:為什麼 DHT 回傳 NaN?

NaN:

Not a Number

代表此次讀取失敗。

可能原因:

接線
供電
GPIO
Sensor Type
Pull-up
Timing
Sensor Error

應使用:

isnan();

檢查。

Q6:如何讀取溫度?

float temperature =
  dht.readTemperature();

取得 Celsius。

Q7:如何讀取濕度?

float humidity =
  dht.readHumidity();

取得 Relative Humidity。

Q8:DHT11 可以讀負溫度嗎?

DHT11 的一般額定溫度範圍約為:

0~50°C

若需要負溫度環境,

DHT22 的額定範圍約:

-40~80°C

更合適。

Q9:DHT11 精度是多少?

Adafruit 提供的規格整理約為:

Temperature
±2°C

Humidity
±5% RH

Q10:DHT22 精度是多少?

Adafruit 提供的資料約為:

Temperature
±0.5°C

Humidity
約 ±2~5% RH

Q11:DHT 可以做高溫警報嗎?

可以。

流程:

DHT
↓
Temperature
↓
Threshold
↓
LED / Buzzer

建議加入:

Hysteresis(補充說明 3)

避免溫度在 Threshold 附近波動造成警報不停切換。

Q12:DHT 可以和 Wi-Fi 同時使用嗎?

可以。

DHT 使用一般 Digital GPIO,

不像原始 ESP32 ADC2 存在 Wi-Fi 共用 ADC Hardware 的問題。

因此很適合製作:

DHT
+
ESP32 Wi-Fi
+
MQTT / Web Server

環境監控系統。

六十七、本章學習重點整理

第一個核心:

DHT11
DHT22

都是:

Digital Temperature + Humidity Sensor

第二個核心:

基本程式:

#include <DHT.h>

DHT dht(
  DHT_PIN,
  DHT_TYPE
);

初始化:

dht.begin();

第三個核心:

溫度:

dht.readTemperature();

濕度:

dht.readHumidity();

第四個核心:

DHT 是低速 Sensor

所以不要快速重複讀取。

本教材統一:

2 seconds

讀一次。

第五個核心:

每次 Sensor Data 都必須先:

isnan();

檢查。

架構:

Read
↓
Validate
↓
Process

而不是:

Read
↓
直接控制設備

第六個核心:

正式程式優先使用:

millis();

管理:

READ_INTERVAL

而不是整個程式一直:

delay(2000);

第七個核心:

高溫控制最好加入:

Hysteresis(補充說明 4)

例如:

30.0°C
→ Alarm ON

28.5°C
→ Alarm OFF

避免 Alarm 抖動。

六十八、本章總結

在第 8 章中,我們正式開始使用 ESP32 讀取:

數位環境感測器

首先比較:

DHT11
vs
DHT22

DHT11:

成本低
適合教學
0~50°C
20~80% RH

DHT22:

範圍較大
精度較佳
-40~80°C
0~100% RH

接著安裝:

DHT sensor library by Adafruit
+
Adafruit Unified Sensor

並使用:

dht.begin();

dht.readTemperature();

dht.readHumidity();

完成基本 Sensor Reading。

我們也特別學會一個非常重要的工程觀念:

Sensor Reading 不一定永遠成功

所以必須:

Sensor
↓
Read
↓
isnan()
↓
Valid?
├── YES → 使用資料
└── NO  → Sensor Error

而 DHT11 / DHT22 都屬於低速 Sensor,

本系列正式程式統一:

2000 ms

讀一次,以兼顧 DHT22 的 0.5 Hz 取樣限制與目前 Adafruit Library 的 2 秒 Minimum Interval 行為。

本章第一個實作完成:

ESP32 數位溫溼度計(補充說明)

架構:

DHT11 / DHT22
↓
ESP32
↓
Temperature
+
Humidity
↓
Sensor Error Check
↓
Serial Monitor

進階則完成:

ESP32 高溫警報器(補充說明)

架構:

DHT Sensor
↓
Temperature
↓
Threshold
+
Hysteresis
↓
ESP32
↓
LED
+
Passive Buzzer

至此,我們已經從:

GPIO
PWM
ADC
Touch

正式進入:

Environmental Sensor

階段。

接下來只要加入:

LCD / OLED
Relay
Wi-Fi
MQTT
Web Server
Database

就可以把這一章的 DHT 溫溼度計逐步擴充成真正完整的:

ESP32 IoT 智慧環境監控系統

下一步學習

不要停在這一篇,接著把內容做成自己的實作

目前主題:ESP32 / IoT。從完整學習路線、免費資源、會員下載,再接到實作所需材料。

同系列:ESP32 |  已是會員?直接前往 Getfile
相關資源與會員文章下載
文章可免費閱讀;下載完整 DOCX 或 PDF 需登入免費基本會員。
查看相關資源 會員免費下載 DOCX 會員免費 PDF/列印

← 回文章列表