ラベル DHT22 の投稿を表示しています。 すべての投稿を表示
ラベル DHT22 の投稿を表示しています。 すべての投稿を表示

2020年8月4日火曜日

サイクルカウントのウェイトを作成してGPIOのPull Up/Downを実装してみました。

150Cycleのウェイトループが作れるようになったのでドライバ側でpull up/downの実装も行ってみました。これで一歩踏み込んだ形になります。
ただし、データシート上に記載してありましたが、pull up/downの設定値はcpuから見えなくなるため、設定値はソフト側で管理する必要があるとのこと。なので、これを行うとRpi.gpioの内部バッファと実際値のズレが生じてRpi.gpoiでうまく機能しなくなる可能性があります。

実装中に色々と新たな発見というか記載を見かけて色々な部分を書き換えながら実装していましたが、一番気になったのがioremapでアクセスできるようになったメモリーに対してアクセスするためにioread/iowriteといった関数を通す必要があるらしいという点。

2020年7月31日金曜日

一回りしてpythonに戻って

一通り手直しを終え、ドライバが無かったらそれなりに動く形のものに落ち着きました。

各変数の位置がいい加減だったり、配列は相変わらずどんぶりだったりと突っ込み所も多いですが、参考になれば

2020年7月19日日曜日

何となく落ち着いた感じと少しまとめ

値段以上に結構元を取れたと思うDHT22ですが、結局のところほぼDHT11と同じだったという感想。測定範囲だけはどうしてもDHT11だとダメなところはありますが。湿度の小数点は実用上ほぼ不要だと思いますし。

DHT11の時に記憶に残っている注意書きとかと同じ現象がDHT22も発生し、データシート上を見続けているとちゃんとそのことが記載されていたりします。そんなところでまとめ的な点として
・その時点の湿度と温度を取得したい場合は2回連続で読み出す必要がある。
・連続で読み出す場合は2秒間をあける必要がある。
この点が、やっぱりDHT11/DHT22が好きになれないところとなっています。
結局2連続で読み出す必要があるので、最低でも2秒もかかってしまいます。

2020年7月18日土曜日

GPIO操作をcで

取りこぼし発覚からpython以外でGPIOを直接操作したくてライブラリやパッケージを検索してたのですが、思っていたよりは乱立していなかったものの、メジャーっぽいので4つぐらい存在していました。ほとんどpythonから利用できるもので、cから使えそうな感じでした。

すでに意識してなかったけど、pythonでも使ってるし、raspberry piの公式ページで見かけていたRPI.GPIOがいいかな?とcのソースを起こし始めたのですが…あれ?cのサンプルになりそうなソースが検索で引っかからない…
もう何でもいいかと投げやりで、最悪デバイスをファイルから出力すればいいか…とかあきらめかけていたところ、もう少しハードよりの低レベルライブラリとしてhttp://www.airspayce.com/mikem/bcm2835/bcm2835-1.64
というものがありました。
良くも悪くもハード寄りなので定数や変数、関数名が分かりづらいですが、これで直接操作できそうです。

扱い方の基本はpythonでGPIOを操作するときと変わらないので手元のスクリプトを基にデータを受信するところまで作って、受信内容を表示させてみたいと思います。

まずは…コメントの日本語でコンパイルがおかしいことにwクソッw
gccのコンパイルでソース記述のコードページと、生成したオブジェクトが出力するコードページを指定するようにしてコンパイルが通るようになりました。
pi@rasp3b:~ $ gcc dht22.c -o dht22 -l rt -l bcm2835  -finput-charset=UTF-8 -fexec-charset=UTF-8

さっそく実行させてみましす。

pi@rasp3b:~ $ ./dht22
sampling_start_lev : 0
length : 81
22 26 54 27 56 23 54 27 53 27 53 27 54 73 54 25 68 74 54 73 54 26 53 27 54 26 54 73 55 26 54 25 67 26 54 27 53 27 53 27 53 26 54 26 54 27 53 25 68 74 54 73 54 74 53 74 54 26 54 26 54 74 54 25 65 73 54 26 54 74 54 73 54 74 54 26 54 26 52 27 47

受信はμs単位ですが、何となくデータは取りこぼしも発生してなさそうで、連続で実行しても見た感じは大丈夫そうです。

ただ、気になるのはデータ送信開始の信号の時間間隔が短すぎるところと、pythonではこち側のHIGHレベルの信号を受信できることがあったのですが、今回作成したものは開始がLOWから始まることしかないのが気になり、そうしていると受信データが79個とかいきなり取りこぼしてそうな感じに。

実際に信号をサンプリングしているループがどの程度の速度で回っているのか様子を見ることから始めました。

