2019年5月28日 星期二

Raspberry Pi筆記:DHT11溫濕度感測器

硬體安裝:

參考ref.1的Figure.1 (page 5)
在pin 2的部份與RPi的GPIO 4連接,並使用一電阻與VCC相連

python程式:

1. 快速上手方式:

在RPi已經安裝好pip的狀態下,只需要額外安裝Adafruit_DHT (ref.2)
sudo pip install Adafruit_DHT
接著,在python環境中輸入:
import Adafruit_DHT as dht

sensor = dht.DHT11
gpio = 4
print( dht.read_retry(sensor, gpio) )
可以快速得到DHT11偵測到的相對濕度與溫度:
(76.0, 29.0)
在Googled結果,可以看到許多類似的maker分享,許多是從git clone Adafruit_DHT 開始,安裝Adafruit_DHT,有興趣的可以參考ref.4。

2. 透過GPIO方式:

要透過GPIO操控DHT11,基本上要了解DHT11的運作機制 (ref.1):
RPi GPIO控制階段:
a. pin 2針腳在高電位下時,給予不少於18ms長的低電位,讓DHT11能截取到電壓變化
b. 恢復pin 2針腳回高電位
DHT11初始化階段:
c. DHT11將pin 2針腳調整為低電壓,持續80μs
d. DHT11將pin 2針腳調整為高電壓,持續80μs
DHT11訊號傳回階段:
e. DHT11會連續的將pin 2針腳,在低電壓與高電壓之間變換共40個循環;
 低電位維持50μs,高電位維持26~28μs時,表示為0
 低電位維持50μs,高電位維持70μs時,表示為1
f. DHT11將pin 2針腳調整為低電位50μs後,結束控制pin 2針腳電位控制

DHT11輸出的40個bits,共由5個8 bits所組成:
1~8 bits: 相對濕度整數部位
9~16 bits: 相對濕度小數部位
17~24 bits: 溫度整數部位
25~32 bits: 溫度小數部位
33~40 bits: checksum檢查碼,等於前4個數字的總和

在了解DHT11的訊號控制方式,可以寫相對應的GPIO控制來讀取訊號
import RPi.GPIO as GPIO
import time

PIN = 4

# initial GPIO
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.cleanup()

# step a, b
GPIO.setup(PIN, GPIO.OUT)
GPIO.output(PIN, GPIO.LOW)
time.sleep(0.0181)
GPIO.output(PIN, GPIO.HIGH)

# step c, d
GPIO.setup(PIN, GPIO.IN, GPIO.PUD_UP)
while GPIO.input(PIN) == GPIO.LOW:
  continue
while GPIO.input(PIN) == GPIO.HIGH:
  continue

# step e
bits = []
MAXCount = 3000
for n in range(40):
  lowCount = 1
  highCount = 1
  while GPIO.input(PIN) == GPIO.LOW:
    lowCount += 1
  while GPIO.input(PIN) == GPIO.HIGH:
    highCount += 1
    if highCount > MAXCount:
      print("missing data")
      exit(1)
  if lowCount < highCount:
    bits.append(1)
  else:
    bits.append(0)

# bits to bytes
RH = 0
RHD = 0
T = 0
TD = 0
checksum = 0
for bit in bits[:8]:
  RH = (RH << 1) | bit
for bit in bits[8:16]:
  RHD = (RHD << 1) | bit
for bit in bits[16:24]:
  T = (T << 1) | bit
for bit in bits[24:32]:
  TD = (TD << 1) | bit
for bit in bits[32:40]:
  checksum = (checksum << 1) | bit

# checksum
if RH+RHD+T+TD != checksum:
  print("checksum error")
  exit(1)

print("RH: {}%, T: {}C".format(RH+0.1*RHD, T+0.1*TD))
執行結果:
RH: 76.0%, T: 29.6C

程式下載:

https://github.com/1412chen/rpi_learning/tree/master/dht11
- dht11.py: 直接呼叫Adafruit_DHT讀取溫濕度值
- dht11_gpio_direct.py: 透過GPIO讀取溫濕度值
- dht11_gpio.py: 透過GPIO讀取溫濕度值,一次讀取完DHT11電壓變化後才解析。

討論:

1. 在寫這個document的時候,我發現到使用Adafruit_DHT跑出來的結果,不會有小數點,讓我在確認GPIO結果一直充滿疑問。我想這個應該是Adafruit_DHT故意忽略小數位輸出,DHT11本身的誤差太大(相對濕度±5%,溫度±2°C),描述到小數點,確實沒有太大意義。
2. 控制GPIO擷取資料,蠻容易出現missing data與checksum error的結果。
3. 相對於Adafruit_DHT是python呼叫C的GPIO控制,ref.3中的dht11是python透過GPIO對DHT11進行操作,該程式為了避免python在擷取pin 2電位過程中額外的操作的時間差導致電位資料遺失或延遲,先將所有電位資料擷取下來後再解析。這樣也許能比較穩定得到結果,我有參照這個邏輯重新撰寫,參考程式下載的dht11_gpio.py。

參考:

1. https://www.mouser.com/ds/2/758/DHT11-Technical-Data-Sheet-Translated-Version-1143054.pdf
2. https://github.com/adafruit/Adafruit_Python_DHT
3. https://github.com/szazo/DHT11_Python
4. https://kknews.cc/zh-tw/digital/ea26b4q.html