// 列車制御
//https://burariweb.info
/* 列車成語スケッチ
Arduino NANO 使用
警報灯(点滅はmillis関数を使用)
・警報灯はLED*2
警報音他
・DFPlayer miniで再生する
遮断機
・サーボモータ制御
2024年2月27日
ツール
➀Arduino IDE :Arduine コンパイラ
➁WOKWI :Arduine シミュレーション(https://wokwi.com/)
➂Desktop Station OPENSOUNDDATA :鉄道 オープンサウンド(https://desktopstation.net/sounds/index.html)
➃DesktopStation SoundProgrammer(DSSP) :SmileSoundのサウンドデータツール
➄NCH Softwaer WavePat 音声編集ソフト
➅NCH Softwaer MixPat 多重録音ソフト
*/
/* リモコンボタンデータテーブル
列| | V1 | V2 | V3 |
行 | | | | |
_______________________________________________________
H1 | DataCode | 0xED127F80 | 0xE51A7F80 | 0xE11E7F80 |
| Bottan | "ON" | "OFF" | "Mode" |
_______________________________________________________
H2 | DataCode | 0xFE017F80 | 0xFD027F80 | 0xFC037F80 |
| Bottan | "4H" | "8H" |"Multi Color"|
_______________________________________________________
H3 | DataCode | 0xFB047F80 | 0xFA057F80 | 0xF9067F80 |
| Bottan | V1-H3 | V2-H3 | V3-H3 |
_______________________________________________________
H4 | DataCode | 0xF8077F80 | 0x7087F80 | 0xF6097F80 |
| Bottan | V1-H4 | V2-H4 | V3-H4 |
_______________________________________________________
H5 | DataCode | 0xF50A7F80 | 0xF41B7F80 | 0xE01F7F80 |
| Bottan | V1-H5 | V2-H5 | V3-H5 |
_______________________________________________________
H6 | DataCode | 0xF30C7F80 | 0xF20D7F80 | 0xF10E7F80 |
| Bottan | V1-H6 | V2-H6 | V3-H6 |
_______________________________________________________
//----------------------------------------//
// AE_KEYPAD4X3 ------ Arduino UNO //
// //
// |> <------ 5.0V //
// X ------> 8 //
// Y ------> 7 //
// Z ------> 6 //
// A <------ 5 //
// B <------ 4 //
// C <------ 3 //
// D <------ 2 //
//----------------------------------------//
キーパッドデータテーブル
列| | X | Y | Z |
行 | | | | |
____________________________________________________
D | DataCode | 前進 | 後退 | |
| Bottan | "1" | "2" | "3" |
____________________________________________________
C | DataCode | 発車 | | |
| Bottan | "4" | "5" | "6" |
____________________________________________________
B | DataCode | point1 | point2 | point3 |
| Bottan | "7" | "8" | "9" |
____________________________________________________
A | DataCode | | | |
| Bottan | "*" | "0" | "#" |
____________________________________________________
*/
//
//********* ライブラリ定義 ******************************************************
#include "DFRobotDFPlayerMini.h" // MP3プレイヤライブラリのインクルード
#include "SoftwareSerial.h" // シリアル通信ライブラリのインクルード
#include <IRremote.h> // 赤外線リモコンライブラリのインクルード
#include <avr/wdt.h> // SleepyDogライブラリのインクルード
//#include "AE_KEYPAD4X3.h"
#include <Wire.h> // ライブラリのインクルード
//#include <Key.h>
#include <Keypad.h>
#include <Key.h>
#include "Adafruit_MCP23X17.h"
Adafruit_MCP23X17 mcp; // MCPライブラリのオブジェクトを作成
//
//********* 変数定義 ******************************************************
const int DIN_PIN1 = 16; // ➀センサー信号入力ピン設定:16ピン
const int DIN_PIN2 = 17; // ➁センサー信号入力ピン設定:17ピン
const int DIN_PIN3 = 18; // ➂センサー信号入力ピン設定:18ピン
const int DIN_PIN4 = 19; // ➃センサー信号入力ピン設定:19ピン
const int PWM1_Hi_PIN = 5; // 列車速度PWM1_Hi制御ピン設定:5ピン
const int PWM1_Low_PIN = 11; // 列車速度PWM1_Low制御ピン設定:11ピン
const int PWM2_Hi_PIN = 12; // 列車速度PWM2_Hi制御ピン設定:12ピン
const int PWM2_Low_PIN = 15; // 列車速度PWM2_Low制御ピン設定:15ピン
const int Mascon_PIN = A3; // マスコン入力入力ピン設定:A3ピン
const int MaxSpeed_PIN = A2; // 自動運転最大速度設定ボリューム入力ピン設定:A2ピン
const int LANP_PIN = 6; // 車庫照明点灯出力:6ピン
const int ReceiverPin = 8; // 赤外線受信モジュール接続ピン設定:D8ピン
const int POINT_PIN1 = 8; // ポイント切替信号ピン設定:拡張8ピン
const int POINT_PIN2 = 9; // ポイント切替信号ピン設定:拡張9ピン
const int POINT_PIN3 = 10; // ポイント切替信号ピン設定:拡張10ピン
const int POINT_PIN4 = 11; // ポイント切替信号ピン設定:拡張11ピン
const int LED_PIN = 7; // テストLEDピン設定:拡張拡張7ピン
int VOLUME; // 変数を整数型で宣言
int MAX_VOLUME; // 変数を整数型で宣言
int IR_data; // 変数を整数型で宣言
volatile char KEY_CHK[3] = {'\0'}; // 変数を整数型で宣言
volatile int n = 1; // センサ変化点辺出シフトレジスタ変数(初期値:1)
volatile int val[4][3] = {1}; // 踏切➀側センサー変化点検出配列(初期値:1)
volatile int val1[3] = {1}; // 踏切➁側センサー変化点検出配列(初期値:1)
volatile int val2[3] = {1}; // 踏切➁側センサー変化点検出配列(初期値:1)
volatile int val3[3] = {1}; // 踏切➂側センサー変化点検出配列(初期値:1)
volatile int val4[3] = {1}; // 踏切➃側センサー変化点検出配列(初期値:1)
volatile int val1_up = 1; // ➀側センサー立上り(0で立上り検出)
volatile int val2_up = 1; // ➁側センサー立上り(0で立上り検出)
volatile int val1_down = 1; // ➀側センサー立下り(0で立下がり検出)
volatile int val2_down = 1; // ➁側センサー立下り(0で立下がり検出)
volatile int Direction = 9; // 進行方向 0:前進、1:後進
volatile int POINT1 = 0; // POINT_SW1変数
volatile int POINT2 = 0; // POINT_SW2変数
volatile int POINT3 = 0; // POINT_SW3変数
volatile int POINT4 = 0; // POINT_SW4変数
unsigned long chatter_currentTime; // センササンプリング基本タイマ
unsigned long chatter_startTime; // センササンプリングスタート時間
const unsigned long chatter_interval = 10; // センササンプリング動間:10ms
unsigned long key_currentTime; // センササンプリング基本タイマ
unsigned long key_startTime; // センササンプリングスタート時間
const unsigned long key_interval = 10; // センササンプリング動間:10ms
IRrecv irrecv(ReceiverPin); // IR受信オブジェクトの生成(使用ピンを指定)
decode_results results; // 受信情報の格納先(メーカー名/受信したデータ&ビット数など)
SoftwareSerial mySoftwareSerial(5, 6); // UART通信ピン(RX, TX)
DFRobotDFPlayerMini myDFPlayer; //
void(* resetFunc)(void) = 0; // declare reset function @ address 0を指定して実行
//---------------------------------------------------------------------------------------------
//const byte ROWS = 4; //4行のキーパッドを使用
//const byte COLS = 4; //4列のキーパッドを使用
/*
char keys[ROWS][COLS] = {
//配列を表す
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
*/
//byte rowPins[ROWS] = {3, 2, 1, 4}; //接続するピン番号
//byte colPins[COLS] = {A5, A4, A1, A0};
volatile int _KEY_X = 2;
volatile int _KEY_Y = 3;
volatile int _KEY_Z = 4;
volatile int _KEY_ZZ = 1;
volatile int _KEY_A = 10;
volatile int _KEY_B = 13;
volatile int _KEY_C = A4;
volatile int _KEY_D = A5;
//
//********* setup関数 ******************************************************
void setup()
{
pinMode( DIN_PIN1, INPUT_PULLUP ); // DIN_PIN1 入力に設定
pinMode( DIN_PIN2, INPUT_PULLUP ); // DIN_PIN2 入力に設定
pinMode( DIN_PIN3, INPUT_PULLUP ); // DIN_PIN3 入力に設定
pinMode( DIN_PIN4, INPUT_PULLUP ); // DIN_PIN4 入力に設定
pinMode( PWM1_Hi_PIN, OUTPUT ); // PWM1_Hi_PIN 出力に設定
pinMode( PWM1_Low_PIN, OUTPUT ); // PWM1_Low_PIN 出力に設定
pinMode( PWM2_Hi_PIN, OUTPUT ); // PWM2_Hi_PIN 出力に設定
pinMode( PWM2_Low_PIN, OUTPUT ); // PWM2_Low_PIN 出力に設定
pinMode( Mascon_PIN, INPUT ); // Mascon_PIN 出力に設定
pinMode( MaxSpeed_PIN, INPUT ); // MaxSpeed_PIN 出力に設定
pinMode( LANP_PIN, OUTPUT); // LANP_PIN 出力に設定
//********* MCP23017 SetUp ******************************************************
Wire.begin();
mcp.begin_I2C(); // MCP23017のI2Cアドレスを0x00に設定(0x00は省略可)
mcp.pinMode( POINT_PIN1, OUTPUT ); // POINT_POI1 出力に設定
mcp.pinMode( POINT_PIN2, OUTPUT ); // POINT_PIN2 出力に設定
mcp.pinMode( POINT_PIN3, OUTPUT ); // POINT_PIN3 出力に設定
mcp.pinMode( POINT_PIN4, OUTPUT ); // POINT_PIN4 出力に設定
mcp.pinMode( LED_PIN, OUTPUT); // LED_PIN 出力に設定
//********* キーパッド ******************************************************
// AE_KEYPAD4X3 KEYPAD = AE_KEYPAD4X3(8,7,6,5,4,3,2); // キーパッド
AE_KEYPAD4X3_Init();
// Keypad customKeypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS); //キーパッドの初期化
// KEYPAD.Init();
//********* タイマー ******************************************************
irrecv.enableIRIn(); // 赤外線(IR)受信モジュールを有効に
digitalWrite(LED_PIN, HIGH); // テストLED 点灯テスト
digitalWrite(LED_PIN, LOW); // テストLED テスト滅灯
// DFPlayerを初期化します。
mySoftwareSerial.begin(9600); // シリアルポートの転送速度を9600bpsに設定
Serial.begin(9600); // シリアルポートを初期化 9,600 or 115,200
Serial.println("setup_start"); // デバッグ用”Multi Color”表示
n = 2; // DFPlyer初期化タイムアウトカウンタ
/*
while(!myDFPlayer.begin(mySoftwareSerial)){ // 初期化待ち。無限ループ
// Serial.print("."); // デバッグ用
delay(500); // 初期化待ちデイレイ
if(n <= 0){ // n = 0 でリセット
Serial.println("Reset Function"); // デバッグ用”Multi Color”表示
resetFunc(); // Reset Function コール
}
n = n - 1; // タイムアウト カウントダウン
}
*/
// myDFPlayer.volume(30); // ボリュームの設定 30(設定値は0から30まで可)
delay(1000); // 1sのタイミングを置く
// myDFPlayer.play(99); // DFPlayer mini準備完了音声を流す
}
//********* loop関数 ******************************************************
void loop()
{
// Serial.println("loop_start"); // デバッグ用”Multi Color”表示
//
IR_Rec();
// Keypad customKeypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS); //キーパッドの初期化
// char customKey = customKeypad.getKey();//押されたキーを検出
// if (customKey){
// Serial.println(customKey);
// }
// Serial.println(IR_data); // デバッグ用”Multi Color”表示
key_currentTime = millis();
if (key_currentTime - key_startTime >= key_interval){ // サンプリング時間毎に実行
key_startTime = key_currentTime; // センササンプリングスタート時間初期化
KEY_CHK[0] = AE_KEYPAD4X3_getKeyChar();
if(KEY_CHK[0] != '\0'){
Serial.println("KEY_CHK");
// Serial.println(KEY_CHK[0]);
// Serial.println(KEY_CHK[1]);
// Serial.println(KEY_CHK[2]);
// if(KEY_CHK[1] == KEY_CHK[0]){
if(KEY_CHK[1] == KEY_CHK[0]){
if(KEY_CHK[2] != KEY_CHK[0]){
Serial.println(KEY_CHK[0]);
}
}
}
KEY_CHK[1] = KEY_CHK[0];
KEY_CHK[2] = KEY_CHK[1];
}
// delay(200);
//
// スピード制御
MAX_VOLUME = analogRead(MaxSpeed_PIN); // アナログ値の読み取り(10ビット:0~1023)
VOLUME = analogRead(Mascon_PIN); // アナログ値の読み取り(10ビット:0~1023)
if(MAX_VOLUME < VOLUME ){
VOLUME = MAX_VOLUME;
}
// PIN_PWM_0に対して、Duty比 100/255※のPWMを周波数2000Hzで使用する
/*
Setting Divisor Frequency
0x01 1 62500
0x02 8 7812.5
0x03 64 976.5625 <--DEFAULT
0x04 256 244.140625
0x05 1024 61.03515625
TCCR0B = (TCCR0B & 0b11111000) | <setting>;
*/
TCCR0B = (TCCR0B & 0b11111000) | 0x05; //
// モード指定
analogWrite(PWM1_Hi_PIN, VOLUME/4); // MOSFIT_Q1 Hi側制御出力(÷4:PWM出力:0~255)
// analogWrite(PWM1_Low_PIN, 255-VOLUME/4); // MOSFIT_Q2 Low側制御出力
// analogWrite(PWM2_Hi_PIN, 255-VOLUME/4); // MOSFIT_Q3 Hi側制御出力
analogWrite(PWM2_Low_PIN, VOLUME/4); // MOSFIT_Q4 Low側制御出力
// Serial.println(VOLUME); // デバッグ用”Multi Color”表示
//********* 列車通過時のセンサーの変化点検出 ******************************************************
chatter_currentTime = millis();
if (chatter_currentTime - chatter_startTime >= chatter_interval){ // サンプリング時間毎に実行
chatter_startTime = chatter_currentTime; // センササンプリングスタート時間初期化
// val1[2] = val1[1]; // シフト(配列0 ⇒ 配列1)
// val2[2] = val2[1]; // シフト(配列1 ⇒ 配列2)
// val1[1] = val1[0]; // シフト(配列0 ⇒ 配列1)
// val2[1] = val2[0]; // シフト(配列1 ⇒ 配列2)
// val3[2] = val3[1]; // シフト(配列0 ⇒ 配列1)
// val4[2] = val4[1]; // シフト(配列1 ⇒ 配列2)
// val3[1] = val3[0]; // シフト(配列0 ⇒ 配列1)
// val4[1] = val4[0]; // シフト(配列1 ⇒ 配列2)
for(int n = 0;n < 4; n++ ){
val[n][2] = val[n][1]; // シフト(配列0 ⇒ 配列1)
val[n][1] = val[n][0]; // シフト(配列0 ⇒ 配列1)
}
val[0][0] = digitalRead(DIN_PIN1); // ➀側センサー状態を読む
val[1][0] = digitalRead(DIN_PIN2); // ➁側センサー状態を読む
val[2][0] = digitalRead(DIN_PIN3); // ➂側センサー状態を読む
val[3][0] = digitalRead(DIN_PIN4); // ➃側センサー状態を読む
/*
この記述では、Sarvo出力が停止する。原因不明
記述をlin168~174に変更する
val1[n] = val1[n - 1]; // シフト(配列0 ⇒ 配列1)
val2[n] = val2[n - 1]; // シフト(配列1 ⇒ 配列2)
if (n < 3) { // 配列引数 0~2の場合
n = n + 1; // 配列引数 + 1
}
else {
n = 1; // 配列引数 0~2以外、初期化
}
*/
if (val1[1] == 0 and val1[2] == 1 ) { // ➀側センサー立上り検出
val1_up = 0; // ➀側センサーupフラグ セット
}
else {
val1_up = 1; // ➀側センサーupフラグ クリア
}
if (val1[1] == 1 and val1[2] == 0 ) { // ➀側センサー立下り検出
val1_down = 0; // ➀側センサーdownフラグ セット
}
else {
val1_down = 1; // ➀側センサーdownフラグ クリア
}
if (val2[1] == 0 and val2[2] == 1 ) { // ➁側センサー立上り検出
val2_up = 0; // ➁側センサーupフラグ セット
}
else {
val2_up = 1; // ➁側センサーupフラグ クリア
}
if (val2[1] == 1 and val2[2] == 0 ) { // ➁側センサー立下り検出
val2_down = 0; // ➁側センサーフラグ セット
}
else {
val2_down = 1; // ➁側センサーdownフラグ クリア
}
}
//
}
//********* 赤外線リモコン 受信処理 ******************************************************
void IR_Rec(){
if (irrecv.decode(/*&results*/)){ // 赤外線受信モジュールからデータを受信したかの確認
// Serial.println(irrecv.decodedIRData.decodedRawData, HEX); // デバッグ用受信データ表示
switch (irrecv.decodedIRData.decodedRawData){ // 赤外線受信モジュールからデータを受信したかの確認
case 0xED127F80 : // "ON":照明 ON
// Serial.println("照明on"); // デバッグ用”照明on”表示
IR_data = 1; //
break;
case 0xE51A7F80: // "OFF":照明 OFF
// Serial.println("照明off"); // デバッグ用”照明off”表示
IR_data = 2; //
break;
case 0xE11E7F80: // "Mode"が押された時
IR_data = 3; //
break;
case 0xFE017F80: // "4H":発射ベル鳴動⇒踏切移動
IR_data = 4; //
break;
case 0xFD027F80: // "8H"が押された時
// Serial.println("8H"); // デバッグ用”8H”表示
IR_data = 5; //
break;
case 0xFC037F80: // "Multi Color"が押された時
// Serial.println("8H"); // デバッグ用”Multi Color”表示
IR_data = 6; //
break;
case 0xFB047F80: // 警笛鳴動
IR_data = 7; //
break;
case 0xFA057F80: // コンプレッサ
IR_data = 8; //
break;
case 0xF9067F80: // "V3-H3"が押された時
// Serial.println("V3-H3"); // デバッグ用”V3-H3”表示
IR_data = 9; //
break;
case 0xF8077F80: // ATS
IR_data = 10; //
break;
case 0xF7087F80: // ドア開ける
IR_data = 11; //
break;
case 0xF6097F80: // ドア閉める
IR_data = 12; //
break;
case 0xF50A7F80: // 発車音
IR_data = 13; //
break;
case 0xE41B7F80: // ブレーキ解放
IR_data = 14; //
break;
case 0xE01F7F80: // V1-H6が押された時
// Serial.println("V1-H5"); // デバッグ用”V1-H6”表示
IR_data = 15; //
break;
case 0xF30C7F80: // V1-H6が押された時
// Serial.println("V1-H6"); // デバッグ用”V1-H6”表示
IR_data = 16; //
break;
case 0xF20D7F80: // V2-H6が押された時
// Serial.println("V2-H6"); // デバッグ用”V2-H6”表示
IR_data = 17; //
break;
case 0xF10E7F80: // V3-H6が押された時
// Serial.println("V3-H6"); // デバッグ用”V3-H6”表示
IR_data = 18; //
break;
default: // それ以外
// Serial.println("未割り当てのキー"); // デバッグ用”未割り当てのキー”表示
IR_data = 19; //
break;
}
}
irrecv.resume(); // 次の値を受け取る
}
/*
void key_pad()
{
Keypad customKeypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS); //キーパッドの初期化
char customKey = customKeypad.getKey();//押されたキーを検出
if (customKey){
Serial.println(customKey);
}
}
*/
void AE_KEYPAD4X3_Init()
{
pinMode(_KEY_X, OUTPUT);
pinMode(_KEY_Y, OUTPUT);
pinMode(_KEY_Z, OUTPUT);
pinMode(_KEY_A, INPUT_PULLUP);
pinMode(_KEY_B, INPUT_PULLUP);
pinMode(_KEY_C, INPUT_PULLUP);
pinMode(_KEY_D, INPUT_PULLUP);
digitalWrite(_KEY_X,1);
digitalWrite(_KEY_Y,1);
digitalWrite(_KEY_Z,1);
digitalWrite(_KEY_ZZ,1);
}
int AE_KEYPAD4X3_getKey()
{
byte xkey,ykey,zkey;
digitalWrite(_KEY_X,0);
digitalWrite(_KEY_Y,1);
digitalWrite(_KEY_Z,1);
xkey = (digitalRead(_KEY_D)<<3)|
(digitalRead(_KEY_C)<<2)|
(digitalRead(_KEY_B)<<1)|
(digitalRead(_KEY_A)<<0);
// Serial.print("xkey=");
// Serial.println(xkey,HEX);
// delay(2000);
digitalWrite(_KEY_X,1);
digitalWrite(_KEY_Y,0);
digitalWrite(_KEY_Z,1);
ykey = (digitalRead(_KEY_D)<<3)|
(digitalRead(_KEY_C)<<2)|
(digitalRead(_KEY_B)<<1)|
(digitalRead(_KEY_A)<<0);
// Serial.print("ykey=");
// Serial.println(ykey,HEX);
// delay(2000);
digitalWrite(_KEY_X,1);
digitalWrite(_KEY_Y,1);
digitalWrite(_KEY_Z,0);
zkey = (digitalRead(_KEY_D)<<3)|
(digitalRead(_KEY_C)<<2)|
(digitalRead(_KEY_B)<<1)|
(digitalRead(_KEY_A)<<0);
// Serial.print("zkey=");
// Serial.println(zkey,HEX);
// delay(2000);
digitalWrite(_KEY_X,1);
digitalWrite(_KEY_Y,1);
digitalWrite(_KEY_Z,1);
if(xkey!=0x0F)
{
if(xkey==0x0E)
return 10;
else if(xkey==0x0D)
return 7;
else if(xkey==0x0B)
return 4;
else if(xkey==0x07)
return 1;
}
if(ykey!=0x0F)
{
if(ykey==0x0E)
return 11;
else if(ykey==0x0D)
return 8;
else if(ykey==0x0B)
return 5;
else if(ykey==0x07)
return 2;
}
if(zkey!=0x0F)
{
if(zkey==0x0E)
return 12;
else if(zkey==0x0D)
return 9;
else if(zkey==0x0B)
return 6;
else if(zkey==0x07)
return 3;
}
else
return 0;
}
char AE_KEYPAD4X3_getKeyChar()
{
// Serial.println(AE_KEYPAD4X3_getKey());
if(AE_KEYPAD4X3_getKey()==1) return '1';
else if(AE_KEYPAD4X3_getKey()==2) return '2';
else if(AE_KEYPAD4X3_getKey()==3) return '3';
else if(AE_KEYPAD4X3_getKey()==4) return '4';
else if(AE_KEYPAD4X3_getKey()==5) return '5';
else if(AE_KEYPAD4X3_getKey()==6) return '6';
else if(AE_KEYPAD4X3_getKey()==7) return '7';
else if(AE_KEYPAD4X3_getKey()==8) return '8';
else if(AE_KEYPAD4X3_getKey()==9) return '9';
else if(AE_KEYPAD4X3_getKey()==10) return '*';
else if(AE_KEYPAD4X3_getKey()==11) return '0';
else if(AE_KEYPAD4X3_getKey()==12) return '#';
else return '\0';
}