sampling_start_lev : 0
length : 81
37 27 53 26 55 25 54 26 55 26 54 26 54 73 54 25 67 74 54 74 54 26 54 26 54 26 54 74 53 26 54 73 67 26 55 26 53 27 54 26 53 27 54 26 54 26 54 25 68 73 54 73 55 73 54 74 53 27 53 27 54 26 54 25 64 74 54 26 54 73 54 74 54 26 54 74 53 74 54 73 46
82 58 119 58 120 57 120 58 120 58 119 58 120 163 119 56 149 164 119 164 119 58 119 58 120 58 118 164 119 58 120 161 149 58 120 58 119 58 120 58 119 58 120 57 120 58 120 55 150 163 120 163 113 153 119 164 119 58 119 58 120 58 119 56 143 163 120 58 120 163 120 163 120 58 120 163 119 164 120 160 104

2個目の数字の塊がループ中カウントを行った回数です。
37μsで82回、回ってるので処理落ちしている様子はありません。
何度か実行していると、ループのカウンタが200を越える数字が見えるようになりました。
66 101 205 100 209 101 214 101 204 101 206 97 205 279 205 96 256 277 208 98 205 280 208 277 209 280 207 277 207 278 208 98 257 102 211 90 192 94 176 88 185 80 179 89 175 85 179 84 221 262 209 281 211 281 208 277 207 101 201 100 207 277 207 91 247 278 210 101 203 289 203 283 203 101 209 94 208 277 207 96 175

もしかしてCPUクロックが変動するので、変なタイミングで処理落ちするのかな?と途方に暮れながらソースを眺めて寝落ちした後、少し落ち着いて考えてみると、sleepさせて開始させているけど、実際の時間はどのぐらいなのだろう?と実際に計測させてみました。

sampling_start_lev : 0
sampling_end_lev   : 1
length : 81
37 26 54 25 54 27 54 25 55 26 54 25 54 74 54 25 68 73 54 26 54 74 54 74 53 74 54 74 54 26 53 73 67 27 54 26 53 27 54 26 53 27 54 26 54 26 54 25 67 74 54 73 54 74 54 74 54 26 54 26 54 73 54 73 64 74 60 20 54 73 54 74 54 26 54 26 54 74 53 26 47
70 53 111 53 111 54 111 52 102 48 110 53 111 152 110 52 139 151 111 54 111 151 111 152 110 152 111 151 111 54 110 150 138 54 111 53 111 54 111 54 110 54 111 53 111 54 111 51 138 152 111 151 111 152 111 151 111 53 111 53 111 152 111 149 132 152 40 38 107 146 107 146 107 51 107 52 106 146 107 50 93
start : 1595040856 964706
p1 : 613
p2 : 50726
p3 : 70816
p4 : 71017
p5 : 71020
p6 : 1075103
p7 : 1075978

すべてμs単位で開始からの経過時間を表示させていますが、p1が最初のHIGH信号を送信する直前で、p2が信号を送信したあとusleep(50000);を行った後なのですが、50113μs経過しています。最初のHIGHの送信は実際は必要ないので経過時間はあまり関係ないのですが、それでもセットアップして信号をHIGHにするだけで100μs程度かかっているのは予想以上にもたついています。
次のp3はLOW信号を送信してusleep(20000);させた後なのですが、ここでも経過時間が20090μsとなっていて90μs程度もかかっています。
時間が不安定→sleepが原因
というわけで、ちょっとCPUに優しくなくなりますが、時間ループを作って様子を見ることに。

start : 1595041084 066267
p1 : 631
p2 : 50639
p3 : 70639
p4 : 70838
p5 : 70840
p6 : 1074781
p7 : 1075044

同じく8μs、1μs以下と、まったく高速化処理をしていない状態でかなりの精度で処理が行われるようになりました。
p3からp4はピンを入力に切り替えた後に信号をプルアップさせてHIGH状態に切り替えています。(この辺の手順が前々から気になっているんですけど、本来はHIGH状態にしてからピンの入出力を切り替えるべきだと思うんですがこの辺の考え方が良くわからない)199μsと、それなりに時間がかかっています。
p4からp5はループさせる前の初期設定で、2μs。p5からp6がループ中の時間で最後は出力系の部分で必要な処理ではありません。
時間的にタイトなところはp3からp4の部分で信号線がHIGHになった後、DHT22のデータシート(Table 6: Single bus signal characteristics)では
Tgo(ホスト側のHIGH信号)とTrel(センサー側のLOW信号)の最低時間が20μs、75μsとなっていて、最低でもHIGH信号をに変えた後、95μs以内にで受信開始できないと最初のデータを取りこぼすことが出てくるというところ。
なのでもう少し時間を見てみましょう。

start : 1595042362 253428
p1 : 607
p2 : 50615
p3 : 70616
p4 : 70618
p5 : 70892
p6 : 70894
p7 : 1074949
p8 : 1074949

