今後のプログラムデバッグの基本になってくるはずですので、USBを介した非同期シリアル通信によって、Arduinoが出力してきたデータを、PCからモニタしてみます。
必要なプログラムはこれだけ↓。
void setup (void)
{
// シリアル
Serial.begin (9600);
// 準備が整うまで待つ(おまじない)
while (!Serial)
;
// ここまでで、準備完了したので、メッセージを送信する
Serial.println ("hello, world");
}
void loop (void)
{
// empty
}
Arduinoの制御に必要なAPI(?)は一通りそろっていますので、もう、これだけでOK。なんと便利。
本来ならば、ATmega328のデータシートを読んで、UART(Universal Asynchronous Receiver Transmitter、非同期シリアル通信モジュール)の仕様を理解、レジスタを設定して、送/受信、という手順。初期化や送/受信の手順が複雑だったりすると、もう、ウンザリ……、ですからね。
ちなみに、while (!Serial) ;という処理は、筆者が購入したUNO(の互換機含む)では、必要ない様です。一部のボードでは必要、ということで、対象機体に移植した場合のために、今回はおまじないとして入れておきました。UNOで実行しても、実害はありません。
Cの最初のサンプルと言えば、「hello, world」と言うことで、シリアルモニタに「hello, world」と表示されれば、テスト終了です。
「第1回 Arduinoへの理解を深める」で宿題にしてあった、floatの表現形式について、確認しておきます。ついでに、エンディアンも確認。
プログラム:
// テスト
float ff;
ff=1.25;
unsigned long *ul;
ul=(unsigned long *)&ff;
Serial.println ((*ul), HEX);
unsigned char *uc;
uc=(unsigned char *)&ff;
Serial.println (uc[0], HEX);
Serial.println (uc[1], HEX);
Serial.println (uc[2], HEX);
Serial.println (uc[3], HEX);
すでに、setup()とか、Serial.begin()とか、省略してしまっているので、ご注意ください。実行結果が以下です。
実行サンプル:
3FA00000
0
0
A0
3F
最初に、わかりやすいエンディアンから確認しましょう。リトルですね。8ビットMCUなので、エンディアンも何も無い様に思いますが、Cの実装としてはリトルエンディアンでした。
さて、本題。最初の行に表示された16進数(0x3fa00000)がfloat 1.25の16進数表現です。これをIEEE単精度のフォーマットに当てはめてみます。
符号ビットS=0(正)です。
指数部E=0b0111_1111=0x7fです。127の下駄をはいているので、はがすと、E=0。
仮数部F=0b010_0000_0000_0000_0000_0000ですが、最上位ビットのさらに上に"1."(1と小数点)がつくので、F=0b1.010_0000_0000_0000_0000_0000 =1+1/4=1.25。
まとめると、数値=(-1)S×F×2E=1.25ということになります。NaNや±無限大等まで実装されているかどうかは不明ですが、IEEEの単精度表現として読んで矛盾しませんでした。
バイナリデータで直接やり取りする機会は少ないと思いますが、シリアル等の通信モジュールでつないだ機器にバイナリデータを投げ込む場合には気にする必要があります。
今度は逆に、PCからArduinoにデータを送ってみます。
以下のプログラムでどうでしょうか?
※筆者の環境では、Arduino IDEのテキストエディタに日本語文字を使うと、何やら動作が重くなります。筆者の環境で何かが邪魔しているのでしょうか?ウザいので、コメント等も英語にしてみました。
プログラム:
const int max_lbuf=64;
static char lbuf[max_lbuf]; // line buffer
void get_line (void)
{
int idx;
int num;
num=Serial.available ();
if (num + 1>max_lbuf)
num=max_lbuf - 1;
for (idx=0; idx<num; idx++)
lbuf[idx]=Serial.read ();
lbuf[idx]='\0';
}
void setup (void)
{
// serial
Serial.begin (9600);
// message
Serial.println ("What is your concern?");
}
void loop (void)
{
// check if data were available
if (Serial.available ()>0)
{
get_line ();
Serial.print ("I got your message : ");
Serial.println (lbuf);
}
}
Arduinoにプログラムを転送して、シリアルモニタを開きます。シリアルモニタウインドウのタイトルの下に入力フィールドがありますので、そこに文字列を記入して「送信」ボタンを押します。
送信:「Do you read me?」
実行サンプル:
What is your concern?
I got your message : D
I got your message : o
I got your message :
I got your message : you r
I got your message : ead me?
……そゆこと……。最初の1バイトを受信した時点でSerial.available()が返したデータ数を受信して返ってしまうので、途切れ途切れになってしまうのですね。
では、get_line()関数の仕様を変更しましょう。
プログラム:(変更部分のみ)
int get_line (void)
{
int idx;
int chr;
for (idx=0; idx<max_lbuf - 1; idx++)
{
do
{
chr=Serial.read ();
} while (chr==-1);
if (chr=='\n')
break;
lbuf[idx]=chr;
}
lbuf[idx]='\0';
return (idx);
}
送信:「Do you read me?」
実行サンプル:
What is your concern?
I got your message : Do you read me?
今度は期待通りの動作を確認できました。シリアルモニタからArduinoに指示(コマンド)を与えて、それに対する反応を確かめることができるということです。
これにて、Arduino IDEのシリアルモニタを使って、Arduino本体とPC間の双方向通信ができることを確認できました。
次回は、PCと連携した動作を具体的なサンプルで試してみたいと思います。