I am wanting to control a Märklin model train using Railuino / Candiy Bus shield / Arduino Mega
the concept:
a museum visitor pushes a button
the sound of a train can be heard starting
the train goes forward ( the track is straight, with end stops, 6 metres long)
when is approaches the end of the track a sensor detects the train and stops it.
shortly thereafter the trains goes in the opposite direction
until it reaches the other end of the track, is detected by a second sensor and is stopped.
end of the loop
until the next visitor comes in
an emergency loop, activated by a button, needs to be built in case the train is somehow stuck in the middle and the whole thing can start from the beginning.
I am having a hard time with the sensors at the end stopping the movement of the train.
in the latest edition of the code I do not even get the train going forward. I tried if / else, while, switch...so far no go
I am not the complete newbie, but I am no pro either...I have to do this kind of stuff once in a while and seem to have to learn all over again.
any help would be super appreciated.
My code is attached
#include <Railuino.h>
const word LOCO = ADDR_MM2 + 78; //motorola MM2 address
const word TIME = 2000;
const boolean DEBUG = true;
// Créer un contrôleur Railuino
TrackController ctrl(0xdf24, DEBUG);
const int buttonPin1 = 12; // main switch
const int buttonPin2 = 3; // switch at end of the forward movement
const int buttonPin3 = 4; // switch at end of the backward movement
int ledPin = 9; //LED
int calibrationTime = 5;
int buttonState1 = LOW;
int buttonState2 = LOW;
int buttonState3 = LOW;
void setup() {
Serial.begin(9600);
// Attendre l'établissement de la connexion
while (!Serial);
// Démarrer le contrôleur
ctrl.begin();
Serial.println("Mise sous tension - Power on");
ctrl.setPower(true);
digitalWrite(12, LOW); // these three lines look to me redundant but when I remove them programm is buggy
digitalWrite(3, LOW);
digitalWrite(4, LOW);
pinMode(10, OUTPUT);
pinMode(ledPin, OUTPUT);
pinMode(buttonPin1, INPUT);
pinMode(buttonPin2, INPUT);
pinMode(buttonPin3, INPUT);
}
void loop() {
buttonState1 = digitalRead(buttonPin1); //main switches turned on
switch (buttonState1) {
case 'HIGH':
delay(1000);
digitalWrite(10, HIGH); // turn the Sound on
delay(1000);
digitalWrite(10, LOW); // deactivate sound
delay(5000);
delay(50);
break;
case 'LOW':
digitalWrite(ledPin, HIGH);
delay(500);
digitalWrite(ledPin, LOW);
delay(500);
}
}
void forward() { //loop for train going forward
buttonState2 = digitalRead(buttonPin2); // switch to detect end of the track going forward
switch (buttonState2) {
case 'HIGH': //train met the switch
digitalWrite(ledPin, LOW); //train should stop but does not
ctrl.setLocoSpeed(LOCO, 0);
Serial.println("stop 1");
delay(50);
pause(); // go to pause()
delay(50);
break; //exit the loop ?
case 'LOW': // if train has not met the switch it goes forward
Serial.println("Marche avant - Direction forward");
digitalWrite(ledPin, HIGH);
ctrl.setLocoDirection(LOCO, DIR_FORWARD);
ctrl.setLocoSpeed(LOCO, 1000); //max speed
delay(1000); // no break to stay in the loop
}
}
void pause() { // short pause
ctrl.setLocoSpeed(LOCO, 0);
delay(500);
backward();
delay(50)
}
void backward() { // loop for train going backward
buttonState3 = digitalRead(buttonPin3); // switch to detect end of the track going backward
switch (buttonState3) {
case 'HIGH':
Serial.println("stop 2"); // if train reaches the end of the track going backward
digitalWrite(ledPin, LOW);
ctrl.setLocoSpeed(LOCO, 0); // train stops
delay(500);
break;
case 'LOW': // train has not met the switch and goes backward
Serial.println("Marche arriere - Direction reverse");
digitalWrite(ledPin, HIGH);
ctrl.setLocoDirection(LOCO, DIR_REVERSE);
ctrl.setLocoSpeed(LOCO, 1000); //max speed
delay(1000); // no break to stay in the loop
}
}
Specifically
case 'LOW':
Well, there's nothing syntactically wrong with 'LOW' and 'HIGH' but they're never going to be the same as 0 or 1, so those comparisons will always fail.
Use LOW and HIGH instead.
Also, using switch/case for a binary comparison is overkill - use if/else instead.
void backward() { // loop for train going backward
buttonState3 = digitalRead(buttonPin3); // switch to detect end of the track going backward
switch (buttonState3) {
case 'HIGH':
...
...
break;
case 'LOW': // train has not met the switch and goes backward
...
...
delay(1000); // no break to stay in the loop
}
}
Hi Awol
Yes you are right
It is also what I meant.
But in my case somehow did not do what it should. In this case go forward until the sensor / switch stops it and the train goes forward.
a museum visitor pushes a button
the sound of a train can be heard starting
the train goes forward ( the track is straight, with end stops, 6 metres long)
when is approaches the end of the track a sensor detects the train and stops it.
shortly thereafter the trains goes in the opposite direction
until it reaches the other end of the track, is detected by a second sensor and is stopped.
A classic "state machine". Your states are:
IDLE
STARTING
GOING_FORWARD
PAUSING
GOING_BACK
if the emergency reset button is pushed (goers from HIGH to LOW) in ANY state then you stop the train, put it in rverse, and move to GOING_BACK state.
if the emergency reset button is not pushed, then:
in IDLE state, if the button is pushed then you start sounding the soundtrack and go to STARTING state.
In STARTING state, if it has been X seconds since the start of the starting state then turn the horn off, turn forward on, and enter GOING_FORWARD state
In GOING_FORWARD state, if you hit the bumper then turn off the train and enter PAUSING state
In PAUSING state, if the pause has completed then pt the engine in reverse and enter GOING_BACK state
In GOING_BACK state, if you hit the other bumper then turn the engine off and enter IDLE state.
thanks that is exactly what I was looking for!!
I will read, learn, test and report.
The pattern is:
// define a type for our states
enum State {
IDLE,
STARTING,
GOING_FORWARD,
PAUSING,
GOING_BACK
};
// create a variable holding the current state
State state = IDLE;
// the loop just does a time slice without delay
void loop() {
// read the button.
if( /* the button has just been pressed */ ) {
// do the emergency stop stuff;
}
else {
switch(state) {
case IDLE:
// do whtever we need to do for IDLE
if(/* something has happened that we need to respond to (button press) */ ) {
/* do some stuff, which may involve entering another state*/
sound hormn;
start horn tmer;
state = STARTING;
}
break;
case STARTING:
// do whtever we need to do for STARTING
if(/* something has happened that we need to respond to - horn timeout*/ ) {
/* do some stuff, which may involve entering another state */
stop horn;
start motor;
state = GOING_FORWARD;
}
break;
case GOING_FORWARD:
// do whtever we need to do for GOING_FORWARD
break;
// etc, etc.
}
}
}
Note that we don't have delays or while() statments in the code. the "starting" case block detects when the start time has timed out, does whatever needs to be done to enter 'going forward' state, and then exits. The next execution of loop() will do the going_forward block.
This means that your loop() can do other things as well. For instance, if you have flashy lights, there might be a bit of code to manage the flashing. The state machine doesn't hang up and delay - it just breifly determines if it needs to do anything in respose to events that might have happened, and then continues on.