2020年7月31日金曜日

ドライバで気になっていること

ドライバでちゃんと動作しているのか確認するために色々見ていたところ、メモリーアクセス時のアライメントはどうなっているのか?という点

試しにGPIOレジスタ周りで実際に表示させてみたところ

[250867.394720] f0abd000 00008900
[250867.394733] f0abd000 00008900
[250867.394749] f0abd001 00677069
[250867.394764] f0abd002 69006770
[250867.394778] f0abd003 70690067
[250867.394790] f0abd004 00024000

アドレスとその内容を出力させてみると、4バイトごとの時には正しくアクセスできているようですが、アライメントからずれているとどうも前後ではない部分にアクセスしている感じ。
気になるのはその「どこか」の内容がそれなりにずれているのも気になります。

一回りしてpythonに戻って

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

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

2020年7月30日木曜日

pythonで作って動かしてたけど、cで作り変えて軽くしようかな?

などととてつもなく面倒そうな事が頭から離れません。

そのまま動くから絶対的なオーバーヘッドが無くなるからraspberry piに優しいはず?
基本的にはmotionを動かしっぱなしなのですが、そこまでcpuリソースが切迫しているわけでもないわけで…
そもそもpythonで揃え始めた理由の一つはスクリプトなのに結構そのまま動かせるというのが一番の理由だった記憶が。

2020年7月29日水曜日

ioctlだと割り込みは発生しない?

寝付こうかと思ったらふとこんな疑問が。

システムコールが割込み処理という話は今まで聞いたことは無いけど、ありえなくはなさそうなので、気になりだすとダメですねwざっくりと検索して見るものの、これといった話はなさそう。なので割込み制御部分をコメントアウトしてしばらく様子を見ることに。

連続実行しているとようやく発生しました。

2020年7月28日火曜日

ようやく綺麗な感じに

のんびりとドライバの作り方を参考にそれなりのものが形になってきました。
ユーザープログラムから一歩踏み込めば割り込み制御もなんの制限もなく行えました。
この辺の部分の経験は全く無く、ハードウェアで遊んでいたところからいきなり離れてソフト的な部分が主体となってしまった結果だと思います。

そんなドライバとアプリを組み合わせた見慣れてきたデバッグ結果は…

2020年7月24日金曜日

ドライバをコンパイルできる環境

ドライバの作り方を見てみると、凝ったことでなければ敷居は低い様なので試してみることに。

最初に躓いたのは、Makefileの名前をmakefileとしたことによってmakeが通らなかったり、ヘッダファイルのバージョンが実行環境と不一致で強引にコンパイルしてinsmodしたときにエラーで失敗したり。

割り込みの様子を見てみる

日照時間が少なく、日が差しても気温が上がるだけで湿気が高い日が続いていて過ごしにくい日が続いていますね…。

色々と無駄な足掻きを必死にしてもう少しで、、、というところでやっぱりダメという感じのことを繰り返しています。

取りこぼしの原因は何らかの割り込み処理なんだろうなぁとは分っているものの、μsやms単位の処理を気にすることはほとんどない世界に居すぎたため、armやlinuxの割り込み処理を調べることがから始めました。

割り込みを一時的に停止させる方法は大きく分けて3つあるようで、アセンブラでCPSIE/CPSIDでIとFのフラグを操作すれば割り込みを有効/無効化する方法と、armのメモリマップドレジスタを操作することで割込み制御を行う方法があるようです。
ただし、CPS命令は特権モードでしか使用できません(無視されるだけかも)。

2020年7月19日日曜日

処理落ち

検索して何とかなるなら何とかしたいので「処理落ち」とかで日本語も検索してたらちょっとびっくりするようなネタが…

そこには処理落ちと並んで「重い」「遅い」とかの蘊蓄が綴ってありましたが、
昔のゲームのスプライト表示が消えたりする事が「処理落ち」です。と、エー😰

