This is working very well for my O scale layout. It's drawn from a lot of other things which have appeared in various YouTube videos and model train magazines. I have always taken the easy way out, rather than trying to reinvent the wheel, and have stuck with whatever functions.
Presently, on my layout, it controls servo turnouts for programable routes, changes track signal lights automatically, and plays various sound effects. Any of it is easy to control with IR sensors as well as buttons. It can also control relays to prevent collisions. I use a mega with a servo shield for this application.
Note, the track signal lights came wired :backwards, and can't be changed, so they are activated pin low.
If any of it proves useful to anyone, I will be gratified.
//Model Railroad
//6 routes, 8 servos, sounds & signals
//by Artie Langston, 2023
#include <Servo.h>
#define numpins 8
Servo servo[numpins];
int i, j;
int pins[] = { 4, 5, 6, 7, 8, 9, 10, 11 }; // Pushbutton/LED pins
int spins[] = { 38, 39, 40, 41, 42, 43, 44, 45 }; // Servo pins
int sstart[] = { 90, 90, 160, 90, 90, 0, 90, 90 }; // Servo Start Positions
int sstop[] = { 160, 160, 80, 180, 180, 90, 160, 160 }; // Servo Stop Positions
byte routes[8][8] = {
// ROUTE TABLE Definition for each route & each servo
{ 0, 0, 0, 0, 0, 0, 0, 0 }, // route 1 Neutral rb1
{ 0, 1, 1, 0, 0, 0, 0, 0 }, // route 3 S2 S3 rb3
{ 0, 0, 0, 1, 1, 0, 0, 0 }, // route 2 S4 S5 rb2
{ 1, 0, 0, 0, 0, 0, 0, 0 }, // route 4 S1 pushbutton 4
{ 0, 0, 0, 0, 0, 0, 0, 0 }, // route 5 pushbutton 5
{ 0, 0, 0, 0, 0, 0, 0, 0 }, // route 6 pushbutton 7
{ 0, 0, 0, 0, 0, 0, 0, 0 }, // route 5 pushbutton 6
{ 0, 0, 0, 0, 0, 0, 0, 0 }, // route 6 pushbutton 8
};
boolean pinval[] = { true, true, true, true, true, true, true, true }; // Servo State
byte newpin;
const int rl1Pin = 46;
const int yl1Pin = 47;
const int gl1Pin = 48;
const int rl2Pin = 49;
const int yl2Pin = 50;
const int gl2Pin = 51;
const int rl3Pin = 50;
const int yl3Pin = 51;
const int gl3Pin = 52;
const int rl4Pin = 53;
const int yl4Pin = 54;
const int gl4Pin = 55;
//*******************************
#include <DFRobotDFPlayerMini.h>
#define FPSerial Serial1
DFRobotDFPlayerMini DFPlayer;
//*******************************
#include <ezButton.h>
ezButton bu1(14);
ezButton bu2(15);
ezButton bu3(16);
ezButton ts1(17);
ezButton rl1(22);
ezButton rl2(23);
ezButton rl3(24);
ezButton rl4(25);
ezButton sn1(26);
ezButton sn2(27);
ezButton sn3(28);
ezButton sn4(29);
//********************************
void setup() {
Serial.begin(9600);
FPSerial.begin(9600);
bu1.setDebounceTime(50);
bu2.setDebounceTime(50);
bu3.setDebounceTime(50);
ts1.setDebounceTime(50);
rl1.setDebounceTime(50);
rl2.setDebounceTime(50);
rl3.setDebounceTime(50);
rl4.setDebounceTime(50);
sn1.setDebounceTime(50);
sn2.setDebounceTime(50);
sn3.setDebounceTime(50);
sn4.setDebounceTime(50);
pinMode(rl1Pin, OUTPUT);
pinMode(yl1Pin, OUTPUT);
pinMode(gl1Pin, OUTPUT);
pinMode(rl2Pin, OUTPUT);
pinMode(yl2Pin, OUTPUT);
pinMode(gl2Pin, OUTPUT);
pinMode(rl3Pin, OUTPUT);
pinMode(yl3Pin, OUTPUT);
pinMode(gl3Pin, OUTPUT);
pinMode(rl4Pin, OUTPUT);
pinMode(yl4Pin, OUTPUT);
pinMode(gl4Pin, OUTPUT);
//**********************************************************************
Serial.println();
Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)"));
if (!DFPlayer.begin(FPSerial, /*isACK = */ true, /*doReset = */ true)) { //Use serial to communicate with mp3.
Serial.println(F("Unable to begin:"));
Serial.println(F("1.Please recheck the connection!"));
Serial.println(F("2.Please insert the SD card!"));
while (true) {
}
}
Serial.println(F("DFPlayer Mini online."));
DFPlayer.volume(25); //Set volume value. From 0 to 30
for (i = 0; i < numpins; i++) {
servo[i].attach(spins[i]); // Set up servo controls
servo[i].write(sstart[i]); // Set each servo to start position
}
pinMode(pins[i], OUTPUT); // Initialize PB/LED pins
digitalWrite(pins[i], HIGH);
}
void loop() {
bu1.loop();
bu2.loop();
bu3.loop();
ts1.loop();
rl1.loop();
rl2.loop();
rl3.loop();
rl4.loop();
sn1.loop();
sn2.loop();
sn3.loop();
sn4.loop();
if (bu1.isPressed())
DFPlayer.play(50);
if (bu2.isPressed())
DFPlayer.play(51);
if (bu3.isPressed())
DFPlayer.play(52);
for (i = 0; i < numpins; i++) {
pinMode(pins[i], INPUT_PULLUP);
if ((digitalRead(pins[i]) == LOW) && (digitalRead(pins[i]) == LOW)) { // Check if a Pushbutton is pressed
//pinval[i]=!pinval[i]; // Assume only one at a time
for (j = 0; j < numpins; j++) pinval[j] = true; // clear all LEDs
pinval[i] = false; // turn on this route LED
for (j = 0; j < numpins; j++) { // set all 8 servos for this route i
if (routes[i][j] == 0) servo[j].write(sstart[j]);
else servo[j].write(sstop[j]);
}
}
pinMode(pins[i], OUTPUT); // Now go back to maintaining LEDs
digitalWrite(pins[i], pinval[i]);
}
}