稼働中

Raspberry Pi Pico(d_06)DHT11 温湿度センサー

DHT11

DHT11はデジタル温湿度センサーです。
DHT22は少し高いので安価なDHT11をRaspberry Pi Picoで使ってみました。
DHT11は1-wireでデータ通信をしますが、1-wire形式での通信ではありません。
そのためデータシートを見てもROMコードなどは見当りません。
抵抗式の水分センサ湿度を、NTCサーミスタで温度を測定し8-bitのマイクロコントローラでデータを送信するそうです。

外観

DHT11モジュールの外観です。水色のケースは容易に外せました。ケースの裏にスペックが記載されていました。
DHT11外観
ケースを外した外観です。表側に水分センサらしきものがあります。サーミスタはどれかよくわかりませんでした。
DHT11内部

接続図

DHT11のデータシートには供給電圧は3.3V~5.5Vと記載されていましたが3.3Vでは、以下のようなエラーが何度か発生して動作しませんでした。


Traceback (most recent call last):
  File "", line 18, in 
  File "dht.py", line 25, in measure
OSError: [Errno 110] ETIMEDOUT

DC/DC電源を繋いで調べてみると3.5Vで動作しました。
DC/DC表示   Tester表示       動作
3.0         3.091           Error
3.3         3.295           Error
3.4         3.380           Error
3.5         3.484           OK

電源供給は外部電源5Vを使うことにしました。レベルコンバータ経由でデータ通信するようにしました。
DATAの送受信はGPIO22ピンを使いました。
※Raspberry Pi Picoの3.3Vから供給してエラーが出ないなら外部電源は不要だと思います。
DHT11接続図
※Raspberry Pi PicoはパソコンとUSB接続されています。

dthモジュール

Raspberry Pi PicoのMicroPythonにはdthモジュールがあります。
class ‘DHT11’が使えます。<class ‘DHT22’>もあります。

DHT(Pin(num)) で初期化します。
以下のメソッドがあります。
measure()
温湿度データを読取ります。
temperature()
測定した温度を返します。
humidity()
測定した湿度を返します。

Thonnyのshellで実行してみます。


>>> import dht
>>> from machine import Pin
>>> d=dht.DHT11(Pin(22))
>>> d.measure()
>>> d.temperature()
28                      #28℃
>>> d.humidity()
65                      #65%RH
>>> 

簡単に測定できました。

モジュールなし

dthモジュールなしで測定したいと思います。
DHT11のデータシートを参考に考えてみました。先に検討した結果を記載します。
測定は出来ていると思いますがデータシートのようには行きませんでした。
後に記載しますが、開始シグナル(LOW_18msec)を送信後、プルアップで20~40usec待機(pulls up and wait 20-40us for DHT11’s response)が書いてありましたが、待機させると80usecで戻るDHT11の応答信号かなり短く戻って来ます。
Integral part of RH + Decimal part of RH + Integral part of T + Decimal part of T + check sum
(上位のRH_8ビット+下位のRH_8ビット+上位のT_8ビット+下位のT_8ビット+パリティ8ビット)
各8ビットの計40データを受信します。
注意書きに「The decimal part of RH and T is always 0000 0000」とあるのですが、
Decimal part of Tの8bit(下位のT_8ビット)はゼロになりませんでした。
(偶発的?にゼロになることがあるかもしれませんが・・)
以上のようにデータシートと異なる部分が生じたので、どこか間違っているのかも知れません。
御留意ください。

DHT11送受信

DHT11の温湿度データを得るタイミングチャートは下図になります。データシートから抜粋しています。
送受信は大雑把には以下の順番です。※詳細はデータシート等を参照してください。
開始信号(Start signal)>>応答信号(Response signal)>>湿度・温度データ・チェックサムの40個の信号を受信>>終了信号(Low end)

