Xbee電力ロガー親機のAPIフレーム表示に苦労する
プログラムは考えたようには動かない(10/27/09)
プログラムは考えたようには動かない、書いたようにしか動かないというのは言い古されたプログラミングの格言だが、まさしくこれを地で行くデバッグだった。Xbeeの親機の開発第2 ステップ、APIフレームの表示にえらい時間を食ってしまった。
たいした開発量でないからと、簡単なメモを書くくらいでコーディングに入ると大抵うまくいかないものである。今度もそうだった。Xbeeから送られたAPIフレームを一旦バッファーに溜めて、画面に人間が見てすぐわかるAPIデータを表示するのが第二ステップの目的である。
前の第一ステップのPCのUART画面に同時にデータを表示するより構造的には、はるかに簡単だ。フレームが送り終わるのを待って、フレームフォーマットに従って、送信モジュールアドレスや、受信サンプル数、データを表示していく。造作のない話である。
ところがこれがうまく動かないのである。コーディングを楽にするため、バッファーを文字配列にし、添字を使ってフレームからデータを抜き出すのだが、書式付の出力関数が思ったような数値を出してくれない。16進でだしたり、2バイトの整数で出したり、ビット表示だったりするので結構やっかいなのだが、出力されるのが、これが全くでたらめの値である。
おかしいな。ChaNさんの書式付出力関数xprintfはprintfのサブセットで軽いので愛用させてもらっている。慣れているはずなのにどうしてだろう。あんまりおかしいので、元に戻って、送られてきたフレームを16進表示してみた。
うひゃあ、これが全く似ても似つかぬAPIフレームが出力された。何だこれは。リセットしたり電源を入り切りすると、時々元の見慣れたAPIフレームが表示される時がある。何だ、何だ。ハードは何もいじっていないぞ。しかし、またすぐにおかしなデータに戻る。元のデータがこれでは、xprintfの書式どころではない。X-CTUを取り出してXbeeを確かめる。いや、何も問題ない。いつものデータを間違いなくはきだしている。
Mega128のVccを3.3Vに落とした副作用だろうか。いや、もうひとつのファイルモニター機能は何事もなく動く、ハードの問題ではない。新しくUARTを追加したMega128のPE0,PE1ピンに何か特殊な機能があるのだろうか。いやデータシートには何もない。するとソフトか。コードを確かめる。間違いなく、Xbeeからの受信バッファーが空になるのを待ち、データ処理に入っている。暴走もしていない。データが入っているところもある。
途方に暮れて、その日の開発はあきらめ寝床についた。次の日は仕事のない日で、朝寝坊して、うつらうつら昨夜のトラブルを思い出していた。このあいだは2つのUARTを同時に動かしたためバッファーをいくら大きくしても取りこぼした。今度はどうだ。今度は間違いがないように、受信バッファーをwhileループで監視し、空になるのを確かめて次のステップに行くようになっている。その間は別のUARTは動かしていない。
ちょっと待て。UARTが1文字を読む時間は、いくら早くても数百マイクロ秒のオーダーだ(38.4kbpsでバイト当たり208μs)。これに対してCPUの処理速度は8Mhzで1ステップ0.125マイクロ秒。千倍以上のスピード差だ。ループの中に別のUARTが入っていないということは、このwhileループはUARTの早さに較べれば猛烈な速さで回転する。
あああ、これだ、これだ。このwhileループは下部のUART関数がデータをバッファーに入れている間に、あっという間にバッファーを読みつくし、余裕で抜けてしまう。そのあとはデータが読み込まれたものとして、まだ殆ど何も入っていないデータ処理バッファーを書き出す。これだ。これが目茶目茶なデータになる原因だ。
やれやれ、わかってしまえば至極当然のことなのだが、思い込みというのは恐ろしい。最初のトラブルだったバッファーオーバーフローが頭に残っていて、UARTのデータはバッファーに溜まって行くばかりと考え、バッファーにデータが入っていることを前提にロジックを組んでいた。今度はある意味でのバッファーアンダーランである。疑ったMega128さん、Xbeeのみなさんごめんなさい。みんなこんなソフトを書いた自分が悪いのです。
このあとも泥沼(10/29/09)
飛び起きて、朝食をとるのももどかしく開発を再開した。しかし、このあとも苦戦が続いたのである。考えてみたら、APIフォーマットでADCデータが10秒ごと(現在の設定)に送られてくるが、データの終わりを判定する条件がない。データが来た時、バッファーに溜まるまで、少し待ち時間を入れれば良いのだろうが、こういう対症療法的なロジックは一番避けたいロジックである(巷にはこういうソフトが蔓延しているが)。これまでの経験からろくなことにはならない。とりあえずは良くてもたいてい後で破綻する。是が非でもしっかりとした終了条件が欲しい。robustness(堅牢さ)というやつである。
データフレームには、普通、そのフレームの長さのデータが入っているものである。このAPIフレームにも勿論ある。これを使えば良いのだが、データを読み始めてから終了条件をダイナミック(読んでいる間)に設定する処理が必要だ。これを安全に確実に暴走しないよう、しっかり作るのは結構神経を遣う。もし、不幸にしてデータサイズが入らなくてもハングしない構造にしなければならない。色々考える。プログラムは最初考えていたよりずっと複雑なものになりそうである。
それにバイト配列のデータから2バイト長のデータを連続して取り出すコードがなかなかスマートに書けない。アセンブラー育ちで、ポインターとキャストを完全に使いこなせていない。試行錯誤である。
uint16_t * ptr; // 2バイト整数のポインター
uint8_t Array[80]; // 1バイト配列(データバッファー)
と定義しておいて、Array[nn]のところにある2バイト長のデータをprintfなどで10進数で表示したいときは、配列のアドレス表現&を使って、
ptr = (uint16_t *) &Array[nn];
としてキャストし2バイト整数のポインターに替え、
printf( "%u", *ptr );
で良い筈なのだが、どうしても1バイトずれてしまう。しかもうまく行くところと行かないところが出て益々混乱する。安易にコーディングを始めたことを後悔する。よほど最初から書き直そうかと考えたが、しかしもう遅すぎる。何とかするしかない。どうしても入らないところは、ひとつづつ入れて8ビットシフトし強引に2バイト整数にする。
少しづつ手を入れては結果を確かめ、正しく表示されるデータを増やしていく。しかしチェックサムが最後まで残った。送られてきたデータとなかなか一致しない。配列番号、アドレス、チェックサムしないデータ数、このあたりの計算はいわゆる並木算で昔からいつも悩まされてきた。紙に何度も図を描いてチェックサムの始めと終わりのアドレスを確認する。無精して++を式の中に入れたりしているので余計厄介だ。
悪戦苦闘、数時間。データフレームにあるチェックサム値と、プログラムで計算した値がぴったり一致した時は、思わず端末の前で一人で拍手をしてしまった。思うように動かない時は、何を好んでこんなことをやっているのか、自分の不甲斐なさに情けなくなるばかりだが、出来たとなると、この充実感が何ともたまらない。実に安上がりな娯楽だ。それにしてもこんな小さなプログラミングでも、ちゃんとした計画を立て、準備を十分にしないと痛い目にあうということを今さらのように学んだ。自戒、自戒である。
| 固定リンク
「AVR」カテゴリの記事
- ソフトI2Cはクロックストレッチまで手を出してあえなく沈没(2017.09.02)
- オシロのテストどころかソフト開発で大はまり(2017.07.26)
- 超音波方式の人感センサーI2C化と新しいオシロ(2017.06.29)
- motionの動体検知はRaspi3の電源が安定しない(2017.04.16)
- 赤外線学習リモコンはデータ再現で挫折したまま進まず(2016.07.21)
コメント