シリアル通信

はじめに

Arduino Unoのシリアル通信の使い方を紹介します。実際に試したのは、Arduino Unoですが、ESP-WROOM-32を含む、他のArduinoでも利用できると思います。ただし、利用可能なオブジェクト(Serialなど)が異なる可能性があります。

Arduinoのシリアル通信は、送信(出力)と受信(入力)の2種類があります。シリアル通信を使うことで、PCのシリアルポートや、他のArduino、シリアル通信を利用する各種デバイスと通信を行うことができます。

動作イメージ

Arduinoソフトウェアによりシリアル通信用の送受信バッファ(Arduino UNOの場合デフォルトではそれぞれ64バイト)が提供され、ハードウェアの機能によりデータが1バイトずつ送受信されます。

使用方法

初期化

シリアル通信を行う場合は、Serial.begin()を使って、初期化を行います。さまざまなパラメータを指定することができますが、通信速度だけを設定することがほとんどです。初期化は、最初に一度だけ行えばいいので、通常はsetup()の中で行います。以下の例では、通信速度を9600bpsに設定します。通信速度は、通信相手と同じ値に設定する必要があります。例えば、Aruinoのシリアルモニタと通信を行う場合は、シリアルモニタの通信速度と同じ値になっている必要があります。

1
2
3
void setup() {
  Serial.begin(9600);
}

送信(出力)

シリアル通信で、データを送信する場合は、以下のいずれかを利用します。

上記の関数の大まかな特徴は以下の通りです。数値の扱いが大きく異なります。

関数 用途 送信データ 改行コード
文字(char) 文字列(char *) 数値
Serial.print() 可読文字(ASCIIコード)を送信する。 文字をASCIIコードに変換して送信する。 文字列を構成するそれぞれの文字をASCIIコードに変換して送信する。
  • 数字を構成する各桁の数を、文字として(ASCIIコードに変換して)送信する。
  • 整数の場合、底を指定可能。
  • 浮動小数の場合、小数点以下の有効桁数を指定可能。
付与されない。
Serial.println() 付与される。
Serial.write() バイナリデータを送信する。
  • 数値のまま(ASCIIコードに変換せずに)送信する。
  • 送信できるデータはuint8_t型。
  • uint8_t * を指定して、複数データを送信することも可能。
付与されない。

文字を送信する

文字を送信するプログラムを作成して、結果を比較してみます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
void setup() {
  Serial.begin(9600);

  Serial.print('A');
  Serial.println('B');
  Serial.write('C');
}

void loop() {
}

実行結果は以下の通りです。A、B、Cがそれぞれ表示されますが、 Serial.println()の後にだけ、改行コードが入ります。

文字列を送信する

文字を送信するプログラムを作成して、結果を比較してみます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
void setup() {
  Serial.begin(9600);

  Serial.print("ABC");
  Serial.println("DEF");
  Serial.write("GHI");
  Serial.write("JKL", 2);
}

void loop() {
}

実行結果は以下の通りです。文字を送信したときと同様、Serial.println()の後にだけ、改行コードが入ります。最後は、引数に”JKL"を指定していますが、送信文字数を2としているので、“JK"だけが表示されています。

文字列の中に明示的に改行コードを含めることができます。この場合は、例えば、“ABC\nDEF"のように、"\n"を文字列中に記載します。

数値を送信する

数値を送信するプログラムを作成して、結果を比較してみます。今回は、Serial.println()Serial.write()だけを使いました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
void setup() {
  uint8_t u8[] = {101, 102, 103, 104, 105};
  
  Serial.begin(9600);

  Serial.println(100);
  Serial.println(100, BIN);
  Serial.println(100, OCT);
  Serial.println(100, HEX);
  Serial.println(100.12345);
  Serial.println(100.12345, 4);
  Serial.write(100);
  Serial.write(u8, 3);
}

void loop() {
}

実行結果は以下の通りです。最初の4行は、上から、10進数、2進数、8進数、16進数で10進数の100を表示しています。これらは、数値を送信しましたが、実際は数値を文字列にしたものを送信しています。

5行目と、6行目は、100.12345を表示したときの結果です。デフォルトでは、小数点以下2桁までを表示します。小数点以下の桁数を指定すると、指定した桁までを表示します。今回は、小数点以下第5位が四捨五入されて、100.1235と表示されました。

最後の、Serial.write()は、数値の100を(文字列に変換しないで)そのまま送信しています。結果として、シリアルモニタは、ASCIIコードと解釈して、“d”(ASCIIコードで100です)を表示しています。