増やした部分はp4のところでbcm2835_gpio_fselの処理が2μsで、信号をHIGHにしているbcm2835_gpio_set_pudが274μsも…
p3 : 70585
p4 : 70586
p5 : 70746
もう一回実行してみると今度は160μsとかなり処理時間にばらつきがあるようです。実際にどのぐらいのタイミングで切り替わっているのか不明ですが、結構こわい感じです。内部で後処理ではなく、前処理に時間がかかっていることになっていることを祈りましょう…

さて、この状態で受信データはどんな感じでしょうかねぇ…

sampling_start_lev : 1
sampling_end_lev   : 1
length : 82
21 54 26 54 26 54 26 54 26 54 26 54 26 54 74 53 26 67 74 54 26 54 73 54 74 54 74 53 74 54 26 54 73 67 26 54 26 54 26 54 26 54 26 54 26 54 26 54 25 68 73 54 74 54 74 53 74 54 26 54 74 54 26 54 72 65 73 54 26 54 74 54 74 53 27 53 74 54 26 54 25 47

開始がHIGHから拾えているのと、データの個数が82個になっていていい感じになってそうです。HIGH信号の部分を取って8bitで区切ってみると
21
26 26 26 26 26 26 74 26
74 26 73 74 74 74 26 73
26 26 26 26 26 26 26 25
73 74 74 74 26 74 26 72
73 26 74 74 27 74 26 25

結果からするとセンサー側の開始信号のLOWを見事に取りこぼしています。
何度か実行していると取得しているデータも79ことか76個に減っているときがあって、なんか嫌な予感です。
実際にプルアップしている処理時間は190μsと245μsと、データシート上のセンサー側からの信号を標準タイミングで計算してみると…80, 80, 50μsで データ信号のLOW状態までとなっていて、190でギリギリデータの開始ビットが見え、240μsだとデータの信号のHIGH信号からしか見えず、ビットの判定ができなくなります。
と、なると…pythonでいままでデータが上手く拾えていなかったパターンもこれじゃね?とか感じますが、cだとサンプリングが数μs単位で綺麗に取れているのでこのまま続けましょうw

さて、こうなると、pullup処理自体がダメなんじゃないかとなるので、データシートの手順に変更してみます。
sampling_start_lev : 1
sampling_end_lev   : 1
length : 82
20 78 82 54 26 54 26 54 27 53 26 54 26 54 26 54 74 54 25 67 74 54 26 54 74 54 73 54 74 54 26 54 26 54 72 68 26 54 26 54 26 54 26 54 26 54 26 54 26 160 60 54 74 54 73 54 74 54 26 54 74 54 73 54 25 64 74 54 26 54 74 54 73 54 26 54 26 54 26 54 73 47
40 162 170 112 55 112 54 112 55 112 54 112 54 112 54 112 153 112 53 140 153 112 54 112 154 112 150 91 149 113 54 112 54 112 151 140 55 112 54 112 54 113 54 112 54 112 55 111 55 83 119 106 146 107 146 108 147 107 53 107 147 108 146 106 50 127 146 107 52 108 147 107 147 108 48 173 94 197 101 208 275 182
start : 1595044413 146081
p1 : 614
p2 : 50624
p3 : 70625
p4 : 70626
p5 : 70626
p6 : 70628
p7 : 1074862
p8 : 1074862
最悪データが送られてこなくなるとも思われましたが、何の問題もなくデータの受信ができました。
p3,p4の間のbcm2835_gpio_set_pud部分はコメントアウトして、p3の手前で信号をHIGHにしてから受信状態にしています。
こうしてしまうと、信号をプルアップしなくなってしまうのですが、ハード的にセンサーの基盤にデータ信号とVCCの間に抵抗が入っていてプルアップされた状態なのでいいんじゃないのかな…と思います。
データもこちら側のHIGH信号から見えているようで、センサー側からの開始信号であるLOW(Trel 80μs)→HIGH(Treh 80μs)信号も取れているようなので時間的には十分問題なさそうです。
何度か実行してみると、データの個数は84個で開始もHIGHから取れているので問題なさそうです。

時間的な制約は問題なくなったので、あとは実際にデータビットを組み立てて数値を組み立てることになりますが、cなんてほんと何年ぶり?何十年ぶりだろうw

2020年7月15日水曜日

結構な頻度で取りこぼすのですが?

色々と試行している最中ですが、そろそろ万策尽きそうです。

DHT11の古い方のデータシートに注意書きとして
Note: The host reads the temperature and humidity data from DHT11 always the last measured value, such as twice the measured interval of time is very long, continuous read twice to the second value of real-time temperature and humidity values.
とデータタイミングダイアグラムの図の下に書いてありました。しかも赤い色の文字で。
英語力が無いのでよくわかりませんが、グーグル翻訳に通すと
注:ホストは、DHT11から温度と湿度のデータを常に読み取ります。たとえば、測定間隔の2倍は非常に長く、リアルタイムの温度と湿度の値の2番目の値を2回連続して読み取るなどです。
一時期のグーグル翻訳はかなりすごい感じで訳されてた気がするのですが、最近なんかパッとしませんね…
とにかく2連続で値を取得して後から取得した値がその時の気温、湿度だよと言ってるのだと思うのですがどうでしょうw

