Learning Objectives
- 用 dht.DHT22 讀取溫度與溼度,並說明為什麼兩次量測要間隔 2 秒
- 用 try/except 處理感測器讀取失敗,讓程式不會停掉
- 用 ssd1306 在 OLED 上畫文字、線與方框,並理解 show() 的作用
- 用字串格式化把數值整理成固定格式,做出顯示溫溼度的小氣象站
讓開發板感覺環境,也讓人看得到結果
前幾課的輸入是按鈕與可變電阻,輸出是 LED 與燈條。這一課把兩個常用的模組接在一起:
- DHT22:溫溼度感測器,告訴程式現在幾度、溼度多少
- OLED 顯示器:128 × 64 像素的小螢幕,不用接電腦也能看到數值
這一課要回答:
- 怎麼從 DHT22 讀到溫度與溼度?讀不到的時候怎麼辦?
- 怎麼在 OLED 上顯示文字與圖形?
- 怎麼把數字整理成好讀的格式,做成一個小氣象站?
DHT22 與 DHT11
DHT 系列感測器只用一條資料線和開發板溝通(另外兩條是電源與 GND),用的是它自己的序列協定。
MicroPython 的 dht 模組已經幫你處理好協定的細節,而且可以接在任何一支 GPIO 上。
兩種常見型號的差別(依 Adafruit 的說明文件):
| DHT11 | DHT22(AM2302) | |
|---|---|---|
| 溫度範圍 | 0~50°C | -40~80°C |
| 溫度準確度 | ±2°C | ±0.5°C |
| 溼度範圍 | 20~80% | 0~100% |
| 最快量測頻率 | 每秒 1 次 | 每 2 秒 1 次 |
| 讀值 | 整數 | 有一位小數 |
兩種的用法一樣,只差在建立物件時寫 dht.DHT11 或 dht.DHT22。
逐步看:每 2 秒讀一次溫溼度
讀取分成兩步:先 measure() 請感測器量一次,再用 temperature()、humidity() 取出這次量到的值。
上方 DHT22 的讀值會隨時間慢慢上升(模擬房間變暖),注意每次 sleep(2) 之後讀到的數字。
讀不到的時候
感測器沒接好、線太長、電源不穩,measure() 都可能失敗,這時 MicroPython 會發生 OSError
(常見的訊息是 ETIMEDOUT,表示等不到感測器的回應)。如果沒有處理,整個程式會停下來,螢幕也不再更新。
放在牆上的氣象站不應該因為一次讀取失敗就停擺,所以把讀取包在 try/except 裡:
try:
sensor.measure()
t = sensor.temperature()
except OSError:
t = None # 這一次讀不到,下一輪再試
OLED:先畫,再 show()
常見的 0.96 吋 OLED 用 SSD1306 控制晶片,透過 I2C 匯流排和開發板連接,只要兩條訊號線(下一課會詳細介紹 I2C):
OLED ESP32
VCC ── 3.3V
GND ── GND
SCL ── GPIO18
SDA ── GPIO19
MicroPython 的 ESP32 硬體 I2C 第 0 組預設就是 SCL=GPIO18、SDA=GPIO19,也可以指定其他腳位。
這類 OLED 的 I2C 位址通常是 0x3C(十進位 60)。
ssd1306 模組的畫法和第 08 課的燈條很像:fill()、text()、rect() 這些函式只是畫在開發板記憶體裡的畫布上,
show() 才把整張畫布送到螢幕。內建字型每個字 8 × 8 像素,所以一行最多 16 個字、最多 8 行;
這套字型只有英文字母、數字與常用符號,不能直接顯示中文,所以螢幕上的單位寫成 C。
逐步看:在 OLED 上畫字與方框
第 8~13 行一直在畫,但上方的螢幕要等第 14 行 show() 之後才一次出現全部內容。座標從左上角 (0, 0) 開始,x 往右、y 往下。
把數字整理成固定格式
螢幕一行只有 16 個字,數字的位數一變,整行就會跳來跳去。字串的 format() 可以固定小數位數:
| 寫法 | 結果 |
|---|---|
"{:.1f}".format(26.43) |
26.4 |
"{:.1f}".format(26) |
26.0 |
"{:5.1f}".format(7.26) |
7.3(總寬 5,前面補空白) |
"Temp: {:.1f} C".format(26.43) |
Temp: 26.4 C |
下一個範例把前面兩件事組起來:每 2 秒讀一次、讀到就用固定格式顯示,讀不到就顯示 Sensor error。
逐步看:溫溼度小氣象站
每一輪都是「讀取 → 組字串 → 清畫面 → 畫字 → show()」。看上方:螢幕的數字跟著 DHT22 的讀值更新。
小測驗:感測器
執行 sensor.measure() 之後連續呼叫兩次 sensor.temperature(),兩次的結果是?
DHT22 的資料線沒接好,measure() 會怎樣?
下面的程式會印出什麼?
t = 26.46
print("Temp: {:.1f} C".format(t))
小測驗:OLED
程式執行了 oled.fill(0) 和 oled.text("Hi", 0, 0),但螢幕沒有出現 Hi,最可能少了哪一行?
內建字型每個字 8 × 8 像素,128 × 64 的 OLED 一行最多幾個字、最多幾行?
下面的程式會印出什麼?
lines = ["Temp: 26.4 C", "Hum: 60.0 %", "HOT!"]
for i, s in enumerate(lines):
if len(s) <= 16:
print(i * 16, s)
重點整理
sensor = dht.DHT22(Pin(4));先measure(),再temperature()、humidity()取值。DHT22 最快每 2 秒量一次。- 讀取失敗會發生
OSError,用try/except接住,下一輪再試,程式才不會停。 - OLED(SSD1306)走 I2C:
SSD1306_I2C(128, 64, I2C(0, scl=Pin(18), sda=Pin(19))),位址通常是 0x3C。 fill、text、hline、rect、fill_rect畫在記憶體裡,show()才更新螢幕;字 8 × 8 像素,一行 16 字、8 行,不能直接顯示中文。"{:.1f}".format(數值)固定小數位數,螢幕上的數字才不會跳動。
下一課深入 I2C:一條匯流排怎麼同時接好幾個裝置,以及怎麼直接讀感測器的暫存器。