Raspberry PiとArduino Unoをシリアルポート(UART)で接続してエコーバックするテストプログラム

##はじめに
　Raspberry Pi（以下、ラズパイ）とArduino Unoをシリアルポート(UART)を利用してエコーバックさせるための配線図とテストプログラムを紹介します。これから似たような試作を検討している方やトラブルに悩んでいる方の参考になれば幸いです。
　テストプログラムは、ラズパイ側からASCIIコードを送信して、Arduino Unoでエコーバックします。ラズパイで受信したASCIIコードが送信したコードと一致したときターミナルに表示します。一致しないときは送信したコードと受信したコードを表示してテストプログラムは停止します。なお、配線図は「[Fritzing](https://fritzing.org/home/)」を使って描いたものを使用しています。
##準備するもの
・　Raspberry Pi 3B+または 4B
・　Raspbian  （コードネーム：Buster、Linuxカーネル:4.19.75で確認）
・　wiringPi Ver.2.52　※Pi 4Bの場合はVer.2.52にアップデートしないと動作しません。アップデートの方法は[こちらの記事](https://qiita.com/tamamori/items/e2a259894a0a7e132656)をご参照ください。
・　ラズパイ用周辺機器一式
・　Arduino Uno Rev3
・　Arduino IDE 1.8.12　　ラズパイにインストールする場合は[こちらの記事](https://qiita.com/tamamori/items/7d40bfb73b3a5766ce29)をご参照ください。
・　USBケーブル(Type-Aオス、Type-Bオス)
・　双方向ロジックレベル変換モジュール　AE-LCNV4-MOSFET（秋月電子通商：K-13837）
・　ブレッドボード
・　ジャンパーワイヤ（オス-メス、オス-オス）

##配線図
　ラズパイのGPIOは3.3V系で、Arduino Unoは5V系です。両者の信号線を接続した場合、Arduino Unoの5V系の高い電圧がラズパイのGPIOの内部回路や本体を壊す危険性があります。また、LレベルとHレベルの判定の基準が異なります。そこで、3.3V系と5V系の論理レベルを変換する回路や専用のデバイスが使用されます。本実験では、秋月電子通商が販売している4ビット双方向ロジックレベル変換モジュール「AE-LCNV4-MOSFET」を使用します。本モジュールには双方向の信号線が4回路あります。1回路を示しますが、Nチャンネルエンハンスメント形MOSFETで、3.3Vとと5V系のロジックレベルを変換しています。バイポーラトランジスタではコレクタ-エミッタ間で電流が流れる方向が決まっていますが、MOSFETではソースを基準にゲートに正バイアスを加えれば、ドレインが正であっても負でもあってもチャネルが開かれるため、双方向とも等しいドレイン-ソース抵抗が得られます。そのため、電流はソース-ドレイン間のどちらの方向にでも流すことができるため、双方向の信号になります。
![Chap1 MOSFET.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631966/e386a105-e9c6-ea72-5044-a14bb5e87566.png)
　本モジュールの電圧が低いLV側にラズパイを、電圧が高いHV側にArduino Unoを接続します。なお、本モジュールでは、ピンヘッダのはんだ付け作業が必要となります。全体の回路図は割愛しますが、ラズパイとArduino Unoの送信(Txd)と受信(Rxd)はクロス配線になります。本実験の通信距離は**約20cm**です。
![Pi Arduino_ブレッドボード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631966/7ed37aea-9698-0720-92fc-11ab97b0c00b.png)
##ソースコード
 ラズパイとArduino Unoのテストプログラムのソースコードを示します。Arduino Unoのシリアルポートの通信速度は9600bpsで使用される事例が多いのですが、今回より高速な115200bpsに設定しました。なお、Arduino IDEはRaspbainにインストールすることができます。こちらの記事「[Arduino IDEをRaspberry Pi 3B+, 4Bにインストールする方法](https://qiita.com/tamamori/items/7d40bfb73b3a5766ce29)」に、IDEのインストールの方法とサンプルスケッチ（Lチカ）の実行手順の解説がありますのでご参照ください。
### Raspberry Pi
　ASCIIコードの0からzまで順番に1文字毎に送信して、Arduino Unoでエコーバックされたデータを受信して、比較しています。データが一致しない場合はエラーを表示して送信をストップします。

```c:EchoBack.c
// gcc -Wall -o EchoBack EchoBack.c -lwiringPi -lpthread -g -O0
#include <stdio.h>			//入出力
#include <stdlib.h>			//一般ユーティリティ
#include <wiringPi.h>		//wiringPi
#include <wiringSerial.h>	//シリアル通信用wiringPi
#include <termios.h>		//ttyパラメータの構造体
#define BPS 115200			//通信速度

int main (void){
	unsigned char txdData;
	int rxdData,error=0;
	int fd;
	struct termios options;		// 通信オプションの構造体の定義
	
	fd = serialOpen( "/dev/ttyS0", BPS);	//シリアルポートのオープン
	tcgetattr(fd, &options);		//通信オプションの取得
	options.c_cflag |= CS8;			//データ 8bit
	options.c_cflag &= ~PARENB;		//パリティなし
	options.c_cflag |= CSTOPB;		//ストップビット 2bit
	options.c_iflag &= ~IXON; 		//出力のXON/XOFFフロー制御なし
	options.c_iflag &= ~IXOFF;		//入力のXON/XOFFフロー制御なし
	tcsetattr(fd,TCSANOW,&options);	// 通信オプションの変更
	
	serialFlush(fd);				//受信バッファのクリア
	
	do{
		for(txdData='0';txdData<='z';txdData++){ 	//0からzまでのASCIIコードを送信する。
			serialPutchar (fd, txdData);			//1文字の送信
			rxdData = serialGetchar(fd);			//1文字の受信
			if(rxdData == txdData){
				printf("%c",rxdData); fflush(stdout);	// 文字の表示		
			}else{
				printf("\n expected %c:%d, received %c:%d \n",txdData,txdData,rxdData,rxdData);
				error++;
				break;
			}
		}
	}while(error == 0);
	serialClose(fd);
	return EXIT_SUCCESS;
}
```

### Arduino Uno
　「受信と送信」の永久ループです。

```c:EchoBack.ino
void setup() {
  Serial.begin(115200,SERIAL_8N2);
}

void loop() {
  if (Serial.available()) {   //受信のチェック
    char  c = Serial.read();
    Serial.write(c);   //エコーバック
  }
}

```

### Arduino Unoの書き込み時の注意点
　Arduino Unoにスケッチを書き込む前に、**Arduino Unoの受信信号線(RX)を一旦外してください**。RXを接続した状態で書き込んだ場合、IDEのテキストコンソールに「**マイコンボードに書き込もうとしましたが、エラーが発生しました**」と表示されます。実はArduino Unoには2つのマイコンチップがあります。一つはユーザーがメインに使用するATMEGA328です。ユーザーはUSBケーブルを利用してArduino Unoにスケッチを書き込みますが、ATMEGA328にはUSBのインタフェース回路がありません。そこで、USBインタフェース回路を備えた2つ目のマイコンチップATMEGA16U2がユーザーのPCとATMEGA328の橋渡しをします。ATMEGA16U2はシリアルポートを介してATMEGA328にスケッチを書き込みます。ATMEGA328はRX信号線で受信するのですが、今回のように外部の信号線が接続されていると正しい送信と受信できないため通信エラーになります。
　書き込みが正常に終了しましたら、RX信号線の配線を戻します。なお、書き込む前に、Arduino Unoのリセットボタンを押してください。
##動作確認
　Raspberry PiのEchoBackのプログラムを実行すると、下図のようにターミナルに0からzまでの文字が繰り返し表示されます。
![Pi2Uno.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631966/b1246feb-64ee-7ec0-acfb-f4177424dc4b.jpeg)
　なお、次に示すArduino Unoのスケッチ（EchoBack2.ino）では、受信した「z」を**故意に**大文字の「Z」で返信します。ラズパイのターミナルでは送信と受信のデータを文字と10進数で表示して、テストプログラムは停止します。
![NG.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631966/6f1db6a0-e8b4-6720-2de1-9934bdaf8bff.png)

```c:EchoBack2.ino
void setup() {
  Serial.begin(115200,SERIAL_8N2);
}

void loop() {
  if (Serial.available()) {   //受信のチェック
    char  c = Serial.read();
    if (c == 'z'){            //zを受信したとき大文字のZを返します。
      Serial.write('Z');
    }else{
      Serial.write(c);   //エコーバック
    }
  }
}
```

##補足
　Raspberry PiとArduino Unoとの通信距離を長くしたい場合やノイズの影響を受ける環境下では、RS-232インタフェース回路等で補強します。
##Reference
- 菊池達也,"[C言語ではじめるRaspberry Pi徹底入門](https://gihyo.jp/book/2020/978-4-297-11299-8)",技術評論社,pp.25-26,(Apr.2020)
- 山口覚,"パワーMOSFET実践活用法",トランジスタ技術,第36巻,第414号,CQ出版社,pp.203-212,(Mar.1999)
- Fritzing Raspberry pi 4 model B [https://forum.fritzing.org/t/raspberry-pi-4-model-b/8622](https://forum.fritzing.org/t/raspberry-pi-4-model-b/8622)
- arduinoのエコーバックテスト　[http://mimono.wpblog.jp/2019/07/05/4363/](http://mimono.wpblog.jp/2019/07/05/4363/)
- Arduino Uno Rev3　回路図　[https://www.arduino.cc/en/uploads/Main/Arduino_Uno_Rev3-schematic.pdf](https://www.arduino.cc/en/uploads/Main/Arduino_Uno_Rev3-schematic.pdf)