Help with sketch for model railroad

Hi:
I have a model railroad and there are 2 parallel tracks that start and end at turnouts. What I want to do is after a magnetic switch is thrown on track 1, I want to shut off it's power, throw both the turnouts, and turn on power to track 2. Then when a second magnetic switch on track 2 is activated, shut power to track 2, throw the turnouts, and power up track 1. Then repeat.

I have an 8 relay board to throw the turnouts and the power but can't seem to program them to wait for the mag switch signals to start the sequence.

Here is a basic relay sketch:

/4-Relays Shield Example/
//define variable
int RELAY1 = 4;
int RELAY2 = 7;
int RELAY3 = 8;
int RELAY4 = 12;
void setup()
{
//set Relays as Output
pinMode(RELAY1, OUTPUT);
pinMode(RELAY2, OUTPUT);
pinMode(RELAY3, OUTPUT);
pinMode(RELAY4, OUTPUT);
}
void loop()
{
digitalWrite(RELAY1,HIGH); // Turns ON Relay1
delay(1000); // Wait 1 seconds
digitalWrite(RELAY2,HIGH); // Turns ON Relay2
delay(1000); // Wait 1 seconds
digitalWrite(RELAY3,HIGH); // Turns ON Relay3
delay(1000); // Wait 1 seconds
digitalWrite(RELAY4,HIGH); // Turns ON Relay4
delay(1000); // Wait 1 seconds
digitalWrite(RELAY4,LOW); // Turns OFF Relay4
delay(1000); // Wait 1 seconds
digitalWrite(RELAY3,LOW); // Turns OFF Relay3
delay(1000); // Wait 1 seconds
digitalWrite(RELAY2,LOW); // Turns OFF Relay2
delay(1000); // Wait 1 seconds
digitalWrite(RELAY1,LOW); // Turns OFF Relay1
delay(1000); // Wait 1 seconds
}

I know how to move the commands around to do a list of things, but can't figure out how to wire in the mag switches. Please help..

Thanks

Hi,

You need to show your circuit diagram and exactly what type of "magnetic" switch or solenoid you are using as some only require a short pulse to activate, yet you seem to have relay1 on for 7 seconds but others only for 1 second ?

The power needs of the switches can be quiet high depending on type eg 00/HO 12v at 2A peak, so assume you have a good power supply for your relay board ?

Also depending on the type of your turnouts ( size,make,model?) some will isolate the track as they change over.

With a view to future developments I suggest you take all the delay()s out of your program and use millis() to manage timing without blocking as illustrated in Several Things at a Time. Even though there may be a need for learning it will be much easier to make the change now rather than later.

The code you have posted does not make any attempt to read switches so it is impossible to know what overall logic you have in mind.

And, for the future, please post your program using the code button </> so it looks like this and is easy to copy to a text editor.

…R

I thought more about the problem and came up with the attached code. I also put in comments where I'm stumped. I have a separate power supply for the turnouts.


// this constant won't change:
const int track1=2; //reed switch on track 1
const int track2=3; // reed switch on track 2
const int relay1=4; // incoming turnout straight
const int relay2=5; // incoming turnout curve
const int relay3=6; // outgoing turnout straight
const int relay4=7; // outgoing turnout curve
const int relay5=8; // Power to track 1
const int relay6=9; // Power to track 2
#define RELAY_ON 1
#define RELAY_OFF 0

// Variables will change:
int track1state = 0;
int track2state = 0;
int lastTrack1state = 0;
int lastTrack2state = 0;

