Model Railway Layout with Mega 2560R2

Is this achievable for a Newbie with hardly any Programming experience?
My requirements are to operate a Model Railway Layout using this configuration;

Arduino Mega 2560R2
Freetronics Relay 8 Shield
Freetronics 16X2 LCD Shield
Sparkfun MP3 Player Shield

The Layout requires;

Mega 2560R2 directly driving Servo Motors to operate Track Points
Mega 2560R2 operating variable Lighting effects
Relay 8 Shield driving Relays to control Track Polarity and Switching
MP3 Player Shield to play Sound Bites triggered by Loco movements on the Layout
The 16X2 LCD Shield is to inform the Operator what Events are occurring at what time

The operation of the Layout is sequential and then repeated ad infinitum.

My intention is to present this Scenario;

The Layout is in a Box viewed from the side and has a Proscenium through which it is viewed.
There is a Bush Scene on the Left and a Timber Mill on the Right. the Layout is 2 Metres long, (6 feet).
The Layout is dark, at night, Night Birds are calling, maybe a snoring Worker is heard.
Lighting as if the Sun is rising, Dawn Chorus of Bellbirds, Tui, Magpies etc.
Full Lighting, Steam Loco's heard in the distance.
Steam Loco with empty Load runs from Right to Left with cross-faded Sounds from Right to Left Speaker.
Bush Scene has Sounds of Chopping, Chainsaws, startled Birds etc. Through Left Speaker
Steam Loco with full Load runs from Left to Right with cross-faded Sounds from Left to Right Speaker.
Timber Mill Sounds of Pit Saws, Band Saws and other associated Machinery through Right Speaker.

This will repeat for about 5 - 8 Minutes and then the 'Sun' will go down and will revert to the Night Sounds of the NZ Bush, Kiwi, Morepork etc.

Night will be about 2 -3 Minutes duration, and then the whole Scenario is repeated for about 8 Hours.

Thanks for reading through so far, is this Scenario workable without too many hassles with Code and Pin conflicts?

.

Welcome to the world of Arduino and model trains.

In principle the idea you have in mind is quite achievable. However I'm not familiar with the shields you have mentioned and so I don't know if there may be conflicts between them. Also, make sure that any shield you buy works with a Mega.

One thing you haven't mentioned is whether you intend to have some form of train position detection - perhaps to identify when sounds should be played or points or signals changed.

My suspicion (based on zero experience) is that programming for the LCD screen may be the most difficult and it might make sense to start by controlling the project from a PC. Indeed if the intention is to have unattended operation there may not be much need for the LCD screen if you could just plug in a PC for changing settings or diagnostics. It think if it was my project I would leave a cheap PC or netbook connected all the time and use it to play the sounds - but that's a personal thing (laziness?).

It would not be difficult to dispense with the relay shield and build a small piece of veroboard with transistors driven by Arduino pins to control relays. Probably much cheaper as well.

...R

I don't know if there may be conflicts between them

dispense with the relay shield and build a small piece of veroboard with transistors

There may be issues with the various shields so this is a good way to go. Using shift registers you can drive as many relays as you like (100s in theory) with just 3 pins.

then the 'Sun' will go down

This will need PWM control over the lights, presumably you'll use LEDs.


Rob

It's feasible. Lack of coding experience will make it quite a challenge - I'd suggest that you don't try to do it all at once.

As noted above, you may find that your shields have conflicts because of what pins they use. You don't need a shield to use an LCD -they're easy enough to wire up directly. Since you have serial ports to spare on the Mega, you can save pins for other things by using an LCD that is controlled over serial instead.

Several people have used one of my Mega Screw shields to allow solid connections to things not plugged directly into the Mega (or to resolve pin conflicts) for model railroading.
http://www.crossroadsfencing.com/BobuinoRev17/

You haven't stated this, but I presume you want to control the speed and direction of your loco from the Arduino. If you use a motor shield for this you won't need relays to switch direction/track polarity, it can all be done in the programming. I suggest you start with just your Mega and a motor shield to get the loco running on a straight piece of track first and then add the servo controls for the points, leaving the lighting and sounds to last.

Gentlemen, thank you for your swift and informative replies.

I've built Layouts like this before, but have always used good old fashioned discreet Componentry, and as a result, I'm still stuck in that mental paradigm. This was intended as a challenge to control it using Arduino and stimulate a few Brain Cells in the process.

If you can imagine the Layout as an old fashioned Font capital D with two verticals, (Yard Tracks), and extended Finials.
The semi-circular part of the 'D' is the only Track visible from the front.