そのためにどうしても2連続でDHTからデータを読み込みさせようとしています。昔の記憶をたどると、DHT11では連続してデータを読み取ろうとするとエラーが頻発してしまうことがありました。これがDHT22も同様に発生しています。
原因を調べるために結構突っ込んで調べたのが昨日で、今日はそれなりに改善策を講じたりしていますが、結果としてこれといったインパクトはありませんでした。
面白かった?ポイントとしては、python3でarray.array('B', [0 for i in range(100)])とかで一気に領域確保を行うと、予想以上に時間がかかってしまい、センサーに送信要求を出すと、受信準備が整う前にデータが送信され始めてしまい、開始部分を思いっきり取りこぼすという事態に陥りました。
安易な逃げ方としてクラス変数にして、領域確保はインスタンス作成時に逃がして、使いまわすためにバッファの長さも変数として追加しました。
そのおかげで、サンプリング時のループがかなり安定したのですが、問題は結構な頻度で取りこぼしてしまう状態に。
過去の経験から、GPIO.inputを短時間に呼び出している部分を1μsとか10μs以下では呼ばないようにしたり、軽くなりすぎたループにあえてウェイトをかけたりと、やってみましたがこれといったインパクトもなく、悪影響しか出なかったので最終的にバッファの確保以外はそのままとなりました。
別の角度から取りこぼしてしまうのならデータから救えそうな状態なら救ってしまえということで、信号を取りこぼしてバースト状態になっている部分を補正する処理も実装してみたものの、予想以上に取りこぼしが発生していて多少は救えましたが決定打にはなりませんでした。
DHT22はDHT11と比べてデータシート上のタイミングが結構違っていて、推奨値なども結構異なっています。実際はDHT11と同様の手順でデータは送られてくるのですが、推奨値にしたり、記載通りの手順に則ると素早く受信開始できるようになりました。けども…どうしても2連続の呼出しを行うためには間に0.5~2秒程度時間を置かないとデータが送られてこないのであまり違いは無いかもw
データシートにはセンサー自体のレスポンスタイムが、条件付きの数字ですが、湿度は6s未満、温度は10s未満とそれなりにかかるので、下手をすると10s程度待たないとだめかもしれません。2sでスリープするっていうのが個人的には引っかかりますが、きっとデータ送受信部分だけなんだよね?と考えてます。
センサーのハード的な問題もありそうなのですが、一番の問題はやはりデータの取りこぼし問題なのは変わらず、ps -aFでプロセス番号を拾って、taskset -cp 0 14437でプロセッサー固定させて実行しても状況は変わらず仕舞い。
プロファイラなどでどこでウェイトがかかっているのか調べてもよさそうなんですが…ここで他の言語の力を借りることを考え始めました。pythonはその辺がメインの使用用途だと思うんですが、正直面倒くさそうwとはいえ、実際にどのくらいの実行時間がかかるのかちょっと様子を見てみたいということで、時間取得から様子を見てみました。
検索すると超単純な時間取得のソース(https://www.mm2d.net/main/prog/c/time-04.html)があったのでそれをコピペして実行時間を見てみることに。
pi@rasp3bprs:~ $ gcc test.c
pi@rasp3bprs:~ $ ./a.out
1594811448 557473
1594811448 557880

これはgettimeofday()で取得した値を表示させることを2回行っただけの物なのですが、これで結構参考になる数字が。秒数はさすがにカウントアップされないものの、μ秒の単位では
407μ秒「も」かかってしまっていて正直ダメかとも思ったのですが、よくよく考えればこれはprintfの処理も含まれているので、gettimeofday()の応答時間をざっくりと様子を見てみたいのであれば、連続して呼び出した後で表示させるしかないわけで…変えて実行してみました。
pi@rasp3bprs:~ $ gcc test.c
pi@rasp3bprs:~ $ ./a.out
1594811575 025131
1594811575 025132

勘違いでなければ1μ秒で変数に格納されているのでいい感じです。(すべてraspberry pi 3b+で実行)
実際に必要な解像度としては安定していれば20μ秒程度になってしまっても行けるとは思うので、とりあえずcで作って、pythonへの組み込みはそのあと考えようかなとか。

2020年7月14日火曜日

DHT22のCRCエラーが発生した様子

いろいろとグダりながらもざっくりと変更していて少しだけ見えてきた部分があります。
現時点での結論を言ってしまえば、原因は実行時の何らかのウェイトによってうまく信号を拾えていない感じ。

そもそもpythonでリアルタイムっぽい処理が書けててすごいなぁと感心してただけなんですが、DHT11で使用していたライブラリっぽい物も大雑把にしか見ていなく細かく追って行ってようやくつかめた感じです。
肝心のデータ信号の受信部分がループを使ったソフト的なカウントのサンプリングを行ってそこからビットを起こすということをやっているのですが、そのサンプリング処理はループに依存してました。時間軸とは異なっているために、ループ自体のサイクルが一定ではない場合データ受信で失敗するということです。
しかも、時間を無視しているために、エラー原因もいくらバッファを見ても原因の究明ができません。
良くも悪くも現状1ループ10μs以下では動いているので、サンプリングレートは10μs程度はあるようです。(Raspberry pi 3b+)処理時間がどの程度変わってしまうか正直わからないままごりっと書き換えてみました。
まずはデータフォーマットも単なるサンプリングから信号が切り替わるまでのループ回数を記録するように変更。これだけで配列への格納タイミングがざっと10分の1程度にはなるはずです。
実測値は 1508個から82個へ処理速度が上がれば上がるほど元々の方はデータが増えますが、変更後はバーストや取りこぼしなどで変動することはあっても極端に巨大化することは無いはずです。(変更後のバージョンは初期の信号状態を保持する必要があると思ったので、本来は81個のはず)

続いてループカウントになっている部分を時間へ変更します。
μ秒単位の時間操作がまともにできるか不安を抱えつつ、time.time()でμ秒を扱うことに。さらに、配列をarray.array('B')で1byteのデータとしてみました。(python3ではint扱いになっているので、1データあたりの消費量は4バイトだとは思います。('B')を指定したため、オーバーフローや範囲エラーが出るので想定外の事態になるとエラーで落ちるというおまけつきです(笑)
データーフローを見ると時間は最大でも80μ秒程度なので255μ秒以上は格納することが無いという前提です。
その結果がこちら
array('B', [1, 29, 81, 54, 52, 26, 52, 26, 54, 24, 53, 26, 54, 26, 51, 26, 54, 73, 53, 26, 66, 74, 52, 26, 54, 73, 52, 74, 52, 75, 52, 73, 54, 73, 54, 71, 68, 26, 54, 24, 54, 26, 56, 23, 53, 26, 51, 26, 54, 26, 52, 73, 66, 26, 54, 24, 55, 24, 55, 73, 54, 72, 53, 73, 55, 73, 52, 25, 64, 73, 54, 73, 54, 74, 52, 26, 54, 24, 54, 25, 54, 26, 52, 26, 45])
最初の1個目の1は信号線が初期状態でHIGH状態だったことを示し、その後からは切り替わるまでのμ秒時間となっています。数字を見る限りそんなに悪くない感じで入っていて、3B+のpython3でもそれなりに処理が行える感じがしました。
このサンプリング結果からビットパターンの信号のみを抜き出している部分があるのですが、もともとトリッキーな部分があったのですが、格納データを変えたのですこしすっきりしました。
データ開始の信号を除去してHIGH状態のビット情報だけのサンプリングデータは
[26, 26, 24, 26, 26, 26, 73, 26, 74, 26, 73, 74, 75, 73, 73, 71, 26, 24, 26, 23, 26, 26, 26, 73, 26, 24, 24, 73, 72, 73, 73, 25, 73, 73, 74, 26, 24, 25, 26, 26]
となります。基本的にはLOWデータは50μs前後の数値を取り除いた部分となります。
逆に取り除いた数字を並べてみましょう…手作業ですがw
52, 52, 54, 53, 54, 51, 54, 53, 66, 52, 54, 52, 52, 52, 54, 54, 68, 54, 54, 56, 53, 51, 54, 52, 66, 54, 55, 55, 54, 53, 55, 52, 64, 54, 54, 52, 54, 54, 54, 52, 45
結構疲れましたが、こんな感じ
最低は最後の45ですが、これはデータが終了した後のLOW信号なので正直関係ありません。
そうすると、範囲は51~66μsで、サンプリングサイクルが数μsということを考えれば悪くない範囲だと思います。
ここの処理以降はまた手を入れていませんが、データビットの0か1かを判断するためにサンプリングデータの最小値と最大値から閾値を決めてビット列を作成し、5byteのデータを組み立てていました。もともとはループカウントなのでこのようなことをやっていますが、サンプリング単位がμsになっているので、もうすこし決め打ちしても大丈夫なような気はします。ただスループットが低いRaspberry pi zeroとか2bとかを考えるともう少し様子を見ないとダメですね。zeroは欲しいと思いつつ、結局まだ買っていない…。

ここまできてようやくCRCエラーやビット落ちの様子が判断できるようになります。

何度かコンソールから連続してセンサー値を拾っていると、結構な確率でエラーが発生するようになります。
その時に発生したCRCエラーの様子。
pi@rasp3bprs:~ $ python3 ./setup/sbin/dht22.py --pin 6 --debug
pin : 6
Output for debugging.
array('B', [0, 74, 79, 55, 24, 55, 23, 54, 28, 52, 24, 54, 27, 54, 23, 53, 76, 51, 28, 67, 71, 54, 71, 54, 28, 54, 23, 54, 76, 50, 28, 54, 72, 54, 72, 67, 28, 54, 23, 54, 28, 54, 23, 54, 24, 56, 24, 54, 28, 54, 71, 67, 28, 50, 28, 51, 28, 51, 77, 50, 28, 54, 72, 54, 23, 54, 25, 63, 76, 54, 71, 54, 71, 54, 27, 54, 23, 54, 28, 54, 71, 54, 23, 49])
[24, 23, 28, 24, 27, 23, 76, 28, 71, 71, 28, 23, 76, 28, 72, 72, 28, 23, 28, 23, 24, 24, 28, 71, 28, 28, 28, 77, 28, 72, 23, 25, 76, 71, 71, 27, 23, 28, 71, 23]
the_bytes
02 cb 01 14 e2 ← これは正常なデータですが、初回なので破棄されますw
array('B', [0, 58, 88, 48, 25, 59, 25, 56, 25, 49, 25, 56, 25, 56, 24, 55, 76, 49, 24, 100, 39, 56, 77, 46, 24, 56, 32, 45, 77, 55, 24, 56, 65, 56, 77, 66, 24, 56, 24, 56, 25, 56, 24, 56, 24, 49, 25, 56, 24, 56, 66, 76, 24, 56, 24, 48, 24, 56, 77, 55, 24, 56, 66, 56, 24, 56, 28, 56, 76, 55, 76, 45, 75, 56, 24, 56, 24, 55, 25, 56, 66, 56, 24, 46])
[25, 25, 25, 25, 25, 24, 76, 24, 39, 77, 24, 32, 77, 24, 65, 77, 24, 24, 25, 24, 24, 25, 24, 66, 24, 24, 24, 77, 24, 66, 24, 28, 76, 76, 75, 24, 24, 25, 66, 24]
the_bytes
02 4b 01 14 e2 ← CRCエラーが発生しているデータ
CRC Error.
2nd. retry. 2
array('B', [0, 50, 76, 48, 36, 49, 25, 56, 25, 49, 25, 56, 24, 56, 24, 56, 76, 48, 25, 67, 77, 56, 66, 56, 24, 57, 32, 46, 77, 45, 35, 45, 76, 56, 66, 66, 35, 45, 35, 46, 24, 55, 24, 55, 24, 58, 24, 55, 24, 55, 76, 65, 24, 55, 24, 48, 35, 45, 76, 55, 24, 56, 66, 55, 24, 55, 28, 115, 31, 46, 66, 55, 76, 56, 24, 55, 24, 55, 26, 56, 65, 56, 24, 45])
[36, 25, 25, 25, 24, 24, 76, 25, 77, 66, 24, 32, 77, 35, 76, 66, 35, 35, 24, 24, 24, 24, 24, 76, 24, 24, 35, 76, 24, 66, 24, 28, 31, 66, 76, 24, 24, 26, 65, 24]
the_bytes
02 cb 01 14 62
CRC Error.
2nd. retry. 2
array('B', [0, 62, 85, 57, 24, 57, 24, 45, 35, 48, 25, 55, 25, 55, 24, 56, 76, 47, 24, 65, 75, 55, 76, 55, 24, 55, 30, 46, 76, 55, 65, 54, 24, 55, 24, 66, 35, 44, 34, 45, 34, 45, 35, 45, 34, 48, 24, 56, 24, 55, 76, 65, 24, 55, 24, 58, 24, 55, 66, 56, 24, 56, 75, 55, 24, 55, 27, 56, 76, 55, 75, 45, 76, 55, 24, 55, 24, 55, 25, 55, 76, 45, 76, 46])
[24, 24, 35, 25, 25, 24, 76, 24, 75, 76, 24, 30, 76, 65, 24, 24, 35, 34, 34, 35, 34, 24, 24, 76, 24, 24, 24, 66, 24, 75, 24, 27, 76, 75, 76, 24, 24, 25, 76, 76]
the_bytes
02 cc 01 14 e3
DHT22 results.
Retry count : 2 time(s)
Error code  : 0
Temperature : 276 C
Humidity    : 716 %
なんのこっちゃというような結果ですが、気温と湿度が10倍されたままというのは置いておいて、最初に正常にデータを得られているのですが、どこかに「センサー値の現時点での値を取得するためには複数回データを取得する必要がある」と書いてあった記憶があり、破棄しています。記述された資料が見当たらないので昨日この部分を外して様子を見ていたら、しばらく放置して2回センサー値を表示させたら1回目と2回目で気温が1℃以上変わったので、スリープ直後はスリープ前のセンサー値を返す感じなのだと思います。
で、問題なのが、DHTが悪いのか、Raspberry piが悪いのかわからない部分で、連続でデータを取得するとエラーが発生しやすい。でも連続で値を取得しないと現時点でのセンサー値が分からないというジレンマに陥ります。
ま、どうでもいいんですがw

で、本題の異常値を探ってみると、2つ目のサンプリングデータで100μsとかある部分があります。3つ目では115μsとなっている部分があり、おそらくこの時に何らかの原因で処理が止まってしまったのだと思われます。
今回は運良く?LOW信号がバーストしたような感じになっていてHIGH信号のビットが1になるべきものが0になっただけと考えられます。(気色塗り部分)

もう一つのデータ落ちに見えてしまったパターンはおそらくHIGH信号がバーストしてしまったようになってしまい、結果としてLOW信号への切り替わりのタイミングを取りこぼした結果だと推測しています。
さてどうでしょうか…

array('B', [0, 70, 76, 56, 27, 54, 21, 54, 26, 54, 29, 49, 26, 54, 26, 54, 73, 54, 22, 71, 70, 54, 75, 54, 25, 49, 25, 57, 27, 54, 26, 49, 77, 54, 26, 65, 24, 55, 26, 54, 26, 53, 26, 49, 26, 54, 29, 48, 26, 53, 76, 65, 26, 54, 25, 55, 24, 54, 72, 54, 26, 49, 76, 54, 26, 54, 71, 61, 77, 54, 71, 183, 30, 49, 72, 55, 26, 49, 78, 52, 27, 43])
[27, 21, 26, 29, 26, 26, 73, 22, 70, 75, 25, 25, 27, 26, 77, 26, 24, 26, 26, 26, 26, 29, 26, 76, 26, 25, 24, 72, 26, 76, 26, 71, 77, 71, 30, 72, 26, 78, 27]
pull up lengths Error. len : 39
ログから漁ってみましたが、こんなデータです。
パット見あまり変なところはありませんでしたが、赤塗したところが怪しそうな気がします。信号的には60μsを超える波形はデータ開始時のみでデータ中には現れることはありません。なのでここの間にHIGH信号を完全に取りこぼしているのかな?と推測しました。
とおもったらもっと怪しいのが183とかトンでも数字がありましたwよって正解は、HIGH信号のバーストではなく、LOW信号バースト、正確には単なる「HIGH信号の取りこぼし」でした(笑)

といった具合に、悪者がはっきりしました。Raspberry pi で動いている環境でした(´-ω-`)

Raspberry pi 4bでDHT11を動かしたときにエラーが発生しにくい気がするというのは気のせいではなく、処理速度が上がってスクリプトの実行が安定した結果だった様です。

あと気づいたのは、正常時はビット信号が綺麗にサンプリングできている感じなのに対して、エラー時は全体的にサンプリングにばらつきがあるように思えます。70μsを越えているデータが頻発している。



データシートに詳しくはホームページ見てね的なことが書かれていたので見てみると、センサーの一押しが、DHT10とか外見はあまり変わらずI2C仕様のDHTが…マジかw早速アマゾンで見てみると、3個セットで2400円ぐらいだったかな…微妙な値段だけど、悪くなさそう?

2020年7月13日月曜日

DHT11とDHT22

色々と思うところがあってセンサーの選定をしていましたが、安価で簡単に手に入りそうなDHT22をポチって見ました。2個セットで1400円。一個700円とか3個セットのBME280より高価ですね…。

で早速手持ちのソースをデータシートをみつつ変更したところ、いきなりうまく行かず…すこし試行錯誤した結果、開始時の手順はDHT11の時と同じにするのが安定しました。データシート上だと細かく記載されていて手順が増えているようにも思えたのですが、結果としてpull upされた状態からダウン状態にした後、アップして待機するだけでデータが送られてきました。

それなりにDHTの共通ライブラリっぽいソースも見てみましたが、DHT11とDHT22とではデータの加工方法が違うだけのようでした。

今回DHT22を選んだ理由として、小数点以下の数字が取得できるっていうのがポイントだったのですが、これは今日発覚したDHT11でも温度の小数点が取得できたということで半減してしまいました…。(解像度が16bitで温度が整数のみってどう考えてもおかしいだろという突っ込みはさておきw)

気を取り直してスペックを見てみましょう。

DHT11DHT22
温度範囲0~50℃
-20~60℃
-40~80℃
温度解像度16bit16bit
温度レスポンス10s10s未満
湿度範囲20~90%RH
5~95%RH
0~99.9%RH
湿度解像度16bit0.1
湿度レスポンス6s5s未満
電源電圧3.5~5.5V3.5~5.5V
消費電力0.3mA8mA
サスペンド時60μA10~20μA
スリープまでの時間2s2s
上記は各データシートからの抜粋値で、レスポンスに関してはデータシート上に前提条件として1/e(63%)とか25℃でとか書いてあるので、センサー素子から値を得るのに何度も試行する必要があるもののようです。範囲が広がっているDHT22の方が消費電力が上がっているのもこの辺が影響してそうです。

正直、アップデート後のDHT11なら一般使用は問題ないんじゃというのはさておき、気になる違いはとしてDHT22の優れている点は温度、湿度の範囲が少し広いところでしょうか。DHTの仕様特性として、ほとんどの時間をサスペンド状態で放置されるはずなので、サスペンド時の消費電力が低くなっているのもいい感じです。
デメリットとして消費電力が数字上結構上がっているところ。データシート上はturn onと書いてありましたが、絶えず値をとり続けるのであれば、DHT11は結構優位ですね。

見直せば見直すほど、DHT22を買った利点がないわけですがw



関係ないところで、Raspberry piの放熱やセンサー自体の放熱もあるようで、DHTシリーズはエラー時のリトライで取得できる温度が少し上ってしまう感じがします。手で連続で値を取得すると温度がすこし上がることが多いのもおそらくセンサー自体の発熱があるためのような。

仕様を呑み込めなく錯乱へ…

昨日電車の中で、スマホでデータシートやソースを見ながら頭の中を整理してかなりの部分がすっきりしたのですが、困ったことにDHT11の仕様で勘違いしていた部分があったようで…

そもそもの起点に戻って整理してみようかと。

まず元となったpythonのソースがどこだったのか謎だったのですが、おそらく
ここだったと思われ。再度アクセスしたらgitサーバーが落ちたのかアクセスがエラーに。
で、経緯としてはその後、修正が加わって大きな違いは
「温度と湿度ともに小数部を加味するようになった。」
という点。
で、DHT11自体は仕様がアップグレードされセンサーの感度範囲が広がったこと。
湿度は 20-90%RHから5-95%RH
温度は 0 to 50 ℃ to - 20 to 60 ℃

大きな違いは温度がマイナス温度から検知できるということ。

で、アップデート後のドキュメントで温度に関しての説明で
The temperature is high as part of the data of the temperature.
The temperature is low as part of the data of the temperature, and the temperature low bit 8 is the negative temperature, otherwise the temperature is positive
と書かれていて、あんまり意識していなかったのですが、これをグーグル翻訳にかけてみると…
気温のデータとして気温が高い。
温度データの一部として温度が低く、温度の低いビット8が負の温度で、それ以外の場合は温度が正
意味不明すぎなのですが、原文を見直せば見直すほど意味が理解できなくなりました(*´Д`)
もともと英語力なんて無いのであれなんですけどw
英語圏の人はこれで理解できるのかな?

で、色々と考えた結果、アップデート後のv1.3では「温度がマイナスの場合は、温度の下位バイトの8ビット目が1になる」というとらえ方でいいのかな?どのタイプのDHT11を手にしているのかもわからないのであれなんですが、、、現状の上記のgitのソースを見ると、
humidity = the_bytes[0] + float(the_bytes[1]) / 10
となっていて、pythonで 0b10000000ってなっててもマイナスの扱いなんてしないし、そもそも足しこんでいるのですっげーやばいと思うんですよね。
そもそも「/10」っていう部分が気になる人には気になるかもw
pi@rasp4b4g:~ $ python3
Python 3.7.3 (default, Dec 20 2019, 18:57:59)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 0b10000000
128
>>> float(0b10000000)
128.0

こんな感じでfloatしても、こんなことに。 仕様上は下位バイトは0~9までしか格納されないのですが、マイナスの場合は符号ビットを取り外さないとイケないわけで。
humidity = (the_bytes[0] + (the_bytes[1] & 0x7F)*0.1)*(-1if(the_bytes[1] & 0x80)else(1))
とか、そんな感じにする必要があるような?

v1.3のセンサーも無いし、データシートのデータサンプルに気温4024℃とパリティーエラーしか載ってないので正確なことは言えませんが。

他にもなんか色々といままで見かけた仕様も矛盾してしまってたりしてほんとに錯乱状態だったので綺麗さっぱりと忘れることにしよう。

この辺が気になりだした原因は、そもそもDHT11の仕様で湿度、温度ともに整数値しかないと思っていたところ、受け取った4バイトのデータを実際に表示して気づいたという。
しかも、decimalを10進と翻訳されて、データフォーマットが回路用に設計されてるのかな?とか勘違いしてたのが原因でした。実際のデータを表示させてその勘違いに気付いた次第です。




そもそもなところで、センサー誤差を拾うために結局DHT22も入手して只今思案中w