void setup() {

pinMode(track1, INPUT);
pinMode(track2, INPUT);
pinMode(relay1, OUTPUT);
pinMode(relay2, OUTPUT);
pinMode(relay3, OUTPUT);
pinMode(relay4, OUTPUT);
pinMode(relay5, OUTPUT);
pinMode(relay6, OUTPUT);

}
void loop() {
\\I NEED CODE TO READ AND REMEMBER THE TRACK 2 REED SWITCH
digitalWrite(relay1, RELAY_ON);// set the Relay ON
delay(1000); // wait for a second
digitalWrite(relay1, RELAY_OFF);// set the Relay OFF
digitalWrite(relay3, RELAY_ON);// set the Relay ON
delay(1000); // wait for a second
digitalWrite(relay3, RELAY_OFF);// set the Relay OFF

\\AT THIS POINT I WANT TO SHUT OFF RELAY #6 AND PUT ON RELAY #5. (I THINK I NEED CODE FOR trackstates and last trackstates???)

\\I THEN NEED CODE TO WAIT UNTIL THE TRACK 1 REED SWITCH IS ACTIVATED AND THEN TO SHUT OFF RELAY #5

digitalWrite(relay2, RELAY_ON);// set the Relay ON
delay(1000); // wait for a second
digitalWrite(relay2, RELAY_OFF);// set the Relay OFF
digitalWrite(relay4, RELAY_ON);// set the Relay ON
delay(1000); // wait for a second
digitalWrite(relay4, RELAY_OFF);// set the Relay OFF

\\AT THIS POINT I WANT TO SHUT OFF RELAY #5 AND PUT ON RELAY #6. (I THINK I NEED CODE FOR trackstates and last trackstates???) AND THEN LOOP

}}
//--(end main loop )---

Hi,
Welcome to the forums.

Please read the first post in any forum entitled how to use this forum.
http://forum.arduino.cc/index.php/topic,148850.0.html
Then look down to item #7 about how to post your code.

It will be formatted in a scrolling window that makes it easier to read.

From what you describe you want to run a loco up and back a mainline and passing loop?

A diagram would be good of your circuit, in CAD or picture of hand drawn circuit, and a diagram or picture of the physical layout of the track and where your sensors are.
When you throw (switch) the turnout (points) do you need a pulse or continuous output for each direction?

Sorry for the points bit, GWR enthusiast (Gods Wonderful Railway).

Hope to help.. Tom... :slight_smile:

The power to the turnouts are pulses. What I want to do is start up a train on the straight track, let it run around a big loop, and when it returns to the segment of straight track, stop. Then a second train on the curved track will start, loop, return to curved track and stop. Then the first train will start again.

Hi,
Layout like this, feel free to edit, to add sensor positions etc.
railloop.jpg

Tom… :slight_smile:

Hi
I tried to compile you program, but to many \\\
I fixed that, haven't run it but I'l put it here in code tags.

// this constant won't change:
const int track1 = 2; //reed switch on track 1
const int track2 = 3; // reed switch on track 2
const int relay1 = 4; // incoming turnout straight
const int relay2 = 5; // incoming turnout curve
const int relay3 = 6; // outgoing turnout straight
const int relay4 = 7; // outgoing turnout curve
const int relay5 = 8; // Power to track 1
const int relay6 = 9; // Power to track 2
#define RELAY_ON 1
#define RELAY_OFF 0

// Variables will change:
int track1state = 0;
int track2state = 0;
int lastTrack1state = 0;
int lastTrack2state = 0;


