前回までに、ArduinoとAM2302を使って、気温・湿度データを取得するところまで完成していますので、今回は、そのデータのログを取ってみたいと思います。
AM2302から読み取ったデータは、気温・湿度とも、16ビットのデータでした。合計32ビット=4バイトです。このデータをひたすらメモリに記憶したとしても、ただの数値の羅列でしかありません。そう、意味のあるデータにするためには、測定日時が必要なのです。しかし、Arduinoは時計を持っていません。データを取得することはできますが、その日時がいつなのか、を知り得ないのです。
日時を知る方法はいくつか考えられます。
ちょっと大掛かりになってきますが、外付けに、バッテリバックアップ付きのRTC(Real Time Clock)回路を接続し、Arduinoから日時を読み出せるように構成する方法です。
「電子工作でデジタル時計を作ってみましょう」みたいなテーマは、すごくありがちなテーマなので、たぶん、RTC ICが数100円で購入できると思います。後は、32.768kHzの水晶発信子と、発信回路の構成に必要なダンピング抵抗やコンデンサ、ICの動作に必要なバッテリホルダなどを揃えれば、そう難しくない工作でしょう。
ことによると、もっと簡単に、モジュールに組み上げられた製品も出回っているかも、です。
もし、PCを介さずに、ACアダプタなどでArduino+AM2302に電源を供給し、独立動作でログを取らせようとするなら、たぶんこの解しかないと思われるので、ご検討ください。
何度か登場しているように、Arduinoが起動してからの経過時間を知ることができる、millis()という関数があります。millis()を使うと、相対的な時間を知ることができます。起動時に一度だけ、PCから「今何時」を教えててもらい、教えてもらった時のmillis()を記録しておくと、次にArduinoが時間を知りたくなった時に、millis()を取って、相対的にその時が何時かを知ることができます。
ただし、millis()は約70日でオーバフロウして0に戻ってしまいますので、例えば、loop()の中で、定期的にmillis()を監視して、オーバフロウしたときに、その回数をカウントしておくなどの処理が必要かと思われます。
プログラム例:(動作は(文法チェックも)未確認です)
static int novf_millis; // millis()がオーバフロウした回数
static unsigned long msec_prev; // 前回確認したときのmillis()
void loop (void)
{
unsigned long msec_now;
msec_now=millis ();
if (msec_now<msec_prev)
{
// millis()がオーバフロウした。
novf_millis++;
msec_prev=msec_now;
}
delay (100);
}
もう一つ重大な制限があります。millis()は、noInterrupts()で停止してしまいます。noInterrupts()の状態で動作させようとしているときはもちろんのこと、何か特定の処理をするときだけ、一瞬noInterrupts()にして、処理終了後直ちにinterrupts()で元に戻したとしても、その間短い時間でもmillis()は停止しているので、「ちりも積もれば……」で、相対的な時間が狂ってきます。noInterrupts()を使用する用途では、この手法は使えませんのでご注意ください。
※もう少し正確にしておくと、millis()をカウントしているタイマは、約1ms毎に割り込みがかかっているので、noInterrupts()で割り込みを禁止する時間が、1msに対して十分短い時間であれば、大きな問題は生じないかもしれません。
ほとんどArduinoだけで何とか出来てしまう処理ではありますが、いかんせん、「起動時にPCから日時を教えてもらう」必要があり、PCの呪縛からは逃れられません(独立動作にはできない)。
さて、日時取得問題は一旦寝かせておいて、先に「ログをどこに記憶するか?」を検討してみましょう。
色々と検討してみましたが、正直、筆者は独立動作させる事には、あまり興味がありません。「Arduinoを使った電子工作の部屋」でお話ししたように、ほぼ常時起動している、自動処理専用PCに面倒を見させるつもりですので、Arduino+AM2302のお二人には、安定した気温・湿度の測定と、そのデータ転送だけを期待しています。
せっかく、ArduinoとPCがUART over USBで接続されているので、このインターフェイスを活かして、Arduinoからデータを読み込み、PCでログを取得します。
この方法だと、PCの時計やストレージが使えるため、日時取得問題も、記録先問題も、一気にクリアです。
「第3回 非同期シリアル通信をPCからモニタする」で、既に体験済みですが、Arduino IDEの「シリアルモニタ」機能で、ArduinoとPC間の双方向通信ができることを確認済みです。しかし、残念ながら、シリアルモニタの機能では、「自動処理」ができません。ユーザが端末をたたいて、Arduinoにコマンドを送り、結果として気温・湿度データを画面に表示させることはできますが、そのデータを保存する方法がありません。テキストを全選択してコピー&ペーストは可能でしたが、「自動」ではありません。なにより、動作中ずっと、IDEが起動していないとなりません。
シリアルモニタは、そもそも、ドライバによってWindowsの通信ポートに偽装済みのArduino機体と、通信ポートで通信しているだけ、なので、WindowsのAPIで通信ポートとの通信を実装したら良いわけです。
筆者にとって、Cなどのプログラミング言語を使ってプログラムを組むのは、結構、根気のいる作業なので、正直あまり好きではありません(1^^;)。スクリプト言語で、ちょちょっと、プログラムできる方が、なんぼかうれしいです。そこで、拙作Olive+のスクリプト言語に、Arduinoとの非同期シリアル通信をサポートするserialmon命令を追加しました。
最新版(R20以降)のOlive+でサポートしていますので、ダウンロードコーナからDLして、お使いください。
PC上Olive+で実行するスクリプトと、その時にArduinoで実行するプログラムをリンクします。
サンプルプログラム:test_echo.ino
サンプルスクリプト:test_echo.olv
筆者の環境では、Arduinoのポート名はCOM3なので、test_echo.olvは、その前提で記述しています。皆さんの環境に合わせて、serialmon open命令のポート名を書き換えてご使用ください。
test_echo.olvを実行する時には、ArduinoがPCにつながっていて、Arduino上でtest_echo.inoが走っていることを期待していますので、あらかじめ、IDEを使ってArduinoにtest_echo.inoを転送しておいてください。
なお、Olive+に追加した命令は、普通に通信ポートと通信するだけの命令ですので、通信ポートに接続される他の周辺機器との通信もできる可能性がありますが、他の機器を持ち合わせていないので、未確認です。ひとまず、Arduinoとの通信には問題なく使えるかと思います。
このような方法でPCからArduinoを制御する利点は、デバッグ初期、まだArduino側を制御するPC側のスクリプトに手を付けていない状態であっても、シリアルモニタを使ってArduino側のプログラムや電子回路のデバッグを実行できる点です。PC側のスクリプトも、シリアルモニタで打ち込んだコマンドとレスポンスを、そのまま送受信するように作成するだけなので、うまく動かなかった場合でも、問題発生個所を切り分け易いことも大きな利点です。
と、これは、次回に回したいと思います。いや、ログを取るだけなら、簡単です。serialmon命令でコマンドを送る、レスポンスとしての測定結果を受け取る、ファイルに書き出す、終了、ですから。すぐです。それでも次回に回すのは、時間稼ぎ(?何の?(1^^;))ではありません。
もう少し、PCがホストになってログを取ることの意義を示したいので、少し手の込んだことを考えているからです。
何をするかは、次回をお楽しみに。