表示落ちとかグラディウスや沙羅漫蛇ぐらいの状態なら処理落ちでいいと思うけど、消えるのはハードウェアの制約で、ちらつくのはただ消えるなら表示タイミングごとにスプライトの優先順位を切り替えた結果ちらついてるだけで、処理落ちとは違うんじゃないかなぁ…と

まぁそんなことはさておき、処理落ちを抑えるためにはcpuロックを行って排他的に占有するぐらいしか手はなさそうです。しかもカーネルじゃないのに出来るのかどうか…う~ん…

英語で検索するしかないかな…

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

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

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

2020年7月18日土曜日

cでも取りこぼしがw

sampling_start_lev : 1
sampling_end_lev   : 1
length : 82
179 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 26 54 73 54 26 54 73 67 26 54 26 55 40 39 27 53 26 55 26 53 27 54 25 67 74 54 73 54 74 54 73 54 74 54 74 54 26 54 25 64 74 54 26 54 73 54 74 54 26 54 26 54 73 54 73 47

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円ぐらいだったかな…微妙な値段だけど、悪くなさそう?

配列への追加についてグダる

時事ネタは突っ込み所が多すぎて全然関係のない話題が続いていますが、今回もそんなネタ。

最近のスクリプトや言語は型や配列に関してものすごい自由度が高いのですが、その代償として何かしらの犠牲を伴っています。

私が言語に最初にどっぷりと漬かった言語はturbo-pascalなのですが、その原因はどうしても細かい所まで見て実装を確認しないと処理が上手く回らなかったり、処理時間を短くするために内部でどのように動いているのかまで見ないといけなかったからだと思います。
あと付属するマニュアルが結構読み応え満点で実際にそうなっているかどうかは別にして、結構突っ込んだ事まで解説されてたりしました。日本語版なので訳者の思い入れが加味されていたのかもしれませんが。

基本的な考え方として、配列は単なるメモリ空間なので、最初に領域を決めたら拡張することができません。動的に変動させるためにはどうするか?というところで実装の仕方は色々な考え方でうまく乗り越えているだけのこと。
追加に対して代償を減らすには領域確保で指定された領域以上に予め確保することで時間的リソースの節約を行います。これはどんな形の配列でも変わりません。繰り返すことでメモリー空間に分断された状態になると、どうしてもアクセス効率が落ちるので、最終的にはガーベージコレクトや再生成するなどして綺麗な状態に保つ必要があるかもしれません。とはいっても、すでにCPUキャッシュやらなんちゃらキャッシュやら、広大なメモリー空間のおかげで実害は出てこないのも事実。ただおまかせ状態だとそれなりに何らかのリソースの浪費はしていると思います。

一番非効率なやり方は、新たに配列全体の領域+追加分の領域を確保して、配列の内容のコピーを行って不要になった領域を解放するという方法ではないでしょうか?これの代償としては配列に対してメモリアドレスなどで直接参照しているものがある場合、参照している部分も変更する必要があるので、結構な処理負荷が出てきたり、放置すると簡単に処理が落ちることになります。
これを結構積極的に行っている言語というかシェルというか物が、おそらくAndroidだと思います。基本的な考え方として、すべてのオブジェクトインスタンスは最悪破棄して再生成すれば元通りという発想のもと、リソース不足になると積極的に破棄したり、どこかに追いやられてしまいます。最近のバージョンではかなりうまい仕組みが導入されて改善されていますが、その辺がどうしても好きになれない場合は苦痛しかないかも。
とはいえ、非効率なやり方の利点として、追加しても常に新規に領域確保された状態になるので配列へのアクセスでまずおかしなことは発生しません。配列の要素へのアクセス速度も常に変わらない状態でアクセスできるため、タイミングの影響を受けるようなこともまず発生しません。

で、なんでこんなたかが配列への要素の追加というくだらない話をだらだら書いているのかといえば、pythonスクリプトで結構安易に .append()で要素の追加を行っている部分を見て、最近気にしたことはなかったけどこの辺どうなのかな?とちょっと突っ込んでみたところ、やはり結構処理的に割高な処理なんだと再認識したのでグダりましたw
たしかに便利なんですけどねw処理時間がμ秒単位の場合はやめた方が良いかも。どうしてもという場合はpythonの場合はarray.array('b')とか、要素の追加を減らす処理に変更するとか、そもそも要素の追加をさせない形にするとか、仕様を見直した方がいいんじゃないかな?