void setup() {

  pinMode(track1, INPUT);
  pinMode(track2, INPUT);
  pinMode(relay1, OUTPUT);
  pinMode(relay2, OUTPUT);
  pinMode(relay3, OUTPUT);
  pinMode(relay4, OUTPUT);
  pinMode(relay5, OUTPUT);
  pinMode(relay6, OUTPUT);

}
void loop() {
  //I NEED CODE TO READ AND REMEMBER THE TRACK 2 REED SWITCH
  digitalWrite(relay1, RELAY_ON);// set the Relay ON
  delay(1000);              // wait for a second
  digitalWrite(relay1, RELAY_OFF);// set the Relay OFF
  digitalWrite(relay3, RELAY_ON);// set the Relay ON
  delay(1000);              // wait for a second
  digitalWrite(relay3, RELAY_OFF);// set the Relay OFF

  //AT THIS POINT I WANT TO SHUT OFF RELAY #6 AND PUT ON RELAY #5. (I THINK I NEED CODE FOR trackstates and last trackstates???)

  //I THEN NEED CODE TO WAIT UNTIL THE TRACK 1 REED SWITCH IS ACTIVATED AND THEN TO SHUT OFF RELAY #5

  digitalWrite(relay2, RELAY_ON);// set the Relay ON
  delay(1000);              // wait for a second
  digitalWrite(relay2, RELAY_OFF);// set the Relay OFF
  digitalWrite(relay4, RELAY_ON);// set the Relay ON
  delay(1000);              // wait for a second
  digitalWrite(relay4, RELAY_OFF);// set the Relay OFF

  //AT THIS POINT I WANT TO SHUT OFF RELAY #5 AND PUT ON RELAY #6. (I THINK I NEED CODE FOR trackstates and last trackstates???) AND THEN LOOP

}

Tom..... :slight_smile:

To use code tags

[code]paste your code here[/code]

The result will be

paste your code here

\\I NEED CODE TO READ AND REMEMBER THE TRACK 2 REED SWITCH

Ever heard of digitalRead :wink:

Using reedcontacts, you want to remember if a track is occupied. When a train enters the section, the reed contact is activated and a little later de-activated. When the train leaves the section, the reed contact is activated and de-activated again.

The below code will toggle the state. It makes the assumption the reed contact is open (and therefor the input will be high) if there is no magnet and closed if there is a magnet (and hence the input will be low).

void setup() {
  // if you don't have external pull-ups, change the below two lines to make use of the internal pullups.
  pinMode(track1, INPUT);
  pinMode(track2, INPUT);
  // just showing how to use internal pull-ups
  //pinMode(track1, INPUT_PULLUP);
  //pinMode(track2, INPUT_PULLUP);
  
  pinMode(relay1, OUTPUT);
  pinMode(relay2, OUTPUT);
  pinMode(relay3, OUTPUT);
  pinMode(relay4, OUTPUT);
  pinMode(relay5, OUTPUT);
  pinMode(relay6, OUTPUT);

  // if you have track states stored in EEPROM
  // read them back here; for you to figure out ;)

}

void loop()
{
  // if activated
  if(digitalRead(track2) == LOW)
  {
    // change the trackstate from occupied to non-occupied or from non-occupied to occupied
    lastTrack2state ^= 1;

    // you can store the lastTrack2state in EEPROM
    // for you to figure out ;)
  }
}

Below the track layout; both sides are dead-end. 'x' represents the reed contact

          x
track 1 ------------------------
               /
          x   /
track2 ------/

Note:
you need some form of debouncing on your reed-contacts as they might, for a short while and in quick succession, close and open when activated. Do a search.

When a train hits the reed switch and the power is turned off the train will go a bit further and open the reed switch. I still can't figure out how to write the code to remember that and turn on the other power.

The code I presented will remember if a track is occupied.

If a track becomes occupied, you want to change the position of the turnout (for the example to track 1) and switch the power to that track. If this is correct

// this constant won't change:
const int track1 = 2; //reed switch on track 1
const int track2 = 3; // reed switch on track 2
const int to1straight = 4; // incoming turnout straight
const int to1curved = 5; // incoming turnout curve
const int to2straight = 6; // outgoing turnout straight
const int to2curved = 7; // outgoing turnout curve
const int power1 = 8; // Power to track 1
const int power2 = 9; // Power to track 2
#define RELAY_ON 1
#define RELAY_OFF 0

// Variables will change:
int track1state = 0;
int track2state = 0;
int lastTrack1state = 0;
int lastTrack2state = 0;

