會議室愈坐愈悶?阿明博士用 SCD41 看懂通風變化
內容整理:台灣智能感測科技|導讀角色:阿明博士
開會一段時間後,冷氣明明還在吹,房間卻愈來愈悶。只看溫度,有時不容易理解發生什麼事。這一篇,阿明博士把 SCD41 接上 ESP32-S3,讓你一起觀察 CO₂ 和溫濕度,看看人員使用與通風時的變化。
ESP32-S3 搭配 SCD41 可以查看 CO₂ 變化,並用 ESPHome 在本機判斷通風提醒;SHT20 則提供另一組環境溫濕度。CO₂ 反映的狀況與 PM2.5 不同,兩者不能互相代替。
- 同時查看 CO₂ 與環境溫濕度
- 讓通風提醒有開啟、解除門檻與等待時間
- 從使用人數和通風時段觀察趨勢
冷氣有開,不代表空氣交換一定足夠
SCD41 提供 CO₂ 量測,可以用來觀察人員活動與通風的趨勢。搭配 SHT20 的環境溫濕度,你會更容易把「房間熱不熱」和「CO₂ 怎麼變」分開看。這些資料可以協助安排開窗或通風,也適合做教室、工作室與會議室的入門觀察。
CO₂、粉塵與氣味是不同的事情。CO₂ 降下來,不代表 PM2.5 也一定降低;這也是後面還會介紹 PMS5003T 的原因。
提醒要有耐心,才不會一直跳來跳去
本課把提醒設成:CO₂ 達到 1000 ppm 並持續 60 秒時開啟;降到 800 ppm 以下、再維持 60 秒後解除。兩個門檻之間先保持原狀,避免數字剛好在邊界附近時,提醒不停切換。
這組數值是方便理解邏輯的起點,可以依場域調整。網頁同時提供資料更新狀態;接頭鬆脫或資料停更時,先處理讀取問題,再判斷環境。這一課先做查看與提醒,讓你掌握變化,之後再評估是否加入設備控制。
兩顆感測器,為什麼溫度不完全一樣?
SCD41 本身也提供溫濕度,但主板熱源、模組自熱、氣流與安裝位置,都可能造成差異。範例保留 SCD41 與 SHT20 各自的名稱,方便比較;不要急著把兩個值平均,或只因數字不一樣就判定其中一顆壞掉。
先避開呼氣正前方、窗邊直吹處與冷氣出風口,再固定位置觀察。SCD41 上的白色保護膜要保持完整。
進一步了解:SCD41 的校正設定
範例明確關閉自動自我校正 ASC,讓讀者先依使用空間評估校正方式。這不代表關閉就一定更準;日常是否會接觸符合條件的新鮮空氣,會影響 ASC 是否適合。關閉後,一樣要安排比對與維護。
不要對著感測器吹氣後,就把它強制設成 400 ppm。強制校正需要穩定、已知的參考濃度和原廠要求的運作條件,不能用隨手量到的數字代替。
先準備這些材料
| 材料 | 準備重點 |
|---|---|
| ESP32-S3 N16R8 | 主板、資料 USB 線、2.4GHz Wi-Fi |
| SHT20 I²C 模組 | 商品 6129,使用 3.3V 供電 |
| Grove SCD41 | 商品 60107;獨立穩定 3.3V,與主板共地 |
主板請確認為 WROOM-1-N16R8,範例使用 16MB Flash 與 Octal PSRAM。接線先斷電,表格中的 GPIO 是腳位名稱,不是排針從上往下數的位置。
照著圖,把線一條條接好
依名稱逐條對照接線