Raspberry Pi Pico側をoutputに設定して①’L’レベルを18msec以上出力します。これが開始信号になります。
その後、20~40usのプルアップ待ちします。
①以降はMicroPythonのtime_pulse_usを使って’H’のパルス幅を測定して行きます。
そのため、Raspberry Pi Pico側をinputに設定します。
‘L-H’80usの応答信号が返ります。(プルアップ待ちを入れると応答信号の’1’が80usより短く返りました。)

②’H’レベルの80usが返れば、以降は湿度+温度+チェックサムデータの計40個(bit)の’H’が返ります。40個(bit)の’H’レベルの時間が違います。
③26~28usならbit値を’0’③70usならbit値を’1’として別途判定します。
40個データの後にエンドデータとして⑤’L’レベル50usを受信します。DHT11は⑤後Pullupされ送信終了します。そのためtime_pulse_usではエラーが返ります。
DHT11データ送受信
※DHT11のデータシートから抜粋、赤は追記

プルアップ待ち時間を変えた時の受信データの例だけ示します。(データ取得などの説明は省いてます)
最初の値が応答信号’H’の時間幅です。
dht11_03_test.py (整理済みのスクリプト)
0us [61, 13, 25, 71, 71, 70, 70, 70, 71, 24,(省略)
10us [29, 15, 24, 70, 70, 70, 70, 71, 71, 24,(省略)
20us [57, 24, 24, 70, 70, 70, 70, 70, 71, 24,(省略)cnt=2
30us [43, 24, 24, 70, 70, 70, 70, 70, 71, 24,(省略)cnt=2
40us [35, 24, 24, 70, 70, 70, 70, 70, 71, 24,(省略)cnt=2
50us [30, 24, 24, 70, 70, 70, 70, 70, 71, 24,(省略)cnt=4
※cnt 繰返し測定回数(通信エラーのため)
プルアップ待ち時間を長くすると応答信号がより短く返りました。プルアップ待ちすると2回測定する必要がありました。以上の結果からプルアップ待ち時間は20usとしました。

スクリプト

Raspberry Pi Picoのピンを出力モードにして開始信号を送信した後は、入力モードにしてtime_pulse_usを使って受信した’H’のパルス幅を測定します。
応答信号(80us’H’)と温湿度・パリティのデータで41個のデータを受信した後の終了信号後は’H’のままなのでtime_pulse_usはタイムエラーを返します。

温湿度・パリティのデータの40個のパルス幅データを’0’、’1’のビットデータに変換処理をします。
上位のRH_8ビット+下位のRH_8ビット+上位のT_8ビット+下位のT_8ビット+パリティ8ビットの40bitになります。

上位のRH_8ビットの値が湿度になります。
上位のT_8ビットの値が温度になります。
パリティ8ビット以外を足し算した値とパリティ8ビットが同じなら正常値です。

データの例を示します。
受信した41個のパルス幅データ
[45, 24, 71, 25, 24, 24, 24, 24, 25, 24, 24, 24, 24, 24, 24, 24, 25, 24, 24, 24, 71, 71, 71, 71, 26, 24, 24, 24, 24, 70, 24, 24, 25, 25, 71, 71, 24, 24, 70, 70, 25]

最初の応答信号(上では45)を除いた40個のパルス幅値から’0”1’に変換したデータ
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0]

8bit毎に整理したデータ
[0, 1, 0, 0, 0, 0, 0, 0] # 湿度データ(上位)–①湿度 0b01000000(64)%
[0, 0, 0, 0, 0, 0, 0, 0] # 湿度データ(下位)
[0, 0, 0, 1, 1, 1, 1, 0] # 温度データ(上位)–②温度 0b0011110(30)℃
[0, 0, 0, 0, 1, 0, 0, 0] # 温度データ(下位) 全て0になるハズなのだが・・
[0, 1, 1, 0, 0, 1, 1, 0] # パリティデータ 上4つ加算値 102と同じ

データシートでは温度データ(下位)は全て0になるハズなのですが・・なっていません。
パルス幅のデータ値も概ね70us、25usと安定した値を取得しているので温度データ(下位)部分だけ異常があるようにも思えないです。原因はよくわかりません。
湿度、温度は上位のデータだけ使うので問題無いような?
41個のパルス幅データ、40個のビットデータ、パリティチェックを確認しています。
エラーが5回になれば終了するようにしました。

スクリプトは以下のようにしました。


dht11_03b2_test.py
from machine import Pin
import time
##Temperature and humidity module DHT11
# init Pin(22) out 'H'
def ht11_ini():
    Pin(22, mode=Pin.OPEN_DRAIN, pull=Pin.PULL_UP)
    sig=Pin(22,Pin.OUT)
    sig.value(1)
# 8ケの'0','1'リストデータをbin 0b_xxxx_xxxxにする
def b_data(l_data):
    b='0b'
    for s in l_data:
        b = b+str(s)
    return b

# 開始信号 HOST OUT'1' 18msec<
def start_sig():
    sig=Pin(22,Pin.OUT)
    sig.value(1)
    time.sleep(2) # 繰返し周期は2sec<=
    sig.value(0)
    time.sleep_ms(18) # 18ms<  NG(10ms)
    sig.value(1)
    #Pulled up to waite 20-40us
    time.sleep_us(20)

# 受信データ パルス幅測定 recive 1 + 40bit data 
def dat_recive():
cnt=0
dat=[]
sig=Pin(22,Pin.IN)
while True:
# measurepulse width 200us< timeout error -1
d = machine.time_pulse_us(Pin(22),1,200)
if d==-1:
#print(d,Pin(22))
break
dat.append(d)
cnt=cnt+1
return cnt,dat

# 測定 DTH11 mesuare (mesuare-main)
def ht11_mes():
e_cnt=0
while True:
e_cnt=e_cnt + 1 # 測定回数
#print('e_cnt=',e_cnt)

start_sig()
cnt,data = dat_recive()
#print(cnt,data)

# 41 data recived 41 != retry
if cnt==41:
r_bit=[]
# pulsewidth data >> '0'or'1' list data
for i in data:
if 20 < i <=28:
r_bit.append(0)
if 68 <= i <=72:
r_bit.append(1)
else:
pass
#print(len(r_bit),r_bit) # mosi 40 != retry

if len(r_bit)==40:
# 8bitにスライスする
h0=r_bit[0:8]
h1=r_bit[8:16]
t0=r_bit[16:24]
t1=r_bit[24:32]
cs=r_bit[32:40]

# 8bit-data list display
#print('LIST')
#for a in [h0,h1,t0,t1,cs]:
#print(a)

# リストデータをbin 0b_xxxx_xxxxに変換する
b_h0=int(b_data(h0))
b_h1=int(b_data(h1))
b_t0=int(b_data(t0))
b_t1=int(b_data(t1))
b_cs=int(b_data(cs))

# calculate check sum
c_sum=b_h0+b_h1+b_t0+b_t1

#print('H,T=',b_h0,b_t0)
#print('b_cs,c_sum=',b_cs,c_sum)
# checksum ok rerurn
if c_sum ==b_cs:
return b_h0,b_t0,e_cnt #RH,T,count

# 5回エラーで測定中止
if e_cnt >=5:
return print('Error',e_cnt)

# DHT11で温湿度を測定
ht11_ini()
H,T,CNT=ht11_mes()
print('num measure=',CNT)
print('HumidityRH%=',H)
print('Temperature=',T)

実行結果

結果は以下のようになりました。測定は2回しています。1回目はエラーになったようです。※ThonnyのShellに表示されます。


dht11_03b2_test.py
>>> %Run -c $EDITOR_CONTENT
num measure= 2
HumidityRH%= 65
Temperature= 30
>>> 

ちなみにモジュールを使って測定した値です。合ってるようです。


>>> %Run -c $EDITOR_CONTENT
65
30

まとめ

Raspberry Pi PicoでDHT11を使って温湿度の測定ができました。
dthモジュールをimportすれば簡単に測定できます。
また、dthモジュールなしで測定を試しました。データシートと異なる部分がありましたが、たぶん測定値には問題ないと思います。