Back and forth train control

Hello,

I am struggling for some time with my back and forth train control.
The idea is that when a train activates a reed switch, it will stop moving, pauze for 2 seconds and then drives in the opposite direction until another reed switch is activated. Then the sequence will start over again.

The track consists of one main line with one junction (Y shape).

The problem is that sometimes the train overshoots the reed switch.
The switch is activated and an indicator light will burn until another switch is activated.
But the train stops and doesn’t move anymore until I manually push it back above the reed switch and than it works again.
Also when it stays stopped above the reedswitch it works fine.

I’ve been puzzling for quite some time now, but haven’t been able to solve this issue.

Can someone help me with this one please?

reedswitch_train_control_test1.ino (3.27 KB)

Please post your code.

// Back and forth train control.
// When a reedswitch is activated the train will stop, pauze for 2 seconds and than continu in opposite direction
// Also a LED will be switched on to indicate which reedswitch is activated

                                          // 3 seperate reed sensors on pin 2, 3 en 4  en led output op 5, 6, 7.


                                          // set pin numbers:
const int reed[] =  {2, 3, 4};            // the number of the reed pins
const int ledPin1 =  5;                   // the number of ledPin block in
const int ledPin2 =  6;                   // the number of ledPin block out
const int ledPin3 =  7;                   // the number of ledPin block out

                                          // connect motor controller pins to Arduino digital pins
int enA = 10;
int in1 = 9;
int in2 = 8;

                                          // variables will change:
int reedState = 0;                        // variable for reading the reed status


void setup() {

  Serial.begin(9600);                     // print program name to serial monitor to indicate which program is running on the arduino
  Serial.print("reedswitch train control test1");


                                          // set all the motor control pins to outputs
  pinMode(enA, OUTPUT);
  pinMode(in1, OUTPUT);
  pinMode(in2, OUTPUT);

                                          // initialize the LED pin as an output:
  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  pinMode(ledPin3, OUTPUT);


  for (int x = 0; x < 3; x++)
  {
    pinMode(reed[x], INPUT);              // Set all sensors as input
  }
}


void Foreward() {

  OFF();                                  // Stop train when reedswitch is activated
  delay(2000);                            // pauze for 2 seconds before moving in opposite direction
  digitalWrite(in1, HIGH);                // set direction
  digitalWrite(in2, LOW);
  analogWrite(enA, 110);
}


void Reverse() {

  OFF();
  delay(2000);
  digitalWrite(in1, LOW);
  digitalWrite(in2, HIGH);
  analogWrite(enA, 130);
}


void OFF() {

  digitalWrite(in1, LOW);
  digitalWrite(in2, LOW);
}


void loop() {

  for (int x = 0; x < 3; x++)
  {
    reedState = digitalRead(reed[x]);     // Read all reedswitches

                                          // check if the reedswitch 2 is activated (HIGH).
                                          // if it is, set LED high and move train foreward.
    if (reedState == HIGH && reed[x] == 2) {

      digitalWrite(ledPin1, HIGH);
      digitalWrite(ledPin2, LOW);
      digitalWrite(ledPin3, LOW);
      Foreward();
    }
                                          // check if the reedswitch 3 is activated (HIGH).
                                          // if it is, set LED high and move train reverse.
    if (reedState == HIGH && reed[x] == 3) {

      digitalWrite(ledPin1, LOW);
      digitalWrite(ledPin2, HIGH);
      digitalWrite(ledPin3, LOW);
      Reverse();
    }

                                          // check if the reedswitch 4  is activated (HIGH).
                                          // if it is, set LED high and move train reverse.
    if (reedState == HIGH && reed[x] == 4) {

      digitalWrite(ledPin1, LOW);
      digitalWrite(ledPin2, LOW);
      digitalWrite(ledPin3, HIGH);
      Reverse();
    }
  }
}

I would suggest to start over. Yes yes, I know, doesn't sound appealing. But let's see this as a great way to learn, I'm certain it's faster in the long run :wink:

But first, if you want quick help further, make a quick sketch of the layout and of the schematic (hand drawing is wayyyyyyyyyyyy better than a Fritzing breadboard mess!). This not only makes it possible for us to oversee this but is also a VERY useful tool for yourself.

Now that's out of the way, to the problem. Or actually, the solution. What can make this very easy is to make everything in a state change machine. Have a Google at it :slight_smile:

