シリアル通信をしてみよう(Serial)

# はじめに
ここでは、Arduino UNOで **<font color="red">シリアル通信</font>** の実験をしてみます。

目次へ戻るには [ここ](https://qiita.com/hsgucci/items/085680123f758c8c3eb4#%E5%AE%9F%E9%A8%93%E6%89%8B%E9%A0%86) をクリック

※本ページは実験のテキストです。

# シリアル通信とは
シリアル(serial)とは「連続した」という意味があります。
連続殺人犯は「serial killer」と言います。

下図のように、１本だけの通信線を使い、HIGH/LOWの電圧レベルを**連続的(=シリアル)**に変化させて、
意味のあるデータを送信/受信する通信方式が**シリアル通信**です。

複数の通信線を使う通信方式は**パラレル通信**と言います。
![seri_para.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/c788d17d-aee3-7ac7-8bff-1fae8afbb927.png)

昔は、複数本の通信線を使えるので1本よりも多くのデータが送れることから
「**シリアル通信よりもパラレル通信の方が高速**」と言われていました。
しかし、あまりに高速な通信になりすぎると、パラレルの通信線は互いにアンテナになり、
高周波数の電磁波を送受信し、データにゴミが乗ってしまいます。これがクロストーク（混信）です。

一方、シリアル通信は単線なので、影響はありません。
したがって現在では「**パラレル通信には限界があったので、シリアル通信の方が高速**」と言われています。
USB(ユニバーサル シリアル バス)、シリアルATAなど、様々な規格はシリアル通信を行っています。

# RS-232C
Arduinoで利用するシリアル通信は、「**RS-232C**」と呼ばれる規格です。
正式名称はEIA-232Dですが、誰もこの呼び名は使っていません。
![backpanel.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/edbf0b33-80fc-9510-b532-bba39820b74d.png)
図のように、古いパソコンにはRS-232Cポートが標準装備されていたのですが、
最近は、使われることもなくなって失われてしまいました。
なので「レガシーシリアル」(時代遅れの/遺産的なシリアル規格)などと呼ばれています。

しかし、一般人には使われなくなりましたが、マイコン開発には現役です。

# USBシリアル変換

物理的なRS-232Cポートは失われましたが、今はUSBで置き換えられています。

USBそのものの通信規格は、デバイスディスクリプタだとかエンドポイントだとか複雑なため、
今までRS-232C規格を使っていたマイコン技術者がUSB規格に乗り換えるのは至難の業でした。

そこで、USBポートに接続して今までのRS-232Cと同じ通信規格の使えるＩＣが登場しました。
代表的な製品は以下の２社です。

・FTDI社　FT232シリーズ
　　　　　https://www.ftdichip.com/FTProducts.htm
・Sillicon Labs社　CP210xシリーズ
　　　　　https://www.silabs.com/products/interface/usb-bridges

具体的には、以下のURLにあるような製品がマイコン開発・電子工作に使われています。
http://akizukidenshi.com/catalog/c/cusb232/

見かけ上はUSB接続ですが、中身は実はRS-232Cという製品も多くあります。

Arduinoも同様です。
昔あった「Arduino Duemilanove」という製品では、FTDIのICが使われていました。

Arduino UNOでは、USBポートのすぐ近くにあるATmega16U2マイコンがUSBシリアル変換をしています。
![usbserial.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/249d3ac5-fc17-049d-4ade-99cfca6ef094.png)

USBシリアル変換のおかげで、RS-232Cとして簡単に通信が行えるのです。


# 通信プログラム
それでは、シリアル通信を試してみます。
メニューバーから[ファイル]-[新規ファイル]をクリックし、新しいプログラムを書く準備をしてください。

以下のプログラムを打ち込み、コンパイル/実行してみましょう。

```cpp:Serial_Test.ino
void setup() {
  Serial.begin( 9600 );     // シリアル通信を初期化する。通信速度は9600bps

  Serial.println( "Hello Arduino!" );   // 最初に1回だけメッセージを表示する
}

void loop() {
  char key;     // 受信データを格納するchar型の変数

  // 受信データがあった時だけ、処理を行う
  if ( Serial.available() ) {       // 受信データがあるか？
    key = Serial.read();            // 1文字だけ読み込む
    Serial.write( key );            // 1文字送信。受信データをそのまま送り返す。
  }
}
```

プログラムを実行しても、LEDが光るなどの効果はありません。
動作を確認するためには、シリアルモニタを使う必要があります。

# シリアルモニタの使い方

シリアルモニタは、図のボタンをクリックすることで起動します。
![serial_monitor.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/9072a055-7b61-2c42-c0a0-4c82ceabb80b.png)

シリアルモニタが起動すると、図のように　Hello Arduino!　と表示されるはずです。
![monitor_hello.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/3a030166-c186-ba5c-6cde-f770eb133966.png)

次に、上部にある小さいテキストボックスに a と打ち込んでから、右の[送信]ボタンをクリックしましょう。
![monitor_keyin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/5d5cd402-cb06-d92b-379e-9c74ed0c814f.png)

すると、Hello Arduino!という文字の下に、a という文字が表示されます。
先ほど入力したa は消えました。
![monitor_echo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/75950012-e21a-4c70-a3d9-5ece3146fce1.png)

同様に、様々な文字を送信して試してみてください。
一度にたくさんの文字を送信することも可能です。
![monitor_str.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/400614d4-7563-6570-f486-2b544b7e4f5f.png)

つまり、 **<font color="red">Arduinoマイコンに文字を送ると、送った文字が返ってきている</font>** のです。

# プログラム解説
次は、プログラムを読んでいきましょう。
まずは`setup`関数です。

```cpp:セットアップ関数
void setup() {
  Serial.begin( 9600 );     // Arduionoとシリアルモニタの通信速度はどちらも9600bpsにする必要がある

  Serial.println( "Hello Arduino!" );   // C言語のprintfのように、メッセージを出したいときに使う。
}
```
`Serial.begin`関数で、シリアル通信を使う準備をします。
引数にある **9600** というのは、9600bps(bit per second)です。
つまり、１秒間に9600ビット分のデータを送れる通信速度に設定する、という意味です。

マイコンでよく使われる通信速度は、
・9600bps
・57600bps
・115200bps
です。数字が大きくなるほど、
多くのデータを送れるスピードだということです。

注意すべき点は、
**<font color="red">パソコン側とマイコン側の通信速度は必ず同じにしなければならない</font>**
です。
図のように、シリアルモニタの通信速度と、プログラムで書いたマイコンの速度とを同じにしないと、
データが正常に伝送されず、文字化けしてしまいます。
![baud.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/d5e24315-4fc4-8d76-1c59-7a250e755072.png)

`Serial.println`関数は、シリアルモニタ上にメッセージを表示する関数です。
Arduinoなどのマイコンにはディスプレイが搭載されていないので、
Ｃ言語で最初に学ぶメッセージ表示関数`printf`は使えません。
その代わりとして、シリアルモニタ上にprintするのです。

`Serial.print`の後ろに`ln`が付いているのは、「改行コード(CR)を自動追加する」を意味しています。
`printf`関数で例えれば、"\n"を自分で書く代わりに、勝手に関数が追加してくれる、ということです。

```c
printf("hogehoge");		// Serial.printだと改行されない

printf("hogehogen\n")		// Serial.printlnだと暗黙に\nが付加されて改行される
```

`Serial.print`と`Serial.println`は非常によく使われます。
メッセージを表示するだけでなく、変数の中身を表示することもできます。

----
次は`loop`関数を見ていきましょう。

```cpp:ループ関数
void loop() {
  char key;     // 受信データを格納するchar型の変数

  // 受信データがあった時だけ、処理を行う
  if ( Serial.available() ) {       // 受信データがあるか？
    key = Serial.read();            // 1文字だけ読み込む
    Serial.write( key );            // 1文字送信。受信データをそのまま送り返す。
  }
}
```

まず注目すべきは`Serial.read`と`Serial.write`の関数です。

`Serial.read`と`Serial.write`は、受信と送信の対の関係になります。
シリアルからreadしたデータをkeyに格納し、keyをwriteしているので、
「受信データをそのまま送信する」という作業をしています。

ポイントは、`if ( Serial.available() ) {  }`です。
シリアルモニタから何かデータが送られてきた時だけ、if文の中を実行するように書いています。

これは **<font color="blue">ブロッキング関数をみだりに使わない</font>** という狙いがあります。

# ブロッキング関数

ブロッキング関数とは、
「**その関数を実行すると、処理が完了するまではプログラムがそこで停止する**」
という関数です。

例えば、以下のように書くとわかりやすいです。

```cpp:２つのループ関数を比較してみよう
void loop1() {
  char key;     // 受信データを格納するchar型の変数

  // 受信データがあった時だけ、処理を行う
  if ( Serial.available() ) {       // 受信データがあるか？
    key = Serial.read();            // データが無いときは呼ばれない
    Serial.write( key );
  }
  // LED点滅
  digitalWrite(13, HIGH);
  delay(50);
  digitalWrite(13, LOW);
  delay(50);
}

void loop2() {
  char key;     // 受信データを格納するchar型の変数

  key = Serial.read();            // データが来るまで待ち続ける
  Serial.write( key );            // データが１個も来ないと，ここから下へは行かない
  // LED点滅
  digitalWrite(13, HIGH);
  delay(50);
  digitalWrite(13, LOW);
  delay(50);
}
```
`loop1`では，受信データがあったときだけif文の中が読み込まれるので，
LEDは50ミリ秒で点滅を繰り返します．

Serial.read()がブロッキング関数です．
`loop2`では，`Serial.read()`が1文字受け取るまで，ずっと待ち続けます．
したがって，その下の`digitalWrite`の方へはプログラムが流れません．
LEDはずっと消えたままになります．

プログラムのメインストリーム(主流)にブロッキング関数を書いてしまうと，
プログラム全体が停止してしまうので気をつけましょう．
if文の様なサブストリーム(支流)に書くべきなのです．

# まとめ

`Serial.begin()`　で初期化と速度設定をする
`Serial.available()`　で受信データがあるかどうかチェックする
`Serial.read()`　で実際にデータを受信する
`Serial.write()`　でデータを送信する

`Serial.read()`はブロッキング関数なので，if文の中に書くようにする．


# おわりに

これでシリアル通信の動作確認ができました。
次はシリアル通信を使ってデジタル出力を制御してみます。

 [目次](https://qiita.com/hsgucci/items/085680123f758c8c3eb4#%E5%AE%9F%E9%A8%93%E6%89%8B%E9%A0%86) へ戻って次の作業を行ってください。