title.png


第3回 非同期シリアル通信をPCからモニタする

 今後のプログラムデバッグの基本になってくるはずですので、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回」の宿題

 「第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単精度のフォーマットに当てはめてみます。

ar3_float.png

 符号ビット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の単精度表現として読んで矛盾しませんでした。

 バイナリデータで直接やり取りする機会は少ないと思いますが、シリアル等の通信モジュールでつないだ機器にバイナリデータを投げ込む場合には気にする必要があります。

Arduinoにシリアルモニタからデータを送ってみる

 今度は逆に、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と連携した動作を具体的なサンプルで試してみたいと思います。


btn_arduino.png

btn_prev.png btn_next.png

home