Go Down

Topic: Model Railway Level Crossing (Read 3090 times) previous topic - next topic

aussiefantom

Feb 23, 2013, 10:53 am Last Edit: Feb 23, 2013, 10:55 am by aussiefantom Reason: 1
Hi Everyone.

I'm new to the world of programming and have bought myself an Arduino UNO. So far I have done pretty good on my own (with a little help from a friend) but I am having trouble.

What I want to do is build a fully functional Level Crossing for my model railway layout.

At the moment once the testing button is pressed it runs through once but I want to have the train trip a miniature reed switch hidden under the track on one side or the crossing which will then set off the flashing lights and lower the boom gates (which will be done with hobby servos) and then on the other side of the crossing trip another switch to raise the gate and stop the lights.

I'm totally stumped and would very much appreciate any help or advice you guys can give me on how to add in the extra switch and servos to my sketch/project.

Thanks for your time Guys  :)


Code: [Select]
// set pin numbers:
int buttonPin = 2;     // the number of the pushbutton pin

// variables will change:
int buttonState = 0;         // variable for reading the pushbutton status

void setup() {
 // initialize the LED pin as an output:
 pinMode(12, OUTPUT);  // crossing inactive RED
 pinMode(11, OUTPUT);  // crossing active GREEN
 
 pinMode(10, OUTPUT);  // crossing lights RED
 pinMode(9, OUTPUT);   // crossing lights RED
 
 // initialize the pushbutton pin as an input:
 pinMode(buttonPin, INPUT);    
 }
 
 // the loop routine runs and stops after last command
void loop() {
 
 int buttonState = digitalRead(buttonPin);
 
   // the switch has been activated and the program can run
 if (buttonState == HIGH) {
    tone(8, 550, 750);      // crossing activated alarm
    digitalWrite(11, HIGH); // traffic stopped
    delay(1000);
    digitalWrite(11, LOW);  
    digitalWrite(10, LOW);  // crossing LEDs off
    digitalWrite(9, LOW);    
    delay(150);
    digitalWrite(9, HIGH);  // turn all crossing LEDs on for 2.5 seconds
    digitalWrite(10, HIGH);
    delay(2500);
    digitalWrite(9, HIGH);  // left flash
    digitalWrite(10, LOW);  // right off
    delay(500);
    digitalWrite(10, HIGH); // right flash
    digitalWrite(9, LOW);   // left off
    delay(500);
    digitalWrite(9, HIGH);  // crossing LEDs on
    digitalWrite(10, HIGH);
    delay(2500);
    digitalWrite(10, LOW);  // crossing LEDs off
    digitalWrite(9, LOW);  
    tone(8, 550, 750);      // crossing inactive alarm
    digitalWrite(12, HIGH); // crossing inactive
    delay(1500);
    digitalWrite(12, LOW);  // traffice can cross
 }

 
}

AWOL

What does your sketch do that it should not, and what does it not do that it should?
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

aussiefantom

At the moment the sketch I added to previous post works fine. When I push the test button a green led lights up and a piezo alarm sounds which tells me the program is active then the lights start flashing (I shortened the sketch cause there are so many delays) then they stop flashing and the piezo alarm sounds again and the red led lights up telling me the program has finished.

That part of the program I had no problem with as I modified the blinking LED sketch and added the "if" statement to allow the button to be read.

My problem is adding the second switch and the servo commands in the right places to make the whole thing fully functional.

I'm sorry if I seem hard to understand or aren't giving you enough info

AWOL

The first thing is all those calls to "delay()" will have to go, or you're never going to detect the second switch in a timely manner.
Look at the blink without delay example to get a feel for the techniques you will need to do this.
Break down the light and servo sequence into a sequence of transitions and states.
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

aussiefantom

Quote
Break down the light and servo sequence into a sequence of transitions and states.


Sorry if I sound dumb but what is meant by this? Am I to do the two separately then integrate them into the one?

I've been doing this for 2 weeks and I've learnt alot but there are some things I just can't get my head around.

michinyon

On the arduino,  your setup() function runs once and your loop() function runs continuously, for ever, until you turn of the power or upload a new sketch or reset the arduino.

Suppose you want your level crossing to close for four seconds.
You could close the gates,  ring the bell,  delay for four seconds,  and then open the gates.
Actually,  the above sentence is what YOU SHOULD NOT DO.

What you should do is this:

When the button or reed switch or whatever gets triggered,  you close the gate,  ring the bell,
and remember somehow what the time will be,  four seconds from now ( there are several ways
of doing this ).   Then your loop function runs,   every few milliseconds,   and one of the things
it does each cycle,  is check whether the four seconds is finished yet.   When the four seconds
is finished, it opens the gate and stops ringing the bell.   It can also check other things and do
other things while this is happening.

Look at the "blink without delay" example sketch for the recommended way of doing this,
there are other ways of doing this also.


majenko

What michinyon is describing is called a Finite State Machine.

I wrote a tutorial a while back about them: http://hacking.majenko.co.uk/finite-state-machine

aussiefantom

#7
Feb 23, 2013, 01:51 pm Last Edit: Feb 23, 2013, 11:17 pm by aussiefantom Reason: 1
Thanks for the tips guys.. I've nad a look at the link you posted majenko and it was very good.. I now have to work out how to integrate my coding with the one you created.

The more tips I get the better i'll become.

aussiefantom

I know I'm going to sound like a real dumba** but I don't understand how the blink without delay works. Can someone explain it to me in dumba** terms?