実際にどんな処理でこの辺の処理時間が気になっかと言えば、DHTxxのライブラリっぽいやつで、エラーが発生したときにどんな状態で発生しているのか気になったので色々と書き換えて気になりました。
どのくらいのインパクトがあるかといえば…raspberry pi 3b+でループでGPIOピンの状態をサンプリングしている処理があるのですが、そこでループのタイミングで配列に要素を追加してサンプリングしていたときは、最大25回ぐらいだったのですが、これを信号が変動した時だけにした場合、最大30回ぐらいになりました。「ただし」が付くのですが、raspberry piで使用しているCPUはマルチコアとなっていてメインとなるプロセッサー0とその他とで処理能力の差があるようで、25回→10回、30回→10回ぐらいに簡単に落ち込みました。処理が落ちてる状態なら変わりないということではなく、おそらく不利になるのがGPIOなどのIO周りのウェイトがプロセッサーによって異なっているためだと思います。逆に言えば、現状ではメモリアクセスの時間リソースへの影響が小さくなったという結果なのかもしれませんw(結局何が言いたいんだ(;´д`)

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の温度は小数点まで持っていたw

錯乱状態が少し解消され様子を見たところ、
16時過ぎぐらいからグラフの形が矩形からいい感じになりました。
今までは小数点以下は無いと思っていたので、ここで100均の基準温度計に表示を合わせてみます。
小数以下がカットされていたという感じの重なり具合に。

すでにDHT22を買ってしまったんですが、温度の小数部があるなら、基準センサーはDHT11でもよかったかも…(==;

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

昨日電車の中で、スマホでデータシートやソースを見ながら頭の中を整理してかなりの部分がすっきりしたのですが、困ったことに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

2020年7月11日土曜日

再起動後、録画直後にmotionが落ちる

再起動した後、気づいたらカメラの動作ランプが消えていて、サービス無効化したっけか?とsudo service motion statusで確認すると、Activeの表示。(topとかでプロセスの状態を確認すればよかったかも…)問題がなさそうなので、ログを見てみることに。
/var/log/motion/motion.log
が存在していたので、起動したのは確実。理由はこのraspberry piはほとんどのlogはtmpなので再起動で消えてしまいます。
中身を見ると、確かに[ERR]がいくつかでててそこで終わっています。

特に考えもせずにsudo service motion restartで、motionを再起動。
そしてカメラを動作させて様子を見ると…普通に動いてます。ですよねぇ…今まで動いていたし。
思い起こせば、マスクをかけていたのを外したからムービーファイルが巨大になって落ちたのかな?とか。でもそんなことはないと思うのでちょっと納得いかない。

しばらく放置しても落ちそうになかったので、再度再起動してみる。
カメラの動作ランプが点灯して、motionを反応させて録画させてみると、保存のタイミングでカメラのランプが消えてしまいました。落ちたか…。
sudo service motion restartで再起動させて、しばらく様子を見ると普通に動作している感じです。
原因がさっぱりわかりません。

再度ログをみてみると、
[1:ml1:F] [NTC] [EVT] [Jul 11 01:38:03] event_newfile: File of type 8 saved to: /tmp/20200711013802F-01.avi
[1:ml1:F] [NTC] [ALL] [Jul 11 01:38:03] motion_detected: Motion detected - starting event 1
[1:ml1:F] [ERR] [ENC] [Jul 11 01:42:20] ffmpeg_avcodec_log: Too large number of skipped frames 235812 > 60000
[1:ml1:F] [ERR] [ENC] [Jul 11 01:42:20] ffmpeg_put_frame: Error while writing video frame: No such file or directory
[1:ml1:F] [NTC] [ALL] [Jul 11 01:42:20] preview_save: different filename or picture only!
[1:ml1:F] [NTC] [EVT] [Jul 11 01:42:20] event_newfile: File of type 1 saved to: /tmp/20200711013809F-01-01.jpg
[0:motion] [NTC] [ALL] [Jul 11 01:56:05] motion_startup: Using log type (ALL) log level (NTC)
[0:motion] [NTC] [ALL] [Jul 11 01:56:05] become_daemon: Motion going to daemon mode
設定は結構変なことをしていて、一旦tmpに動画を作らせてからそのあと保存用のディレクトリに移動させてたりするので、出力が/tmpになってたりします。

原因の一端にその辺もありそうですが、エラーメッセージで検索をかけたところ、
を見てみると、timelapse設定をオフにしてみると対処できるらしい。が、そんな機能使ってないんですが。
まぁそれはそれとしてmotion.confを見てみると、timelapse関連の設定は、

ffmpeg_timelapse 0
ffmpeg_timelapse_mode daily

の2つありました。ffmpeg_timelapseが0なのでオフのはずなのですが、エラーメッセージから推測すると、ファイル名を変更しようとしているのでffmpeg_timelapse_modeも影響がありそうなので、
ffmpeg_timelapse_mode manual
と設定変更してみました。

変更後、とりあえず落ちることは無くなりました。





motion -hで表示されたバージョン情報
motion Version 4.0, Copyright 2000-2016 Jeroen Vreeken/Folkert van Heusden/Kenneth Lavrsen/Motion-Project maintainers

パッケージバージョン
4.0-1

2020年7月10日金曜日

サンワサプライのマウス

サンワサプライから発売された無線2.4GHz/bluetoothのマウスがちょっと気になりました。
カッコいいかと言われれば微妙ですが、使い心地が気になります。

結局今使ってるマウスは軽くて、電池で使用できるただのホイール付きのマウスで、利点としては内蔵バッテリーではないので電池切れを起こしたときに即座に入れ替えて使い続けられること。
bluetoothヘッドフォンで結構痛いのが、内蔵バッテリーの欠点はバッテリー切れの時の復帰時間が必要になってしまうこと。これを補うために、ヘッドフォンは最低でも2つ無いとかなり不便です。
ヘッドフォンは軽くするためにバッテリー内蔵させた方が軽くなるので選択肢が無かったりしますので諦めざるおえない部分です。

細かい記載が無かったのでサンワダイレクトのページにあった取扱い説明書によると、USBケーブルで接続しても直接マウスデバイスとはならないものの、充電中もマウスの操作は可能と書いてありました。
結構欲しいかもw

2020年7月6日月曜日

グラフのセンスが無さすぎる

センサーごとのアジャストを行ったり、色の調整を少ししたら、

そういえば温度のメモリも1℃刻みでほしいよなとか、
温度、湿度、気圧と表示は気温しか出していなかったので、温度と湿度の軸を揃えてみようかなとか、

まずは温度の刻みを直してみると、グレーの濃さが微妙だなとか、、、きりがないw

私以外見ることが無いマイグラフなのでとにかく把握しやすいものということであえてy軸は温度、湿度、気圧と分けていたわけですが、phpのスクリプトは単純にそれぞれ独立して上限下限を決めてプロットしているだけなので、軸を揃えたければ単純に一致させてあげるだけでグラフも簡単にそろう作りになっていたので試しにやってみたところ
高湿度のこの時期はこんな感じに。温度の波が今までの半分程度になってしまうので結構寂しい感じに。
元々、温度を中心に考えて、湿度と気圧はおまけ的に表示させていたのですが、改めて見てみると、やっぱり軸を揃える意味は少ないようです。
温度に関しては、気温は0℃~40℃、Raspberry pi の温度が~60℃として考えても、20目盛ぐらい助長になっています。
軸の状態を元に戻してみると、、
高湿度なのでRaspberry piの温度と重なってしまう感じになっていますが、数字の重なりさえなければ実用的に問題ないかなぁ…グラフとしては上下に踊ってくれた方が楽しいし。
やはりほとんど波が無いグラフは面白くない。

正直、グラフなんてこだわったことがことがほとんどないのでセンスも何もないのが一番悲しいw





DHT11とBME280の差異は上下に数値を補正しただけで波形はほぼ一致しました。試しに湿度や温度をエアコンで変えてみましたが振れ幅も一緒なのでこの温度、湿度下ではいい感じです。冬場までこのままセンサーをつなげたままの状態を維持すれば少し違った結果が得られそうな感じはします。
ただ、驚くべきことに、安価なDHT11の湿度の方が反応が良いようです。温度の反応速度はどちらも一緒のような雰囲気です。

意外と変なところに躓く…

グラフの表示をかなーり低レベルで自前で折れ線グラフを書いているわけですが、いろいろと引っかかってきました。
設定ファイルとしてiniファイルを使ってみたところ、phpではコメントに2byteコードを書いても無視してくれるのですが、python3だとコードページが違うだとか結構面倒な感じに。
Windows10のメモ帳でもUTF-8が扱えることを考慮すると、すべてUTF-8で揃えてしまうのが幸せになれそうなのですが、iniファイルといえばシフトJISとか変なプライドが邪魔してしまい、shft-jisファイル形式のままアップロードしています。

昔はクライアント内ではクライアント側のファイル形式や文字コードで管理してftpで変換し、サーバー側ではサーバーのファイル形式、文字コードという考え方でよかったのですが、最近はやってもファイル形式(とはいっても改行コード変換程度)で文字コードはそのままというかなりアバウトな感じで管理していました。
その一つにphpが結構いい感じに文字コードや改行コードを無視してくれるというのがあったのですが、これがpython3になると、スクリプト内の文字コードも結構厳しかったり、パーサーなどもしっかりとコードページを意識しているので簡単に処理が通らなくなってきます。

一番最悪だったのがiniファイルをsift-jisのままアップロードして、サーバー側のテキストエディタでコメントまでコピペして保存するとそのファイルはsift-jisとUTF-8が混在した不思議なファイルが出来上がってしまうと、python3のiniパーサーで直接扱うことができなくなってしまいます。
サーバー側で直接触らなければいいのでしょうけど、設定ファイルだし直接触りたい欲求はあるわけです。
で、python3でiniファイルを扱うときは予めコメントをカットしてパーサーに渡してあげれば…とか考えたんですけど、そこまでしてオーバーヘッド増やすなら、いっそのこと全部UTF-8でいいんじゃ?的なw

phpのiniファイル絡みでもう一つ。
iniファイル上のセクション名などに関して数字で始まる文字は数値になるとかいう仕様があってこれが値に対しても行われるのかな?と思ったら行われていなかったという。
色の設定を行いたくて、
name=0xF0F000
みたいな設定を行ったら、結果としてiniパーサで、文字列として取り込まれていたのですが、これが分かるまでに結構四苦八苦しました。
結果としてデバッグ用に if (php_sapi_name() == 'cli') とか判断して色々出力するようにしておきました。これで今後はかなり複雑怪奇な形になっても内部が目で見えるようになるので便利かな?

2020年7月5日日曜日

センサー類の情報、そして個人的なセンサーに対する結論

センサーについて何かないかと検索してみると、見知らぬ型番が出てきて少し気になっていました。そもそも出発点は電子部品セットの中にあったDHT11とBMP180の2つの温度センサーからなので、それ以外の系統の部品やメーカーは知りもしませんでした。

その一つとしてSHT11という型番。
メーカー特性としては精度は高いが、部品も高いという感じらしい。

視野が狭すぎると感じたので他にも温度系のセンサーはない物か?と検索し続けてみましたが、デジタル部品としては主にこれらの3社系統がメジャーな雰囲気です。

でそんな中、似たような趣旨の記事を発見!


ようやく複数のセンサーを比較している内容を見つけた感じです。言語を広げればもっと良かったのかな?
同じ型の部品も複数比較していたり、かなり突っ込んでいると思います。

上のブログ記事にも書かれていますが他のブログ記事にもDHTシリーズの湿度は実際の値より少し高めになってしまうようです。

デジタル部品を見回すと、メーカーや型番によりかなり差があることは当たり前のようで、絶対的な温度、湿度の精度を求めるのならアナログ部品を使って直接値を加工した方がよさそうな気がしてきました。

寝る前にはその気になるセンサーを入手して使ってみようかとも考えていたのですが、どうせ個体差があるならこれ以上細かいことを気にしても用途(ただ室内の温度を知りたい)的に意味がなさそうです。
デジタル部品であっても基準を決めて補正するとか、複数の種類のセンサーを組み合わせて補正するしかなさそうですね。



最後に昨日の夜からDHT11とBME280を1分間隔でロギングしたグラフを残しておこうかな。
温度に関してはDHT11は少数なしの整数値なので矩形波っぽくなっていますが、温度に関してはDHT11が30℃、このBME280とはほぼ一致している感じです。目視の温度計は29.0℃ぐらいなので、まぁ誤差範囲でもいいとおもいます。
湿度は、DHT11では70%、BME280では58%と、数値として12程度ずれています。このBME280は湿度が異様に低くなっていますねぇ…

(2020/08/03追記) 大きな訂正となりますが、DHT11の気温は小数点第一位まで出力しますが、この時点では整数しかもっていないものと勘違いしていました。

2020年7月4日土曜日

気になりだすと止まらないセンサーの精度

使っているスクリプトを色々な角度から確認してみたところこれ以上おかしな点はなさそうなので、さらに別の角度から考えてみようかと。
極論は温度、湿度、気圧計を横に並べて比べてみるしかないとおもいます。
ですが、手持ちに無いので、既に使わなくなってしまったDHT11を発掘してつなげてみました。
個人的には、DHT11は温度、湿度センサーで、精度も整数値しか取得できない上、センサー値を取得するときにどうしてもエラーが発生してしまうことからあまり信用していませんでした。
エラーに関しては仕様上の問題もありますが、レスポンスが確実に悪くなるのと、リトライが発生するので時間上不安定になりがちになります。いままで10分間隔にロギングしていた理由の一つとして当初はDHT11も使用していて分刻みだと1分以内に処理が終わらなくなるかも?と考えていたからです。もうすこしエラー率が低くて、リトライもあまり発生していなければよかったのですが。あとは、分刻みだとログデータの発生量が結構インパクトありそうな気がしていたところです。ざっくりと計算すると、固定長のバイナリファイルに収めると1年間で10Mbyte程度になりました。これが大きいか小さいかというのは考え方次第です(が、他に本体の電源電圧やCPUクロックなどのログも取り入れていけばそれだけ大きくなってしまいます)。現状はいろいろ便利そうという理由だけでSQLite3を使ってデータを記録しているので、だいたい1日80Kbyte程度。これが365日なので約26Mbyte程度となります。(ざっくり昼間計算したら1年間で30Mbyteという数字が出てきたきがしますw)
まぁその他もろもろの事情で10分間隔だったわけでが。

さて、少しロギングできたので様子を見てみると…

DATE TIMEBoardTempHumiPressTemp3Humi3error3Temp1-3Humi1-3B - T
2020-07-04 23:07452772877066309100405429000780000-230-1169116507
2020-07-04 23:06452772882066196100414029000770000-180-1080416457
2020-07-04 23:05447902883066111100419329000770000-170-1088915960
2020-07-04 23:04452772888065887100418130000770000-1120-1111316397
2020-07-04 23:03457642892065794100422330000770000-1080-1120616844
2020-07-04 23:02452772884066079100417430000770000-1160-1092116437

データは1000倍して整数値で格納しているので、温度のBME280とDHT11の差は1℃程度でしたが、気になっている湿度は10%以上の差が発生していました。同じBME280の別の個体との差が7%程度あったので、気になっていましたが、まさかの10%以上の差がでているとは…。

昨日の夜に天気HPの地点情報の湿度を比べてもBME280の湿度の数値はかなり低く、室内とはいえ窓を開けた状態で30%程度低かった記憶があります。
現時点でDHT11と比べると…HPでは89%、78%、BME280の湿度は…66%となってます。同じ部屋にあるもう一つのBME280は、73%と出ています。

結構な期間温度センサーを動かしていましたが、あまり細かいところは気にしていなかったとはいえ、BME280は気圧センサー以外はあまり正確な値ではないようです。ただ、センサー感度は悪くないので、ちょっとした変化はすぐに掴める気はします。

結構気に入ってBME280を買いそろえてしまいましたが、別の気温センサー探そうかな?

2020年7月3日金曜日

BME280のオーバーサンプリングを試してみた

どこかのライブラリを基にして、python3で使えるようにしてクラスっぽい実装にしたものを使っているので、データシートを見ながら1ステップづつ確認しているような状態ですが、モード値以外はほぼ問題なさそうというのが現時点での結論となっています。

そんなこんなで、まずはオーバーサンプリングを試してみましたが、結果から言えばほんとに小数点以下の誤差が若干落ち着くかな?といった感じでした。
フィルタリングに関しては、データシートの説明でノーマルモードで使用することが推奨されていて、よくよく読んでみると、設定のレジスタに書き込むと内部のフィルタメモリがクリアされる仕組みになっているので、現状の使い方(毎回初期設定して温度を読み取っている)ではあまり効果が期待できなさそうです。このへんはもう少し手を加えれば変えられそうですが、そこまでの精度の値が欲しいわけではないので踏ん切りがつきません。

データシートにはフィルタを有効にすると、気圧と温度の解像度(分解性能?)が上がると書かれているので、気になりますが、原文とグーグル翻訳を使って読んでいる限りドアの開閉や扇風機などの気流の乱れの誤差を減らすのが目的なようなので現状関係ないかも(笑)

オーバーサンプリングやフィルタ効果を見るために、同じ場所の2つのセンサーのグラフを実際に重ねて比較してみると、
気温

湿度(上部にCPU温度がかぶってます)

気圧
温度、湿度、気圧とものにほぼ同じ形になっているのでやっぱりオーバーサンプリングやフィルタリングの効果は少ないようです。
気圧に関しては、数値もほぼ同じになっていて気持ちがいい反面、温度は0.5℃、湿度は5%程度の差がありました。
やはりキャリブレーションデータの計算が間違っているのか、個体差か、劣化による誤差なのかは謎のままです…





コピー品が出回ってるかも?と検索したら、日本語版のデータシートが存在してました。
細かいスペックは英語の方がわかりやすかったりしますが、製品そのもののイメージを捉えるためには日本語で読めると全体がつかみやすいです。英語力ほとんどないからなぁw

と、いろいろとはっきりしたポイントとして、英語でも「ゲームモード」と書かれていて何に使うのかな?と引っかかっていましたが、気圧と温度で高度の差異を捉えて高精度の高度センサーとして利用するのが目的のようです。そのためにわざわざノーマルモードなるひたすらセンサー値をレジスタに書き込むモードがあるようです。実際に数Hz程度のサイクルで値が更新され続けるので、動態検知だけでなく、ヘッドマウントディスプレイなどに組み込めば加速度センサーとは違った実際の上下移動距離をリアルタイムで取得できそうです。
なるほどねぇといった感じですが、、、実際に使うのはただの温度、湿度、おまけで気圧の観測記録だけなのですw

ちろっと、サンプルを見かけたと思ったんですが、やはり日本語です。はっきりと認識できました。
気象観測用のおすすめ設定は…
オーバーサンプリングは気圧、気温、湿度すべてx1。フィルターはオフ。強制モードで、サイクルは1分間隔とのこと。
理由はよくわかりませんが、一昨日から昨日までノーマルモード(毎回初期化してるので上記の想定からはやや離れているのですが、)1分単位で取得した温度の値が、百均の温度計との差が少なくなりました。


色々とスクリプトを見直していたら今まで使っていたものはどうもdouble型で簡易的に求めるもののようで、基準となる変数が本来は整数なのに対してdoubleのままになっていました。記憶はあまりないですが、BMP180のソースを基に自分で起こしたような気もしてきました。とはいっても、やはり小数点以下の誤差しか出てこないので、温度と湿度の個体差が予想以上に出ているという残念な結果が残りました。

2020年7月2日木曜日

"Direct firmware load for brcm/brcmfmac43430-sdio.raspberrypi,3-model-b.txt failed with error -2"

起動時に電圧不足となっているRaspberry piを再起動して気になったエラーメッセージがありました。


[    7.411836] brcmfmac: brcmf_fw_alloc_request: using brcm/brcmfmac43430-sdio for chip BCM43430/1
[    7.412142] usbcore: registered new interface driver brcmfmac
[    7.436191] brcmfmac mmc1:0001:1: Direct firmware load for brcm/brcmfmac43430-sdio.raspberrypi,3-model-b.txt failed with error -2
[    7.652810] brcmfmac: brcmf_fw_alloc_request: using brcm/brcmfmac43430-sdio for chip BCM43430/1
[    7.652954] brcmfmac: brcmf_c_process_clm_blob: no clm_blob available (err=-2), device may have limited channels available
[    7.655857] brcmfmac: brcmf_c_preinit_dcmds: Firmware: BCM43430/1 wl0: Oct 23 2017 03:55:53 version 7.45.98.38 (r674442 CY) FWID 01-e58d219f

検索してみると
https://askubuntu.com/questions/1252760/how-do-i-activate-the-bluetooth-function-on-my-ubuntu-system-if-theres-no-defa
I finally found a solution that works: sudo hciattach /dev/ttyAMA0 bcm43xx 921600 After running this command everything seemed to be fine.
とのこと
詳細を見る前にアップデートするとファーム更新まで走らないかな?試してみよう

BME280センサーの個体差が気になるので

いままで何回かバラバラで購入しているので結構個体差があります。
百均で温度計も購入して比べてみましたが、予想以上にズレがありました。とはいえ、±2℃程度ではあるので実用的には全く問題はありませんがw
センサーチップにキャリブレーションデータがあるので誤差の原因がその補正計算で問題があるのではないかと気になって仕方がなかったので、使っているソースとデータシートを見比べてみました。

結果としては、データシートの参考ソースから補正計算をさせた結果と、今までの結果の差は

今まで使っているライブラリの値
温度 28.008137599710608
気圧 1004.5331902330901
湿度 68.45038478114147

データシートから起こしたスクリプトの値
温度 28.01
気圧 1004.5329296875
湿度 68.4453125

誤差の根源は内部で使いまわしているt_fineという基礎数の計算時の誤差が由来のようです。

143401.6645105183
143401

データシートに記載されているソースは桁あふれが発生させないように整数で扱っているのに対して、今まで扱っているライブラリの方は誤差は演算処理任せにして演算している感じです。

その結果、今回データシートから起こしたスクリプトは気圧と湿度に関しては算出された値からさらに表示用にそれぞれ256と1024で割り算しているのでその誤差が出てきているだけの格好です。

ついでに細かいところでレジスタから取得している部分も見直してみたのですが、結局は問題ありませんでした。ビットで切り離している値などもあるので、怪しいと思ってアドレス計算とかまでして確認したんですけどね無駄でしたw

とはいえ、根本的なところで、
The “config”register sets therate, filter and interface options of the device. Writes to the “config” register in normal mode may be ignored.In sleep mode writes are not ignored.
「config」レジスタは、デバイスのレート、フィルタ、およびインターフェースオプションを設定します。通常モードでの「config」レジスタへの書き込みは無視される場合があります。スリープモードでは書き込みは無視されません。
という記述を発見。
ライブラリではノーマルモードのままとなっているので、若干心配ではあります。

あと、サンプリングの回数を10分間隔から1分間隔に変更して様子を見ているのですが、1分間隔にしたら温度計の温度との差がかなり少なくなった感じがします。
と、すると、オーバーサンプリングうんたらかんたらでセンサーの値を拾ってあげると結果が変わりそうな気配がしますが…そのうち確認してみましょう。