さて、一番手を加えたのが通信部分である。関係するパラメータはCOM_REFRESHで、コメントでは50msになっているが、最終的には25msとした。またバッファサイズを定義するBUFSIZE以外に、通信用のバッファサイズとしてCOMBUFSIZEを定義し、これを32にしている。
コードそのものは、WinProc()の中のMessage Dispatchの中で、WM_TIMERイベントの処理である。温度センサーの時と同じく、今回はタイマーを2種類利用しており、TIMER_ID1はCOMポートと通信を行ってArduinoからデータを読み取る処理、TIMER_ID2は最終的に消費電力を画面を更新する処理である。まずこのTIMER_ID1であるが、確実にArduinoからデータを受け取るためには、
・不必要に受信バッファサイズを大きくしない
・1回の受信で複数回分のデータを受け取る可能性を考慮する
の2つの配慮が必要となった。温度センサーのときはそこまでデータ送信頻度が高くなかったので、これらがそれほど問題になることは無かったのだが、最大だと2周期毎(50Hz地域では毎秒25回:40ms毎)にデータを送りつけてくるWattMeterでは、
・バッファサイズを大きくしすぎると、ReadFile()がバッファが一杯になるまで帰ってこないため、時として25ms毎のTimer Eventの間に読み込みが終わらないトラブルが頻発した。そこでバッファそのものは256Bytes(BUFSIZE)で確保しつつも、受信サイズは32Bytes(COMBUFSIZE)に制限を掛ける事で早めにReadFile()を終わらせる事が可能となる。
ロジック的に言えば、ReadFile()の代わりにReadFileEx()を使って非同期読み出しを掛け、同時に更にタイマーを掛けて「時間までに帰ってこなかったら強制タイムアウト」という処理を実装することも可能だが、ここまでやる必要があるか? というのはちょっと疑問であり、今回はあっさりReadFile()で済ませることにした。
で、実はここまでやってもまだ1回のReadFile()の読み取りで、複数回分のデータを読み取っている可能性がある。温度センサーの場合はこれを切り捨てて最初の1つだけデータを読み取って終わりにしたが、今回はこれだと精度が下がるため、ReadFile()の後で読み取ったバッファの内容をfor()ループで廻して、全データの読み取りを行うことにした。この場合に問題になるのが、「それぞれのデータが正しく読み取れているか」である。例えばArduinoから、
15573 | 363 | 105 |
15535 | 352 | 104 |
15590 | 349 | 104 |
というデータが送られたとする。ところが、必ずしもこれをReadFile()で正しく読み取れるとは限らず、例えば、
5573 | 363 | 105 |
15535 | 352 | 104 |
15590 | 349 | 10 |
といった具合に前後が欠損して受け取る場合も考えられる。これを避けるために、かならずデータの頭に"a"、最後に"z"を付加して、
a15573 | 363 | 105z |
a15535 | 352 | 104z |
a15590 | 349 | 104z |
と送るようにした訳だ。これにより、やはりデータが欠損して、
15573 | 363 | 105z |
a15535 | 352 | 104z |
a15590 | 349 | 104 |
となっても、一つ目のデータはaがないので読み飛ばし、二つ目のデータの処理を行う。そして3つ目はzがないのでやはり読み飛ばして終了である。前後のデータが欠落するのは勿体無いが、変な値として読み取るよりはずっとマシであろう。そんなわけで、for()ループの中ではstrchr()を使い、まず"a"、ついで"z"を検索し、この両方がある場合にのみsscanf_s()で値のParseを行って次に進むという処理を行うように書き換えている。
もう一つ、平均値の求め方もちょっと工夫した。例えば5回データをまとめて受け取ったとする。この電圧の値とサンプル回数が、
電圧値 | サンプル数 |
---|---|
1500 | 100 |
1510 | 101 |
1500 | 100 |
1490 | 99 |
1500 | 100 |
となったとする。この平均値をどう求めるか? 一番簡単なのは、まず積算してそのあと割り算である。こうすると平均値はジャスト15になる。ところが個別に計算すると、
電圧値 | サンプル数 | 1回あたりの電圧平均値 |
---|---|---|
1500 | 100 | 15 |
1510 | 101 | 14.9505 |
1500 | 100 | 15 |
1490 | 99 | 15.0505 |
1500 | 100 | 15 |
5回の平均値 | 15.0002 |
で、微妙にずれることになる。
実際これがどの程度影響するのかは微妙なところで、例えば個別に平均値を求めて合算だと計算数が増えることになり、これが取り込みに深刻な影響を与えるなら多少誤差があってもまとめて合算したほうがマシである。ところがこの処理はPC側で行っており、この程度の計算は殆ど影響を与えない。そんなわけで、Arduinoから1回分のデータをとるごとに1回あたりの電圧平均値を算出し、これをあとで平均するという仕組みにした。この平均を取るために、storeVol/storeAmp/storeWatt/numSampleという変数を新たに導入している。
(続く)