Master Controller for Train Simulator(ワンハンドルのもの、以下TSマスコン)および、

Master Controller for Train Simulator2(ツーハンドルのもの、以下TSマスコン2)をコントローラとして利用するパワーパックを製作しました。

ノーマルなパワーパックと基本的に回路は同一ですが、当パワーパック単体での運転はできませんのでご注意ください。

また、ソースコードの下に操作方法およびトラブルシューティングを掲載しておりますので、必要に応じて参照してください。

 

回路図

画像をクリックすると拡大表示できます。

 

注1:

線路出力の制御にリレーモジュールを使用していますが、回路図および実体配線図では省略しています。

使用するモジュールに合わせ、適宜結線してください。

<2020/09/17 追記>

リレーモジュールの配線図(例)を用意しました。また回路図の線路出力部を訂正しました。

回路図内「リレーモジュール」=リレーモジュール配線図内「出力」です。

<2021/07/29 追記>

リレーモジュール配線図とソースコード内定数について、母線制御⇔P_BOSEN、方向制御⇔P_DIRECにそれぞれ対応します。

初期設定ではP_BOSENはD12ピン、P_DIRECはD13ピンに設定してあります。不都合がある場合は適宜変更して下さい。

 

試作時に使用したモジュールはこちらです。(Amazonに飛びます)

<2020/07/04 追記>

試作時に使用したモジュールの販売が終了していました。

代替品ですが、5V制御かつ12V 2A以上の耐圧で4回路のリレーモジュールを推奨します。

(Amazonで「リレーモジュール」と検索すると色々出てきます)

※実際に使用するのは3回路だけですが、3回路のモジュールは無いので4回路のモジュールを代用します。1回路のモジュールを複数使うのも手ですが、電源ラインをモジュール数だけ用意する必要があり、手間が増えるだけなので非推奨当パワーパックのコードは正論理・負論理どちらのモジュールにも対応できるようコーディングしてありますので、上記の条件を満たすモジュールであれば何でも使用出来ます。

なので、検索時に上記の条件を満たす一番安いモジュールを使うのが良いと思います。

 

なお、パワー不足によりArduinoからリレーの直接駆動は不可能です。

必ずリレーモジュールを使うようにしてください。

 

注2:

RS232C変換基板は秋月電子で販売されているこちらを推奨します。(部品一覧にも載せています)

RS232の接続方法(TSマスコンとの接続方法)は大きく分けて2種類考えられます。

1. 回路基板ーRS232変換基板間(5V系)をシールド線で結線し、RS232変換基板(RS232レベル)ーD-sub 9Pinメスコネクタを直結させることで、パワーパックからTSマスコンのRS232(D-sub 9Pinオス)に直接接続できるようにする方法

この方法はRS232変換基板(RS232レベル)ーD-sub 9Pinメスコネクタ間のTXDとRXDについては変換基板のパターンとコネクタ端子が短絡しないように処理の上、別途導線でクロスさせて接続する等の対応が必要ですが、変換基板及びコネクタをシェルに収めることが出来る他、完成後の接続ミスが無くなる、ケーブル長を自分で調整できる等の利点があるためおすすめです。

なお、シールド線の長さは~2m程度を推奨します。

 

2. 回路基板ーRS232変換基板間(5V系)はピンヘッダやはんだ付け等で回路基板に直接結線し、RS232変換基板(RS232レベル)側はD-sub 9Pinオスコネクタに結線することで、パワーパックーTSマスコンのRS232(D-sub 9Pinオス)間の接続を市販のRS232ケーブルで出来るようにする方法

要はパワーパック側もオスコネクタとして、TSマスコンとの接続に市販のRS232ケーブルを用いる方法です。筐体からケーブルが出ないのでパワーパック自体はスマートな外見となりますが、基板ーオスコネクタの結線方法によりストレート・クロスが変わってしまう(必要なRS232ケーブルの種類が変わってしまう)、コネクタ部に負荷がかかるため強度設計をしっかりしておかないと将来的に故障を引き起こす可能性があるという欠点があります。

 

基本的には1.の方法をお勧めします。一度完成させれば、その後は接続で迷うことが無くなるためです。

なお、どちらの場合でも、Arduinoのシリアル端子と変換基板を直結させるとPCーArduino間の通信が阻害され、プログラム書き換え時に毎回Arduinoを回路から取り外さなければならないという手間が生じます。この手間を避けたい場合は、トグルスイッチ等でArduinoのシリアル端子と変換基板間の接続を物理的に切断できるようにしておくと良いかと思います。まあ通信路に異物が入る事になるので、通信エラーが増える可能性もありますが……。

 

 

 

実体配線図はこちらです。

<2021/07/19 追記>

実体配線図にリレーモジュール出力部の表記が無かったため修正しました。

 

部品一覧

回路に必要な部品のみ掲載しております。基板、ピンソケット、筐体等は別途ご用意下さい。

また、回路図・部品一覧には記載しておりませんが470pFのコンデンサをSB340LSと並列に接続する事で、列車の動きが多少滑らかになる場合があります。

列車の挙動に応じて使用するか否か選択してください。

なお、RS232C用ケーブルですが1m程度ならシールド線でなくても問題ないようです。(正常に通信できました)

心配な方やケーブルを長めにする場合はシールド線の方が良いかもしれません。

運転方法

以下に操作方法および運転方法を記載しておきます。

操作は上から順に行ってください。

 

<パワーパック起動前>

1. ノッチ段数の設定

1-A. TSマスコンの場合

TSマスコンの段数設定(マスコンタイプ)にはTYPE A~Hの8タイプがあります。

TSマスコン裏面で段数を設定しましょう。

 

1-B. TSマスコン2の場合

筐体左右のハンドルでTSマスコン2のマスコン段数およびブレーキ段数を設定します。

 

2. キャリブレーション

TSマスコン等の電源を入れた後、段数をN位置にした状態でA、B、C、Sボタン(全ボタン)を同時押しします。

この操作でマスコンの位置をキャリブレーションすることができます。

 

3. パワーパック起動

専用パワーパックとTSマスコン等を接続し、パワーパックの電源を入れます。

 

<パワーパック起動後>

1. 使用マスコンの設定

TSマスコンとTSマスコン2のどちらを使用するか選択します。

TSマスコンを使用する場合は「1Handle」を、TSマスコン2を使用する場合は「2Handle」を選択してください。

TSマスコン等のA、Cボタンで選択し、Sボタンで決定します。

(A、Cボタンで「>」カーソルをどちらかに合わせます)

設定例:

TSマスコンを使用する場合、  「>1Handle 2Handle」と表示された状態でSボタンを押して決定します。

TSマスコン2を使用する場合、「 1Handle>2Handle」と表示された状態でSボタンを押して決定します。

 

2. ノッチ段数の設定

2-A. TSマスコンの場合

あらかじめTSマスコンで設定したマスコンタイプ(TYPE A~H)をパワーパックに設定します。

TSマスコンのA、CボタンでA~Hのアルファベットを選択し、Sボタンで決定します。

設定例:

TYPE Cを設定する場合、「SET MASCONTYPE:C」と表示された状態でSボタンを押して決定します。

 

2-B. TSマスコン2の場合
2-B-1. マスコン段数の設定

TSマスコン2のA、CボタンでP3~P6を筐体左のハンドルの数字に合わせて選択し、Sボタンで決定します。

設定例:

マスコン段数を5段(筐体左のハンドルが「5」を指している)に設定する場合、「MAX-NOTCH:P5」と表示された状態でSボタンを押して決定します。

※「EX」を使用する場合、パワーパックは「P6」に設定してください。

 

2-B-2. ブレーキ段数の設定

TSマスコン2のA、CボタンでB5~B8を筐体右のハンドルの数字に合わせて選択し、Sボタンで決定します。

設定例:

ブレーキ段数を7段(筐体右のハンドルが「7」を指している)に設定する場合、「MAX-BRAKE:B7」と表示された状態でSボタンを押して決定します。

※「EX」を使用する場合、パワーパックは「B8」に設定してください。

 

2. 走行音設定

走行音を設定します。

TSマスコン等のA、Cボタンで選択し、Sボタンで決定します。

 

3. 運転

以上の操作をすべて終えると、運転が可能になります。

列車の進行方向はTSマスコン等のレバーサで制御可能です。

運転中は以下の操作となります。

 

・レバーサ……進行方向の制御(N位置では線路への電源供給が止まります)

・マスコン/ブレーキハンドル……列車制御(現在のノッチ段数が常時LCDに表示されます)

・Sボタン……走行音の再設定(停車中のみ動作。走行中はSボタンが無反応になります)

・Aボタン、Bボタン……割り当てなし

Cボタン……非常停止(線路への電源供給が止まります)

もう一度Cボタンを押すと復帰します。なお、非常停止中は「NOW STOPPING」と表示されます。

 

また、V2.10より誤発進防止機能を追加しています。

走行音の設定後および非常停止からの復帰後に「Pls Apply BRAKE.」と表示された場合、ノッチをブレーキ位置に入れることで運転が可能となります。

 

トラブルシューティング

・電源が付かない

→ACアダプタが刺さっているか、電源スイッチを入にしているか、回路が正しく組めているか、はんだ付けが適切に行われているか、断線などはないかを確認してください。

 

・液晶(LCD)が表示されない

→LCD輝度を適切に設定してください。輝度つまみを回しても表示されない場合は回路が正しく組めているか、はんだ付けが適切に行われているか、断線などはないかを確認してください。

 

・液晶(LCD)の表示がおかしい

→電源を入れなおしてください。電源を何回か入れなおしても改善されない場合、回路が正しく組めているか、はんだ付けが適切に行われているか、断線などはないかを確認してください。

 

・TSマスコン等の操作が反映されない

→RS232C端子をTSマスコン等にきちんと刺しているか、メス端子と変換基板までの配線および変換基板から回路基板への配線が正しいか確認してください。また、回路基板と変換基板の配線が長すぎる可能性があります。

 

・列車が動かない

→保護ランプが点灯している場合は線路が短絡していないか確認してください。それ以外の場合、回路が正しく組めているか、はんだ付けが適切に行われているか、断線などはないか、リレーモジュールの配線は適切か、リレーモジュールの論理に合わせてコードが正しく設定されているかを確認してください。

 

ソースコード

現在の最新バージョンは3.20です。

 

<以前のバージョン>

V3.10・・・スピードスケール調整機能を強化。

V3.00・・・走行機能を刷新。

V2.00(安定版)・・・誤発進防止機能が正常に動作しない場合にご利用ください。

 

<V3.20の更新内容>

(V3.20)

・惰行時・各力行ノッチ最高速度以上時の減速率計算方法を変更しました。

 

誤発進防止機能が正常に動作しない場合(パワーパックが動作しなくなる等が頻発する場合)、お手数ですが上記のV2.00(安定版)を使用してください。

/*
 ***************************************************************************************
  TSマスコン(master controller)パワーパック
 ***************************************************************************************
  ・PWM制御による鉄道模型用コントローラー。
  ・TSマスコン(ワンハンドルのやつ)を接続して運転する。
  ・段数はTSマスコンの設定(TYPE)に準じ、TSマスコンのボタン操作でTYPEを選択する仕様。
  ・各種状況はキャラクタLCD(16文字x2段)で表示。
  ・力行各段の最高速度を設定可能。設定はソースコード内であらかじめ設定。
  ・ノッチによる加速度の違いをついでに再現。
  ・速度に応じた加速度の変化もだいたい再現。
  ・PWM周波数の変調により、VVVFなど制御機器の音を再現。
  ・変調音は最大255種類までプログラム可能。(メモリ容量によってはプログラム可能な最大数が少なくなります)
  ・加速時と減速時で異なる変調音を設定可能。
  ・変調音の選択はTSマスコンのボタン操作で行う。
 ***************************************************************************************
更新履歴
2020/05/22 V0.10 初期版。動作確認済
2020/05/22 V1.00 段数読替設定追加
2020/09/17 V2.00 TSマスコン2に対応
2020/09/22 V2.10 誤発進防止機能追加、加速度調節の細分化(8段階→16段階)、RAM使用量の削減
2020/11/17 V3.00 PWM周波数および出力設定の変更、加速度調節の細分化(16段階→32段階)、
                 常点灯の感度調整機能追加、スケールスピード調整機能追加、
                 最高速度400km/hに変更(モーターの性能により400km/hに届かない場合もあります)
2021/07/03 V3.10 加速曲線と減速度を音データ毎に決められるようになりました。
                 表示速度の変化率に対するDuty比の変化率を調整できるようにしました。
2022/07/15 V3.20 惰行時・各力行ノッチ最高速度以上時の減速率計算方法を変更しました。
 ***************************************************************************************
*/



