BVE 鉄道シミュレーターで自宅運転台を動かそう~Arduino Unoで動かす


BVE 鉄道シミュレーターで自宅運転台を動かそう~パイロットランプをつけるの続編として

ArduinoUnoで速度計とパイロットランプを動かす方法を記します。
ArduinoUnoだけでなく、ArduinoNanoなどのATmega328系のマイコンボードでも対応可能です。
主な変更箇所は、転送速度と速度計のDACをanalogwriteに変更することです。

転送速度

転送速度を 19200bps に変更します。
これはATmega328で 115200bps の転送は荷が重い為です。

Serial.begin(19200);

速度計:PWM出力

ArduinoUno は数字の横に がついているものが PWM出力 できるポートになります。
3,5,6,9,10,11の5ピンが使用可能です。

注意:5・6番ピンはデューティー比の問題がある為使わない方が良いようです。

http://www.musashinodenpa.com/arduino/ref/index.php?f=0&pos=2153
今回は5番ピンを使用していますが、実際の場合は、別のピンを指定してください。

analogWrite(ピン番号, 速度 * 補正);

パイロットランプ・走行表示灯:LED出力

ドア状態を2番に出力します。
後で使用する走行表示灯は3番にしました。
BVEはドアが開くと1に閉まると0になるので

1⇒HIGH
0⇒LOW

にします。

  if (dai0 == 1) {
    digitalWrite(2, HIGH );
  }
  if (dai0 == 0) {
    digitalWrite(2, LOW );
  }

配線の様子

今回は、リレーをLEDの代わりにつないで説明します。
5番ピンに可変抵抗をつなげます。
LEDのマイナス側に2,3番ピンをつなげ、電源とLEDの間に抵抗をつなげます。
これで2,3番ピンはLOWになる事で点灯する回路となります。

BveSerialOutputの設定

BveSerialOutputはドア状態,速度を出力するようにしました。
なお、固定文字列には  , を入れます。

プログラム

//https://qiita.com/Ninagawa_Izumi/items/6a18249a31a87dad84e2
char dai0;  //2つのデータを扱うのでdai0とdai1を用意します。
char dai1;

#define elements 2 //カンマで区切るデータの最大項目数 今回は2個
String data_string; //シリアルで受け取る全文字列
char *p; //文字列をカンマで分割するstrtok処理で使うポインタ
String p_string; //上記ポインタで区切った文字列の仮格納用
String data_array[elements]; //カンマ分割されたstrデータを格納する

void setup() {
  pinMode(2, OUTPUT); //出力ピンを設定します。
  pinMode(5, OUTPUT);

  Serial.begin(19200);
  Serial.setTimeout(10); // タイムアウトの時間を10msに変更
}

void loop() {
  if (Serial.available() > 2 ) {
    data_string = Serial.readStringUntil(0x0a); //シリアルデータを改行記号が現れるまで読み込む
    data_string.trim(); //文字列を念のためトリミングする
    int data_len = data_string.length() + 1; //str→char変換用にデータの長さを調べる
    char data_char[data_len]; //str→char変換用のchar配列を準備
    data_string.toCharArray(data_char, data_len); //ようやくstr→charに変換
    p = strtok(data_char, ","); //カンマ分割の1要素目を行う
    p_string = p; //一旦strにいれる
    data_array[0] = p_string; //最終目的の配列に1要素目を格納

    for (int i = 1; i < elements; i++) { //2要素名以降について、要素数分だけデータ配列に格納
      p = strtok(NULL, ","); //カンマ分割の2要素目以降のstrtokはこの書式になる
      if (p != NULL) { //要素が空でない場合はその要素をデータ配列に格納
        p_string = p;
        data_array[i] = p_string;
      } else {
        data_array[i] = "0"; //要素が空の場合はデータ配列に0を格納
      }
    }
    //データを表示する
    for (int i = 0; i < elements ; i++) {
      dai0 = data_array[0].toInt();
      if (dai0 == 1) {
        digitalWrite(2, HIGH );
      }
      if (dai0 == 0) {
        digitalWrite(2, LOW );
      }

      dai1 = data_array[1].toInt();
      if (dai1 >= 0 && dai1 <= 125) {
        analogWrite(5, dai1 * 1.45); //補正値を修正する
      }

    }
    Serial.println();
  }
}

走行表示灯を点ける

少し改造します。
走行表示灯は時速5kや10Kで点灯します。
今回は10Km/hになると点灯するようにします。

  if (dai1 >= 10) {
    digitalWrite(3, LOW );
  }
  if (dai1 < 10) {
    digitalWrite(3, HIGH );
  }

プログラム

プログラムです。

//https://qiita.com/Ninagawa_Izumi/items/6a18249a31a87dad84e2
char dai0;
char dai1;

#define elements 2 //カンマで区切るデータの最大項目数
String data_string; //シリアルで受け取る全文字列
char *p; //文字列をカンマで分割するstrtok処理で使うポインタ
String p_string; //上記ポインタで区切った文字列の仮格納用
String data_array[elements]; //カンマ分割されたstrデータを格納する

void setup() {
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(5, OUTPUT);

  Serial.begin(19200);
  Serial.setTimeout(10);        // タイムアウトの時間を10msに変更
}

void loop() {
  if (Serial.available() > 2 ) {
    data_string = Serial.readStringUntil(0x0a); //シリアルデータを改行記号が現れるまで読み込む
    data_string.trim(); //文字列を念のためトリミングする
    int data_len = data_string.length() + 1; //str→char変換用にデータの長さを調べる
    char data_char[data_len]; //str→char変換用のchar配列を準備
    data_string.toCharArray(data_char, data_len); //ようやくstr→charに変換
    p = strtok(data_char, ","); //カンマ分割の1要素目を行う
    p_string = p; //一旦strにいれる
    data_array[0] = p_string; //最終目的の配列に1要素目を格納

    for (int i = 1; i < elements; i++) { //2要素名以降について、要素数分だけデータ配列に格納
      p = strtok(NULL, ","); //カンマ分割の2要素目以降のstrtokはこの書式になる
      if (p != NULL) { //要素が空でない場合はその要素をデータ配列に格納
        p_string = p;
        data_array[i] = p_string;
      } else {
        data_array[i] = "0"; //要素が空の場合はデータ配列に0を格納
      }
    }
    //データを表示する
    for (int i = 0; i < elements ; i++) {
      dai0 = data_array[0].toInt();
      if (dai0 == 1) {
        digitalWrite(2, HIGH );
      }
      if (dai0 == 0) {
        digitalWrite(2, LOW );
      }

      dai1 = data_array[1].toInt();
      if (dai1 >= 0 && dai1 <= 125) {
        analogWrite(5, dai1 * 1.45);
      }
      if (dai1 >= 10) {
        digitalWrite(3, LOW );
      }
      if (dai1 < 10) {
        digitalWrite(3, HIGH );
      }
    }
    Serial.println();
  }
}

以上でArduinoUnoで速度計とパイロットランプ、走行表示灯が制御できたと思います。
なお、リレーの使い方はこちらを参照ください。