The Events will be triggered by a pair of Photo Interrupters at each end and 1 each on the Yard Tracks.

Hmmm, maybe this is better;

--A------x---------------------
I I
x--------I I
I I I
I I I
B C I
I I I
I I I
x--------I I
I I
--D-----x----------------------

Empty Loco at C
Full Loco at B
Points are located at 'x'
A, B, C, & D are Opto Interrupters
Empty Loco travels to D then out to the Front of the Layout and finishing at A
Empty Loco then returns to C from A

Full Loco at travels to A then out to the Front of the Layout and finishing at D
Full Loco then returns to B from D

At the moment, a friend has written Code, (which I vaguely understand), using an 'Interrupt', (Pin 12), which controls one Pair of Servos in a Flip-Flop sequence, i.e. each time either 'A' or 'B' Optos are occluded the outer 2 Points change state.
My main issue at the moment is trying to get the other Interrupt which is on Pin 13 to change the Inner Points.

I've re-evaluated the LCD and realised that, when the Layout is running it is superfluous, as the Operator can see what is happening. In the interim, I've configured the Serial println command to feed the state of Points and Polarity back to the Operator via a Laptop - thanks Robin.

Any help on getting the two 'Interrupts' working together would be appreciated. I then need to get the Relay8 Shield stitched into the Code. The Relay8 Shield works via I2C and only uses two Pins, The Code on this is not a problem, just getting it all to work together.

.

Should not be using interrupt for a model train control system.

Someone is confused about the purpose of interrupts.

Paul__B:
Should not be using interrupt for a model train control system.

Someone is confused about the purpose of interrupts.

