はじめに
先日購入した電子工作ステーションで購入したラズパイ福袋2026に入っていたDHT11温湿度センサーモジュールをMachiKaniaから利用できないをChatGPTと相談しながら試して温度と湿度を測定できました。そのまとめを記します。
検証環境
今回は下記のハードウェア、ソフトウェアで検証しました。
ハードウェア
下記のハードウェアを使用しました。
- MachiKania簡易キット:Raspberry Pi Pico 2 Wを搭載
- DHT11温湿度センサーモジュール
ソフトウェア
- MachiKania type P 1.6.1.0 KM-1511
接続
DHT11モジュールとRaspberry Pi Picoは次の表に示すように接続します。
DATA端子は4.7kΩの抵抗でプルアップしています。
| DHT11 | Pico(物理) |
|---|---|
| VCC | 3V3(36) |
| DATA | GP1(2) |
| GND | GND(38) |
実態配線図を以下に示します。
DHT11
DHT11は安価な温度・湿度センサーで、その仕様は電子工作ステーションの製品ページでは次のように記されています。精度が高いセンサーとはいえないですね。
- 電源電圧:3.3~5.5V
- 測定範囲温度:0℃~50℃(±2℃)
- 測定範囲湿度:20〜90%(±5%)
- サンプリング間隔:2秒以上
電子工作ステーションのDHT11温湿度センサーは-20°C〜60°C(±2°C)とありますが今回使用した基板に搭載されているモジュールは2017年以前のバージョンのようです。
DHT11の通信方式は秋月電子通商のDHT11の製品ページにはシリアル単線(1-wireライク)通信と記されています。詳しくはDHT11のデータシートに記されています。
MachiKaniaには1-wireの関する命令がないのでデータシートに従った信号の送受信が必要です。
測定手順
データシートによるとホストとDHT11の間でデータ通信は次の手順で実行します。
- 測定を開始する信号をホストからDHT11に送信します
- ホストから18m秒以上のローレベルを送信
- 最大で35マイクロ秒ハイレベルの信号を送信
- DHT11は測定開始の信号を受信後、応答信号として83マイクロ秒のローレベルを送信します
その後、周辺機器へ受信準備完了を通知する87マイクロ秒のハイレベルの信号を送信します - 次に40個のデータを送信します。データの0と1は54マイクロ秒のローレベルの後のハイレベルの信号の長さで次のように判別します
- データ0:23から27マイクロ秒のハイレベル
- データ1:68から74マイクロ秒のハイレベル
- 40個のデータ送信後、DHT11が入力状態に遷移したことを知らせる54マイクロ秒のローレベルの信号を送信します
40個のデータは順番に湿度の整数部8ビット、湿度の小数部8ビット、温度の整数部8ビット、温度の小数部8ビット、チェックディジット8ビットの合計5バイトのデータです。ただし、湿度の小数部は0です。
チェックディジットは湿度と温度の4バイト全てを加算した値です。4つのデータを加算した値と受信したチェックディジットの値を比較すれば受信したデータに誤りがあるかを確認できます。
DHT11の温度の精度が±2℃、湿度の精度が±5%なので小数部の値は正確でないと言えるので表示は整数部のみで十分です。
プログラム
MachiKaniaによるDHT11を使った温度と湿度測定プログラムをChatGPTに作ってもらいました。
手順1の測定開始の信号を作るコード
測定手順1の測定開始の信号は次のコードで生成します。DHT11からデータを読み取るサブルーチンDHT11_READの冒頭部で実行します。DHT11を接続したピン番号をサブルーチンの引数にしています。
LABEL DHT11_READ
VAR PIN,SUM
PIN=ARGS(1)
<略>
OUT PIN,0
DELAYMS 20
OUT PIN,1
DELAYUS 30
<略>
RETURN
手順2の応答信号との検出
測定開始信号を受信下DHT11が応答の信号(ローレベル83マイクロ秒、ハイレベル87マイクロ秒、その後ローレベル)を送信するのでそれを検出を次のコードで実行します。サブルーチンWAIT_LEVELはピン番号、受信する信号レベル、タイムアウト時間を引数としています。
指定したレベルの信号が得られないときはサブルーチンを状態コードを設定してサブルーチンを抜けます。タイムアウト時間は余裕を持った値を指定しています。もう少し小さな値にしても動作しました。
IF GOSUB(WAIT_LEVEL, PIN, 0, 200) THEN STAT=1 : RETURN
IF GOSUB(WAIT_LEVEL, PIN, 1, 200) THEN STAT=2 : RETURN
IF GOSUB(WAIT_LEVEL, PIN, 0, 200) THEN STAT=3 : RETURN
サブルーチンWAIT_LEVELのコードは次のとおりです。受信する信号レベルがタイムアウトするまでに得られると0を、得られないときは1を返します。
REM -------------------------------------------------
REM Wait until IN(PIN) == LEVEL with timeout [us]
REM return 0: success, 1: timeout
REM -------------------------------------------------
LABEL WAIT_LEVEL
VAR PIN,LEVEL,TOUT,START
PIN=ARGS(1)
LEVEL=ARGS(2)
TOUT=ARGS(3)
START=CORETIMER()
WHILE (CORETIMER()-START) < TOUT
IF IN(PIN)=LEVEL THEN RETURN 0
WEND
RETURN 1
手順3の40個のデータ受信
DHT11から送信される40個のデータの受信とチェックサムの確認は次のコードで実行します。
配列Bは受信した5バイトのデータを順に格納します。
FORループの最初でDHT11からの信号がハイレベルになる時間とローレベルになる時間を計り、その差からハイレベルになっている時間を得ます。
ハイレベルになっている時間が50マイクロ秒より大きいときはデータ1を受信したことになるのでデータを記憶する配列要素の値をインクリメントします。これを8回繰り返すと1バイト分の値が得られます。
40個分のデータを受信し終えたら湿度、温度の各データを加算してチェックサムを求め、受信したチェックサムの値と比較して受信したデータの誤りを確認し、誤りがればコード6を返してサブルーチンを抜けます。
BI=0
BC=0
FOR I=0 TO 4
B(I)=0
NEXT
FOR I=0 TO 39
REM each bit: LOW ~50us then HIGH (26-28us=0, ~70us=1)
REM wait for HIGH start
IF GOSUB(WAIT_LEVEL, PIN, 1, 120) THEN STAT=4 : RETURN
T0=CORETIMER()
REM wait for LOW end of HIGH pulse
IF GOSUB(WAIT_LEVEL, PIN, 0, 180) THEN STAT=5 : RETURN
T1=CORETIMER()
HIW=T1-T0
REM MSB first: shift left then add bit
B(BI)=B(BI)*2
IF HIW>50 THEN B(BI)=B(BI)+1
BC=BC+1
IF BC=8 THEN
BI=BI+1
BC=0
ENDIF
NEXT
REM ---- Checksum ----
SUM=(B(0)+B(1)+B(2)+B(3)) AND $FF
IF SUM!=B(4) THEN STAT=6 : RETURN
全プログラム
以下がMachiKaniaでDHT11を使って2秒間隔で温度と湿度を測定して表示するプログラムです。
USEVAR DHTPIN,TEMP,HUM,STAT
USEVAR BI,BC,HIW,T0,T1,DUMMY
USEVAR PIN,LEVEL,TOUT,START,SUM
REM =========================
REM DHT11 read sample (KM-BASIC / MachiKania)
REM DATA pin: GP1 (change DHTPIN if needed)
REM =========================
DHTPIN=1
DO
GOSUB DHT11_READ, DHTPIN
IF STAT=0 THEN
PRINT "TEMP(C)= "; TEMP
PRINT "HUM(%) = "; HUM
ELSE
PRINT "DHT11 ERROR = "; STAT
ENDIF
REM DHT11は読み取り間隔 2秒以上推奨
WAIT 120
LOOP
END
REM -------------------------------------------------
REM Wait until IN(PIN) == LEVEL with timeout [us]
REM return 0: success, 1: timeout
REM -------------------------------------------------
LABEL WAIT_LEVEL
VAR PIN,LEVEL,TOUT,START
PIN=ARGS(1)
LEVEL=ARGS(2)
TOUT=ARGS(3)
START=CORETIMER()
WHILE (CORETIMER()-START) < TOUT
IF IN(PIN)=LEVEL THEN RETURN 0
WEND
RETURN 1
REM -------------------------------------------------
REM DHT11_READ(PIN)
REM STAT=0 ok
REM STAT!=0 error
REM HUM: integer %RH
REM TEMP: integer degC
REM -------------------------------------------------
LABEL DHT11_READ
VAR PIN,SUM
PIN=ARGS(1)
STAT=0
DIM B(4)
REM ---- Start signal (host) ----
OUT PIN,0
DELAYMS 20
OUT PIN,1
DELAYUS 30
REM ---- Release / switch to input by reading ----
DUMMY=IN(PIN)
REM ---- Sensor response: LOW 80us, HIGH 80us, then LOW ----
IF GOSUB(WAIT_LEVEL, PIN, 0, 200) THEN STAT=1 : RETURN
IF GOSUB(WAIT_LEVEL, PIN, 1, 200) THEN STAT=2 : RETURN
IF GOSUB(WAIT_LEVEL, PIN, 0, 200) THEN STAT=3 : RETURN
REM ---- Read 40 bits (5 bytes) ----
BI=0
BC=0
FOR I=0 TO 4
B(I)=0
NEXT
FOR I=0 TO 39
REM each bit: LOW ~50us then HIGH (26-28us=0, ~70us=1)
REM wait for HIGH start
IF GOSUB(WAIT_LEVEL, PIN, 1, 120) THEN STAT=4 : RETURN
T0=CORETIMER()
REM wait for LOW end of HIGH pulse
IF GOSUB(WAIT_LEVEL, PIN, 0, 180) THEN STAT=5 : RETURN
T1=CORETIMER()
HIW=T1-T0
REM MSB first: shift left then add bit
B(BI)=B(BI)*2
IF HIW>50 THEN B(BI)=B(BI)+1
BC=BC+1
IF BC=8 THEN
BI=BI+1
BC=0
ENDIF
NEXT
REM ---- Checksum ----
SUM=(B(0)+B(1)+B(2)+B(3)) AND $FF
IF SUM!=B(4) THEN STAT=6 : RETURN
HUM=B(0)
TEMP=B(2)
RETURN
実行結果
実行結果を以下に示します。
冷蔵庫の冷凍室にセンサーを入れてると温度が0°Cを下回ると50や51という値になり、0°C未満が測れないことが確認できました。
さいごに
DHT11がシリアル単線通信でデータを送信するということでどうプログラムするのか大まかにはわかるもののコードはどう書くのか、迷ったのでChatGPTに投げかけたところほぼ動作するプログラムが得られました。正しく動作するよだったのでデータシートとにらめっこしながらその内容を理解するという逆の流れになってしまいました。スパッとコードが書けると良かったのですがが・・・(^◇^;)