#include <avr/io.h> // ATmega328P用のヘッダファイル
#include <avr/pgmspace.h>
#include <LiquidCrystal.h> // キャラクタ液晶ディスプレイ制御用のヘッダファイル

// ***********************************************
// ユーザー設定領域
// 環境に合わせて各値を適宜調整してください。
// ***********************************************

#define SOUNDNUM 16 //走行音データの数
#define STOPSPD 9 //列車をピタッと止めるための値。環境に合わせて調整可能
#define SDATANUM 45 //3n(n=最も行数が多い音データの行数)以上の値に設定して下さい。あまり数字を大きくし過ぎるとメモリ不足になるため、むやみに大きくしないように。
#define ANTI_CHAT_RATE 40 //マスコン読み込み待ち時間
const int ACCEL_RATIO = 5;    // (加速率調整小型ボリュームからの入力値) × ACCEL_RATIO ⇒ 加速率
const int BRAKE_RATIO = 2;    // (加速率調整小型ボリュームからの入力値) × BRAKE_RATIO × マスコンブレーキ値 ⇒ 減速率
const float lmax = 2; //常点灯ボリュームの最大値。lmax=1→出力MAX10%まで、1.5→出力MAX15%まで、のようにボリュームの範囲を調整可能。選択可能な範囲を狭くするほど微調整が可能となる。
const float sscale = 3.1; //スケールスピード変換値。環境に合わせ実測して調整。
const float adrate = 0.5; //加速度及び減速度の変化に対するDuty比の変化率。環境に合わせ実測して調整。
const float coast = 0.07; //惰行時最高速度以上時の減速率。
const float pcoast = 0.01; //各力行ノッチ最高速度以上時の減速率。
/*
sscaleとadrateの関係について:
sscaleは表示速度に対するduty比を全域にわたって調整します。グラフをそのまま上下に動かすイメージです。
主に列車の起動速度調整に使用します。
一方、adrateは表示速度の変化に対するDuty比の変化率を調整します。
加速時を例とすると、表示速度の上昇速度に対するDuty比の上昇速度を調整します。
加速度ボリュームは表示速度の変化率を調整するのに対し、adrateは表示速度に対する実速度の変化率を調整する項目であるといえます。
*/

boolean viewduty = false;  //(開発用)走行中、走行音名の代わりにDUTY比を表示します。

//モジュール論理選択
boolean relay = false; //true:LOW=リレーON(負論理),false:LOW=リレーOFF(正論理)で設計。使用するモジュールの論理により切り替えてください。
//boolean relay = true;



// ***********************************************
// 走行音データ領域
// データの追加や変更を行う場合は慎重に行ってください。
// ***********************************************

// 走行音名称。16桁で指定すること
const char* soundName[SOUNDNUM] = {
  "   201          ",
  " 209,70-000,etc ",
  " E231-0,500,etc ",
  "   E231-1000    ",
  "   E233, etc    ",
  "KQ 2100 (SI-GTO)",
  " N1000 (SI-GTO) ",
  " MITSUBISHI-GTO ",
  "   Toyo-GTO     ",
  "   Toyo-IGBT    ",
  "Toei 5300(Akuma)",
  "TOSHIBA 3LevIGBT",
  "MITSUBISHI Chop.",
  "N1000 (SUS-IGBT)",
  "N1000 (SI-IGBT) ",
  " Toyo-IGBT(AE)  "
};



// 各パターンの各ノッチ位置での最高速度値を配列で保持。
// 左から順に 1ノッチ, 2ノッチ, 3ノッチ, 4ノッチ, 5ノッチ。
const int maxSpdDataBase[] PROGMEM = {
  31, 46, 101, 111, 111, //パターン 1 201 
  41, 56, 101, 111, 111, //パターン 2 209,70-000,etc
  41, 56, 111, 121, 121, //パターン 3 E231-0,500,etc
  41, 56, 111, 121, 121, //パターン 4 E231-1000
  41, 56, 111, 121, 121, //パターン 5 E233, etc
  51, 66, 121, 131, 131, //パターン 6 KQ 2100 (SI-GTO)
  51, 66, 121, 131, 131, //パターン 7 N1000 (SI-GTO)
  51, 66, 121, 131, 131, //パターン 8 MITSUBISHI-GTO
  51, 66, 121, 131, 131, //パターン 9 Toyo-GTO
  51, 66, 121, 131, 131, //パターン10 Toyo-IGBT
  41, 56, 101, 111, 111, //パターン11 Toei 5300(Akuma)
  41, 56, 101, 111, 111, //パターン12 TOSHIBA 3LevIGBT
  41, 56, 101, 111, 111, //パターン13 MITSUBISHI Chop.
  51, 66, 121, 131, 131, //パターン14 N1000 (SUS-IGBT)
  51, 66, 121, 131, 131, //パターン15 N1000 (SI-IGBT)
  51, 66, 151, 161, 161  //パターン16 Toyo-IGBT(AE)
};



//定加速度領域値。
//指定した速度まで一定の加速度を保ちます。
const int keepac[SOUNDNUM] = {37, 39, 39, 39, 39, 55, 55, 55, 55, 59, 50, 50, 45, 59, 55, 85};

//加速曲線。
//数値を大きくすると加速性能が良くなります。お好みで調整して下さい。
const float Aracurve[SOUNDNUM] = {1.2, 1.3, 1.4, 1.4, 1.5, 1.55, 1.55, 1.5, 1.5, 1.4, 1.4, 1.45, 1.45, 1.5, 1.5, 1.4};

//減速度。
//数値を大きくすると減速性能が良くなります。お好みで調整して下さい。
const float Arbcurve[SOUNDNUM] = {0.13, 0.15, 0.15, 0.15, 0.16, 0.14, 0.14, 0.14, 0.15, 0.17, 0.14, 0.14, 0.14, 0.14, 0.14, 0.17};

// 音データ。
// 設定可能周波数 150Hz~100kHz(値は整数で指定)
// ~~~(例) パターン3の場合~~~
// 1行目 速度0~ 2の間:380Hzで音程変化なし
// 2行目 速度3~11の間:380Hzから980Hzまで変化する

//加速時用の音データ。
const int AsoundDataBase[] PROGMEM = {
  //パターン1 201
  590, 590, 66,  // 1 開始周波数 終了周波数 切替スピード
  550, 980, 101, // 2 開始周波数 終了周波数 切替スピード
  -1,            // 3 終了コード

  //パターン2 209,70-000,etc
  230, 230, 2,     // 1 開始周波数 終了周波数 切替スピード
  90, 90, 3,       // 2 開始周波数 終了周波数 切替スピード
  230, 950, 11,    // 3 開始周波数 終了周波数 切替スピード
  600, 1150, 21,   // 4 開始周波数 終了周波数 切替スピード
  750, 1150, 31,   // 5 開始周波数 終了周波数 切替スピード
  600, 950, 46,    // 6 開始周波数 終了周波数 切替スピード
  470, 1150, 91,   // 7 開始周波数 終了周波数 切替スピード
  1150, 1150, 111, // 8 開始周波数 終了周波数 切替スピード
  -1,              // 9 終了コード

  //パターン3 E231-0,500,etc
  380, 380, 2,     // 1 開始周波数 終了周波数 切替スピード
  380, 980, 11,    // 2 開始周波数 終了周波数 切替スピード
  560, 780, 26,    // 3 開始周波数 終了周波数 切替スピード
  480, 1150, 101,  // 4 開始周波数 終了周波数 切替スピード
  1150, 1150, 121, // 5 開始周波数 終了周波数 切替スピード
  -1,              // 6 終了コード

  //パターン4 E231-1000
  1050, 1050, 11,  // 1 開始周波数 終了周波数 切替スピード
  1050, 700, 27,   // 2 開始周波数 終了周波数 切替スピード
  700, 6000, 35,   // 3 開始周波数 終了周波数 切替スピード
  480, 1150, 101,  // 4 開始周波数 終了周波数 切替スピード
  1150, 1150, 121, // 5 開始周波数 終了周波数 切替スピード
  -1,              // 6 終了コード

  //パターン5 E233, etc
  760, 760, 31,    // 1 開始周波数 終了周波数 切替スピード
  350, 1150, 101,  // 2 開始周波数 終了周波数 切替スピード
  1150, 1150, 121, // 3 開始周波数 終了周波数 切替スピード
  -1,              // 4 終了コード

  //パターン6 KQ 2100 (SI-GTO)
  350, 350, 2,   // 1 開始周波数 終了周波数 切替スピード
  390, 390, 3,   // 2 開始周波数 終了周波数 切替スピード
  440, 440, 4,   // 3 開始周波数 終了周波数 切替スピード
  490, 490, 5,   // 4 開始周波数 終了周波数 切替スピード
  530, 530, 6,   // 5 開始周波数 終了周波数 切替スピード
  580, 580, 7,   // 6 開始周波数 終了周波数 切替スピード
  650, 650, 8,   // 7 開始周波数 終了周波数 切替スピード
  700, 700, 9,   // 8 開始周波数 終了周波数 切替スピード
  780, 780, 23,  // 9 開始周波数 終了周波数 切替スピード
  510, 980, 111, //10 開始周波数 終了周波数 切替スピード
  580, 580, 131, //11 開始周波数 終了周波数 切替スピード
  -1,            //12 終了コード

  //パターン7 N1000 (SI-GTO)
  350, 350, 2,   // 1 開始周波数 終了周波数 切替スピード
  390, 390, 3,   // 2 開始周波数 終了周波数 切替スピード
  440, 440, 4,   // 3 開始周波数 終了周波数 切替スピード
  490, 490, 5,   // 4 開始周波数 終了周波数 切替スピード
  530, 530, 6,   // 5 開始周波数 終了周波数 切替スピード
  580, 580, 7,   // 6 開始周波数 終了周波数 切替スピード
  650, 650, 8,   // 7 開始周波数 終了周波数 切替スピード
  700, 700, 9,   // 8 開始周波数 終了周波数 切替スピード
  780, 780, 16,  // 9 開始周波数 終了周波数 切替スピード
  950, 970, 18,  //10 開始周波数 終了周波数 切替スピード
  850, 970, 21,  //11 開始周波数 終了周波数 切替スピード
  800, 970, 23,  //12 開始周波数 終了周波数 切替スピード
  510, 980, 111, //13 開始周波数 終了周波数 切替スピード
  580, 580, 131, //14 開始周波数 終了周波数 切替スピード
  -1,            //15 終了コード

  //パターン8 MITSUBISHI GTO
  490, 710, 16,    // 1 開始周波数 終了周波数 切替スピード
  710, 710, 25,    // 2 開始周波数 終了周波数 切替スピード
  600, 780, 31,    // 3 開始周波数 終了周波数 切替スピード
  420, 1250, 110,  // 4 開始周波数 終了周波数 切替スピード
  1250, 1250, 131, // 5 開始周波数 終了周波数 切替スピード
  -1,              // 6 終了コード
  
  //パターン9 Toyo-GTO
  490, 780, 19,    // 1 開始周波数 終了周波数 切替スピード
  490, 780, 31,    // 2 開始周波数 終了周波数 切替スピード
  420, 1250, 110,  // 3 開始周波数 終了周波数 切替スピード
  1250, 1250, 131, // 4 開始周波数 終了周波数 切替スピード
  -1,              // 5 終了コード

  //パターン10 Toyo-IGBT
  1050, 1050, 26,  // 1 開始周波数 終了周波数 切替スピード
  630, 1300, 41,   // 2 開始周波数 終了周波数 切替スピード
  650, 1150, 111,  // 3 開始周波数 終了周波数 切替スピード
  1150, 1150, 131, // 4 開始周波数 終了周波数 切替スピード
  -1,              // 5 終了コード

  //パターン11 Toei 5300(Akuma)
  780, 780, 11,    // 1 開始周波数 終了周波数 切替スピード
  450, 450, 16,    // 2 開始周波数 終了周波数 切替スピード
  500, 780, 22,    // 3 開始周波数 終了周波数 切替スピード
  430, 580, 25,    // 4 開始周波数 終了周波数 切替スピード
  400, 1150, 105,  // 5 開始周波数 終了周波数 切替スピード
  1150, 1150, 121, // 6 開始周波数 終了周波数 切替スピード
  -1,              // 7 終了コード

  //パターン12 TOSHIBA 3LevIGBT
  730, 730, 11,    // 1 開始周波数 終了周波数 切替スピード
  510, 730, 16,    // 2 開始周波数 終了周波数 切替スピード
  600, 1150, 101,  // 3 開始周波数 終了周波数 切替スピード
  1150, 1150, 111, // 4 開始周波数 終了周波数 切替スピード
  -1,              // 5 終了コード

  //パターン13 MITSUBISHI Chop.
  590, 590, 4,     // 1 開始周波数 終了周波数 切替スピード
  900, 900, 36,    // 2 開始周波数 終了周波数 切替スピード
  590, 590, 41,    // 3 開始周波数 終了周波数 切替スピード
  390, 390, 46,    // 4 開始周波数 終了周波数 切替スピード
  390, 1150, 101,  // 5 開始周波数 終了周波数 切替スピード
  1150, 1150, 111, // 6 開始周波数 終了周波数 切替スピード
  -1,              // 7 終了コード

  //パターン14 N1000 (SUS-IGBT)
  710, 710, 31,    // 1 開始周波数 終了周波数 切替スピード
  350, 1150, 101,  // 2 開始周波数 終了周波数 切替スピード
  1150, 1150, 131, // 3 開始周波数 終了周波数 切替スピード
  -1,              // 4 終了コード

  //パターン15 N1000 (SI-IGBT)
  1180, 1180, 16,  // 1 開始周波数 終了周波数 切替スピード
  1180, 1960, 33,  // 2 開始周波数 終了周波数 切替スピード
  460, 1150, 101,  // 3 開始周波数 終了周波数 切替スピード
  1150, 1150, 131, // 4 開始周波数 終了周波数 切替スピード
  -1,              // 5 終了コード

  //パターン16 Toyo-IGBT(AE)
  1000, 1000, 26,  // 1 開始周波数 終了周波数 切替スピード
  580, 1300, 41,   // 2 開始周波数 終了周波数 切替スピード
  650, 1150, 141,  // 3 開始周波数 終了周波数 切替スピード
  1150, 1150, 161, // 4 開始周波数 終了周波数 切替スピード
  -1               // 5 終了コード

};