Hi Paul, he was saying something about allowing the Arduino to do other things until an Interrupt occurred. I've only just started playing with the Arduino, so I'm sitting here looking up at a steep learning curve 8-(

.

I wouldn't get bogged down with interrupts. They are quite good at detecting something within a millisecond, but that hardly applies to a model train trundling around a track.

My advice would be to get each piece working on its own (even if a "piece" is detecting where the train is). I always break my work into small, manageable chunks, which my poor brain can comprehend. Then I put it all together.

Sounds like fun, though. You should enjoy doing it.

Thanks for your comments Nick,
I'm not trying to detect the 'Trundling' but specific Loco positions as per the Opto-Interruptor positions.
Referring to my ASCII Drawing, I've broken it down to this;

IF (Opto) A OR D LOW

THEN
RLY1 = OFF (Track Power OFF)
Outside Points Change State
RLY2 Change State (Track Polarity)
Delay 1000
RLY1 = ON (Track Power ON)

IF (Opto) B OR C LOW

THEN
Delay 3000 (Allow Train to pass Rx)
RLY1 = OFF (Track Power OFF)
Inside Points Change State
RLY3 Change State (Track X,Z, Change)
RLY2 Change State (Track Polarity)
Delay 1000
RLY1 = ON (Track Power ON)

Any advice is always taken. I'm trying to get a few ideas on Coding without bludging on anyone to write it for me.

.

The code in an Arduino loop() runs through very quickly (assuming there are no delay() commands) so even without interrupts it will be able to check the value of the photo-interrupters hundreds or thousands of times per second which is plenty quick enough to deal with train movements.

Your pseudo code looks to be on the right track (sorry). Bear in mind that in actual code an if statement has to be like this pseudo code

if (OptoA == LOW OR OptoB == LOW)

It may be useful to read elsewhere about State Machines here http://www.gammon.com.au/forum/?id=12316. From you example I suspect you already have the concept in mind.

I don't know if you already have photo interrupters. I have found light dependent resistors (LDRs) set within the sleepers work well - and are simple and cheap.

If you are familiar with discrete electronics you probably will get very little value from shields.

...R

Your pseudo code looks to be on the right track (sorry).

Of course, you don't want to go off the rails. Or jump the points. :wink:

Perhaps if you post your code rather than pseudo code, we could give better advice.

You should have noted by now that a lot of the people on here are pretty fond of model trains.

With signals.

And points.

And lights.

...

There are two situations or empty loco is running or full loco.
You dont need a lot of fancy stuff here.
if the points are controlled with a servo just set them in correct position and switch them off.
old coilpoints are also possible just use a transistor to switch them(use on delay(100) off.
now for the loco use a relais dpdt for direction control
and two diodes (in both B and C place ) to stop the reverse.
when loco is back detection is possible with a microswitch.
that is all.
you can add PWM very fast just put a FET in the line going back to transformer

For this simple type i would use a old camswitch with motor.

But yes you can make anything complicated as you like.
my train runs in blocksystem on a UNO.

Hmmmm, Looks like the Train has been moved on. I seemd to have lost my last Posting along with it!

A quick rehash;
I have two sets of Servos driving the inside pair and the outside pair of Points. 
I have a Relay8 Shield running on I2C, (I've dropped the LCD and am feeding back data to a Laptop using Serial.println)
All events are triggered by Infra Red Photo Interrupters, (as per the Letters in the ASCII Drawing).

I can get either the Outside Points OR the Inside Points OR the Relay8 Shield working but cannot seem to integrate each Block of Working Code into one Sketch.

As requested, here is the Code, some of which is mine, (That I Understand), and the rest is written by a Friend, (whose Code I don't understand).

[code]

/* Controller for On30 Logging Layout
Tested on Layout with EAST & WEST Points set up correctly 07-12-2013 - OK
Flip Flop WEST END & EAST END Servos each time either WEST END or EAST END IR Rx are occluded - OK
Flip Flop YARD Servos each time either 'X' Track or 'Z' Track IR Rx are occluded - NOT OK
Operate RELAY8 Shield for Track Polarity and Track Voltage - OK for "All Relays OFF" at end of void setup Section then Fails
Most Code Compiles OK but all Operation then Fails when Commented Code is un-Commented
*/

#include <Servo.h> // Include the Servo Library Code
Servo WestServoA; // create servo object to control WestServoA. Controls WEST END 'A' POINTS SERVO
Servo EastServoD; // create servo object to control EastServoD. Controls EAST END 'D' POINTS SERVO
Servo YardServoB; // create servo object to control YardServoB. Controls FIDDLE YARD 'B' POINTS SERVO
Servo YardServoC; // create servo object to control YardServoC. Controls FIDDLE YARD 'C' POINTS SERVO

#include "Wire.h"
#define I2C_ADDR 0x20 // 0x20 is the address of the RELAY8 Shield with all Jumpers removed
#define R1 1 //Relay OFF = Track Power OFF, Relay ON = Track Power ON
#define R2 2 //Relay OFF = Track Polarity EAST, Relay ON = Track Polarity WEST
#define R3 4 //Relay OFF = X Track ON, Relay ON = Z Track ON
#define R4 8 // Unused - Yet
#define R5 16 // Unused - Yet
#define R6 32 // Unused - Yet
#define R7 64 // Unused - Yet
#define R8 128 // Unused - Yet

#define Rx2 2 // WEST END & EAST END IR Rx Pair connected to digital pin 2
#define Rx3 3 // FIDDLE YARD IR Rx Pair connected to digital pin 3.
#define left 1
#define right 2
#define yard 1
#define main 2
#define Xtrack 1
#define Ztrack 2

#define WSA_yard 108
#define WSA_main 98
#define ESD_yard 80
#define ESD_main 92

#define YSB_Xtrack 91 //NEW
#define YSB_Ztrack 89 //NEW
#define YSC_Xtrack 89 //NEW
#define YSC_Ztrack 91 //NEW

int val = 0; // val stores state of input pin
boolean state = false;
int end_points = yard;
int yard_points = Xtrack;

void setup() {
Serial.begin(9600);
{
Serial.println(" ** ON30 Logging Layout Points and Polarity Status ** ");
}
Wire.begin(); //Wake up I2C Bus

// Set addressing Style for RELAY8 Shield
Wire.beginTransmission(I2C_ADDR);
Wire.write(0x12);
Wire.write(0x20); // Use Table 1.4 addressing
Wire.endTransmission();

// Set I/O bank A to Outputsfor RELAY8 Shield
Wire.beginTransmission(I2C_ADDR);
Wire.write(0x00); // IODIRA register
Wire.write(0x00); // Set all of bank A to Outputs
Wire.endTransmission();

sendValueToLatch(00); // All RELAYs OFF
Serial.println("Track Power Off");

WestServoA.attach(10); // attaches WestServoA to digital pin 10
EastServoD.attach(11); // attaches EastServoD to digital pin 11
YardServoB.attach(12); // attaches YardServoB to digital pin 12
YardServoC.attach(13); // attaches YardServoC to digital pin 13

pinMode(Rx2, INPUT); // initialize the digital pin as an input:
digitalWrite (Rx2, HIGH); // Sets Rx2 HIGH, Negates the need for external Pull-Up Resistor
pinMode(Rx3, INPUT); // initialize the digital pin as an input:
digitalWrite (Rx3, HIGH); // Sets Rx3 HIGH, Negates the need for external Pull-Up Resistor
attachInterrupt(0, sensorRX2, FALLING); // reads sensor on port pin 2. function sensorRX2 is the interrupt handler
attachInterrupt(0, sensorRX3, FALLING); // reads sensor on port pin 3. function sensorRX3 is the interrupt handler
WestServoA.write(WSA_yard); // tell WEST END SERVO to point to YARD
EastServoD.write(ESD_yard); // tell EAST END SERVO to point to YARD
YardServoB.write(YSB_Xtrack); // tell WEST END SERVO to point to X Track
YardServoC.write(YSC_Xtrack); // tell EAST END SERVO to point to X Track

end_points = yard;
yard_points = Xtrack;

Serial.println("Track Polarity West");
Serial.println("End Points to Yard");
Serial.println("Yard Points to 'X' Track");
delay(5000);
sendValueToLatch(R1); // Track Power ON - Starts Layout Operation
Serial.println("Track Power On");

}

void loop()
{
noInterrupts();
if ( state == true)
{
ChangePoints();
state = false;
}
interrupts();
}
void sensorRX2()
{
state = true;
}
void sensorRX3()
{
state = true;
}
void ChangePoints()
{
if (end_points == yard)
{
// sendValueToLatch(00); // All Relays OFF
WestServoA.write(WSA_main); // tell WEST END 'A' SERVO to point to MAIN
EastServoD.write(ESD_main); // tell EAST END 'D' SERVO to point to MAIN
delay(1000);
// sendValueToLatch(00); // All Relays OFF
// sendValueToLatch(R2); // RELAY 2 ON = Polarity chage to WEST
// sendValueToLatch(R1); // RELAY 1 ON = Track Power ON
end_points = right;
Serial.println("End Points to Main");

}
else
{
// sendValueToLatch(00); // All Relays OFF
WestServoA.write(WSA_yard); // tell WEST END 'A' SERVO to point to YARD
EastServoD.write(ESD_yard); // tell EAST END 'D' SERVO to point to YARD
delay(1000);
// RELAY 2 Already OFF = Polarity change to EAST
// sendValueToLatch(R1); // RELAY 1 ON = Track Power ON

end_points = left;
Serial.println("End Points to Yard");

/* // NEW CODE BLOCK
if (yard_points == Xtrack)
{
YardServoB.write(YSB_Ztrack); // tell Yard 'B' SERVO to point to Z Track
YardServoC.write(YSC_Ztrack); // tell Yard 'C' SERVO to point to Z Track

yard_points = right;
Serial.println("Yard Points to 'Z' Track");

}
else
{
YardServoB.write(YSB_Xtrack); // tell Yard 'B' SERVO to point to X Track
YardServoC.write(YSC_Xtrack); // tell Yard 'C' SERVO to point to X Track

yard_points = left;

Serial.println("Yard Points to 'X' Track");

//END OF NEW CODE BLOCK
*/ }
}

void sendValueToLatch(int latchValue)
{
Wire.beginTransmission(I2C_ADDR);
Wire.write(0x12); // Select GPIOA
Wire.write(latchValue); // Send value to bank A
Wire.endTransmission();
}

[/code]

Please use code tags.

Read this before posting a programming question

Looks like the Train has been moved on.

Let's hope the lady doesn't vanish!

I have written that sort of code myself but I'm afraid I'm too lazy to struggle through that much of someone else's code.

My approach to debugging would be to put in lots of print commands that show when certain pieces of code are used and the values of critical variables at decision points.

You seemed to have a fairly clear concept in your earlier post. Why not start by implementing that in real code?

If there is a specific piece of code you don't understand I will be happy to help.

There can only be one skipper on a boat and one "skipper" for a piece of software. Two people contributing code without a common understanding is a recipe for long nights, lots of coffee and grey hair.

...R

Robin2:
My approach to debugging would be to put in lots of print commands that show when certain pieces of code are used and the values of critical variables at decision points.

There can only be one skipper on a boat and one "skipper" for a piece of software. Two people contributing code without a common understanding is a recipe for long nights, lots of coffee and grey hair.

Thanks Robin, I've started doing the Serial.println thing and found it helpful so far.

Regarding your second comment; The long nights, copious Coffee and grey Hairs are happening already, and there is only me trying to sort the Code out! (Well, I must admit that most of the Hairs were grey anyhow).

.