Grove SCD41 使用獨立穩定 3.3V,與主板共地。
| 感測器接腳 | ESP32-S3/電源 | 核對事項 |
|---|---|---|
| SHT20 VCC | 主板 3V3 | 本課限 3.3V I²C 模組,先核對實物 |
| SHT20 GND | 主板 GND | 所有訊號參考共地 |
| SHT20 SDA | GPIO8 | 上拉至 3.3V |
| SHT20 SCL | GPIO9 | 100kHz;先用短線 |
| Grove SCD41 VCC/GND | 獨立穩定 3.3V/共地 | 商品 60107;勿與主板 3V3 輸出並接 |
| SCD41 SDA/SCL | GPIO8/GPIO9 | 位址 0x62;不要依排針順序猜接腳 |
所有模組都要與主板共地。若使用外接電源,不要把外接 5V 和 USB 5V 直接並接,也不要把獨立 3.3V 穩壓器的輸出接到主板 3V3 輸出。先量好電壓,再接入訊號線。
把設定放進主板
- 下載整個 examples 資料夾,保留 components;把 secrets.yaml.example 複製成 secrets.yaml,填入自己的 Wi-Fi 與密碼。
- 依照Windows 燒錄指南安裝 ESPHome 2026.10.1,選擇本課 YAML,先檢查設定、再編譯與 USB 燒錄。
- 在 USB 日誌或路由器裝置清單找到主板 IP,瀏覽器輸入該 IP,再用 secrets 裡的網頁帳密登入。
以下命令在 examples 資料夾執行,COM5 請換成自己的埠號。USB 接頭、日誌與後續 OTA 更新的操作,都整理在共用指南。
..\.venv\Scripts\python.exe -m esphome config 02-scd41.yaml
..\.venv\Scripts\python.exe -m esphome compile 02-scd41.yaml
..\.venv\Scripts\python.exe -m esphome upload 02-scd41.yaml --device COM5
下載完整範例(含 02-scd41.yaml) · 下載完整範例(含 secrets.yaml.example)
完整 YAML:點此展開
esphome:
name: course-02-scd41
friendly_name: "SCD41 通風提醒"
min_version: 2026.10.1
compile_process_limit: 1
esp32:
board: esp32-s3-devkitc-1
flash_size: 16MB
framework:
type: esp-idf
psram:
mode: octal
speed: 80MHz
logger:
hardware_uart: USB_SERIAL_JTAG
level: DEBUG
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
power_save_mode: none
reboot_timeout: 0s
ota:
- platform: esphome
password: !secret ota_password
web_server:
port: 80
version: 3
local: true
auth:
type: digest
username: !secret web_username
password: !secret web_password
i2c:
id: sensor_bus
sda: GPIO8
scl: GPIO9
scan: true
frequency: 100kHz
external_components:
- source:
type: local
path: components
components: [sht20_local]
sensor:
- platform: sht20_local
id: sht20_driver
address: 0x40
update_interval: 10s
temperature:
id: sht_temperature
name: "SHT20 Temperature"
on_value:
then:
- lambda: |-
id(last_sht_ms) = millis();
id(sht_seen) = true;
humidity:
id: sht_humidity
name: "SHT20 Humidity"
on_value:
then:
- lambda: |-
id(last_rh_ms) = millis();
id(rh_seen) = true;
- platform: scd4x
id: scd41_driver
address: 0x62
measurement_mode: periodic
# Enable ASC only after evaluating the actual fresh-air exposure pattern.
automatic_self_calibration: false
update_interval: 10s
co2:
id: co2_value
name: "SCD41 CO2"
on_value:
then:
- lambda: |-
id(last_co2_ms) = millis();
id(co2_seen) = true;
temperature:
name: "SCD41 Internal Temperature"
humidity:
name: "SCD41 Internal Humidity"
globals:
- id: last_sht_ms
type: uint32_t
restore_value: false
initial_value: '0'
- id: sht_seen
type: bool
restore_value: false
initial_value: 'false'
- id: last_rh_ms
type: uint32_t
restore_value: false
initial_value: '0'
- id: rh_seen
type: bool
restore_value: false
initial_value: 'false'
- id: last_co2_ms
type: uint32_t
restore_value: false
initial_value: '0'
- id: co2_seen
type: bool
restore_value: false
initial_value: 'false'
binary_sensor:
- platform: template
name: "SHT20 Temperature Fresh"
lambda: |-
return id(sht_seen) && (uint32_t)(millis() - id(last_sht_ms)) < 35000;
- platform: template
name: "SHT20 Humidity Fresh"
lambda: |-
return id(rh_seen) && (uint32_t)(millis() - id(last_rh_ms)) < 35000;
- platform: template
name: "SCD41 Data Fresh"
lambda: |-
return id(co2_seen) && (uint32_t)(millis() - id(last_co2_ms)) < 35000;
- platform: template
name: "CO2 Ventilation Reminder"
lambda: |-
if (!id(co2_seen) || (uint32_t)(millis() - id(last_co2_ms)) >= 35000) return {};
if (id(co2_value).state >= 1000) return true;
if (id(co2_value).state <= 800) return false;
return {};
filters:
- delayed_on: 60s
- delayed_off: 60s
接好後,先試這幾件事
先從正常顯示開始,再試一次拔除、恢復或更換環境條件。這些小步驟有助於確認你看到的資料,確實來自現在的接線與位置。
| 測試項目 | 應觀察的結果 |
|---|---|
| 冷啟動 | 確認 0x62、CO₂ 有更新、資料新鮮度有效 |
| 門檻邏輯 | 核對 1000/800 ppm 的開啟與解除門檻,以及各 60 秒的等待時間 |
| 斷開 SCD41 | 35 秒後資料失效;不得把最後 CO₂ 當成即時值 |
| 正常使用與通風 | 保留時間序列與場域條件,觀察而不強行校正 |
遇到問題時,從這裡開始找
先查供電和共地,再核對端子名稱與訊號方向。I²C 裝置可看掃描位址;PMS5003T 走 UART,不會出現在 I²C 清單。接著確認設定的型號與最後一次更新時間,最後才檢查網頁或後端。資料停住時,不要先把它當成環境一直沒變。
材料與延伸閱讀
ESP32-S3 N16R8 主板 · SHT20 I²C 模組。依實際用途選擇所需感測器即可。
想看原廠文件與元件說明
常見問題
SCD41 能順便告訴我 PM2.5 嗎?
不能。SCD41 量測 CO₂ 與溫濕度,PM2.5 則需要對應的粒子感測器。本系列用 PMS5003T 處理懸浮微粒,兩類數據分開顯示。

發佈留言
很抱歉,必須登入網站才能發佈留言。