BVE 鉄道シミュレーターで自宅運転台を動かそう～ATC対応速度計を動かす～シフトレジスタ編

BVE 鉄道シミュレーターで自宅運転台を動かそう～ATC対応速度計の続きとして
シフトレジスタ編に続きBVEと連携してみます。

https://qiita.com/usashirou/items/45e8910cdd54b772d3b3

# 構成
ESP32に74HC595とトランジスタアレイを接続してます。
トランジスタアレイは4個のTD62083と2個のTBD62783が使用できるようにします。
これにより16個の24V出力、24個のGND入力ができます。
![DSC_0169 (2).JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/4f109b42-8385-c90c-99a7-8bfd5ac83425.jpeg)
# プログラム
早速ですがプログラムです。
最初に今回ATCの値が多いためまずは項目数を29個にしました。
次に、ATCの値が __1__ の場合対応するビットを上げる __or演算__ をします。
__or 演算__ というと難しいと感じられますが
プログラムとしては単純でXが1であったら該当のビットを1にするというだけです。
これを該当のピン番号と出力データを設定していきます。

```
      if (X == 1) {
        b1 |= B00000001;
      }
```
プログラムの最後にシフトレジスタの値を0に戻します。
これをしないと以前のデータが残ってしまい、Highになるものがどんどん増えて行ってしまいます。

    X = B00000000;

参考プログラムは以下です
```
//https://qiita.com/Ninagawa_Izumi/items/6a18249a31a87dad84e2
const int dac1 = 25;
const int dac2 = 26;
const int dac3 = 32;
const int dac4 = 33;
const int PIN_SER   =  15;
const int PIN_LATCH  =  13;
const int PIN_CLK    = 12;
const int PIN_SCLR    = 14;

char dai1;
char dai2;
char dai3;
char dai4;
char dai5;
char dai6;
char dai7;
char dai8;
char dai9;
char dai10;
char dai11;
char dai12;
char dai13;
char dai14;
char dai15;
char dai16;
char dai17;
char dai18;
char dai19;
char dai20;
char dai21;
char dai22;
char dai23;
char dai24;
char dai25;
char dai26;
char dai27;
char dai28;

long int di5;
byte b1 = B00000000;
byte b2 = B00000000;
byte b3 = B00000000;

long int dao0;
long int dao1;
long int dao2;
long int dao3;
long int dao4;

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


void setup() {
  pinMode(dac1, OUTPUT);
  pinMode(dac2, OUTPUT);
  pinMode(dac3, OUTPUT);
  pinMode(dac4, OUTPUT);
  pinMode(2, OUTPUT);

  ledcSetup(1, 12800, 8);
  ledcSetup(2, 12800, 8);
  ledcSetup(3, 12800, 8);
  ledcSetup(4, 12800, 8);
  ledcAttachPin(dac1, 1);
  ledcAttachPin(dac2, 2);
  ledcAttachPin(dac3, 3);
  ledcAttachPin(dac4, 4);
  pinMode( PIN_SER, OUTPUT );
  pinMode( PIN_LATCH, OUTPUT );
  pinMode( PIN_CLK, OUTPUT );
  pinMode( PIN_SCLR, OUTPUT );
  delay(100);
  digitalWrite(PIN_SCLR, HIGH);

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

void loop() {
  if (Serial.available() > 29 ) {
    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++) {
      Serial.print("A");
      Serial.print(data_array[0]);
      dai1 = data_array[0].toInt();
      if (dai1 >= 0 && dai1 <= 90) {
        ledcWrite(1, dai1 * 1.45);
      }
      dai2 = data_array[1].toInt();
      if (dai2 >= 0 && dai2 <= 90) {
        ledcWrite(2, dai2 * 1.45);
      }
      dai3 = data_array[2].toInt();
      if (dai3 >= 0 && dai3 <= 200) {
        ledcWrite(3, dai3 * 1);
      }
      dai4 = data_array[3].toInt();
      if (dai4 >= 0 && dai4 <= 200) {
        ledcWrite(4, dai4 * 1);
      }
      dai5 = data_array[4].toInt();
      if (dai5 == 1) {
        b1 |= B00000001;
      }
      dai6 = data_array[5].toInt();
      if (dai6 == 1) {
        b1 |= B00000010;
      }
      dai7 = data_array[6].toInt();
      if (dai7 == 1) {
        b1 |= B00000100;
      }
      dai8 = data_array[7].toInt();
      if (dai8 == 1) {
        b1 |= B00001000;
      }
      dai9 = data_array[8].toInt();
      if (dai9 == 1) {
        b1 |= B00010000;
      }
      dai10 = data_array[9].toInt();
      if (dai10 == 1) {
        b1 |= B00100000;
      }
      dai11 = data_array[10].toInt();
      if (dai11 == 1) {
        b1 |= B01000000;
      }
      dai12 = data_array[11].toInt();
      if (dai12 == 1) {
        b1 |= B10000000;
      }
      dai13 = data_array[12].toInt();
      if (dai13 == 1) {
        b2 |= B00000001;
      }
      dai14 = data_array[13].toInt();
      if (dai14 == 1) {
        b2 |= B00000010;
      }
      dai15 = data_array[14].toInt();
      if (dai15 == 1) {
        b2 |= B00000100;
      }
      dai16 = data_array[15].toInt();
      if (dai16 == 1) {
        b2 |= B00001000;
      }
      dai17 = data_array[16].toInt();
      if (dai17 == 1) {
        b2 |= B00010000;
      }
      dai18 = data_array[17].toInt();
      if (dai18 == 1) {
        b2 |= B00100000;
      }
      dai19 = data_array[18].toInt();
      if (dai19 == 1) {
        b2 |= B01000000;
      }
      dai20 = data_array[19].toInt();
      if (dai20 == 1) {
        b2 |= B10000000;
      }
      dai21 = data_array[20].toInt();
      if (dai21 == 1) {
        b3 |= B00000001;
      }
      dai22 = data_array[21].toInt();
      if (dai22 == 1) {
        b3 |= B00000010;
      }
      dai23 = data_array[22].toInt();
      if (dai23 == 1) {
        b3 |= B00000100;
      }
      dai24 = data_array[23].toInt();
      if (dai16 == 1) {
        b3 |= B00001000;
      }
      dai25 = data_array[24].toInt();
      if (dai25 == 1) {
        b3 |= B00010000;
      }
      dai26 = data_array[25].toInt();
      if (dai26 == 1) {
        b3 |= B00100000;
      }
      dai27 = data_array[26].toInt();
      if (dai27 == 1) {
        b3 |= B01000000;
      }
      dai28 = data_array[27].toInt();
      if (dai28 == 1) {
        b3 |= B10000000;
      }
      //シフトレジスタに書込
      digitalWrite(PIN_SCLR, HIGH);
      digitalWrite(PIN_LATCH, LOW);
      shiftOut(PIN_SER, PIN_CLK, LSBFIRST, b3);
      shiftOut(PIN_SER, PIN_CLK, LSBFIRST, b2);
      shiftOut(PIN_SER, PIN_CLK, LSBFIRST, b1);
      digitalWrite(PIN_LATCH, HIGH);
    }
    Serial.println();
    b1 = B00000000;
    b2 = B00000000;
    b3 = B00000000;
  }
}
```
これでシフトレジスタでATCメータが動かせましたね。
応用することでATCメータだけでなく、計器類も動かすことが可能です。