Sorry to be a pain

Shpaget

You check the time and store it in a variable.
Later on you check time again and compare it with the time stored in the variable. If the predetermined period has not yet elapsed, you go about your business, do something else.
You then check again. If the predetermined period elapsed you do what you are waiting to do and store current time in the variable for later usage.

aussiefantom

#10
Mar 15, 2013, 11:14 am Last Edit: Mar 15, 2013, 11:33 am by aussiefantom Reason: 1
Well its been almost 3 weeks since I started this little project and I have almost got it down pat but there is one part I need a little help with.

Code: [Select]
#include <Servo.h>

Servo myservo;
 
const int R1 = 9;  // 1st Red LED
const int R2 = 10;  // 2nd Red LED
const int G = 11;  // Green Active LED
const int R = 12;  // Red inactive LED
const int buttonPin = 2; // pushbutton pin


int buttonState = 0;     // variable for reading the pushbutton status
int running = 0;
int long interval = 500;
long previousMillis = 0;
int R1State = HIGH;
int pos = 0;

void setup() {
 pinMode(R1, OUTPUT);
 pinMode(R2, OUTPUT);
 pinMode(G, OUTPUT);
 pinMode(R, OUTPUT);
 pinMode(buttonPin, INPUT);    
 myservo.attach(7);
 digitalWrite(buttonPin, HIGH);

}
 
 // the loop routine runs and stops after last command
void loop() {
 
 int buttonState = digitalRead(buttonPin);
 
     
   // the switch has been activated and the program can run
 if (buttonState == HIGH && running == 0) {
   int running = 1;
    tone(8, 550, 750); // play SOUND
    digitalWrite(G, HIGH); // turn on GREEN LED
    myservo.write(45);
    delay(1500);
    digitalWrite(G, LOW); // turn off GREEN LED
    digitalWrite(R2, LOW); // turn the LED off
    digitalWrite(R1, LOW); // turn the LED off
    delay(250);
    digitalWrite(R1, HIGH); // turn the LED on
    digitalWrite(R2, HIGH); // turn the LED on
    delay(2500);
          buttonState = digitalRead(buttonPin);
       while(buttonState == LOW && running == 1)  {

// Timer used instead of delay to allow input to be read at the same time
         unsigned long currentMillis = millis();
         
         if(currentMillis - previousMillis > interval) {
           previousMillis = currentMillis;
           
           if (R1State == HIGH){ // Switches lights over
             digitalWrite(R1, LOW);
             digitalWrite(R2, HIGH);
             R1State = LOW;
           }
           else {
             digitalWrite(R1, HIGH); // Switches lights over
             digitalWrite(R2, LOW);
             R1State = HIGH;
           }
         }
         else {
// Terminates program and switches off lights if the trigger input is detected
           buttonState = digitalRead(buttonPin);
           if (buttonState == HIGH){
             digitalWrite(10, LOW); // turn the LED off
             digitalWrite(9, LOW); // turn the LED off
             tone(8, 550, 1000); // play SOUND
             digitalWrite(12, HIGH); // turn on RED LED
             delay(1500);
             myservo.write(0);
             digitalWrite(12, LOW); // turn off RED LED
             int R1State = LOW;
             delay(5000);
             int running = 0;
             break;
           }
         }
       }
 }
}


My problem is that I have tried to incorporate servo code into the sketch to make the boom gates on the level crossing  lower and raise slowly as with real ones but I can only get them to go up to 45' and down to 0' in one hit instead of in increments.

I have tried to insert the servo code where I need it but it won't work how can I fix it?

majenko

You basically need to have a variable in which you store the current angle of the boom.  Then you slowly (by comparing another variable to millis()) increase / decrease that variable until you have the desired final angle.  Every time you increase / decrease the angle, you do the servo.write call.

A little function such as (untested):
Code: [Select]

void moveTo(unsigned char angle)
{
  static unsigned char currentAngle = 0;
  if (currentAngle < angle) {
    currentAngle++;
    myservo.write(currentAngle);
    return;
  }
  if (currentAngle > angle) {
    currentAngle--;
    myservo.write(currentAngle);
    return;
  }
}


will only move the boom by one degree per call, if it needs to move it at all.  In your main loop, you can do something like:
Code: [Select]

static unsigned long boomMoveTime = millis(); // These are either local static variables, or global non-static variables.
static unsigned char desiredBoomAngle = 0;
...
if (millis() - boomMoveTime > 10) {
  boomMoveTime = millis();
  moveTo(desiredBoomAngle);
}
...
if (whatever) {
  desiredBoomAngle = 45;
}


So, every 10 milliseconds you make a call to get the boom closer to its target angle, if it's not already there.  At any time in your main loop, you can then set the variable desiredBoomAngle, and it will instantly start to move the boom slowly up or down, to whatever the desiredBoomAngle variable is set to.

aussiefantom

will that work using a trigger to set then again to reset?

majenko


will that work using a trigger to set then again to reset?

A trigger?  Not sure what you mean.

As long as the moveTo function is being called it will try to move the boom to a specific angle.  How you set what that angle should be (desiredBoomAngle) is entirely up to you.  Where you would normally do myservo.write(45), do desierdBoomAngle=45 instead.  The repeated, timed, call to moveTo() will do the actual moving, if moving is needed.

aussiefantom

Sorry. By trigger I mean as the train passes over the switch ( miniature reed ) it starts the program running then when the train passes over the second switch it resets or terminates the program

Go Up