// 踏切制御
//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 |
_______________________________________________________
*/
//
//********* ライブラリ定義 ******************************************************
#include "DFRobotDFPlayerMini.h" // MP3プレイヤライブラリのインクルード
#include "SoftwareSerial.h" // シリアル通信ライブラリのインクルード
#include <Servo.h> // Servoライブラリのインクルード
#include <IRremote.h> // 赤外線リモコンライブラリのインクルード
#include <avr/wdt.h> // SleepyDogライブラリのインクルード
//#include <VarSpeedServo.h> // VarSpeedServoライブラリのインクルード
Servo myservo; // Servoオブジェクトの宣言#include
//VarSpeedServo myspeedservo;
//
//********* 変数定義 ******************************************************
const int DIN_PIN1 = 8; // ➀側センサー信号入力ピン設定:D8ピン
const int DIN_PIN2 = 9; // ➁側センサー信号入力ピン設定:D9ピン
const int DIN_PIN3 = 10; // ➀側センサー信号入力ピン設定:D10ピン
const int DIN_PIN4 = 11; // ➁側センサー信号入力ピン設定:D11ピン
const int LED_PIN1 = A2; // LED出力(警報1)ピン設定:A2ピン
const int LED_PIN2 = A3; // LED出力(警報2)ピン設定:A3ピン
const int LED_PIN3 = A4; // LED出力(遮断機DOWN)ピン設定:A4ピン
const int LED_PIN4 = A5; // LED出力(遮断機UP)ピン設定:A5ピン
const int LANP_PIN = 7; // 駅照明点灯出力:D7ピン
const int ReceiverPin = 2; // 赤外線受信モジュール接続ピン設定:D2ピン
const int SV_PIN = 5; // サーボモーター制御信号ピン設定:D5ピン
const int UP_LOCATION = 0; // 遮断機が上がった状態のサーボ角度
const int DOWN_LOCATION = 70; // 遮断機が下りた状態のサーボ角度
volatile int flg = 9; // 状態フラグ
// 状態1 : ➀側センサー感知。警報鳴動、遮断機降下
// 状態2 : ➁側センサー感知。警報鳴動、遮断機降下
// 状態3 : 警報停止、遮断機上昇
// 状態9 : 空き状態
volatile int i = UP_LOCATION; // 遮断機位置変数(初期値:上がった状態)
volatile int n = 1; // センサ変化点辺出シフトレジスタ変数(初期値:1)
volatile int val1[3] = {1}; // 踏切➀側startセンサー変化点検出配列(初期値:1)
volatile int val2[3] = {1}; // 踏切➁側startセンサー変化点検出配列(初期値:1)
volatile int val3[3] = {1}; // 踏切➀側endセンサー変化点検出配列(初期値:1)
volatile int val4[3] = {1}; // 踏切➁側endセンサー変化点検出配列(初期値: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 bell_Flag = 0; // 発車ベル鳴動フラグ(1で発車ベル起動)
unsigned long startTime; // 警報機周期タイマ
unsigned long currentTime; // 基本タイマ
const unsigned long interval = 500; // 警報機LED点灯時間:500ms
unsigned long barrier_startTime; // 遮断機周期スタート時間
const unsigned long barrier_interval = 50; // サーボ 動作時間/度:50ms
int ledState = LOW; // 警報機LEDステータス
unsigned long bell_currentTime; // 発車ベル基本タイマ
unsigned long bell_startTime; // 発車ベル鳴動スタート時間
const unsigned long bell_interval = 6500; // 発車ベル鳴動間:6500ms
unsigned long chatter_currentTime; // センササンプリング基本タイマ
unsigned long chatter_startTime; // センササンプリングスタート時間
const unsigned long chatter_interval = 10; // センササンプリング動間:10ms
IRrecv irrecv(ReceiverPin); // IR受信オブジェクトの生成(使用ピンを指定)
decode_results results; // 受信情報の格納先(メーカー名/受信したデータ&ビット数など)
SoftwareSerial mySoftwareSerial(13, 12); // UART通信ピン(RX, TX)
DFRobotDFPlayerMini myDFPlayer; //
void(* resetFunc)(void) = 0; // declare reset function @ address 0を指定して実行
//
//********* 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( LED_PIN1, OUTPUT ); // LED_PIN1 出力に設定
pinMode( LED_PIN2, OUTPUT ); // LED_PIN2 出力に設定
pinMode( LED_PIN3, OUTPUT ); // LED_PIN3 出力に設定
pinMode( LED_PIN4, OUTPUT ); // LED_PIN4 出力に設定
pinMode( SV_PIN, OUTPUT); // SV_PIN 出力に設定
pinMode( LANP_PIN, OUTPUT); // bell_PIN 出力に設定
myservo.attach(SV_PIN, 500, 2400); // サーボの割当(パルス幅500~2400msに指定)
startTime = millis(); // start time初期化
bell_startTime = millis(); // bell_current time初期化
irrecv.enableIRIn(); // 赤外線(IR)受信モジュールを有効に
digitalWrite(LED_PIN1, HIGH); // LED1 点灯テスト
digitalWrite(LED_PIN2, HIGH); // LED2 点灯テスト
myservo.write(0); // サーボモーターテスト:UP_LOCATION(0度)の位置
delay(2000);
myservo.write(DOWN_LOCATION); // サーボモーターテスト:DOWN_LOCATION(90度)の位置
delay(2000);
myservo.write(UP_LOCATION); // サーボモーターテスト終了:遮断機を上げた位置まで動かす
delay(2000);
digitalWrite(LED_PIN1, LOW); // LED1 テスト滅灯
digitalWrite(LED_PIN2, LOW); // LED2 テスト滅灯
// DFPlayerを初期化します。
mySoftwareSerial.begin(9600); // シリアルポートの転送速度を9600bpsに設定
Serial.begin(9600); // シリアルポートを初期化 9,600 or 115,200
/*
n = 2; // DFPlyer初期化タイムアウトカウンタ
while(!myDFPlayer.begin(mySoftwareSerial)){ // 初期化待ち。無限ループ
// Serial.print("."); // デバッグ用
delay(500); // 初期化待ちデイレイ
if(n <= 0){ // n = 0 でリセット
digitalWrite(LED_PIN3, HIGH); // LED1 点灯テスト
delay(1000);
resetFunc(); // Reset Function コール
}
n = n - 1; // タイムアウト カウントダウン
}
*/
// myDFPlayer.volume(30); // ボリュームの設定 30(設定値は0から30まで可)
delay(1000); // 1sのタイミングを置く
// myDFPlayer.play(99); // DFPlayer mini準備完了音声を流す
}
//
//********* loop関数 ******************************************************
void loop()
{
//
//********* 赤外線リモコン 受信処理 ******************************************************
if (irrecv.decode(/*&results*/)){ // 赤外線受信モジュールからデータを受信したかの確認
Serial.println(irrecv.decodedIRData.decodedRawData, HEX); // デバッグ用受信データ表示
switch (irrecv.decodedIRData.decodedRawData){ // 赤外線受信モジュールからデータを受信したかの確認
// case 0xED127F80 : // "ON":照明 ON
case 0x5DA2FF00 : // シュミレーション用
// Serial.println("照明on"); // デバッグ用”照明on”表示
digitalWrite(LANP_PIN, HIGH); // 駅照明点灯
break;
// case 0xE51A7F80: // "OFF":照明 OFF
case 0x1DE2FF00: // "OFF":照明 OFF
// Serial.println("照明off"); // デバッグ用”照明off”表示
digitalWrite(LANP_PIN, LOW); // 駅照明滅灯
break;
case 0xE11E7F80: // "Mode"が押された時
Serial.println("Mode"); // デバッグ用”Mode”表示
resetFunc(); //call reset
wdt_enable(0); // Watchdogの実行
while (1) {
Serial.println("WDT"); // デバッグ用”Mode”表示
}
break;
case 0xFE017F80: // "4H":発射ベル鳴動⇒踏切移動
bell_Flag = 1; // ベル鳴動フラグ 1:ベル鳴動
// Serial.println("発射ベル鳴動"); // デバッグ用”発射ベル鳴動”表示
myDFPlayer.loop(1); // DFPlayer mini ベル音再生
bell_startTime = bell_currentTime; // bell_startTimeheへ現在のタイム値を設定
break;
case 0xFD027F80: // "8H"が押された時
// Serial.println("8H"); // デバッグ用”8H”表示
break;
case 0xFC037F80: // "Multi Color"が押された時
// Serial.println("8H"); // デバッグ用”Multi Color”表示
break;
case 0xFB047F80: // 警笛鳴動
myDFPlayer.play(3); // DFPlayer mini 警笛音再生
break;
case 0xFA057F80: // コンプレッサ
myDFPlayer.play(4); // DFPlayer mini コンプレッサ音再生
break;
case 0xF9067F80: // "V3-H3"が押された時
// Serial.println("V3-H3"); // デバッグ用”V3-H3”表示
break;
case 0xF8077F80: // ATS
myDFPlayer.play(5); // DFPlayer mini ATS音再生
break;
case 0xF7087F80: // ドア開ける
myDFPlayer.play(6); // DFPlayer mini ドア開音再生
break;
case 0xF6097F80: // ドア閉める
myDFPlayer.play(7); // DFPlayer mini ドア閉音再生
break;
case 0xF50A7F80: // 発車音
myDFPlayer.play(8); // DFPlayer mini 発車音再生
break;
case 0xE41B7F80: // ブレーキ解放
myDFPlayer.play(9); // DFPlayer mini ブレーキ解放音再生
break;
case 0xE01F7F80: // V1-H6が押された時
// Serial.println("V1-H5"); // デバッグ用”V1-H6”表示
break;
case 0xF30C7F80: // V1-H6が押された時
// Serial.println("V1-H6"); // デバッグ用”V1-H6”表示
break;
case 0xF20D7F80: // V2-H6が押された時
// Serial.println("V2-H6"); // デバッグ用”V2-H6”表示
break;
case 0xF10E7F80: // V3-H6が押された時
// Serial.println("V3-H6"); // デバッグ用”V3-H6”表示
break;
default: // それ以外
// Serial.println("未割り当てのキー"); // デバッグ用”未割り当てのキー”表示
break;
}
}
irrecv.resume(); // 次の値を受け取る
//
//********* 列車通過時のセンサーの変化点検出 ******************************************************
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)
val1[0] = digitalRead(DIN_PIN1); // ➀側センサー状態を読む
val2[0] = digitalRead(DIN_PIN2); // ➁側センサー状態を読む
val3[0] = digitalRead(DIN_PIN3); // ➀側センサー状態を読む
val4[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 (val4[1] == 1 and val4[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 (val3[1] == 1 and val3[2] == 0 ) { // ➁側センサー立下り検出
val2_down = 0; // ➁側センサーフラグ セット
}
else {
val2_down = 1; // ➁側センサーdownフラグ クリア
}
}
//
//********* 発車ベル鳴動 ⇒ 踏切始動 ******************************************************
bell_currentTime = millis();
if(bell_Flag == 1){
if (bell_currentTime - bell_startTime >= bell_interval){
val1_up = 0; // ➀側センサーupフラグ セット : 踏切動作開始
// Serial.println("発車ベル鳴動 val1_up=0"); // デバッグ表示_状態1へ移行
bell_Flag = 0; // bell鳴動フラグ 0:ベル停止
}
}
//
//********* 踏切の始動(警報音の鳴動) ******************************************************
if (val1_up == 0 and flg == 9) { // ➀側センサー入力ピン状態がLOW(0)に変化したら以下の処理を行う
// myDFPlayer.loop(2); // DFPlayer mini 警報音再生
flg = 1; // 状態フラグを1にする
Serial.println("状態1"); // デバッグ表示_状態1へ移行
i = UP_LOCATION; // 遮断機位置変数iを遮断機が上がった状態に初期化
}
else{
if (val2_up == 0 and flg == 9) { // ➁側センサー入力ピン状態がLOW(0)に変化したら以下の処理を行う
// myDFPlayer.loop(2); // DFPlayer mini 警報音再生
flg = 2; // 状態フラグを2にする
Serial.println("状態2"); // デバッグ表示_状態2へ移行
i = UP_LOCATION; // 遮断機位置変数iを遮断機が上がった状態に初期化
}
}
//
//********* 遮断機降下 ******************************************************
if (flg == 1 or flg == 2) { // 状態フラグが1または2の場合、以下の処理を行う
currentTime = millis(); // current time読み込み
if (i <= DOWN_LOCATION ) { // 遮断機位置変数を遮断機が下がった状態まで繰り返す
if (currentTime - barrier_startTime >= barrier_interval) // 遮断機を1度ずつ動作させる時間判断
{
barrier_startTime = currentTime; // barrier_startTimeheへ現在のタイム値を設定
// Serial.print("サーボ角度 i="); // デバッグ表示_状態1へ移行
// Serial.println(i); // デバッグ表示_状態1へ移行
myservo.write(i); // 遮断機位置変数の位置へサーボモーターを動かす
i = i + 1; // 遮断機位置変数をカウントUP
digitalWrite(LED_PIN3, HIGH); // テスト用遮断器DOWN LED
}
}
else {
digitalWrite(LED_PIN3, LOW); // テスト用遮断器DOWN LED
}
//
//********* 警報機の点滅 ******************************************************
if (currentTime - startTime >= interval) { // 信号を切替える時間判断
startTime = currentTime; // startTimeheへ現在のタイム値を設定
if (ledState == LOW) { // 交互に点灯させる判断
ledState = HIGH; // 信号LEDステータスにHIGHを設定
} else {
ledState = LOW; // 信号LEDステータスにLOWを設定
}
digitalWrite(LED_PIN1, ledState); // 信号LED1を設定
digitalWrite(LED_PIN2, !ledState); // 信号LED2を設定
}
}
//
//********* 踏切の停止 ******************************************************
if ((val2_down == 0 and flg == 1) or (val1_down == 0 and flg ==2)) {
// 踏切出側センサー入力ピン状態がLOW(0)だったら以下の処理を行う
Serial.println("状態3"); // デバッグ表示_状態3へ移行
// Serial.println(i); // デバッグ表示_iカウンタ
flg = 3; // フラグを3(にして、LED点滅停止
digitalWrite( LED_PIN2, LOW ); // 信号LED2を消灯
digitalWrite( LED_PIN1, LOW ); // 信号LED1を消灯
// myDFPlayer.pause(); // DFPlayer mini 警報音停止
}
//
//********* 遮断機の上昇 ******************************************************
if (flg == 3) { // 状態フラグが3の場合、以下の処理を行う
currentTime = millis(); // current time読み込み
if (i >= UP_LOCATION ) { // 遮断機位置変数を遮断機が上がった状態まで繰り返す
// Serial.println(currentTime); // デバッグ表示_currentTime
// Serial.println(barrier_startTime); // デバッグ表示_barrier_startTime
if (currentTime - barrier_startTime >= barrier_interval) {
// Serial.println(i); // デバッグ表示_状態3へ移行
barrier_startTime = currentTime;
myservo.write(i); // 遮断機位置変数iの位置へサーボモーターを動かす
i = i - 1; // 遮断機位置変数をカウントDOWN
digitalWrite(LED_PIN4, HIGH); // テスト用遮断器UP LED
}
}
else {
// Serial.println("状態9"); // デバッグ表示_状態9へ移行
flg = 9; // 状態フラグを9にする
digitalWrite(LED_PIN4, LOW); // テスト用遮断器UP LED
}
}
}