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

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

[ **#7 EEPROM** ](https://www.youtube.com/watch?v=fuEfX6tYjkQ&list=PLZiGFIg4PRZgTl1XSvexduIyOTa_1W7_4&index=9) ( 動画 ) $\qquad$ [Tinkercad によるシミュレーション](https://www.tinkercad.com/things/kkhwg90eKLN-7-eeprom2?sharecode=LwHHxzAPr_LJ7N9S1pIUrQCxJ8kkVJsM-RHYKF16sYg)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549839/813aaa1a-9720-9450-db48-972312b8339f.png)

Arduino 内蔵の EEPROM ( 不揮発メモリ ) にデータを書き込み、それを読み出してみます。Tinkercad では Arduino の電源を切る操作はできないのと、シミュレーションを停止すると EEPROM のデータもクリアされてしまいます。そこで、[Tinkercad による Arduino シミュレーション 39](https://atatat.hatenablog.com/entry/tikercad_arduino39_eeprom) から引用して、リセットをかけても EEPROM の内容が保持されていることを確認してみます。

シミュレーションを開始すると、
1. Add=0 の値をまず読み込み、1秒待ってから、Add=0 に 0 を書き込みます。
2. 2回目のループでは Add=1 の値を読み込み、1秒待ってから、Add=1 に 1 を書き込みます。
3. 3回目のループでは Add=2 の値を読み込み、1秒待ってから、Add=2 に 1 を書き込みます。

以後、アドレスをインクリメントしながら同じこと繰り返します。下記の実行例では、Add=4 を読み込んで 1 秒待つときに、リセット ( 基板上の赤いボタンをクリック ) をかけています。Add=0～3 には、リセット前に書き込み済の値が読み出されています。( リセットをかけても EEPROM に書き込んだ値は保持される )

> リセット時に Add=0 の前にスペースが入ってますが、手元にあった Arduino Uno Rev3 で、同じプログラムを書き込んで実行してみたところ、スペースが入ることはありませんでした。
また、EEPROM の初期値は 0 ではなく、0xFF ( 1111 1111 ) でした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549839/5bdedef9-3a95-b461-0bc1-91c41caace81.png)

1. EEPROM のライブラリを読み込みます。
2. シリアル通信のビットレートを 9600bps に設定します。
3. 最初に EEPROM の 0 番地の値 (value ) を読み込みます。
4. "Add=0" の後にタブ(行頭から 8 文字目) して、value をパソコンに送信します。
5. a をインクリメントして、256 に達したら、0 に戻します。
6. 1 秒待ちます。
7. 最初に EEPROM の 0 番地に 0 を書き込みます。
8. i をインクリメントして、256 に達したら、0 に戻します。  

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

int a = 0;
int value = 0;
int i = 0;

void setup()
{
  Serial.begin(9600);	//シリアル通信のビットレートの設定...2
}

void loop()
{
  value = EEPROM.read(a);	//EEPROMの0番地から読み込み開始...3

  Serial.print("Add=");	//読み込んだvalueをシリアル出力...4
  Serial.print(a);
  Serial.print('\t');	//タブ
  Serial.print(value);
  Serial.println();		//改行
  
  a++;
  if (a == 256) a = 0;  //aが256になったら、 0に戻す...5
  
  delay(1000);  //1秒待つ...6
  
  EEPROM.write(i, i);	//EEPROMの0～255番地に0～255を書き込む...7
  i++;
  if(i == 256) i = 0;  // iが256になったら、0に戻す...8
}
```
[ **#8 加速度センサー** ](https://www.youtube.com/watch?v=JlNq3ZM-lmo&list=PLZiGFIg4PRZgTl1XSvexduIyOTa_1W7_4&index=10) ( 動画 ) $\qquad$ [Tinkercad によるシミュレーション](https://www.tinkercad.com/things/d9UeUZiFRKH-8-?sharecode=yxV38wHyLF2vBn-hJZOKLz4K-p9WWAr7X727qae8wh4)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549839/7f0b55ed-1883-8ab2-6ede-6fa673102699.png)

- Tinkercad には、加速度センサーがないので、ポテンショメータを使って 1.5～2.5V の電圧を A/Dコンバータ ( A0, A1 ) に入力してみました。
- A/Dコンバータの出力 ( accel_x : 0～5V の電圧に対応する 0～1023 の数値 ) を実際の角度 ( angle_x ) に換算する式は、オフセット ( 0° の時の電圧 ) が 2.5V ( 512 ) なので、

$$\begin{align}
\text{angle_x}&=(\text{accel_x} - 512)(90°/204.8)\\\\
&=(\text{accel_x}-307.2-204.8)(90°/204.8)\\\\
&=(\text{accel_x}-307.2)(180°/409.6)-90°
\end{align}$$

> 右辺の計算は float型でおこなわれますが、angle_x は int 型なので、小数点以下を切り捨てた整数になります。

|$\text{volt}$|$\text{(volt/5 V)}\times1024$
:-:|:-:
1.0 V|204.8
1.5 V|307.2
2.0 V|409.6
2.5 V|512
3.5 V|716.8

- ポテンショメータのノブを左一杯にまわすと 1.5V ( -90° )、中央で 2.5V ( 0° )、右一杯で 3.5V ( 90° ) が Arduino の A0、A1 端子に入力されます。
- シリアルモニタを開いた状態で、正弦波形が描かれたボタンをクリックすると、シリアルモニタが分割されてシリアルプロッタが開きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549839/8f0759d4-4bd3-c7f3-368a-13960f8c5a31.png)

- シリアルプロッタは、取得値に応じて自動的に縦軸のスケールが変わるので、90 と -90 の固定値も角度 ( angle_x, angle_y ) と一緒にパソコンに送信しています。
( [シリアルプロッタの縦軸を固定する](https://qiita.com/MasakiOhno/items/438bcd0285227dc3024c) を参照 )
- プログラムでは、X, Y という凡例も送っていますが、Tinkercad のシリアルプロッタには表示されません。
- 加速度センサーのデータシートと傾斜角度の求め方は下記を参照してください。
[ＫＸＲ９４－２０５０モジュール](https://akizukidenshi.com/goodsaffix/AE-KXR94-2050.pdf)
[加速度センサーから傾斜角度を求める](https://www.microstone.co.jp/2016/12/21/14777324/)

【回路図】
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549839/94aa2474-5ffa-5648-5562-c49fa349a3fd.png)

1. シリアル通信のビットレート設定 ( 9600 bps ) します。
2. X 軸の電圧 ( A0 ) を読み取ります。( 実際は 307～717 )
3. Y 軸の電圧 ( A1 ) を読み取ります。( 実際は 307～717 )
4. X 軸の読み取り値を角度に換算します。( -90°～90° )
5. Y 軸の読み取り値を角度に換算します。( -90°～90° )
6. X, Y の凡例をパソコンに送ります。しかし、Tinkercad では対応していないので、表示されません。
7. シリアルプロッタの縦軸固定用の 90 を送ります。
8. X 軸の角度を送ります。
9. Y 軸の角度を送ります。
10. シリアルプロッタの縦軸固定用の -90 を送ります。

```c++
// C++ code
//
int accel_x, accel_y;	//A/Dコンバータの読み取り値
int angle_x, angle_y;	//水平方向に対する角度

void setup()
{
  Serial.begin(9600);	//シリアル通信のビットレート...1
}

void loop()
{
  accel_x = analogRead(A0);	//X軸の角度の読み取り(0～1023)...2
  accel_y = analogRead(A1);	//Y軸の角度の読み取り(0～1023)...3
  angle_x = (((accel_x - 307.2) / 409.6) * 180) - 90;//読み取り値を角度へ換算...4
  angle_y = (((accel_y - 307.2) / 409.6) * 180) - 90;//読み取り値を角度へ換算...5
  
  Serial.print("X");	//凡例を送る。Tinkercadでは、凡例は表示されない...6
  Serial.print(",");
  Serial.println("Y");
  
  Serial.print(90);		//シリアルプロッタの縦軸固定用...7
  Serial.print(",");
  Serial.print(angle_x);//X軸の角度を送る...8
  Serial.print(",");
  Serial.print(angle_y);//Y軸の角度を送る...9
  Serial.print(",");
  Serial.println(-90);	//シリアルプロッタの縦軸固定用...10
}
```

[ **#9 光センサー** ](https://www.youtube.com/watch?v=ITF4EUquYiM&list=PLZiGFIg4PRZgTl1XSvexduIyOTa_1W7_4&index=11) ( 動画 ) $\qquad$ [Tinkercad によるシミュレーション](https://www.tinkercad.com/things/0WPv7zeBmut-9-?sharecode=5ZB3YnU6o0LBKs04QOdnzF8xIUdJv8ZvLKTqjK9lQ_s)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549839/ca690051-a26f-ae9d-f213-e3df7c09514c.png)

光センサーの明るさによる抵抗値の変化は以下のとおりでした。下図の接続で、「シミュレーション開始」をクリックして、センサーをクリックすると、センサーの周囲の明るさをスライダで調整できるようになります。スライダの位置によって抵抗値は以下のように変わりました。

スライダの位置|抵抗値
:-:|:-:
明|506 Ω
中央|912 Ω
暗|180 kΩ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549839/eea51601-1790-2020-3c6a-6d01bce7dd69.png)

動画では実例が紹介されていなかったので、以下のような機能をもつ回路とプログラムを作成しました。
- 周囲が明るいときには電球 ( 5V 100 mA ) を消灯させ、周囲が暗いときには電球を点灯させる。
- Arduino のポートは、電球を点灯するだけの電流 ( 100 mA ) を供給できないので、リレーを介して電球を ON/OFF する。
- A/Dコンバータで読み取った値 ( 0～1023 ) を map( ) 関数で 0～255 に変換し、シリアルプロッタに表示する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549839/b1b622de-7269-c887-ab98-b97f9e915adf.png)

1. 5 ピンを出力に設定します。
2. シリアル通信のビットレートを 9600 bps に設定します。
3. A0 の電圧 ( value ) を読み取ります。
4. value ( 0～1023 ) を 0～255 に変換します。
5. 明暗のしきい値を 50 として、明るくなったら ( val < 50 )、電球を消灯します。
6. 暗くなったら ( val > 50 )、電球を点灯します。
7. シリアルプロッタの縦軸を固定するために、0 と 250 をパソコンに送信します。
8. val ( 0～255 ) をパソコンに送信します。

> シミュレーション開始後にセンサーをクリックすると、周囲の明るさを調整するスライダーが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549839/d5faebfa-183f-9ee8-4a5a-d26348d8ff41.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549839/83c89cb3-2af1-5717-d192-4fca1728bcb3.png)

```c++
// C++ code
//
int value, val;

void setup()
{
  pinMode(5, OUTPUT);	//5ピンを出力端子に設定...1
  Serial.begin(9600);	//シリアル通信のビットレート設定...2
}

void loop()
{
  value = analogRead(A0);	//A0の電圧(value)の読み込み(0～1023)...3
  val = map(value, 0, 1023, 0, 255); //valueを0～255に変換...4
  if(val < 50)
  {
    digitalWrite(5, HIGH);	//明るくなったら電球消灯...5
  }
  else
  {
    digitalWrite(5, LOW);	//暗くなったら電球点灯...6
  }
  
  Serial.print(0);		//シリアルプロッタの縦軸固定用(下側)...7
  Serial.print(",");
  Serial.print(250);	//シリアルプロッタの縦軸固定用(上側)
  Serial.print(",");
  Serial.println(val);	//valをパソコンに送信...8
}
```