efgは、変数u8の最初の3バイトを送信しています。101、102、103は、それぞれ、ASCIIコードではe、f、gを表しているので、シリアルモニタにはそのように表示されています。

Arduino IDEによるデータの表示

Arduino IDEには、シリアルモニタとシリアルプロッタが実装されています。

シリアルモニタ

シリアルモニタは、Arduino IDEの右上のボタン(アイコン)を押すと起動されます(ツールメニューからも起動できます)。下の図は、アイコンにマウスを重ねた状態です(マウスカーソルは写っていませんが)。通常は、「シリアルモニタ」という表示は行われません。

シリアルプロッタ

Arduino IDEには、受信したデータをグラフ表示するための、シリアルプロッタも実装されています。ただし、シリアルモニタとシリアルプロッタは同時に起動することはできず、どちらか一方のみ起動できます。

シリアルプロッタは、ツールメニューから起動します。

例えば、以下のような表示をすることができます。スケールは自動設定のようです。また、表示領域の変更もできないようです。

プロットのためのプロトコルは、SerialPlotter protocolに記載されています。大雑把に書くと、以下のプロトコルです。

  • 「ラベルと数値の組」を1行で送信します。プロッタの上部にラベルが表示され、同一のX軸上に数値がプロットされていきます。
  • ラベルと数値は、コロン(’:’)で区切ります。
  • ラベルと数値の組の間は、スペース(’ ‘)、タブ(’\t’)、コンマ(’,’)のどれかで区切ります。

ラベルと数値の組の間をコンマで区切るときは、以下のようになります。

1
ラベル:数値,ラベル:数値, ..., ラベル:数値
  • ラベルだけや数値だけを送信することもできます。
    • ラベルだけを送信する場合は、コロンを省略可能です。ただし、数値だけのラベルは送信することができません。
    • 数値だけを送信する場合は、ラベルと数値の間の区切り文字のコロンは不要です。
      • 数値だけを送信することは、推奨していないようです。シリアルプロッタを表示する前にラベルが送られてしまうと、ラベルが表示されないからです。
  • 最後に送信したラベルが表示されます。

上記のプロットを行った際のスケッチを以下に示します。青と緑はrandom()を使い、赤はsin()を使いました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
void setup() {
  Serial.begin(9600);
}

void loop() {
  static int r = 0;

  Serial.print("random(0-25):");
  Serial.print(random(0, 25));
  Serial.print(",");
  Serial.print("sin(t):");
  Serial.print(12 * sin(r++ * PI / 50.0) + 37);
  Serial.print(",");
  Serial.print("random(50-100):");
  Serial.print(random(50, 100));
  Serial.print("\n");
  delay(100);
}

受信(入力)

シリアル通信で、データを受信する場合は、以下のいずれかを利用します。基本的には、read()を利用しますが、便利に利用するための関数も追加されています。以下に、各関数の大まかな特徴を示します。

個々の関数の使い方は、リファレンスを参照してください。

関数 用途 受信バイト数 関数の終了方法 備考
即時 指定データ数受信 タイムアウト 終端文字(列)受信 指定文字列受信 数値受信
Serial.read() 1バイトのデータを受信する。 1 - - - - -
Serial.peek() シリアル通信用のバッファは変更しない(次も同じ文字が読みこまれる)。
Serial.readBytes() 複数データを受信する。 複数 - - - -
Serial.readBytesUntil()
Serial.readString() 文字列を受信する。 複数 - - - - - タイムアウトするまで終了しない(と思われる)。
Serial.readStringUntil() 指定できる終端文字は1文字。
Serial.find() 指定した文字列を受信するかを調べる。 複数 - - - - 受信したデータは捨てられる。
Serial.findUntil()
Serial.parseInt() 数値を受信する。 複数 - - - - 整数を受信する。
Serial.parseFloat() 浮動小数点数を受信する。

受信データ存在確認

Serial.available()を利用することで、受信バッファにデータが何バイト存在するかを確認することができます。例えば、後の処理に必要なデータがすべてそろっているかを確認する際に利用できます。

以下の例では、データが2バイトそろうまでwhileを利用して待ちます。その後、データを2バイト読みます。

i
ただし、データが2バイトそろうまでプログラムはビジーウエイトしてしまいます。
1
2
3
4
5
6
int x;
while (Serial.available() < 2) {
  ;
}
x = Serial.read() << 8;
x |= Serial.read();

バージョン

Hardware:Arduino Uno
Software:Arduino 1.8.19

最終更新日

March 17, 2024

inserted by FC2 system