DHT11 + ThingSpeak 是物聯網實驗典型必做項目, 只要一個 DHT11 模組即可架構一個小小氣象測候站. 參考我之前在 Arduino 上所做的測試紀錄 :
# Arduino 溫濕度感測器 DHT11 測試
# 以 Arduino+ESP8266 物聯網模組重作 DHT11 溫溼度實驗
現在則是要把同樣的試驗移植到我的新歡 ~ MicroPython + ESP8266 上面, 這是因為 ESP8266 就是一個內建 WiFi 網路功能的 32 位元 MPU, 比起 8 位元的 Arduino 而言功能與效能強多了, 加上 MicroPython 與檔案系統的加持, 不僅使整個系統最小化, Python 程式可隨時改隨時上傳, 不需要像 Arduino 每次都要覆寫韌體, 操作介面與環境方便多了.
本系列之前的測試紀錄參考 :
# MicroPython on ESP8266 (二) : 數值型別測試
# MicroPython on ESP8266 (三) : 序列型別測試
# MicroPython on ESP8266 (四) : 字典與集合型別測試
# MicroPython on ESP8266 (五) : WiFi 連線與 WebREPL 測試
# MicroPython on ESP8266 (六) : 檔案系統測試
# MicroPython on ESP8266 (七) : 時間日期測試
# MicroPython on ESP8266 (八) : GPIO 測試
# MicroPython on ESP8266 (九) : PIR 紅外線移動偵測
# MicroPython on ESP8266 (十) : socket 模組測試
# MicroPython on ESP8266 (十一) : urllib.urequest 模組測試
# MicroPython on ESP8266 (十二) : urequests 模組測試
MicroPython 文件參考 :
# MicroPython tutorial for ESP8266 (官方教學)
# http://docs.micropython.org/en/latest/micropython-esp8266.pdf
# http://docs.micropython.org/en/latest/pyboard/library/usocket.html#class-socket
# http://docs.micropython.org/en/v1.8.7/esp8266/library/usocket.html#module-usocket
# https://gist.github.com/xyb/9a4c8d7fba92e6e3761a (驅動程式)
DHT11 模組有四隻腳, 包含電源 Vcc 與 GND, 數據輸出 Data 以及一隻用不到的腳 (在 I2C 模式才會用到). 電源 Vcc 可吃 3~5V, 因此吃 3.3V 的 ESP8266 可直用 (Data 腳可直連 GPIO 腳), 其接腳定義如下 :
為了使測量數據穩定, 最好在 Vcc 與 Data 腳之間並接一個 10K 歐姆電阻 (棕黑橙); 在 Vcc 與 GND 之間並接一個 0.1uF 電容 (104).
在 MicroPython 上使用 DHT11 很方便, 因為其功能已經被寫進 MicroPython 的內建模組 dht 了, 參考 :
# 12. Temperature and Humidity
首先匯入 dht 模組與 Pin 模組 (綁定哪一支 GPIO 腳擷取 DHT11 輸出之數據) :
import dht
from machine import Pin
接著就可以呼叫 dht 模組的 DHT 類別建構式來建立 DHT11 物件, 傳入要綁定之 GPIO 腳之 Pin 物件 :
d=dht.DHT11(Pin(0)) #綁定 GPIO 0 為 DHT11 數據輸入腳
不過在讀取溫濕度數據前必須先呼叫 DHT11 物件的 measure() 方法來擷取測量結果, 這樣才能讀取到最新的數據 :
d.measure() #更新測量結果
這樣就可以讀取溫溼度數據了 :
t=d.temperature() #傳回攝氏溫度
h=d.humidity() #傳回相對溼度 (%)
注意, DHT11 的測量頻率是至多每秒一次, 亦即 measure() 方法每秒只能呼叫一次 :
"The DHT11 can be called no more than once per second and the DHT22 once every two seconds for most accurate results."
下面程式以每兩秒一次頻率更新所測量到之溫溼度 :
測試 1 : 連續測量溫溼度
from machine import Pin
import dht
import time
p0=Pin(0, Pin.IN)
d=dht.DHT11(p0) #建立 DHT11 物件
while True:
d.measure() #重新測量溫溼度
t=d.temperature() #讀取攝氏溫度
h=d.humidity() #讀取相對溼度
print('Temperature=', t, 'C', 'Humidity=', h, '%')
time.sleep(2) #暫停 2 秒
將檔案存成 main.py 後用 ampy 等工具上傳 ESP8266 模組, 參考 :
# MicroPython on ESP8266 (六) : 檔案系統測試
然後開啟 Putty 按 Ctrl+D 軟開機即可看到每兩秒輸出的溫溼度測量結果 :
用吹風機吹一下, 就會看到濕度下降, 溫度上升的變化了.
接下來我想把溫濕度測量資料透過 WiFi 網路記錄在物聯網網站 ThingSpeak, 參考以前 Arduino 的測試紀錄 :
# ESP8266 WiFi 模組與 Arduino 連線測試 (ThingSpeak 初體驗)
# 以 Arduino+ESP8266 物聯網模組重作 DHT11 溫溼度實驗
我的 ThingSpeak 帳號已經設定好如下五個資料通道 (channels) 了 :
這裡會用到 field1 (攝氏溫度), field1 (華氏溫度) 與 field3 (濕度) 這三個通道, 由於 MicroPython 的 dht 模組的 DHT11.temperature() 僅提供攝氏溫度, 所以如果要記錄華氏溫度於 filed2 的話須另外用公式轉換, 換算公式如下 :
F=C*9/5 + 32
亦即華氏度數是攝氏的 9/5 倍再加 32.
物聯網伺服器 ThingSpeak 通道的 HTTP GET 寫入格式例如 :
GET /update?api_key=NO5N8C7T2KINFCQE&field1=29.00&field2=84.20&field3=73.00
亦即是利用 HTTP 的 GET 方法傳遞溫濕度資料給 ThingSpeak 伺服器的 update 程式執行資料庫寫入動作, 這裡 api_key 是我在 ThingSpeak 上一個帳號的的資料庫寫入認證碼 (讀取認證碼是另外一個, 用於向 ThingSpeak 讀取資料用).
此處我們要用 urequests 模組的 get() 方法向 ThingSpeak 伺服器提出 GET 請求, 必須將 url 傳進 get() 裡面, 因此必須知道 ThingSpeak 伺服器的 IP 或網域名稱, 可以使用 http://184.106.153.149 或 http://api.thingspeak.com, 這樣就可以組成 URL 字串了 :
host='http://api.thingspeak.com'
api_key='NO5N8C7T2KINFCQE'
url='%s/update?api_key=%s&field1=%s&field2=%s&field3=%s' %(host, api_key, t, f, h)
r=urequests.get(url)
這裡 t, f, h 就是上面測試 1 中取得的攝氏溫度, 華氏溫度, 以及濕度.
完整的程式如下列測試 2 :
測試 2 : 將溫濕度記錄在 ThingSpeak 網站
#main.py
ffrom machine import Pin
import dht
import time
import urequests
p0=Pin(0, Pin.IN)
d=dht.DHT11(p0)
host='http://api.thingspeak.com'
api_key='NO5N8C7T2KINFCQE'
while True:
d.measure()
t=d.temperature()
f=round(t * 9/5 + 32)
h=d.humidity()
url='%s/update?api_key=%s&field1=%s&field2=%s&field3=%s' %(host, api_key, t, f, h)
print('Temperature=', t, 'C', '/', f, 'F', 'Humidity=', h, '%')
#print('url=', url)
r=urequests.get(url)
print('response=', r.text)
time.sleep(16)
注意, 這裡迴圈裡的延遲改為 16 秒是為了符合 ThingSpeak 的要求, 即每個通道的資料更新週期必須大於 15 秒, 參考 :
# Data points and frequency
"there's no limit to the number of data points, but a Channel can only be updated every 15s"
將此程式存成 main.py 後用 ampy 上傳到 ESP8266 模組 :
D:\test>ampy --port COM4 put main.py
然後開啟 Putty, 按 Ctrl+D 軟啟動後就會重新執行剛上傳的 main.py.
REPL 介面輸出訊息如下 :
MicroPython v1.9.1-8-g7213e78d on 2017-06-12; ESP module with ESP8266
Type "help()" for more information.
>>>
PYB: soft reboot
#22 ets_task(40100164, 3, 3fff829c, 4)
Temperature= 31 C / 88 F Humidity= 38 %
response= 29884
Temperature= 31 C / 88 F Humidity= 37 %
response= 29885
Temperature= 31 C / 88 F Humidity= 37 %
response= 29886
Temperature= 31 C / 88 F Humidity= 37 %
response= 29887
Temperature= 31 C / 88 F Humidity= 37 %
response= 29888
Temperature= 31 C / 88 F Humidity= 43 %
response= 29889
Temperature= 31 C / 88 F Humidity= 37 %
response= 29890
Temperature= 31 C / 88 F Humidity= 37 %
response= 29891
Temperature= 31 C / 88 F Humidity= 37 %
response= 29892
這裡 response 為伺服器新增一筆紀錄後所傳回的該筆資料之 key, 可見已經儲存近 3 萬筆資料了. ThingSpeak 網頁上的溫濕度動態圖表如下 :
接下來為了在電腦關機時能知道整個程式還有在跑沒當掉, 我在 GPIO 2 腳上接了一個 220 歐姆電阻串接一個 LED 後接地, 然後參考下列這篇 :
# MicroPython on ESP8266 (九) : PIR 紅外線移動偵測
我定義了一個 LED_blink() 函數的進階版, 可以傳一個設定 LED 要持續閃爍幾秒之參數進去, 用 LED_blink(16) 取代等待 ThingSpeak 更新週期限制的 time.sleep(16), 亦即同樣是等待 16 秒, 但現在等待期間是由閃爍 LED 來代替, 如下列測試 3 所示 :
測試 3 : 等待 ThingSpeak 更新週期時閃爍 LED
from machine import Pin
import dht
import time
import urequests
p0=Pin(0, Pin.IN)
p2=Pin(2, Pin.OUT)
d=dht.DHT11(p0)
host='http://api.thingspeak.com'
api_key='NO5N8C7T2KINFCQE'
def LED_blink(s):
for i in range(1,10*s):
p2.value(1)
time.sleep_ms(50)
p2.value(0)
time.sleep_ms(50)
while True:
d.measure()
t=d.temperature()
f=round(t * 9/5 + 32)
h=d.humidity()
url='%s/update?api_key=%s&field1=%s&field2=%s&field3=%s' %(host, api_key, t, f, h)
print('Temperature=', t, 'C', '/', f, 'F', 'Humidity=', h, '%')
try:
r=urequests.get(url)
print('response=', r.text)
except:
print('urequests.get() exception occurred!')
LED_blink(16)
這裡將 urequests.get() 放在 try except 裡面是因為網路存取跟檔案存取一樣可能會出現例外, 當例外出現時將使整個程式停止執行. 為了避免一次 GET 要求失敗就讓程式垮掉, 務必將其放在 try except 裡面偵錯.
實際測試發現在量測溫濕度並上傳 ThingSpeak 伺服器時 LED 會停止閃爍約 1 秒, 傳完又繼續快速閃爍 16 秒. 這樣即使電腦關機無法從 REPL 或 ThingSpeak 網站觀察系統是否正常運作, 看到 LED 閃爍即知 ESP8266 還在正常運作.
如果要在手機上觀看 ThingSpeak 資料, 可以到 Google Play 下載 ThingView 這個 App :
開啟 App 後按底下的 + ㄒ新增頻道 (Channel) :
輸入 ThingSpeak 的 Channel ID, 就會顯示我的 Atmosphere 通道了.
點擊 Atmosphere 通道就會顯示此通道的各 field 圖表. 不過此多欄位圖形不會自動更新, 必須按右下角藍色 Refresh 鍵才會更新資料 (只有顯示單一欄位時才會自動更新).
點擊任何一張 field 的圖即顯示單一 field :
同樣地, 按右下角藍色 Refresh 鍵會更新此欄位資料 (但也不是很靈光), 但也可以在 Settings 中設定自動更新週期 , 勾選 "Auto refresh charts", 點 "Auto refresh time" 可設定週期 :
這個 DHT11 實驗延宕了三周了, 昨晚因業務需要去加班, 早上回來補眠卻睡不著, 乾脆就一口氣把 ThingSpeak 實驗做完了, 使用 ESP8266+MicroPython 真是簡潔啊! 難怪之前有網友說我用 Arduino+ESP8266 是 "見樹不見林", 何不單用 ESP8266+NodeMCU? 我覺得他說的是有道理的. 雖然以後不太可能這麼用, 不過那時我的目的是要藉此學習 Arduino 的各種可能罷了. 人生也是如此, 有時無用是為大用.
參考 :
# Interfacing the DHT11/DHT22 Humidity/Temperature sensor
# update data to thingspeak
# MicroPython on ESP8266: sending data to ThingSpeak
# 5.1. Star Wars Asciimation
# https://www.slideshare.net/ParshwadeepLahane/remote-temperature-monitor-dht11
# micropython socket example
# Controlling relays using Micropython and an ESP8266
2017-06-24 補充 :
上面測試 3 的程式跑了一天後突然停住了, 觀察 REPL 介面, 發現是在執行 main.py 的第 20 行時發生例外, 導致程式跳掉 :
response= 37003
Temperature= 30 C / 86 F Humidity= 50 %
response= 37004
Traceback (most recent call last):
File "main.py", line 20, in <module>
File "dht.py", line 13, in measure
OSError: [Errno 110] ETIMEDOUT
MicroPython v1.9.1-8-g7213e78d on 2017-06-12; ESP module with ESP8266
Type "help()" for more information.
測試 3 的第 20 行是 d.measure() 指令, 可見是測量 DHT11 的指令因 timeout 而跳掉了. 因此應該把 try 的範圍擴大到 d.measure() 才行, 如下列測試 4 :
from machine import Pin
import dht
import time
import urequests
p0=Pin(0, Pin.IN)
p2=Pin(2, Pin.OUT)
d=dht.DHT11(p0)
host='http://api.thingspeak.com'
api_key='NO5N8C7T2KINFCQE'
def LED_blink(s):
for i in range(1,10*s):
p2.value(1)
time.sleep_ms(50)
p2.value(0)
time.sleep_ms(50)
while True:
try:
d.measure()
t=d.temperature()
f=round(t * 9/5 + 32)
h=d.humidity()
url='%s/update?api_key=%s&field1=%s&field2=%s&field3=%s' %(host, api_key, t, f, h)
print('Temperature=', t, 'C', '/', f, 'F', 'Humidity=', h, '%')
r=urequests.get(url)
print('response=', r.text)
except:
print('urequests.get() exception occurred!')
LED_blink(16)
其實應該用 WDT (watchdog timer) 來監控系統是否有正常執行才對, 應用程式必須在時限內通知 WDT, 否則 WDT 會認為程式已崩潰而 Reset 系統.
MicroPython 的 machine 模組有支援 WDT, 參考 :
# class WDT – watchdog timer
# http://docs.micropython.org/en/latest/micropython-esp8266.pdf (搜尋 WDT)
但此類別底下註明 "Availability of this class: pyboard, WiPy.", 似乎不支援 ESP-01, 我實際測試雖然可建立 WDT 實體, 但卻無法設定 timeout, 目前在 ESP-01 確實無作用 :
>>> from machine import WDT
>>> dir(WDT) #顯示 WDT 型別的方法
['feed', 'deinit'] #實作 feed() 與 deinit() 方法
>>> wdt=WDT(timeout=5000) #無法設定 timeout
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: function does not take keyword arguments #不需參數?
>>> wdt=WDT(id=-1,timeout=2000) #無法設定 timeout
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: function does not take keyword arguments
>>> wdt=WDT() #無參數就可以順利建立 WDT 物件
>>> wdt
<WDT>
參考 :
# ESP8266: Watchdog functions
# https://forum.micropython.org/viewtopic.php?f=16&t=2788&p=16576
# https://www.tapatalk.com/topic/182447-micropython-forum/3221-watchdog-in-esp8266
2 則留言 :
Hello, Tony 大哥
參考 ThingSpeak 網站對於土壤含水測試範例, 我想比照來做, 也就是 DHT11 的 VCC pin1 接上一個 NodeMCU V0.9 的 GPIO 接腳, DHT11 要量測前給電壓(有給到 3 秒準備時間), 量完後切掉電力休眠. 這目的在省電, 同樣 NodeMCU 也可以 deepsleep, 結果不成功,接上的 LED 有昏暗閃了一下. 似乎 DHT11 真的需要 Vcc 才能動作. 您的看法怎樣?
https://www.mathworks.com/help/thingspeak/MoistureMonitor.html?searchHighlight=deepsleep&s_tid=doc_srchtitle
陳兄您好, 我看 DHT11 規格只要 1 秒的等待時間即可, 喚醒 ESP8266 後給予 3 秒照理應該足夠.
或許可以試試 :
1. 加長到 5 秒以上等待時間
2. DHT11 之 VCC 與 GND 間接個電容濾波.
張貼留言