void setup() {
  // if you don't have external pull-ups, change the below two lines to make use of the internal pullups.
  pinMode(track1, INPUT);
  pinMode(track2, INPUT);
  // just showing how to use internal pull-ups
  //pinMode(track1, INPUT_PULLUP);
  //pinMode(track2, INPUT_PULLUP);

  pinMode(to1straight, OUTPUT);
  pinMode(to1curved, OUTPUT);
  pinMode(to2straight, OUTPUT);
  pinMode(to2curved, OUTPUT);
  pinMode(power1, OUTPUT);
  pinMode(power2, OUTPUT);

  // if you have track states stored in EEPROM
  // read them back here; for you to figure out ;)

}

void loop()
{
  // if activated
  if (digitalRead(track2) == LOW)
  {
    // change the trackstate from occupied to non-occupied or from non-occupied to occupied
    lastTrack2state ^= 1;

    if (lastTrack2state == 1)
    {
      // keep track powered for a short while
      delay(1000);
      // power to track 2 off
      digitalWrite(power2, RELAY_OFF);
      delay(50);
      // switch turnout to straight
      digitalWrite(to1straight, RELAY_ON);
      delay(1000);
      digitalWrite(to1straight, RELAY_OFF);
      // power to track 1 on
      digitalWrite(power1, RELAY_ON);
    }

    // you can store the lastTrack2state in EEPROM
    // for you to figure out ;)
  }
}

Note that I've renamed your relay pins to something that makes (a little) more sense.

The problem with this is that as long as the lastTrack2state is 1, it will keep on activating the relay for the turnout. You can solve this by reading back if the tracks power is active. Change the if statement to

    if (lastTrack2state == 1 && digitalRead(power2) == HIGH)

As the above code takes a long time to execute and in the mean time the code can't do anything, it might be useful to do a search for the 'Blink without delay' example on the web.

Will the EPRON remember the last state even when the Arduino is shut off. If there is a train in the middle of the big loop when I power down, I need that train to restart from where it is.

EEPROM will remember things when shut down but you don’t want to write to it every time you switch because of the limited amount of write cycles.

But you don’t need to. If the turnouts (which you can just wire up in parallel to two relays instead of four :wink: ) are in a position the only thing that can happen is the other reed to be triggered. So it can work without saving it to EEPROM just fine :wink:

I used the 4 relays because the trains are LGB (Big!!) and I wanted my capacitors to recharge to throw each turnout.

The question I had was in regard to the power to the tracks, not the turnouts. Will the program remember the last reed switch that was thrown so the appropriate track gets power?

BernieB:
I used the 4 relays because the trains are LGB (Big!!) and I wanted my capacitors to recharge to throw each turnout.

Which capacitors?They were not mentioned before. Are they part of the turnouts? Or have you added them somewhere?

BernieB:
The question I had was in regard to the power to the tracks, not the turnouts. Will the program remember the last reed switch that was thrown so the appropriate track gets power?

If you expand the code that I presented for a second track, it will remember the state of both reed contacts if you store it in eeprom. As indicated in the presented code, you can read back the eeprom content in the setup() routine and based on the remembered state of the reed contacts set turnouts again and apply power to whichever track you want.

If the number of write cycles to an eeprom (roughly 100000) is a worry, you can use an I2C FRAM module and store the data in there. E.g. Adafruit I2C Non-Volatile FRAM Breakout - 256Kbit / 32KByte : ID 1895 : $9.95 : Adafruit Industries, Unique & fun DIY electronics and kits.

Before I try and attack how to save anything in an EPRON, here is your modified code for 2 tracks. Can you look it over and comment? (The capacitors to throw the turnouts are in the circuit on the switch side of the relays.)

// this constant won't change:
const int track1 = 2; //reed switch on track 1
const int track2 = 3; // reed switch on track 2
const int to1straight = 4; // incoming turnout straight
const int to1curved = 5; // incoming turnout curve
const int to2straight = 6; // outgoing turnout straight
const int to2curved = 7; // outgoing turnout curve
const int power1 = 8; // Power to track 1
const int power2 = 9; // Power to track 2
#define RELAY_ON 1
#define RELAY_OFF 0