//減速時用の音データ。
const int BsoundDataBase[] PROGMEM = {
  //パターン1 201
  10000, 10000, 6, // 1 開始周波数 終了周波数 切替スピード
  590, 590, 61,    // 2 開始周波数 終了周波数 切替スピード
  590, 650, 76,    // 3 開始周波数 終了周波数 切替スピード
  550, 980, 101,   // 4 開始周波数 終了周波数 切替スピード
  -1,              // 5 終了コード

  //パターン2 209,70-000,etc
  150, 150, 6,     // 1 開始周波数 終了周波数 切替スピード
  120, 1180, 26,   // 2 開始周波数 終了周波数 切替スピード
  600, 900, 36,    // 3 開始周波数 終了周波数 切替スピード
  550, 1800, 96,   // 4 開始周波数 終了周波数 切替スピード
  1150, 1150, 111, // 5 開始周波数 終了周波数 切替スピード
  -1,              // 6 終了コード

  //パターン3 E231-0,500,etc
  350, 350, 6,     // 1 開始周波数 終了周波数 切替スピード
  350, 980, 31,    // 2 開始周波数 終了周波数 切替スピード
  580, 900, 86,    // 3 開始周波数 終了周波数 切替スピード
  530, 1150, 101,  // 4 開始周波数 終了周波数 切替スピード
  1150, 1150, 121, // 5 開始周波数 終了周波数 切替スピード
  -1,              // 6 終了コード

  //パターン4 E231-1000
  1050, 1050, 16,  // 1 開始周波数 終了周波数 切替スピード
  1050, 700, 41,   // 2 開始周波数 終了周波数 切替スピード
  700, 2900, 44,   // 3 開始周波数 終了周波数 切替スピード
  780, 880, 46,    // 4 開始周波数 終了周波数 切替スピード
  580, 900, 86,    // 5 開始周波数 終了周波数 切替スピード
  530, 1150, 101,  // 6 開始周波数 終了周波数 切替スピード
  1150, 1150, 121, // 7 開始周波数 終了周波数 切替スピード
  -1,              // 8 終了コード

  //パターン5 E233, etc
  760, 760, 31,    // 1 開始周波数 終了周波数 切替スピード
  300, 1000, 106,  // 2 開始周波数 終了周波数 切替スピード
  1000, 1000, 121, // 3 開始周波数 終了周波数 切替スピード
  -1,              // 4 終了コード

  //パターン6 KQ 2100 (SI-GTO)
  780, 780, 22,  // 1 開始周波数 終了周波数 切替スピード
  780, 1150, 35, // 2 開始周波数 終了周波数 切替スピード
  780, 970, 45,  // 3 開始周波数 終了周波数 切替スピード
  600, 980, 111, // 4 開始周波数 終了周波数 切替スピード
  580, 580, 131, // 5 開始周波数 終了周波数 切替スピード
  -1,            // 6 終了コード

  //パターン7 N1000 (SI-GTO)
  780, 780, 16,  // 1 開始周波数 終了周波数 切替スピード
  780, 970, 19,  // 2 開始周波数 終了周波数 切替スピード
  780, 820, 22,  // 3 開始周波数 終了周波数 切替スピード
  780, 1150, 35, // 4 開始周波数 終了周波数 切替スピード
  780, 970, 45,  // 5 開始周波数 終了周波数 切替スピード
  600, 980, 111, // 6 開始周波数 終了周波数 切替スピード
  580, 580, 131, // 7 開始周波数 終了周波数 切替スピード
  -1,            // 8 終了コード

  //パターン8 MITSUBISHI GTO
  580, 720, 16,    // 1 開始周波数 終了周波数 切替スピード
  720, 720, 26,    // 2 開始周波数 終了周波数 切替スピード
  460, 650, 36,    // 3 開始周波数 終了周波数 切替スピード
  450, 1250, 110,  // 4 開始周波数 終了周波数 切替スピード
  1250, 1250, 131, // 5 開始周波数 終了周波数 切替スピード
  -1,              // 6 終了コード
  
  //パターン9 Toyo-GTO
  580, 580, 7,     // 1 開始周波数 終了周波数 切替スピード
  580, 750, 20,    // 2 開始周波数 終了周波数 切替スピード
  380, 650, 31,    // 3 開始周波数 終了周波数 切替スピード
  450, 1250, 110,  // 4 開始周波数 終了周波数 切替スピード
  1250, 1250, 131, // 5 開始周波数 終了周波数 切替スピード
  -1,              // 6 終了コード

  //パターン10 Toyo-IGBT
  1050, 1050, 26, // 1 開始周波数 終了周波数 切替スピード
  600, 930, 41,   // 2 開始周波数 終了周波数 切替スピード
  600, 950, 95,   // 3 開始周波数 終了周波数 切替スピード
  950, 950, 111,  // 4 開始周波数 終了周波数 切替スピード
  580, 580, 131,  // 5 開始周波数 終了周波数 切替スピード
  -1,             // 6 終了コード

  //パターン11 Toei 5300(Akuma)
  780, 780, 18,    // 1 開始周波数 終了周波数 切替スピード
  440, 460, 22,    // 2 開始周波数 終了周波数 切替スピード
  460, 570, 36,    // 3 開始周波数 終了周波数 切替スピード
  500, 1150, 105,  // 4 開始周波数 終了周波数 切替スピード
  1150, 1150, 121, // 5 開始周波数 終了周波数 切替スピード
  -1,              // 6 終了コード

  //パターン12 TOSHIBA 3LevIGBT
  730, 730, 11,    // 1 開始周波数 終了周波数 切替スピード
  510, 1150, 101,  // 2 開始周波数 終了周波数 切替スピード
  1150, 1150, 111, // 3 開始周波数 終了周波数 切替スピード
  -1,              // 4 終了コード

  //パターン13 MITSUBISHI Chop.
  900, 900, 36,    // 1 開始周波数 終了周波数 切替スピード
  590, 590, 41,    // 2 開始周波数 終了周波数 切替スピード
  390, 390, 46,    // 3 開始周波数 終了周波数 切替スピード
  390, 1150, 101,  // 4 開始周波数 終了周波数 切替スピード
  1150, 1150, 111, // 5 開始周波数 終了周波数 切替スピード
  -1,              // 6 終了コード

  //パターン14 N1000 (SUS-IGBT)
  710, 710, 31,    // 1 開始周波数 終了周波数 切替スピード
  350, 1150, 101,  // 2 開始周波数 終了周波数 切替スピード
  1150, 1150, 131, // 3 開始周波数 終了周波数 切替スピード
  -1,              // 4 終了コード

  //パターン15 N1000 (SI-IGBT)
  1180, 1180, 16, // 1 開始周波数 終了周波数 切替スピード
  1180, 2050, 31, // 2 開始周波数 終了周波数 切替スピード
  780, 1150, 36,  // 3 開始周波数 終了周波数 切替スピード
  560, 980, 111,  // 4 開始周波数 終了周波数 切替スピード
  580, 580, 131,  // 5 開始周波数 終了周波数 切替スピード
  -1,             // 6 終了コード

  //パターン16 Toyo-IGBT(AE)
  1000, 1000, 26, // 1 開始周波数 終了周波数 切替スピード
  600, 880, 41,   // 2 開始周波数 終了周波数 切替スピード
  600, 950, 95,   // 3 開始周波数 終了周波数 切替スピード
  950, 950, 106,  // 4 開始周波数 終了周波数 切替スピード
  580, 580, 161,  // 5 開始周波数 終了周波数 切替スピード
  -1              // 6 終了コード

};










// ***********************************************
// ここから先はみだりに変更しないで下さい。
// 誤動作や故障の原因となります。
// ***********************************************

const char *MOJI1 = "TSmascon  PPUNIT";  // Welcomeメッセージ。LCD1段目。16桁にすること。
const char *MOJI2 = "Program Ver 3.20";  // Welcomeメッセージ。LCD2段目。16桁にすること。



