特価品(見切り品?)のGR-CITRUSボード(ルネサスRX631搭載)で組み込みRuby(mruby)してます。前回は定番の温湿度センサDHT11の読み取りプログラムを作成しましたが、湿度の読み取りまでで止まってました。今回は温度も読み取れるようにしてみました。成り行きで現物合わせのタイミング制御、危ないっす。
※Rubyと一緒 投稿順indexはこちら
ソフトウエア制御のDHT11読み取り3レベル
DHT11は、Arduinoなど「電子工作業界」定番の温湿度センサですが、1-wire的な半二重双方向通信(マイコンからDHT11へは測定のトリガをかけるだけだけれども)で、かつ速度も遅いので「ソフトウエア制御」でインタフェースすることが多いのではないかと思われます。しかし同じソフトウエアでタイミングを取るといっても処理系の性能によってレベルが違う、と。
-
- 機械語命令コードで制御している場合(C言語などで記述されているライブラリの使用。Arduino環境、あるいはMicroPythonでのDHT向けライブラリの使用)
- DHT11の「プロトコル」速度にそこそこ対応できる速度の処理系(MircoPythonのPython記述によるソフトウエア制御)の場合
- DHT11の「プロトコル」速度にキチンと追従するのが難しい速度の処理系(今回のmruby)で、無理やり現物あわせの場合
上記の1のケースは何度となくやってますが、タイミングが遅れるということもなく安定。最近も以下の別シリーズで、MicroPython処理系でやってみています。
MicroPython的午睡(106) ESP32版、DHT11を接続してみる
ライブラリが存在しなくても、処理系の速度がそこそこ速いのであれば、以下の別シリーズ記事のように、MicroPython記述でタイミング制御しても安定してデータ受信(CRC検証までOK)できてます。
MicroPython的午睡(27) ラズパイPico、DHT11接続、ソフト現物合わせ
しかし今回のGR-CITRUSボード上のmrubyは、ソフトウエア的にタイミングがクリティカルです。ちょっとループ処理などでオーバヘッドがあるとデータを取りこぼしてしまいます。また、タイミングの頭出し(シンクロナイズ)も困難です。失敗するとこんな感じ。‐25℃ってなによ?きょうはちょっと蒸し蒸しする感じなんだけれども。
温度、湿度対応に「改良」したコード
DHT11は先に湿度16ビット、後に温度16ビット、末尾に検証用のCRC8ビットというデータ構成です。前回は湿度の整数部分8ビットでタイミング的にギリギリで残りは捨ててました。しかし、折角の温湿度センサです。温度、湿度両方読みたいということで今回再びのタイミング現物合わせに挑戦。
-
- 前回はタイミングを外部から観察するために1ピン使っていたが、その制御のタイミングすら惜しいのでその制御を削除した。闇夜に手探りで調整するがごとし。
- どうせ湿度の小数部分にはデータが載ってないと思われるので無視
- 温度の整数部分まで読み取る、小数部分は断念。また一部読んでないフィールドがあるのでCRCは計算できない。
現物合わせの「やっつけ」なRubyコードが以下に。
#!mruby class Dht11 def initialize(pinname) @pin = pinname @buf = [] @mjVH = [0, 0, 0] @mjVT = [0, 0, 0] @temperature = 0 @humidity = 0 @error = 0 pinMode(@pin, 3) #output, opendrain end def readERROR return @error end def readTemperature return @temperature end def readHumidity return @humidity end def readERROR return @error end def readBUF return @buf.join end def clearBUF @buf = [] end def majorityVotesH(v) @mjVH.shift @mjVH.push(v) if @mjVH[0] == @mjVH[1] then return @mjVH[0] else return @mjVH[2] end end def majorityVotesT(v) @mjVT.shift @mjVT.push(v) if @mjVT[0] == @mjVT[1] then return @mjVT[0] else return @mjVT[2] end end def startSignal digitalWrite(@pin, 0) delay(19) #min low pulse > 18msec digitalWrite(@pin, 1) end def waitSIG(sig, timeOut) count = 0 while digitalRead(@pin) == sig do count +=1 if count > timeOut then return false end end return true end def bitRead while digitalRead(@pin) == 0 do end return 0 if digitalRead(@pin) == 0 return 0 if digitalRead(@pin) == 0 return 1 if digitalRead(@pin) == 0 return 1 end def b2B(lis) work = 0 for i in lis do work <<=1 work += i end return work end def convertData rhI = b2B(@buf[1..8]) tmI = b2B(@buf[10..17]) temp= tmI & 0x7F if (tmI & 0x80) != 0 then temp *= -1 end @temperature = majorityVotesT(temp) if rhI < 99 then @humidity = majorityVotesH(rhI) end end def read @error = 0 startSignal if !waitSIG(0, 100) then @error = 1 return false end # rhI b1 = bitRead #bit 0 @buf.push(b1) b1 = bitRead #bit 1 @buf.push(b1) b1 = bitRead #bit 2 @buf.push(b1) b1 = bitRead #bit 3 @buf.push(b1) b1 = bitRead #bit 4 @buf.push(b1) b1 = bitRead #bit 5 @buf.push(b1) b1 = bitRead #bit 6 @buf.push(b1) b1 = bitRead #bit 7 @buf.push(b1) # rhD b1 = bitRead #bit 8 @buf.push(b1) b1 = bitRead #bit 9 b1 = bitRead #bit 10 b1 = bitRead #bit 11 b1 = bitRead #bit 12 b1 = bitRead #bit 13 b1 = bitRead #bit 14 b1 = bitRead #bit 15 # tmI b1 = bitRead #bit 16 @buf.push(b1) b1 = bitRead #bit 17 @buf.push(b1) b1 = bitRead #bit 18 @buf.push(b1) b1 = bitRead #bit 19 @buf.push(b1) b1 = bitRead #bit 20 @buf.push(b1) b1 = bitRead #bit 21 @buf.push(b1) b1 = bitRead #bit 22 @buf.push(b1) b1 = bitRead #bit 23 @buf.push(b1) # b1 = bitRead #bit 24 @buf.push(b1) convertData return true end end usb = Serial.new(0, 9600) usb.println("DHT11 Read, Pin.4") dht11pin=4 dht11 = Dht11.new(dht11pin) loop_interval = 2000 while true do usb.println("DHT11 temperature & humidity") if dht11.read then usb.println("BUF:" + dht11.readBUF) dht11.clearBUF usb.println("TEMPERATURE:" + dht11.readTemperature.to_s) usb.println("HUMIDITY: " + dht11.readHumidity.to_s) usb.println("ERROR:" + dht11.readERROR.to_s) end delay(loop_interval) end
上記ソース読んでいただけると、タイミング調整のためのやっつけな工夫が随所に。わざわざ読むようなもんでもないですが。
実機実行結果
Rubic環境から、GR-CITRUSボード上で上記プログラムを動作させている様子が以下に。
上記みると、温度25℃、湿度66%とな。ちょっと手元の温度計より温度は高めか。湿度はほぼほぼ一致。まあ、ここだけだと読めているような気がする。大丈夫か?ま、成り行きの現物合わせなので信用しないでね。