And in that, only a certain reed switch can move it to the next state. And because in the next state it only looks for another reed switch your problem is solved. :slight_smile:

PS, may I suggest to add all comments just left aligned, all the extra space makes me dizzy :wink:

I suggest you get rid of the delays.

Thanks for the fast reaction.
I'll try to make a drawing soon and for now look into the state machines...
It sounds like a good solution. :slight_smile:

I suggest you put some serial prints in your code so you can see what it's doing.

What I would expect your code to do though is that when it triggers the reed switch it will run the off routine and wait two seconds then ask for power to move. If it's still over the reed switch though, I'd expect the Arduino to immediately run off again before the train has a chance to move and it'll be stuck. The opposite of what you observe. Contrary to AWOL's suggestion, I think a delay after you turn the power on may be called for.

Serial will provide enlightenment I hope.

wildbill:
Contrary to AWOL’s suggestion, I think a delay after you turn the power on may be called for.

But that delay will only be in setup().

Serial will provide enlightenment I hope.

Amen.

IMHO it would be a good idea to separate the program into three conceptual chunks - {A} reading the switches,{B} figuring out the logic for actions and {C} implementing the action.

A state machine (look it up) is probably the best way to manage the logic. State machine is just a fancy name for using one or more variables to keep track of a sequence of states, and to allow different actions to move the system from one state to the next.

In your case the states seem to be WaitingAtA, MovingTowardsB, WaitingAtB, MovingTowardsA WaitingAtA, MovingTowardsC, WaitingAtC. These could be encoded with the letters A, b, B, a, A, c, C in a char variable. And if you learn about ENUMs you could use meaningful names.

When a switch is triggered it will cause the state to move from (say) 'b' to 'B' and in the state 'B' the value of millis() can be checked until the time expires, and the state will move on to 'a' etc etc etc.

The train control function will keep checking the state variable and if it finds it in any of upperCase values it will stop the train. In any of the lowerCase values it will set the turnout and set the train moving in the appropriate direction.

...R
Several Things at a Time
Planning and Implementing a Program

It seems to me if the train overshoots a switch it will begin moving, hit the switch again and change direction then hit the switch again and so on, but that's not the behavior you describe. Or maybe when it overshoots it goes past the switch without triggering it?

At any rate, the same switch should never trigger a direction change twice in a row. Below is your code rearranged a bit with protection against re-triggering after an overshoot, maybe good for some ideas on how to proceed.

int lastSwitch = -1;
int switchIdx = 0;

void loop() {

  reedState = digitalRead(reed[switchIdx]);

  // if over switch
  if ((reedState == HIGH) && (switchIdx != lastSwitch) )
  {

    // remember switch
    lastSwitch = switchIdx;

    // turn off leds
    digitalWrite(ledPin1, LOW);
    digitalWrite(ledPin2, LOW);
    digitalWrite(ledPin3, LOW);

    // respond to switch
    switch (switchIdx)
    {
      case 0:
        digitalWrite(ledPin1, HIGH);
        Foreward();

        break;

      case 1:
        digitalWrite(ledPin3, HIGH);
        Reverse();

        break;

      case 2:
        digitalWrite(ledPin2, HIGH);
        Reverse();

        break;

      default:
        break;



    } // switch


    // advance index
    switchIdx = (switchIdx == 2) ? 0 : switchIdx + 1;


  } // if

}

You aren't using mode INPUT_PULLUP with the reed switches - do you have pullup or pulldown resistors?

Hitting the switch doesn't reverse direction. It stops, waits, and moves 'away'. There is one switch that moves the train in the forward direction and two that move the train in the reverse direction. I think that wildbill's explanation in reply #6 is accurate. A few microseconds after the train is given power it notices that it is still over the switch and turns the power off again. The train can't get up to speed in a few microseconds so it just sits there.
One solution would be to keep track of the last chosen direction and not pause if the direction is not reversing:

// Back and forth train control.
// When a reedswitch is activated the train will stop, pauze for 2 seconds and than continu in opposite direction
// Also a LED will be switched on to indicate which reedswitch is activated


// 3 seperate reed sensors on pin 2, 3 en 4  en led output op 5, 6, 7.




// set pin numbers:
const int ledPin1 =  5;                   // the number of ledPin block in
const int ledPin2 =  6;                   // the number of ledPin block out
const int ledPin3 =  7;                   // the number of ledPin block out


// connect motor controller pins to Arduino digital pins
int enA = 10;
int in1 = 9;
int in2 = 8;