//使用ピン指定
const int P_LIGHT_DIAL = A0; // Arduino接続ピン番号:(Analog) 常点灯調節ダイアル = A0
const int P_ACCEL_DIAL = A1; // Arduino接続ピン番号:(Analog) 加速率調節ダイアル = A1
const int P_PWM2B      = 3; // Arduino接続ピン番号:(Digital) OCR2B出力 to Rail = D3
const int P_LCD_RS     = 4; // Arduino接続ピン番号:(Digital) LCD-REGISTER      = D4
const int P_LCD_EN     = 5; // Arduino接続ピン番号:(Digital) LCD-ENABLE        = D5
const int P_LCD_D4     = 6; // Arduino接続ピン番号:(Digital) LCD-D4            = D6
const int P_LCD_D5     = 7; // Arduino接続ピン番号:(Digital) LCD-D5            = D7
const int P_LCD_D6     = 8; // Arduino接続ピン番号:(Digital) LCD-D6            = D8
const int P_LCD_D7     = 9; // Arduino接続ピン番号:(Digital) LCD-D7            = D9
const int P_PWM1B     = 10; // Arduino接続ピン番号:(Digital) OCR1B出力 to Rail = D10
const int P_PWM2A     = 11; // Arduino接続ピン番号:(Digital) OCR2A出力 to Rail = D11 (MOSI)

const int P_BOSEN     = 12; // Arduino接続ピン番号:(Digital) 母線リレー制御 = D12 (MISO)
const int P_DIREC     = 13; // Arduino接続ピン番号:(Digital) 方向リレー制御 = D13 (SCK)
//const int P_BOSEN     = 18; // Arduino接続ピン番号:(Digital) 母線リレー制御 = D18 (A4)
//const int P_DIREC     = 19; // Arduino接続ピン番号:(Digital) 方向リレー制御 = D19 (A5)



//マスコン関連
boolean changeS = true; //走行音変更モード判定
boolean EB = false; //非常停止ボタン
boolean masok = false; //ブレーキ投入判定
boolean revok = false; //レバーサ位置確認

const int MC_EB =  1; // マスコン位置 非常
const int MC_B8 =  2; // マスコン位置 制動8
const int MC_B7 =  3; // マスコン位置 制動7
const int MC_B6 =  4; // マスコン位置 制動6
const int MC_B5 =  5; // マスコン位置 制動5
const int MC_B4 =  6; // マスコン位置 制動4
const int MC_B3 =  7; // マスコン位置 制動3
const int MC_B2 =  8; // マスコン位置 制動2
const int MC_B1 =  9; // マスコン位置 制動1
const int MC_N  = 10; // マスコン位置 惰行
const int MC_P1 = 11; // マスコン位置 力行1
const int MC_P2 = 12; // マスコン位置 力行2
const int MC_P3 = 13; // マスコン位置 力行3
const int MC_P4 = 14; // マスコン位置 力行4
const int MC_P5 = 15; // マスコン位置 力行5
const int MC_P6 = 16; // マスコン位置 力行5

const int RE_F = 1; // レバーサ位置 前
const int RE_N = 2; // レバーサ位置 切
const int RE_R = 3; // レバーサ位置 後

const int TS_1 = 1; //TSマスコン1(ワンハンドル)
const int TS_2 = 2; //TSマスコン2(ツーハンドル)


const int TYPE_A = 1; // TYPEA
const int TYPE_B = 2; // TYPEB
const int TYPE_C = 3; // TYPEC
const int TYPE_D = 4; // TYPED
const int TYPE_E = 5; // TYPEE
const int TYPE_F = 6; // TYPEF
const int TYPE_G = 7; // TYPEG
const int TYPE_H = 8; // TYPEH

// マスコン位置名称を保持する配列
const char* masconPosName[16] = {"EB", "B8", "B7", "B6", "B5", "B4", "B3", "B2", "B1", "N ", "P1", "P2", "P3", "P4", "P5", "P6"};
//レバーサ位置名称を保持する配列
const char* reverserPosName[3] = {"F", "N", "R"};
//マスコンタイプ名称を保持する配列
const char* typeName[8] = {"A", "B", "C", "D", "E", "F", "G", "H"};



