Tinkercad版 Arduino 基本回路のシミュレーション ( 2 )

今回は、[エンジニアが教える Arduino 入門](https://www.youtube.com/playlist?list=PLZiGFIg4PRZgTl1XSvexduIyOTa_1W7_4) から
**#4 A/D変換
#5 シリアル通信
#6 LCDの使い方**
を Tinkercad でシミュレーションしてみます。ユーザー登録とチュートリアルは、[Tinkercad Circuits で Arduino 入門1](https://qiita.com/code0327/items/121327756d77953ee6f8) をご参照ください。[Arduino 日本語リファレンス](http://www.musashinodenpa.com/arduino/ref/index.php) ( Arduino の命令の説明 ) も随時ご参照ください。

[**#4 A/D 変換**](https://www.youtube.com/watch?v=DY_yxtEtl3o&list=PLZiGFIg4PRZgTl1XSvexduIyOTa_1W7_4&index=6) ( 動画 ) $\qquad$ [Tinkercad によるシミュレーション #4](https://www.tinkercad.com/things/7AyPkceKq4I-4-ad?sharecode=5LpL4cyjb4qZ8OqU_GBDrbSqyDrlwP-2im-y3eUGD1o)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549839/c526ea9c-68a7-787b-35ca-b31e225a058c.png)

左上のポテンショメータ ( RPOT1 ) で LCD ( 液晶表示器 ) の明るさを調整できます。
右下のポテンショメータ ( RPOT4 ) は 5V-GND間の電圧を分割して、A/D コンバータの入力ピン ( A1 ) に供給します。( [ポテンショメーターとは](https://note.com/shingo_shirogane/n/n3fe4f3c1911d) 参照 )
ポテンショメータのノブのマーカーを、マウスポインタでプレスしながら外形の円周に沿って移動させると、ノブを回すことができます。右にまわすと電圧が上がり、左にまわすと電圧が下がります。
A/D コンバータが読み取る電圧は ( $\text{data}$ ) は、0～5V の電圧を 0～1023 の数値に変換した値になります。従って、この A/D コンバータの精度は 10 ビット $(1024=2^{10})$ ということになり、この data の値から、次式で実際の電圧値が計算できます。
$$\text{volt}=5\text V\times\dfrac{\text{data}}{1024}~[~\text V~]$$

【回路図】
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549839/1f2c580f-1936-5740-403d-78d207f50f93.png)

(1) LCD 表示用のライブラリが入っている LiquidCrystal.h をインクルードします。
(2) LiquidCrystal クラスのオブジェクト lcd を作成すると同時に、引数でピンの割り当てをおこないます。
 
 LCD|Arduino|説明
 :-:|:-:|:--
 RS|7|レジスタ・セット
 E|8|イネーブル
 D4-7|9-12|コマンドやデータの送受信用
 
(3) LCD の文字数と行数を指定します。16 文字 $\times$ 2 行。
(4) A1 端子の電圧を読み込みます ( data )。
(5) LCD の 1 行目に data を表示します。
(6) data を電圧に換算します ( volt )。
(7) LCD の 2 行目に volt を表示します。 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549839/33e7563e-1747-f424-71dc-3a699a3b1814.png)

```c++
//C++ code
//
#include <LiquidCrystal.h>  //ライブラリのインクルード...1

int data = 0; //AD変換のデータ
float volt = 0;

LiquidCrystal lcd(7, 8, 9, 10, 11, 12); //オブジェクトの生成とピンの割り当て...2
//ピン配置 RS:7 E:8 D4:9 D5:10 D6:11 D7:12

void setup()
{
  lcd.begin(16, 2);	//文字数と行数を指定...3
}

void loop()
{
  data = analogRead(A1);	//AD変換:A1...4
  
  lcd.setCursor(0, 0); //LCDの1行目の出力...5
  lcd.print("A/D data = ");
  lcd.print(data);
  lcd.print(" ");
  
  volt = ((float)data * 5) / 1023; //dataを電圧に換算...6
  lcd.setCursor(0, 1); //LCDの2行目の出力...7
  lcd.print("V = ");
  lcd.print(volt);
  lcd.print(" V");
}
```

[**#5 シリアル通信**](https://www.youtube.com/watch?v=vQnsNq7zfBw&list=PLZiGFIg4PRZgTl1XSvexduIyOTa_1W7_4&index=7) ( 動画 ) $\qquad$ [Tinkercad によるシミュレーション](https://www.tinkercad.com/things/81iCLRXrUfY-5-?sharecode=L4kxDQJOgK-Qj42ch5x2m79FC-VIvvHu6i-1UXLrZSk)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549839/d912c99f-bc49-f80c-023c-551a79d7b25b.png)

パソコンから 1 桁の正の数をシリアル通信で 2 回、Adruino に送り、Arduino でそれらの足し算をおこなってシリアル通信でパソコンに返します。計算結果は 1 桁の範囲内 ( 0～9 ) で正しく計算できます。
シリアル通信は [アスキーコード](https://www.k-cube.co.jp/wakaba/server/ascii_code.html) で行われます。例えば、0～9 は 16 進数で 0x30～0x39 と表されます。
これを格納する変数 ( data_a, data_b ) のサイズは 1 バイト ( 8 ビット ) あればよく、char 型としています。足し算の結果を格納する sum も char 型としているので、0～9 までしか正しく表すことができません。
コード・エディタの下にある「シリアル・モニタ」をクリックするとシリアル・モニタが開きますので、そこで Arduino とデータの送受信ができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549839/3e5770cd-b1b6-7235-b898-92b3b5e9fcac.png)

1. 変数 data_a, data_b, sum, count を char 型 ( 1 バイトサイズ ) に指定します。count は、パソコンからのデータが 1 文字目か ２ 文字目からの判定用です。
2. シリアル通信のビット・レートを 9600 bps に設定します。Serial オブジェクトは自動的に作られます。( [Arduinoで遊ぶページ](https://garretlab.web.fc2.com/arduino/inside/hardware/arduino/avr/cores/arduino/HardwareSerial_private.h/HardwareSerial.html) 参照 )
3. Serial.available( ) は受信バッファに格納されているバイト数を返します。
4. 1 文字目であれば、Serial.read( ) で 1 バイト読み込み、"data_a=" の後に、そのままパソコンに送り返します。data_a の下位 4 ビットを取り出し、数値に変換します。
5. 2 文字目であれば、"data_b=" の後に、そのままパソコンに送り返します。data_b を数値に変換した後、data_a + data_b を計算して sum に格納します。
そして、sum の数値をアスキーコードに変換してから、"sum=" の後に、パソコンに送ります。
例えば、sum= 7 ( 0000 0111 ) の場合、sum | 0x30 は ( 0011 0111 = 0x37 ) となり、アスキーコードの '7' に変換されます。

> 計算結果を 0～99 まで正しく表示するには、`char sum = 0;` を `//char sum = 0;` のようにコメントアウト、`//int sum = 0;` を `int sum = 0;` のように有効にして、sum を int 型 ( 2 バイトサイズ ) にします。
そして、`Serial.write(sum | 0x30);` を `//Serial.write(sum | 0x30);` のようにコメントアウト、`//Serial.print(sum)` を `Serial.print(sum)` のように有効にしてから、試してみてください。

```c++
// C++ code
//
char data_a = 0;	//受信データ a...1
char data_b = 0;	//受信データ b
char sum = 0;	//計算結果
//int sum = 0;
char count = 0;

void setup()
{
  Serial.begin(9600);	//ビットレートの設定...2
}

void loop()
{
  //データを読み込み、パソコンにそのまま返す
  if(Serial.available() > 0)	//受信データがあるか?...3
  {
  	if(count == 0)				//1文字目か?...4
    {
      data_a = Serial.read();	//データ読み込み
      Serial.write("data_a=");	//""で囲まれた文字列を送信
  	  Serial.write(data_a);		//アスキーコードを送信
  	  Serial.write("\n");		//改行を送信
  	  data_a &= 0x0F;			//下位4ビット取り出し、数値データに変換
  	  count = 1;
    }
    else //...5
    {
      //データを読み込み、パソコンにそのまま返す
      data_b = Serial.read();	//データ読み込み
      Serial.write("data_b=");	//""で囲まれた文字列を送信
      Serial.write(data_b);		//アスキーコードを送信
      Serial.write("\n");		//改行を送信
      data_b &= 0x0F;			//下位4ビットを取り出し、数値データに変換
      count = 0;		
      //data_a + data_bを計算して、その結果を送信
      sum = data_a + data_b;	// a+bを計算
      Serial.write("sum=");
      Serial.write(sum | 0x30);	//数値データをアスキーコードに変換
      //Serial.print(sum);
      Serial.write("\n");
    }
  }
}
```

[**#6 LCD の使い方**](https://www.youtube.com/watch?v=MUIgdE0w-9c&list=PLZiGFIg4PRZgTl1XSvexduIyOTa_1W7_4&index=8) ( 動画 ) $\qquad$ [Tinkercad によるシミュレーション](https://www.tinkercad.com/things/ck2xGH8FNs8-6-lcd-?sharecode=SVWAbxPBAsyjRLIlgJVv84w6S77SwYOMrKHdE6gz-eU)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549839/00091c9d-a082-8ccf-5ab6-b2be53911688.png)

パラレル接続の LCD 制御ライブラリのクラス LiquidCrystal に用意されているメソッドを使って、いろいろな文字表示の例を示しています。
- 表示例 1 $\cdots$ カーソルの表示 / 非表示
- 表示例 2 $\cdots$ カーソルの点滅 / 点滅停止
- 表示例 3 $\cdots$ ディスプレイ消灯 / 点灯
- 表示例 4 $\cdots$ 文字の左方向スクロール
- 表示例 5 $\cdots$ 文字の右方向スクロール
- 表示例 6 $\cdots$ テキストを右方向に表示
- 表示例 7 $\cdots$ テキストを左方向に表示

【回路図】
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549839/2707f7b9-7668-37d3-a3d8-96743716f28d.png)

(1) LiquidCrystal クラスのオブジェクト lcd を作成すると同時に、引数でピンの割り当てをおこないます。

  LCD|Arduino|説明
  :-:|:-:|:--
  RS|9|レジスタ・セット
  E|8|イネーブル
  D4-7|A0-A3|コマンドやデータの送受信用
 
(2) LCD の文字数と行数を指定します。16 文字 $\times$ 2 行。
(3) カーソルの非表示と表示を 3 回繰り返します。
(4) カーソルの点滅と点滅停止を 3 回繰り返します。
(5) ディスプレイの消灯と点灯を 3 回繰り返します。
(6) "LCD" の左方向スクロールを 3 回繰り返します。
(7) "LCD" の右方向スクロールを 3 回繰り返します。
(8) "ab$\cdots$p" の右方向表示を 3 回繰り返します。
(9) "ab$\cdots$p" の左方向表示を 3 回繰り返します。

```c++
//C++ code
//
#include <LiquidCrystal.h>

LiquidCrystal lcd(9, 8, A0, A1, A2, A3);
//ピン配置 RS:9 E:8 D4:A0 D5:A1 D6:A2 D7:A3...1

void setup()
{
  lcd.begin(16, 2);	//文字数, 行数...2
}

void loop()
{ 
  //表示例1...3
  lcd.home();		//カーソルを左上の角へ移動
  lcd.print("LCD"); //テキストをLCDに表示
  delay(500);
  for(int i=0; i<3; i++)
  {
    lcd.noCursor();	//カーソル非表示
    delay(1000);
    lcd.cursor();	//カーソル表示
    delay(1000);
  }
  lcd.clear();
  
  //表示例2...4
  lcd.home();
  lcd.print("LCD");
  delay(500);
  for(int i=0; i<3; i++)
  {
    lcd.blink();	//カーソル点滅
    delay(1500);
    lcd.noBlink();	//カーソル点滅停止
    delay(1000);
  }
  lcd.clear();
  
  //表示例3...5
  lcd.home();
  lcd.noCursor();
  lcd.print("LCD");
  delay(500);
  for(int i=0; i<3; i++)
  {
    lcd.noDisplay();  //ディスプレイ消灯
    delay(1000);
    lcd.display();	  //ディスプレイ点灯
    delay(1000);
  }
  lcd.clear();

  //表示例4...6
  for(int j=0; j<3; j++)
  {
    lcd.home();
    lcd.setCursor(13, 0);	//カーソルの位置を指定
    lcd.print("LCD");
    delay(500);
    for(int i=0; i<16; i++)
    {
      lcd.scrollDisplayLeft();	//文字とカーソルを1文字分左へ
      delay(250);
    }
  }
  lcd.clear();

  //表示例5...7
  for(int j=0; j<3; j++)
  {
    lcd.home();
    lcd.noCursor();
    lcd.print("LCD");
    delay(500);
    for(int i = 0; i < 16; i++)
    {
      lcd.scrollDisplayRight();	//文字とカーソルを1文字分右へ
      delay(250);
    }
  }
  lcd.clear();
  
  //表示例6...8
  for(int j=0; j<3; j++)
  {
    lcd.home();
    lcd.noCursor();
    lcd.leftToRight();	//テキスト表示の向きを左から右に指定
    for(int i=0; i<16; i++)
    {
      lcd.write('a' + i);	//文字をLCDに表示
      delay(250);
    }
    lcd.clear();
  }
  lcd.clear();
  
  //表示例7...9
  for(int j=0; j<3; j++)
  {
    lcd.home();
    lcd.noCursor();
    lcd.setCursor(15, 0);
    lcd.rightToLeft();
    for(int i=0; i<16; i++)
    {
      lcd.write('a' + i);
      delay(250);
    }
    lcd.clear();
  }
  lcd.clear();
}
```