// variables will change:
int reedState = 0;                        // variable for reading the reed status
bool MovingForeward = true;




void setup()
{
  Serial.begin(9600);
  // print program name to serial monitor to indicate which program is running on the arduino
  Serial.print("reedswitch train control test1");




  // set all the motor control pins to outputs
  pinMode(enA, OUTPUT);
  pinMode(in1, OUTPUT);
  pinMode(in2, OUTPUT);


  // initialize the LED pin as an output:
  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  pinMode(ledPin3, OUTPUT);


  // Set all sensors as input
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  pinMode(4, INPUT);


  Foreward(); // Get started
  MovingForeward = true;
}




void Foreward()
{
  if (!MovingForeward)
  {
    OFF();                                  // Stop train when reedswitch is activated
    delay(2000);                            // pauze for 2 seconds before moving in opposite direction
    digitalWrite(in1, HIGH);                // set direction
    digitalWrite(in2, LOW);
    analogWrite(enA, 110);
    MovingForeward = true;
  }
}




void Reverse()
{
  if (MovingForeward)
  {
    OFF();
    delay(2000);
    digitalWrite(in1, LOW);
    digitalWrite(in2, HIGH);
    analogWrite(enA, 130);
    MovingForeward = false;
  }
}




void OFF()
{
  digitalWrite(in1, LOW);
  digitalWrite(in2, LOW);
}
void loop()
{
  // check if the reedswitch on pin 2 is activated (HIGH).
  // if it is, set LED high and move train foreward.
  if (digitalRead(2) != LOW)
  {
    digitalWrite(ledPin1, HIGH);
    digitalWrite(ledPin2, LOW);
    digitalWrite(ledPin3, LOW);
    Foreward();
  }


  // check if the reedswitch on pin 3 is activated (HIGH).
  // if it is, set LED high and move train reverse.
  if (digitalRead(3) != LOW)
  {
    digitalWrite(ledPin1, LOW);
    digitalWrite(ledPin2, HIGH);
    digitalWrite(ledPin3, LOW);
    Reverse();
  }


  // check if the reedswitch on pin 4  is activated (HIGH).
  // if it is, set LED high and move train reverse.
  if (digitalRead(4) != LOW)
  {
    digitalWrite(ledPin1, LOW);
    digitalWrite(ledPin2, LOW);
    digitalWrite(ledPin3, HIGH);
    Reverse();
  }
}

Yes, that explanation by wildbill does sound plausible.

I don't get the distinction between "drives in the opposite direction" and "reverse direction". I used the words "change direction" because based on my understanding of the setup the train will move down a track in one direction then stop and move in the other direction.

If the goal is to not turn the power off because the train is still over the same switch that caused it to stop, then ignoring that switch will allow it to get up to speed, no?

I think the Y layout means the train never sees the same switch twice in a row, so if the train sees switch #0 and pauses then moves forward until it sees one of the other switches it will not be stuck by continually reading switch #0 and turning the power off.

I've uploaded johnwasser's code and it works like a charm, exactly what I needed.

Although I will look into the state machines and start experimenting with that as well. Now the track is simple, but when the layout will be finished there will be much more sensors controlling the trains.

Thank you all for your input guys!!!!

maartentukker:
but when the layout will be finished there will be much more sensors controlling the trains.

I have a layout with about 20 sensors and 12 turnouts.

…R

Now indeed after a switch is activated and the train overshoots it doesn't stop for a second time anymore.
Before it pauzed every time it hits the switch.

But i guess the program could be shorter and more clear using state machines.

Now indeed after a switch is activated and the train overshoots it doesn't stop for a second time anymore.
Before it pauzed every time it hits the switch.

But i guess the program could be shorter and more clear using state machines, especially when the track becomes more complicated.

maartentukker:
Now indeed after a switch is activated and the train overshoots it doesn't stop for a second time anymore.
Before it pauzed every time it hits the switch.

You need to post the program that is showing this behaviour - the fine detail is important.

What have you changed between the time it was working and now?

...R

The code shown in the beginning of this topic was used. Now your adjusted code is used.

When the train activates the contact it stops, waits and changes direction. Even when overshooting and it ignores the same switch when passing it for the second time on the way back.

maartentukker:
The code shown in the beginning of this topic was used. Now your adjusted code is used.

That is confusing. It is referring to two different programs. Please post the latest version of your program in your next Reply so we are all working from the same page.

...R