// Variables will change:
int track1state = 0;
int track2state = 0;
int lastTrack1state = 0;
int lastTrack2state = 0;

void setup() {
  pinMode(track1, INPUT_PULLUP);
  pinMode(track2, INPUT_PULLUP);
  pinMode(to1straight, OUTPUT);
  pinMode(to1curved, OUTPUT);
  pinMode(to2straight, OUTPUT);
  pinMode(to2curved, OUTPUT);
  pinMode(power1, OUTPUT);
  pinMode(power2, OUTPUT);

  // if you have track states stored in EEPROM
  // read them back here; for you to figure out ;)

}

void loop()
{
  // if activated
  if (digitalRead(track2) == LOW)
  {
    // change the trackstate from occupied to non-occupied or from non-occupied to occupied
    lastTrack2state ^= 1;

    if (lastTrack2state == 1 && digitalRead(power2) == HIGH)
      {
      // keep track powered for a short while
      delay(100);
      // power to track 2 off
      digitalWrite(power2, RELAY_OFF);
      delay(50);
      // switch turnout to straight
      digitalWrite(to1straight, RELAY_ON);
      delay(1000);
      digitalWrite(to1straight, RELAY_OFF);
      // power to track 1 on
      digitalWrite(power1, RELAY_ON);

      if (digitalRead(track2) == LOW)
  {
    // change the trackstate from occupied to non-occupied or from non-occupied to occupied
    lastTrack1state ^= 1;

    if (lastTrack1state == 1 && digitalRead(power1) == HIGH)
      {
      // keep track powered for a short while
      delay(100);
      // power to track 1 off
      digitalWrite(power1, RELAY_OFF);
      delay(50);
      // switch turnout to straight
      digitalWrite(to2straight, RELAY_ON);
      delay(1000);
      digitalWrite(to2straight, RELAY_OFF);
      // power to track 1 on
      digitalWrite(power2, RELAY_ON);
    }

    // you can store the lastTrack2state in EEPROM
    // for you to figure out ;)
  }
}

It has been a long time since I did model railroading. Back then, if you applied constant power to a track switch you could burn up the coil.

You use a CDU?

Ow yes. Couple of options:
a) Ignore the write cycle of the EEPROM and store it in EEPROM anyway. It has 100.000 cycles so it will last some time but it will always break at the most inconvenient time...

b) Make it turn on in pause state, you have to select it. Not very practical

c) Use more then one byte in the EEPROM to get more write cycles. It's a bit of a hack.

d) Make it write to EEPROM on power loss. To do this, you need to be able to detect loss of power and have a bit o power left to save everything. If you disconnect the power led and let the arduino only drive logic lever (so no power) things a simple diode + capacitor will do.

e) Make the layout smarter. For example, use a second set of reed contacts just affter the turnout to detect a train to that track so the Arduino knows where the train is. That way you can turn on the power to the rest of the track and just go on from there. Of you can use a IR barrier or a IR reflection sensor to detect the whole train. That way you know which track is occupied even when you turn on the power.

What's a CDU.

Is there anyway to modify the post #15 above to get away from using EEPROM? The big problem I see is when restarting making sure the correct track has power.

I know I could always wait till the trains have stopped (via a delay) and always restart the same way, i.e. track 1 powered.

septillion:
You use a CDU?

? Well I have never seen LGB but looking on the web its seems many turnouts for LGB are 16V AC motors rather than solenoid operated due to their size.

Think Bernie needs to detail the devices more clearly so we can see exactly what they are and whats needed to control them.

Like the relay board question, everyones seems to assume it has opto inputs - but no details of the board given at all ? It probably is one of those common opto boards, but we need to know for sure otherwise we are guiding you in the wrong direction.