//変数
const int MD_STOP  = 1; // モード 停止
const int MD_BRAKE = 2; // モード 減速
const int MD_NTRL  = 3; // モード 惰行
const int MD_ACCEL = 4; // モード 加速
int vvvfPtn;            // 走行音パターン。
int disp_masconPos = MC_EB;  // マスコンの位置(表示用)。加減速力確保の為、マスコンタイプによっては指定の段数より強い段数を内部処理用に指定することから。
int masconPos = MC_EB;  // マスコンの位置を保持。1~16となる。
int reverserPos = RE_N; // レバーサの位置を保持。1~3となる。
int notchnum = 0; //マスコン段数
int brakenum = 0; //ブレーキ段数
int tstype = 0; //TSマスコン1or2
int type = 0; //マスコンタイプ
int lightVol;       // 常点灯ダイアルから拾った値を0~127に変換して保持する。変換処理はloop()内にて。
int accelVol;       // 加速率ダイアルから拾った値を1~8に変換して保持する。変換処理はloop()内にて。
int kasoku;         // 走行用出力する加減速率
int mode;           // 走行状態。1=停止, 2=減速, 3=惰行, 4=加速
int ReceiveData[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; //受信データ格納用
long notch1;        // ノッチ1での最高速度
long notch2;        // ノッチ2での最高速度
long notch3;        // ノッチ3での最高速度
long notch4;        // ノッチ4での最高速度
long notch5;        // ノッチ5での最高速度
long stopSpd;       // 走行用出力をカットする速度
long AsttFrq;   // 開始周波数
long AendFrq;   // 終了周波数
long Afrq;      // 周波数
long AsttSpd;   // 開始スピード
long AendSpd;   // 終了スピード
long BsttFrq;   // 開始周波数
long BendFrq;   // 終了周波数
long Bfrq;      // 周波数
long BsttSpd;   // 開始スピード
long BendSpd;   // 終了スピード
long spd;      // 内部スピード 0~4,000,000(速度×10,000)
long orderSpd; // 指示スピード
float duty; //duty比
float acurve; //加速曲線
float bcurve;  //減速度
int i; // ループカウンター
int j; // ループカウンター
int k; // ループカウンター
int maxSpdData[5];
int AsoundData[SDATANUM];

// 走行音パターンの各パターンデータ開始位置インデックスを保持。
// 実際の値設定は、setup処理内にて終了コード(-1)を検出し動的に設定する。
int AdataNum[SOUNDNUM + 1];

int BsoundData[SDATANUM];

// 走行音パターンの各パターンデータ開始位置インデックスを保持。
// 実際の値設定は、setup処理内にて終了コード(-1)を検出し動的に設定する。
int BdataNum[SOUNDNUM + 1];

// キャラクタ液晶
LiquidCrystal lcd(P_LCD_RS, P_LCD_EN, P_LCD_D4, P_LCD_D5, P_LCD_D6, P_LCD_D7);










// **********************************************************************************************
// 関数定義 ここから
// **********************************************************************************************



//マスコン及びレバーサ位置の表示
void dispPos() {
  lcd.setCursor(12, 1);
  lcd.print(reverserPosName[reverserPos - 1]);
  lcd.setCursor(14, 1);
  lcd.print(masconPosName[disp_masconPos - 1]);
}



//スケールスピード表示
void dispSpd() {
  int disp;
  disp = spd / 10000;
  lcd.setCursor(4, 1);
  if (disp / 100 > 0) {
    // 3digits.
  }
  else {
    if (disp / 10 > 0) {
      // 2digits.
      lcd.print(" ");
    }
    else {
      // 1digit.
      lcd.print("  ");
    }
  }
  lcd.print(disp);
}



//走行音設定用
void SetSound(){

  lcd.clear();

  i = 1;
  k = 0;

  //受信データ初期化
  while (Serial.available() > 0) { // 受信したデータが存在する
    Serial.read();
  }
  
  for(k=0;k<6;k++){
  ReceiveData[k] = {0x00};
  }

  k = 0;
  
  lcd.setCursor(0, 0);
  lcd.print(F("SET SoundPattern"));
  delay(3000);
  lcd.clear();
  lcd.setCursor(0, 1);
  lcd.print(F("<A  Enter:S  C> "));
  lcd.setCursor(0, 0);
  lcd.print(soundName[0]); //1番目の走行音データ名をさっさと表示
  
  do{
  Receive(4);
  } while(k == 0);
  
  vvvfPtn = i - 1;

  acurve = Aracurve[vvvfPtn];
  bcurve = Arbcurve[vvvfPtn];
  
  k = 0;

  for (i = vvvfPtn * 5; i < vvvfPtn * 5 + 5; i++)
  {
  maxSpdData[k] = pgm_read_word_near(&maxSpdDataBase[i]);
  k++;
  }

  notch1  = (long) maxSpdData[0] * 10000 - 1;
  notch2  = (long) maxSpdData[1] * 10000 - 1;
  notch3  = (long) maxSpdData[2] * 10000 - 1;
  notch4  = (long) maxSpdData[3] * 10000 - 1;
  notch5  = (long) maxSpdData[4] * 10000 - 1;
  stopSpd = STOPSPD * 1000 - 1;

  k = 0;
  
  for (i = AdataNum[vvvfPtn]; i<AdataNum[vvvfPtn+1]; i++)
  {
  AsoundData[k] = pgm_read_word_near(&AsoundDataBase[i]);
  k++;
  }

  k = 0;
 
  for (i = BdataNum[vvvfPtn]; i<BdataNum[vvvfPtn+1]; i++)
  {
  BsoundData[k] = pgm_read_word_near(&BsoundDataBase[i]);
  k++;
  }

  changeS = false;
  
  // 決定したパターンを点滅表示
  lcd.clear();
  delay(180);
  lcd.setCursor(0, 0);
  lcd.print(F("SET SoundPattern"));
  lcd.setCursor(0, 1);
  lcd.print(soundName[vvvfPtn]);
  delay(2000);

  lcd.clear();
  lcd.setCursor(1, 0);
  lcd.print(F("CATION:Push C"));
  lcd.setCursor(2, 1);
  lcd.print(F("in emergensy."));
  delay(3000); 
  lcd.clear();

  //誤発進防止動作
  do{
    SetStatus();
  }while(i == 0);

  lcd.clear();
  
  lcd.setCursor(0, 0);
  lcd.print(F(" Start Controll "));
  delay(1000);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(soundName[vvvfPtn]);
  lcd.setCursor(0, 1);
  if(tstype==TS_1){
    lcd.print(F("< >    km/h< >"));
    lcd.setCursor(1, 1);
    lcd.print(typeName[type - 1]); //TYPE表示もつける。無駄に。
  }
  else{
    lcd.print(F("       km/h< >"));
  }

  dispPos();
}



//シリアル受信用。受信データを基にマスコン及びレバーサ位置の判定も同時に行う。
void Receive(int SETTYPE){
  while (Serial.available() >= 6) { // コマンドを完全に受けた
    j = 0;
    do{
      ReceiveData[j] = Serial.read();
      if(ReceiveData[j] == 0x0D) {
        break;
      }
      
      if(j < 5) {
        j++;
      }
      else {
        j = 0;
      }
    } while (Serial.available() > 0);
    
    if(ReceiveData[0] == 0x54) { //ヘッダが先頭である
      if(ReceiveData[1] == 0x53) { //パケットを正常に受信している
          
        if(SETTYPE == 0) { //マスコン設定時
          switch(ReceiveData[2]) {
            case 0x58: //Aボタン
              if(ReceiveData[3] == 0x39) {
                if(i == 0) { //一巡処理
                  i = 1;
                  lcd.setCursor(0, 0);
                  lcd.print(' ');
                  lcd.setCursor(8, 0);
                  lcd.print('>');
                }
                else {
                  i--;
                  lcd.setCursor(0, 0);
                  lcd.print('>');
                  lcd.setCursor(8, 0);
                  lcd.print(' ');
                }
              }
              break;

            case 0x5A: //Cボタン
              if(ReceiveData[3] == 0x39) {
                if(i == 1) { //一巡処理
                  i = 0;
                  lcd.setCursor(0, 0);
                  lcd.print('>');
                  lcd.setCursor(8, 0);
                  lcd.print(' ');
                }
                else {
                  i++;
                  lcd.setCursor(0, 0);
                  lcd.print(' ');
                  lcd.setCursor(8, 0);
                  lcd.print('>');
                }
              }              
              break;

            case 0x4B: //Sボタン
              tstype = i + 1; //マスコンタイプ決定
              lcd.clear();
              lcd.setCursor(0, 0);
              lcd.print(F("SET  Handle"));
              lcd.setCursor(4, 0);

              if(i==0){
                lcd.print('1');
              }
              else{
                lcd.print('2');
              }
              delay(2000);
              lcd.clear();
              return;
             
            default:
              break;

          }
        }
        else if(SETTYPE == 1) { //(TSマスコン用)マスコンタイプ設定時
          switch(ReceiveData[2]) {
            case 0x58: //Aボタン
              if(ReceiveData[3] == 0x39) {
                if(i == 0) { //一巡処理
                  i = 7;
                }
                else {
                  i--;
                }
              lcd.setCursor(15, 0);
              lcd.print(typeName[i]);
              }
              break;

            case 0x5A: //Cボタン
              if(ReceiveData[3] == 0x39) {
                if(i == 7) { //一巡処理
                  i = 0;
                }
                else {
                  i++;
                }
              lcd.setCursor(15, 0);
              lcd.print(typeName[i]);
              }              
              break;

            case 0x4B: //Sボタン
              type = i + 1; //マスコンタイプ決定
              lcd.clear();
              lcd.setCursor(0, 0);
              lcd.print(F("SET TYPE"));
              lcd.setCursor(9, 0);
              lcd.print(typeName[i]);
              delay(2000);
              lcd.clear();
              return;
             
            default:
              break;

          }
        }
        else if(SETTYPE == 2) { //(TSマスコン2用)マスコン段数設定
          switch(ReceiveData[2]) {
            case 0x58: //Aボタン
              if(ReceiveData[3] == 0x39) {
                if(i == 3) { //一巡処理。(P3~P6までの範囲)
                  i = 6;
                }
                else {
                  i--;
                }
              lcd.setCursor(15, 0);
              lcd.print(i);
              }
              break;

            case 0x5A: //Cボタン
              if(ReceiveData[3] == 0x39) {
                if(i == 6) { //一巡処理
                  i = 3;
                }
                else {
                  i++;
                }
              lcd.setCursor(15, 0);
              lcd.print(i);
              }              
              break;

            case 0x4B: //Sボタン
              notchnum = i; //マスコンタイプ決定
              lcd.clear();
              lcd.setCursor(0, 0);
              lcd.print(F("SET MAX-NOTCH:P "));
              lcd.setCursor(15, 0);
              lcd.print(i);
              delay(2000);
              lcd.clear();
              return;
             
            default:
              break;

          }
        }
        else if(SETTYPE == 3) { //(TSマスコン2用)ブレーキ段数設定
          switch(ReceiveData[2]) {
            case 0x58: //Aボタン
              if(ReceiveData[3] == 0x39) {
                if(i == 5) { //一巡処理。(B5~B8までの範囲)
                  i = 8;
                }
                else {
                  i--;
                }
              lcd.setCursor(15, 0);
              lcd.print(i);
              }
              break;

            case 0x5A: //Cボタン
              if(ReceiveData[3] == 0x39) {
                if(i == 8) { //一巡処理
                  i = 5;
                }
                else {
                  i++;
                }
              lcd.setCursor(15, 0);
              lcd.print(i);
              }              
              break;

            case 0x4B: //Sボタン
              brakenum = i; //マスコンタイプ決定
              lcd.clear();
              lcd.setCursor(0, 0);
              lcd.print(F("SET MAX-BRAKE:B "));
              lcd.setCursor(15, 0);
              lcd.print(i);
              delay(2000);
              lcd.clear();
              return;
             
            default:
              break;

          }
        }
        else if(SETTYPE == 4) { //走行音設定時
          switch(ReceiveData[2]) {
            case 0x58: //Aボタン
              if(ReceiveData[3] == 0x39) {
                if(i == 1) { //一巡処理
                  i = SOUNDNUM;
                }
                else {
                  i--;
                }
              lcd.setCursor(0, 0);
              lcd.print(soundName[i-1]);
              }
              break;

            case 0x5A: //Cボタン
              if(ReceiveData[3] == 0x39) {
                if(i == SOUNDNUM) { //一巡処理
                  i = 1;
                }
                else {
                  i++;
                }
              lcd.setCursor(0, 0);
              lcd.print(soundName[i-1]);
              }              
              break;
              
            case 0x4B: //Sボタン
              if(ReceiveData[3] == 0x39) {
                k = 1;
                return;
              }
              break;

            default:
              break;
          }
        }
        else if(SETTYPE == 5) { //非常停止時
          switch(ReceiveData[2]) {
            case 0x5A: //Cボタン
              if(ReceiveData[3] == 0x39) {
                EB = false;
              }         
              return;

            default:
              break;
          }
        }
        else { //通常運転時
          if(tstype == TS_1){//TSマスコン1用
            switch(ReceiveData[2]) {
              case 0x42:
                switch(ReceiveData[3]) {
                  case 0x32: //共通EB
                    masconPos = MC_EB;
                    disp_masconPos = MC_EB;
                    bosenON();
                    break;
  
                  case 0x33: //共通B8
                    if(type==TYPE_B || type==TYPE_D){
                      masconPos = MC_EB;
                      disp_masconPos = MC_EB;
                      bosenON();
                    }
                    else if(type==TYPE_G){
                      masconPos = MC_B8;
                      disp_masconPos = MC_B7;
                      bosenON();
                    }
                    else{
                      masconPos = MC_B8;
                      disp_masconPos = MC_B8;
                      bosenON();
                    }
                    break;
                    
                  case 0x34: //共通B7
                    if(type==TYPE_B || type==TYPE_D || type==TYPE_G){
                      masconPos = MC_B8;
                      disp_masconPos = MC_B7;
                      bosenON();
                    }
                    else{
                      masconPos = MC_B7;
                      disp_masconPos = MC_B7;
                      bosenON();
                    }
                    break;
  
                  default: //TYPEHの(P6)~(P8)はP5扱い
                    masconPos = MC_P6;
                    disp_masconPos = MC_P5;
                    bosenON();
                    
                }
                break;
  
              case 0x45: //共通B6
                if(type==TYPE_A || type==TYPE_C){
                  masconPos = MC_EB;
                  disp_masconPos = MC_EB;
                  bosenON();
                }
                else if(type==TYPE_B || type==TYPE_D || type==TYPE_G){
                  masconPos = MC_B7;
                  disp_masconPos = MC_B6;
                  bosenON();
                }
                else{
                  masconPos = MC_B6;
                  disp_masconPos = MC_B6;
                  bosenON();
                }
                break;
                  
              case 0x41:
                switch(ReceiveData[3]) {
                  case 0x30: //共通B5
                    if(type==TYPE_A || type==TYPE_C){
                      masconPos = MC_B8;
                      disp_masconPos = MC_B5;
                      bosenON();
                    }
                    else if(type==TYPE_G){
                      masconPos = MC_B6;
                      disp_masconPos = MC_B5;
                      bosenON();
                    }
                    else {
                      masconPos = MC_B5;
                      disp_masconPos = MC_B5;
                      bosenON();
                    }
                    break;
  
                  case 0x31: //共通B4
                    if(type==TYPE_A || type==TYPE_C){
                      masconPos = MC_B7;
                      disp_masconPos = MC_B4;
                      bosenON();
                    }
                    else if(type==TYPE_G){
                      masconPos = MC_B5;
                      disp_masconPos = MC_B4;
                      bosenON();
                    }
                    else {
                      masconPos = MC_B4;
                      disp_masconPos = MC_B4;
                      bosenON();
                    }
                    break;
                    
                  case 0x32: //共通B3
                    if(type==TYPE_A || type==TYPE_C){
                      masconPos = MC_B6;
                      disp_masconPos = MC_B3;
                      bosenON();
                    }
                    else if(type==TYPE_G){
                      masconPos = MC_B4;
                      disp_masconPos = MC_B3;
                      bosenON();
                    }
                    else {
                      masconPos = MC_B3;
                      disp_masconPos = MC_B3;
                      bosenON();
                    }
                    break;
  
                  case 0x33: //共通B2
                    if(type==TYPE_A || type==TYPE_C){
                      masconPos = MC_B4;
                      disp_masconPos = MC_B2;
                      bosenON();
                    }
                    else if(type==TYPE_G){
                      masconPos = MC_B3;
                      disp_masconPos = MC_B2;
                      bosenON();
                    }
                    else {
                      masconPos = MC_B2;
                      disp_masconPos = MC_B2;
                      bosenON();
                    }
                    break;
  
                  case 0x34: //共通B1
                    if(type==TYPE_A || type==TYPE_C || type==TYPE_G){
                      masconPos = MC_B2;
                      disp_masconPos = MC_B1;
                      bosenON();
                    }
                    else {
                      masconPos = MC_B1;
                      disp_masconPos = MC_B1;
                      bosenON();
                    }
                    break;
  
                  case 0x35:
                    switch(ReceiveData[4]) {
                      case 0x30: //共通N
                        masconPos = MC_N;
                        disp_masconPos = MC_N;
                        bosenON();
                        break;
  
                      case 0x35: //共通P1
                        masconPos = MC_P2;
                        disp_masconPos = MC_P1;
                        bosenON();
                        break;
                    }
                    break;
  
                  case 0x36: //共通P2
                    masconPos = MC_P3;
                    disp_masconPos = MC_P2;
                    bosenON();
                    break;
  
                  case 0x37: //共通P3
                    if(type==TYPE_B){
                      masconPos = MC_P5;
                      disp_masconPos = MC_P3;
                      bosenON();
                    }
                    else {
                      masconPos = MC_P4;
                      disp_masconPos = MC_P3;
                      bosenON();
                    }
                    break;
  
                  case 0x38: //共通P4
                    if(type==TYPE_A || type==TYPE_D || type==TYPE_E){
                      masconPos = MC_P6;
                      disp_masconPos = MC_P4;
                      bosenON();
                    }
                    else {
                      masconPos = MC_P5;
                      disp_masconPos = MC_P4;
                      bosenON();
                    }
                    break;
  
                  case 0x39: //共通P5
                    masconPos = MC_P6;
                    disp_masconPos = MC_P5;
                    bosenON();
                    break;
                }
                break;
  
              case 0x47: //レバーサ
                switch(ReceiveData[3]) {
                  case 0x39: //前
                    reverserPos = RE_F;
                    forward();
                    bosenON();
                    break;
  
                  case 0x35: //切
                    bosenOFF();
                    reverserPos = RE_N;
                    break;
  
                  case 0x30: //後
                    reverserPos = RE_R;
                    reverse();
                    bosenON();
                    break;
  
                }
                break;
                
              case 0x5A: //Cボタン
                if(ReceiveData[3] == 0x39) {
                  bosenOFF();
                  spd = 0;
                  mode = MD_STOP;
                  masconPos = MC_EB;
                  reverserPos = RE_N;
                  EB = true;        
                  return;
                }
                break;
  
              case 0x4B: //Sボタン
                if(ReceiveData[3] == 0x39) {            
                  if(spd == 0) { //走行音設定へ
                    bosenOFF();
                    changeS = true;
                    return;
                  }
                }
                break;
  
              //default:

            }
          }
          else{//TSマスコン2用
            switch(ReceiveData[2]) {
              case 0x42:
                switch(ReceiveData[3]) {
                  case 0x32: //共通EB
                    masconPos = MC_EB;
                    disp_masconPos = MC_EB;
                    bosenON();
                    break;
  
                  case 0x33: //共通B8
                    if(brakenum <= 7){
                      masconPos = MC_EB;
                      disp_masconPos = MC_EB;
                      bosenON();
                    }
                    else{
                      masconPos = MC_B8;
                      disp_masconPos = MC_B8;
                      bosenON();
                    }
                    break;
                    
                  case 0x34: //共通B7
                    if(brakenum <= 6){
                      masconPos = MC_EB;
                      disp_masconPos = MC_EB;
                      bosenON();
                    }
                    else if(brakenum == 7){
                      masconPos = MC_B8;
                      disp_masconPos = MC_B7;
                      bosenON();
                    }
                    else{
                      masconPos = MC_B7;
                      disp_masconPos = MC_B7;
                      bosenON();
                    }
                    break;
  
                  default: //P6
                    masconPos = MC_P6;
                    disp_masconPos = MC_P6;
                    bosenON();

                }
                break;
  
              case 0x45: //共通B6
                if(brakenum == 5){
                  masconPos = MC_EB;
                  disp_masconPos = MC_EB;
                  bosenON();
                }
                else if(brakenum == 6){
                  masconPos = MC_B8;
                  disp_masconPos = MC_B6;
                  bosenON();
                }
                else{
                  masconPos = MC_B6;
                  disp_masconPos = MC_B6;
                  bosenON();
                }
                break;
                  
              case 0x41:
                switch(ReceiveData[3]) {
                  case 0x30: //共通B5
                    if(brakenum == 5){
                      masconPos = MC_B8;
                      disp_masconPos = MC_B5;
                      bosenON();
                    }
                    else if(brakenum == 6){
                      masconPos = MC_B7;
                      disp_masconPos = MC_B5;
                      bosenON();
                    }
                    else {
                      masconPos = MC_B5;
                      disp_masconPos = MC_B5;
                      bosenON();
                    }
                    break;
  
                  case 0x31: //共通B4
                    if(brakenum == 5){
                      masconPos = MC_B7;
                      disp_masconPos = MC_B4;
                      bosenON();
                    }
                    else if(brakenum == 6){
                      masconPos = MC_B5;
                      disp_masconPos = MC_B4;
                      bosenON();
                    }
                    else {
                      masconPos = MC_B4;
                      disp_masconPos = MC_B4;
                      bosenON();
                    }
                    break;
                    
                  case 0x32: //共通B3
                    if(brakenum == 5){
                      masconPos = MC_B6;
                      disp_masconPos = MC_B3;
                      bosenON();
                    }
                    else if(brakenum == 6){
                      masconPos = MC_B4;
                      disp_masconPos = MC_B3;
                      bosenON();
                    }
                    else {
                      masconPos = MC_B3;
                      disp_masconPos = MC_B3;
                      bosenON();
                    }
                    break;
  
                  case 0x33: //共通B2
                    if(brakenum == 5){
                      masconPos = MC_B4;
                      disp_masconPos = MC_B2;
                      bosenON();
                    }
                    else {
                      masconPos = MC_B2;
                      disp_masconPos = MC_B2;
                      bosenON();
                    }
                    break;
  
                  case 0x34: //共通B1
                    if(brakenum == 5){
                      masconPos = MC_B2;
                      disp_masconPos = MC_B1;
                      bosenON();
                    }
                    else {
                      masconPos = MC_B1;
                      disp_masconPos = MC_B1;
                      bosenON();
                    }
                    break;
  
                  case 0x35:
                    switch(ReceiveData[4]) {
                      case 0x30: //共通N
                        masconPos = MC_N;
                        disp_masconPos = MC_N;
                        bosenON();
                        break;
  
                      case 0x35: //共通P1
                        if(notchnum == 6){
                          masconPos = MC_P1;
                          disp_masconPos = MC_P1;
                          bosenON();
                        }
                        else{
                          masconPos = MC_P2;
                          disp_masconPos = MC_P1;
                          bosenON();
                        }
                        break;
                    }
                    break;
  
                  case 0x36: //共通P2
                    if(notchnum == 6){
                      masconPos = MC_P2;
                      disp_masconPos = MC_P2;
                      bosenON();
                    }
                    else{
                      masconPos = MC_P3;
                      disp_masconPos = MC_P2;
                      bosenON();
                    }
                    break;
  
                  case 0x37: //共通P3
                    if(notchnum == 6){
                      masconPos = MC_P3;
                      disp_masconPos = MC_P3;
                      bosenON();
                    }
                    else if(notchnum == 3){
                      masconPos = MC_P5;
                      disp_masconPos = MC_P3;
                      bosenON();
                    }
                    else{
                      masconPos = MC_P4;
                      disp_masconPos = MC_P3;
                      bosenON();
                    }
                    break;
  
                  case 0x38: //共通P4
                    if(notchnum == 6){
                      masconPos = MC_P4;
                      disp_masconPos = MC_P4;
                      bosenON();
                    }
                    else if(notchnum == 4){
                      masconPos = MC_P6;
                      disp_masconPos = MC_P4;
                      bosenON();
                    }
                    else{
                      masconPos = MC_P5;
                      disp_masconPos = MC_P4;
                      bosenON();
                    }
                    break;
  
                  case 0x39: //共通P5
                    if(notchnum == 6){
                      masconPos = MC_P5;
                      disp_masconPos = MC_P5;
                      bosenON();
                    }
                    else{
                      masconPos = MC_P6;
                      disp_masconPos = MC_P5;
                      bosenON();
                    }
                    break;
                }
                break;
  
              case 0x47: //レバーサ
                switch(ReceiveData[3]) {
                  case 0x39: //前
                    reverserPos = RE_F;
                    forward();
                    bosenON();
                    break;
  
                  case 0x35: //切
                    bosenOFF();
                    reverserPos = RE_N;
                    break;
  
                  case 0x30: //後
                    reverserPos = RE_R;
                    reverse();
                    bosenON();
                    break;
  
                }
                break;
                
              case 0x5A: //Cボタン
                if(ReceiveData[3] == 0x39) {
                  bosenOFF();
                  spd = 0;
                  mode = MD_STOP;
                  masconPos = MC_EB;
                  reverserPos = RE_N;
                  EB = true;        
                  return;
                }
                break;
  
              case 0x4B: //Sボタン
                if(ReceiveData[3] == 0x39) {            
                  if(spd == 0) { //走行音設定へ
                    bosenOFF();
                    changeS = true;
                    return;
                  }
                }
                break;
  
              //default:

            }
          }
        }
      }
    }
  }
}



//誤発進防止(始業検査のようなもの)。TSマスコンの現在ノッチ位置、レバーサ位置の状態を確認しパワーパック起動時の急発進を防ぐ。
void SetStatus(){
  
  i = 0;
  
  for(k=0;k<6;k++){
  ReceiveData[k] = {0x00};
  }

  k = 0;

    //受信データ初期化
    while (Serial.available() > 0) { // 受信したデータが存在する
      Serial.read();
    }
    
    //ステータス送信要求
    do{
      Serial.write(0x0D);
    } while (Serial.available() >= 12); //受信完了まで待機

    delay(50);

    for(j = 0; j < 12; j++){
      ReceiveData[j] = Serial.read();
    }

    if(ReceiveData[0] == 0x54) { //ヘッダが先頭である
      if(ReceiveData[1] == 0x53) { //パケットを正常に受信している
        if(tstype == TS_1){//TSマスコン1用
          switch(ReceiveData[2]) {
            case 0x42:
              switch(ReceiveData[3]) {
                case 0x32: //共通EB
                  masconPos = MC_EB;
                  disp_masconPos = MC_EB;
                  masok = true;
                  break;
    
                case 0x33: //共通B8
                  if(type==TYPE_B || type==TYPE_D){
                    masconPos = MC_EB;
                    disp_masconPos = MC_EB;
                    masok = true;
                  }
                  else if(type==TYPE_G){
                    masconPos = MC_B8;
                    disp_masconPos = MC_B7;
                    masok = true;
                  }
                  else{
                    masconPos = MC_B8;
                    disp_masconPos = MC_B8;
                    masok = true;
                  }
                  break;
                  
                case 0x34: //共通B7
                  if(type==TYPE_B || type==TYPE_D || type==TYPE_G){
                    masconPos = MC_B8;
                    disp_masconPos = MC_B7;
                    masok = true;
                  }
                  else{
                    masconPos = MC_B7;
                    disp_masconPos = MC_B7;
                    masok = true;
                  }
                  break;
    
                default:
                  masok = false;
              }
              break;
    
            case 0x45: //共通B6
              if(type==TYPE_A || type==TYPE_C){
                masconPos = MC_EB;
                disp_masconPos = MC_EB;
                masok = true;
              }
              else if(type==TYPE_B || type==TYPE_D || type==TYPE_G){
                masconPos = MC_B7;
                disp_masconPos = MC_B6;
                masok = true;
              }
              else{
                masconPos = MC_B6;
                disp_masconPos = MC_B6;
                masok = true;
              }
              break;
                
            case 0x41:
              switch(ReceiveData[3]) {
                case 0x30: //共通B5
                  if(type==TYPE_A || type==TYPE_C){
                    masconPos = MC_B8;
                    disp_masconPos = MC_B5;
                    masok = true;
                  }
                  else if(type==TYPE_G){
                    masconPos = MC_B6;
                    disp_masconPos = MC_B5;
                    masok = true;
                  }
                  else {
                    masconPos = MC_B5;
                    disp_masconPos = MC_B5;
                    masok = true;
                  }
                  break;
    
                case 0x31: //共通B4
                  if(type==TYPE_A || type==TYPE_C){
                    masconPos = MC_B7;
                    disp_masconPos = MC_B4;
                    masok = true;
                  }
                  else if(type==TYPE_G){
                    masconPos = MC_B5;
                    disp_masconPos = MC_B4;
                    masok = true;
                  }
                  else {
                    masconPos = MC_B4;
                    disp_masconPos = MC_B4;
                    masok = true;
                  }
                  break;
                  
                case 0x32: //共通B3
                  if(type==TYPE_A || type==TYPE_C){
                    masconPos = MC_B6;
                    disp_masconPos = MC_B3;
                    masok = true;
                  }
                  else if(type==TYPE_G){
                    masconPos = MC_B4;
                    disp_masconPos = MC_B3;
                    masok = true;
                  }
                  else {
                    masconPos = MC_B3;
                    disp_masconPos = MC_B3;
                    masok = true;
                  }
                  break;
    
                case 0x33: //共通B2
                  if(type==TYPE_A || type==TYPE_C){
                    masconPos = MC_B4;
                    disp_masconPos = MC_B2;
                    masok = true;
                  }
                  else if(type==TYPE_G){
                    masconPos = MC_B3;
                    disp_masconPos = MC_B2;
                    masok = true;
                  }
                  else {
                    masconPos = MC_B2;
                    disp_masconPos = MC_B2;
                    masok = true;
                  }
                  break;
    
                case 0x34: //共通B1
                  if(type==TYPE_A || type==TYPE_C || type==TYPE_G){
                    masconPos = MC_B2;
                    disp_masconPos = MC_B1;
                    masok = true;
                  }
                  else {
                    masconPos = MC_B1;
                    disp_masconPos = MC_B1;
                    masok = true;
                  }
                  break;

                default:
                  masok = false;
              }
              break;
    
            default:
              masok = false;
          }

          if(ReceiveData[6] == 0x54) { //ヘッダが先頭である
            if(ReceiveData[7] == 0x53) { //パケットを正常に受信している
              switch(ReceiveData[8]) {
                case 0x47: //レバーサ
                  switch(ReceiveData[9]) {
                    case 0x39: //前
                      reverserPos = RE_F;
                      revok = true;
                      break;
        
                    case 0x35: //切
                      reverserPos = RE_N;
                      revok = true;
                      break;
        
                    case 0x30: //後
                      reverserPos = RE_R;
                      revok = true;
                      break;

                    default:
                      revok = false;
     
                  }
                  break;
        
                default:
                  revok = false;
              }
            }
          }
        }
        else{ //TSマスコン2用
          switch(ReceiveData[2]) {
            case 0x42:
              switch(ReceiveData[3]) {
                case 0x32: //共通EB
                  masconPos = MC_EB;
                  disp_masconPos = MC_EB;
                  masok = true;
                  break;
    
                case 0x33: //共通B8
                  if(brakenum <= 7){
                    masconPos = MC_EB;
                    disp_masconPos = MC_EB;
                    masok = true;
                  }
                  else{
                    masconPos = MC_B8;
                    disp_masconPos = MC_B8;
                    masok = true;
                  }
                  break;
                  
                case 0x34: //共通B7
                  if(brakenum <= 6){
                    masconPos = MC_EB;
                    disp_masconPos = MC_EB;
                    masok = true;
                  }
                  else if(brakenum == 7){
                    masconPos = MC_B8;
                    disp_masconPos = MC_B7;
                    masok = true;
                  }
                  else{
                    masconPos = MC_B7;
                    disp_masconPos = MC_B7;
                    masok = true;
                  }
                  break;
    
                default: //P6
                  masok = false;
              }
              break;
    
            case 0x45: //共通B6
              if(brakenum == 5){
                masconPos = MC_EB;
                disp_masconPos = MC_EB;
                masok = true;
              }
              else if(brakenum == 6){
                masconPos = MC_B8;
                disp_masconPos = MC_B6;
                masok = true;
              }
              else{
                masconPos = MC_B6;
                disp_masconPos = MC_B6;
                masok = true;
              }
              break;
                
            case 0x41:
              switch(ReceiveData[3]) {
                case 0x30: //共通B5
                  if(brakenum == 5){
                    masconPos = MC_B8;
                    disp_masconPos = MC_B5;
                    masok = true;
                  }
                  else if(brakenum == 6){
                    masconPos = MC_B7;
                    disp_masconPos = MC_B5;
                    masok = true;
                  }
                  else {
                    masconPos = MC_B5;
                    disp_masconPos = MC_B5;
                    masok = true;
                  }
                  break;
    
                case 0x31: //共通B4
                  if(brakenum == 5){
                    masconPos = MC_B7;
                    disp_masconPos = MC_B4;
                    masok = true;
                  }
                  else if(brakenum == 6){
                    masconPos = MC_B5;
                    disp_masconPos = MC_B4;
                    masok = true;
                  }
                  else {
                    masconPos = MC_B4;
                    disp_masconPos = MC_B4;
                    masok = true;
                  }
                  break;
                  
                case 0x32: //共通B3
                  if(brakenum == 5){
                    masconPos = MC_B6;
                    disp_masconPos = MC_B3;
                    masok = true;
                  }
                  else if(brakenum == 6){
                    masconPos = MC_B4;
                    disp_masconPos = MC_B3;
                    masok = true;
                  }
                  else {
                    masconPos = MC_B3;
                    disp_masconPos = MC_B3;
                    masok = true;
                  }
                  break;
    
                case 0x33: //共通B2
                  if(brakenum == 5){
                    masconPos = MC_B4;
                    disp_masconPos = MC_B2;
                    masok = true;
                  }
                  else {
                    masconPos = MC_B2;
                    disp_masconPos = MC_B2;
                    masok = true;
                  }
                  break;
    
                case 0x34: //共通B1
                  if(brakenum == 5){
                    masconPos = MC_B2;
                    disp_masconPos = MC_B1;
                    masok = true;
                  }
                  else {
                    masconPos = MC_B1;
                    disp_masconPos = MC_B1;
                    masok = true;
                  }
                  break;

                default:
                  masok = false;
              }
              break;
    
            default:
              masok = false;
          }
    
          if(ReceiveData[6] == 0x54) { //ヘッダが先頭である
            if(ReceiveData[7] == 0x53) { //パケットを正常に受信している
              switch(ReceiveData[8]) {
                case 0x47: //レバーサ
                  switch(ReceiveData[9]) {
                    case 0x39: //前
                      reverserPos = RE_F;
                      revok = true;
                      break;
        
                    case 0x35: //切
                      reverserPos = RE_N;
                      revok = true;
                      break;
        
                    case 0x30: //後
                      reverserPos = RE_R;
                      revok = true;
                      break;

                    default:
                      revok = false;
        
                  }
                  break;
        
                default:
                  revok = false;
              }
            }
          }
        }
      }
    }
    else{
      masok = false;
      revok = false;
    }

    if (masok == false){
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print(F("Pls Apply BRAKE."));
    }
  
  for(k=0;k<12;k++){
  ReceiveData[k] = {0x00};
  }

  k = 0;

  if (masok == true && revok == true){
    i = 1;
    if(reverserPos == RE_R){
      reverse();
    }
    else if(reverserPos == RE_F){
      forward();
    }
    else{
      bosenOFF();
    }
  }
  delay(50);
}



//加速度調整。現在速度が0km/hのとき1倍、各ノッチの最高速度以上のとき0倍になるよう調整。
void kaccel(int notch) {
  float rate, rate2;
  switch(notch){
    case 1:
      if (spd == 0 || spd <= (long)keepac[vvvfPtn]*10000) { //現在速度が0km/hまたは定加速度領域以下
        rate = 1;
      }
      else if (notch1 - spd > 0) {  //各ノッチの最高速度未満
        rate2 = (float)keepac[vvvfPtn]*10000 / (float)spd;
        rate = pow(rate2, acurve);
      }
      else {  //各ノッチの最高速度以上
       rate = 0; 
      }
      kasoku = accelVol * ACCEL_RATIO * 0.2 * rate;
      break;

    case 2:
      if (spd == 0 || spd <= (long)keepac[vvvfPtn]*10000) { //現在速度が0km/hまたは定加速度領域以下
        rate = 1;
      }
      else if (notch1 - spd > 0) {  //各ノッチの最高速度未満
        rate2 = (float)keepac[vvvfPtn]*10000 / (float)spd;
        rate = pow(rate2, acurve);
      }
      else {  //各ノッチの最高速度以上
       rate = 0; 
      }
      kasoku = accelVol * ACCEL_RATIO * 0.3 * rate;
      break;

    case 3:
      if (spd == 0 || spd <= (long)keepac[vvvfPtn]*10000) { //現在速度が0km/hまたは定加速度領域以下
        rate = 1;
      }
      else if (notch2 - spd > 0) {  //各ノッチの最高速度以下
        rate2 = (float)keepac[vvvfPtn]*10000 / (float)spd;
        rate = pow(rate2, acurve);
      }
      else {  //各ノッチの最高速度未満
       rate = 0; 
      }
      kasoku = accelVol * ACCEL_RATIO * 0.5 * rate;
      break;

    case 4:
      if (spd == 0 || spd <= (long)keepac[vvvfPtn]*10000) { //現在速度が0km/hまたは定加速度領域以下
        rate = 1;
      }
      else if (notch3 - spd > 0) {  //各ノッチの最高速度未満
        rate = ((float)keepac[vvvfPtn]*10000 / (float)spd) * 2.0;
        rate2 = (float)keepac[vvvfPtn]*10000 / (float)spd;
        rate = pow(rate2, acurve);
      }
      else {  //各ノッチの最高速度以上
       rate = 0; 
      }
      kasoku = accelVol * ACCEL_RATIO * 0.7 * rate;
      break;

    case 5:
      if (spd == 0 || spd <= (long)keepac[vvvfPtn]*10000) { //現在速度が0km/hまたは定加速度領域以下
        rate = 1;
      }
      else if (notch4 - spd > 0) {  //各ノッチの最高速度未満
        rate2 = (float)keepac[vvvfPtn]*10000 / (float)spd;
        rate = pow(rate2, acurve);
      }
      else {  //各ノッチの最高速度以上
       rate = 0; 
      }
      kasoku = accelVol * ACCEL_RATIO * 0.9 * rate;
      break;

    case 6:
      if (spd == 0 || spd <= (long)keepac[vvvfPtn]*10000) { //現在速度が0km/hまたは定加速度領域以下
        rate = 1;
      }
      else if (notch5 - spd > 0) {  //各ノッチの最高速度未満
        rate2 = (float)keepac[vvvfPtn]*10000 / (float)spd;
        rate = pow(rate2, acurve);
      }
      else {  //各ノッチの最高速度以上
       rate = 0; 
      }
      kasoku = accelVol * ACCEL_RATIO * rate;
      break;
  }
}



//減速度。現在速度が0km/hのとき1倍、P5の最高速度のとき約0.3倍になるよう調整。
void kbrake(int notch) {
  float rate;
  if (spd == 0) { //0km/h
    rate = 1;
  }
  else{
    rate = 1 - sqrt((float)((spd * bcurve) / notch5));
  }
  kasoku = accelVol * BRAKE_RATIO * notch * rate;
}



//緊急停止
void stopEB() {

  //受信データ初期化
  while (Serial.available() > 0) { // 受信したデータが存在する
    Serial.read();
  }
  
  for(k=0;k<6;k++){
  ReceiveData[k] = {0x00};
  }

  k = 0;

  lcd.clear();
  lcd.setCursor(2, 0);
  lcd.print(F("NOW STOPPING"));
  lcd.setCursor(0, 1);
  lcd.print(F("To Reset:Push C"));
  
  do {
    Receive(5);
  } while (EB == true);

  //誤発進防止動作
  do {
    SetStatus();
  } while (i == 0);
  
  lcd.clear();
  
  lcd.setCursor(0, 0);
  lcd.print(F("ReStart Controll"));
  delay(1000);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(soundName[vvvfPtn]);
  lcd.setCursor(0, 1);
  if(tstype==TS_1){
    lcd.print(F("< >    km/h< >"));
    lcd.setCursor(1, 1);
    lcd.print(typeName[type - 1]); //TYPE表示もつける。無駄に。
  }
  else{
    lcd.print(F("       km/h< >"));
  }

  dispPos();
  
}



//母線引き通し(線路出力制御用)リレー制御
void bosenON() { //リレーON
  if(reverserPos != RE_N) {
    if(relay==true) {
      digitalWrite(P_BOSEN, LOW);
    }
    else {
      digitalWrite(P_BOSEN, HIGH);
    }
  }
}

void bosenOFF() { //リレーOFF
  if(relay==true) {
    digitalWrite(P_BOSEN, HIGH);
  }
  else {
    digitalWrite(P_BOSEN, LOW);
  }
}



//方向制御
void forward() { //リレーOFF
  if(relay==true) {
    digitalWrite(P_DIREC, HIGH);
  }
  else {
    digitalWrite(P_DIREC, LOW);
  }
}

void reverse() { //リレーON
  if(relay==true) {
    digitalWrite(P_DIREC, LOW);
  }
  else {
    digitalWrite(P_DIREC, HIGH);
  }
}



//常点灯および走行制御
void pwm(){
  
  //常点灯用と走行用
  TCCR2A = B10100001;
  TCCR2B = B00000001;
  OCR2B = (unsigned int)(255 * ((float)(lightVol * lmax) / 10230.0));
  OCR2A = (unsigned int)(255 * duty);

}



//走行音制御
void spwm(int stype){
  
  //走行音用
  TCCR1A = B00100001;
  TCCR1B = B00010001;
  
  if(stype == 0){
    OCR1A = (unsigned int)(8000000 / Afrq); // TOP値 = CPUの動作周波数 / 2 / 分周 / 希望周波数 = 16,000,000 / 2 / 1 / frq
    OCR1B = (unsigned int)(8000000 / Afrq / 100);
  }
  else if(stype == 1){
    OCR1A = (unsigned int)(8000000 / Bfrq); // TOP値 = CPUの動作周波数 / 2 / 分周 / 希望周波数 = 16,000,000 / 2 / 1 / frq
    OCR1B = (unsigned int)(8000000 / Bfrq / 100);
  }
  else{
    OCR1A = (unsigned int)400; // TOP値 = CPUの動作周波数 / 2 / 分周 / 希望周波数 = 16,000,000 / 2 / 1 / 20000 = 400
    OCR1B = (unsigned int)(1);
  }
}








// ***********************************************
// setup関数。最初に1度だけ実行。
// ***********************************************
void setup() {
  // PWM PIN設定
  pinMode(P_PWM2A, OUTPUT); // 常点灯用PWM出力
  pinMode(P_PWM1B, OUTPUT); // 走行音(VVVF音)用PWM出力
  pinMode(P_PWM2B, OUTPUT); // 走行用PWM出力
  
  //リレー制御用
  pinMode(P_BOSEN, OUTPUT); // 母線引き通し(線路出力制御用)リレー
  pinMode(P_DIREC, OUTPUT); // 方向指示リレー

  bosenOFF();

  // WELCOMEメッセージ表示
  lcd.begin(16, 2);  // 16桁、2行タイプと宣言。
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(MOJI1);
  delay(2000);
  lcd.setCursor(0, 1);
  lcd.print(MOJI2);
  delay(2000);
  lcd.clear();

  // 走行音パターン各開始位置取得処理。
  // soundData配列のうち、各パターンの最初の値となる配列インデックスをdataNumで保持する。
  AdataNum[0] = 0;
  BdataNum[0] = 0;
  
  k = 0;
 
  for (i=1; i<=SOUNDNUM; i++) {
    while (pgm_read_word_near(&AsoundDataBase[k]) != -1) {
      k++;
    }
    k++;
    AdataNum[i] = k;
  }

  k = 0;
  
  for (i=1; i<=SOUNDNUM; i++) {
    while (pgm_read_word_near(&BsoundDataBase[k]) != -1) {
      k++;
    }
    k++;
    BdataNum[i] = k;
  }

  k = 0;
  
  for (i = 0; i<SDATANUM; i++)
  {
  AsoundData[k] = 0;
  k++;
  }

  k = 0;
 
  for (i = 0; i<SDATANUM; i++)
  {
  BsoundData[k] = 0;
  k++;
  }
  
  lcd.home();  // 1文字目、1行目



  //TSマスコン1(ワンハンドル)とTSマスコン2(ツーハンドル)の選択
  lcd.clear();
  delay(180);
  lcd.setCursor(0, 0);
  lcd.print(F("SELECT TSMASCON."));
  delay(480);  
  lcd.clear();
  
  i = 0;
  
  Serial.begin(19200);
  delay(480);

  lcd.setCursor(0, 0);
  lcd.print(F(">1Handle 2Handle"));
  lcd.setCursor(0, 1);
  lcd.print(F("<A  Enter:S  C> "));

  //いきなり決定してしまう可能性を排除。
  while (Serial.available() > 0) { // 受信したデータが存在する
    Serial.read();
  }

  do {
    Receive(0);
  } while(tstype == 0);


  if(tstype==TS_1){ //マスコンタイプの設定
    lcd.clear();
    delay(180);
    lcd.setCursor(0, 0);
    lcd.print(F("SET MASCONTYPE: "));
    lcd.setCursor(0, 1);
    lcd.print(F("<A  Enter:S  C> "));
    
    i = 0;
  
    //いきなり決定してしまう可能性を排除。
    while (Serial.available() > 0) { // 受信したデータが存在する
      Serial.read();
    }
  
    lcd.setCursor(15, 0);
    lcd.print(typeName[0]); //初期状態のTYPE Aを表示しておく
    
    do {
      Receive(1);
    } while(type == 0);
  }
  else{
    lcd.clear();
    delay(180);
    lcd.setCursor(0, 0);
    lcd.print(F("SET MAX-NOTCH:P "));
    lcd.setCursor(0, 1);
    lcd.print(F("<A  Enter:S  C> "));
    delay(480);
    
    i = 3;
  
    //いきなり決定してしまう可能性を排除。
    while (Serial.available() > 0) { // 受信したデータが存在する
      Serial.read();
    }
  
    lcd.setCursor(15, 0);
    lcd.print('3'); //最小であるP3を表示しておく
    
    do {
      Receive(2); //マスコン段数設定
    } while(notchnum == 0);

    lcd.clear();
    delay(180);
    lcd.setCursor(0, 0);
    lcd.print(F("SET MAX-BRAKE:B "));
    lcd.setCursor(0, 1);
    lcd.print(F("<A  Enter:S  C> "));
    delay(480);
    
    i = 5;
  
    //いきなり決定してしまう可能性を排除。
    while (Serial.available() > 0) { // 受信したデータが存在する
      Serial.read();
    }
  
    lcd.setCursor(15, 0);
    lcd.print('5'); //最小であるB5を表示しておく
    
    do {
      Receive(3); //ブレーキ段数設定
    } while(brakenum == 0);
  }
  
  SetSound();

  //初期化
  while (Serial.available() > 0) { // 受信したデータが存在する
    Serial.read();
  }

}










// ***********************************************
// ***********************************************
// loop関数
// ***********************************************
// ***********************************************
void loop() {
  //動作速度確保のため、音データをメモリに格納。

  accelVol = analogRead(P_ACCEL_DIAL) / 32 + 1; // analogRead 0~1023 → 1~16
  lightVol = analogRead(P_LIGHT_DIAL);  // analogRead 0~1023

  Receive(6);

  dispPos();

  switch (masconPos) {
  case MC_EB:
    // 非常
    orderSpd = 0;
    kbrake(9);
    mode = MD_NTRL;
    break;
  case MC_B8:
    // 制動8
    orderSpd = 0;
    kbrake(8);
    mode = MD_BRAKE;
    break;
  case MC_B7:
    // 制動7
    orderSpd = 0;
    kbrake(7);
    mode = MD_BRAKE;
    break;
  case MC_B6:
    // 制動6
    orderSpd = 0;
    kbrake(6);
    mode = MD_BRAKE;
    break;
  case MC_B5:
    // 制動5
    orderSpd = 0;
    kbrake(5);
    mode = MD_BRAKE;
    break;
  case MC_B4:
    // 制動4
    orderSpd = 0;
    kbrake(4);
    mode = MD_BRAKE;
    break;
  case MC_B3:
    // 制動3
    orderSpd = 0;
    kbrake(3);
    mode = MD_BRAKE;
    break;
  case MC_B2:
    // 制動2
    orderSpd = 0;
    kbrake(2);
    mode = MD_BRAKE;
    break;
  case MC_B1:
    // 制動1
    orderSpd = 0;
    kbrake(1);
    mode = MD_BRAKE;
    break;
  case MC_N:
    // 惰行
    orderSpd = 0;
    mode = MD_NTRL;
    break;
  case MC_P1:
    // 力行1
    orderSpd = notch1;
    kaccel(1);
    mode = MD_ACCEL;
    break;
  case MC_P2:
    // 力行2
    orderSpd = notch1;
    kaccel(2);
    mode = MD_ACCEL;
    break;
  case MC_P3:
    // 力行3
    orderSpd = notch2;
    kaccel(3);
    mode = MD_ACCEL;
    break;
  case MC_P4:
    // 力行4
    orderSpd = notch3;
    kaccel(4);
    mode = MD_ACCEL;
    break;
  case MC_P5:
    // 力行5
    orderSpd = notch4;
    kaccel(5);
    mode = MD_ACCEL;
    break;

  case MC_P6:
    // 力行6
    orderSpd = notch5;
    kaccel(6);
    mode = MD_ACCEL;
    break;
  }
  
  for ( k = 0; k < ANTI_CHAT_RATE; k++ ) { // チャタリング防止ループ

    if (EB == true) {
      break;
    }
    
    if (spd < orderSpd) {
      if (orderSpd - spd <= kasoku) {
        spd = orderSpd;
      } else {
        spd = spd + kasoku;
      }
    }
    else if ( spd > 0 ) {
      if ( masconPos >= MC_P1 ) {
        // 力行
        if ( spd - orderSpd <= 0 ) {
          spd = orderSpd;
        } else {
          spd = spd * (1.0 - (pcoast / 10000.0));
        }
      }
      else if ( masconPos == MC_N ) {
        // 惰行
        spd = spd * (1.0 - (coast / 10000.0));
      }
      else {
        // 制動
        if ( spd - kasoku < 0 ) {
          spd = 0;
        } else {
          spd = spd - kasoku;

          if ( spd < stopSpd ) {
          // 惰行or制動ノッチのとき、ピタ停止速度値よりも現行速度が下回ったらピタッと停止させる。
          spd = 0;
          mode = MD_STOP;
          }
        }
      }
    }
  
    
    duty = ((float)spd * adrate / 4000000.0)* sscale;
    if (duty > 1.0){
      duty = 1.0;
    }

    if(viewduty == true){
      lcd.setCursor(0, 0);
      lcd.print("DUTY=           ");
      lcd.setCursor(5, 0);
      lcd.print(duty);
    }
  
    if (mode == MD_ACCEL) { 
      for (i = 0; ;i = i + 3) {
          AsttFrq = AsoundData[i];
          AendFrq = AsoundData[i + 1];
            if (i == 0) {
            AsttSpd = 0;
            AendSpd = AsoundData[i + 2];
            }
            else { 
            AsttSpd = AsoundData[i - 1];
            AendSpd = AsoundData[i + 2];
            }
  
        if (i == 0 && AsttFrq == -1) { // 走行音が設定されていない場合の処理

          pwm();
          spwm(2);
          
          break;
        }
  
        if (mode == MD_BRAKE) { // 変調モード切替
        break; 
        }
        
        if (mode == MD_STOP) { // 変調モード切替
        break; 
        }
  
        if (mode == MD_NTRL) { // 変調モード切替
        break; 
        }
  
        if (AsttFrq == -1) { // 高速時 VVVF音停止

          pwm();
          spwm(2);
          
          break;
        }
  
        // VVVF音
        if ((spd >= AsttSpd * 10000) && (spd < AendSpd * 10000)) {
          Afrq = ( AendFrq * 10 - AsttFrq * 10 ) / ( AendSpd - AsttSpd ) * ( spd - AsttSpd * 10000 ) / 100000 + AsttFrq;
          if ( Afrq < 50 )        Afrq = 50;
          else if ( Afrq > 100000 ) Afrq = 100000;
          
          pwm();
          spwm(0);
          
          break;
        }
      }
    }
    else if (mode == MD_BRAKE) {
      for (i = 0; ;i = i + 3) {
        BsttFrq = BsoundData[i];
        BendFrq = BsoundData[i + 1];
        if (i == 0) {
        BsttSpd = 0;
        BendSpd = BsoundData[i + 2];
        }
        else { 
        BsttSpd = BsoundData[i - 1];
        BendSpd = BsoundData[i + 2];
        }
  
        if (i == 0 && BsttFrq == -1) { // 走行音が設定されていない場合の処理

          pwm();
          spwm(2);
          
          break;
        }
  
        if (mode == MD_ACCEL) { // 変調モード切替
        break; 
        }
        
        if (mode == MD_STOP) { // 変調モード切替
        break; 
        }
     
        if (mode == MD_NTRL) { // 変調モード切替
        break; 
        }
         
        if (BsttFrq == -1) { // 高速時 VVVF音停止
 
          pwm();
          spwm(2);
          
          break;
        }
  
        // VVVF音
        if ((spd >= BsttSpd * 10000) && (spd < BendSpd * 10000)) {
          Bfrq = ( BendFrq * 10 - BsttFrq * 10 ) / ( BendSpd - BsttSpd ) * ( spd - BsttSpd * 10000 ) / 100000 + BsttFrq;
          if ( Bfrq < 50 )        Bfrq = 50;
          else if ( Bfrq > 100000 ) Bfrq = 100000;
          
          pwm();
          spwm(1);
          
          break;
        }
      }
    }
    else if (mode == MD_NTRL) { // 惰行・非常
      
      pwm();
      spwm(2);
      
    }
    else { // 停止時 音停止

      pwm();

      //走行音用
      TCCR1A = B00000001;
      TCCR1B = B00010001;

    }
  
    dispSpd();

  }

  if (changeS == true) {
    SetSound();
  }

  if (EB == true) {
    stopEB();
  }
  
}