Trains.com

Subscriber & Member Login

Login, or register today to interact in our online community, comment on articles, receive our newsletter, manage your account online and more!

Arduino uses and wondering why there aren't more articles here

19283 views
82 replies
1 rating 2 rating 3 rating 4 rating 5 rating
  • Member since
    July 2009
  • From: lavale, md
  • 4,642 posts
Posted by gregc on Sunday, December 25, 2016 3:58 PM

I spent some time looking over this code.  I was interested in seeing how hobbyists might write code.  I compiled it on my laptop and exercised it in a simulation that allowed me to set inputs and see outputs.

It appears that there are two tracks at a crossing and three sub-blocks for each track: east, west and mid, that control flashers and gates.  There's also a block occupied for each track.

Logic suggests that flashers would become active when a train enters the east or west blocks, the gates would close when the mid block is occupied and both the flashers would stop and the gates raised when the mid block became unoccupied.

But it appears that the code, depending on train direction would only leave the flashers on while the train occupies either the east or west block.  When the train enters the mid block, the flashers shut off and the gate lowered.  The gates are raised when the train exits the mid block.  Perhaps this is OK because each track is dedicated to one direction.

 

I think its a common problem that novices view software simply as a means to control hardware, instead of having separate funtions for the control logic and and functions to actually control the hardware.  This leads to complicated code that does too much and is hard to debug.  Sub-functions focus on one piece of the problem.  Not using sub-functions also results in redundant code because it's easy to copy and paste.

 

I believe the following code is smaller and more generic.  Not sure Aruino supports printf().


// ----------------------------------------------------------------------------- void check ( int block, int west, int mid, int east, int & westSeq, int & eastSeq, int & sig ) { printf ("%s: %d west %d, mid %d, east %d\n", __func__, block, west, mid, east); if (mid == LOW) // gates down { printf ("%s: %d mid\n", __func__, block); gates_down_1 = HIGH; flashers_on_1 = HIGH; }

else if (west == HIGH && mid == HIGH && east == HIGH) { if (_dbg) printf ("%s: %d clear\n", __func__, block); westSeq = HIGH; eastSeq = HIGH; sig = LOW; } else if (west == LOW && eastSeq == HIGH) { westSeq = LOW; flashers_on_1 = HIGH; sig = HIGH; } else if (east == LOW && westSeq == HIGH) { eastSeq = LOW; flashers_on_1 = HIGH; sig = HIGH; } }

loop (void)
{
...

flashers_on_1 = LOW; gates_down_1 = LOW; check (1, west_block_1, mid_block_1, east_block_1, westbound_sequence_1, eastbound_sequence_1,
signal_block_1); check (2, west_block_2, mid_block_2, east_block_2, westbound_sequence_2, eastbound_sequence_2,
signal_block_2); digitalWrite (flashers_on_pin, flashers_on_1); digitalWrite (gates_down_pin, gates_down_1); digitalWrite (signal_block_1_pin, signal_block_1); digitalWrite (signal_block_2_pin, signal_block_2);
}
 // ---------------------------------------------------------
void loop ()
{

    //  turn the  "heartbeat" LED on
    digitalWrite (13, HIGH);

    // read the input values into the variables
    west_block_1 = digitalRead (west_block_1_pin);
    mid_block_1  = digitalRead (mid_block_1_pin);
    east_block_1 = digitalRead (east_block_1_pin);

    west_block_2 = digitalRead (west_block_2_pin);
    mid_block_2  = digitalRead (mid_block_2_pin);
    east_block_2 = digitalRead (east_block_2_pin);


    // sets deafult values for outputs
    flashers_on_1   = LOW;
    gates_down_1    = LOW;
    signal_block_1  = LOW;
    signal_block_2  = LOW;

    // check each crossing
    crossing (west_block_1, mid_block_1,
            westbound_sequence_1, gates_down_1,
            signal_block_1);

    crossing (mid_block_1, east_block_1,
            eastbound_sequence_1, flashers_on_1,
            signal_block_1);


    crossing (west_block_2, mid_block_2,
            westbound_sequence_2, gates_down_1,
            signal_block_2);

    crossing (mid_block_2, east_block_2,
            eastbound_sequence_2, flashers_on_1,
            signal_block_2);


    // set gate/flasher and signals outputs
    digitalWrite (flashers_on_pin, flashers_on_1);
    digitalWrite (gates_down_pin,  gates_down_1);

    digitalWrite (signal_block_1_pin, signal_block_1);
    digitalWrite (signal_block_2_pin, signal_block_2);


    //  turn the  "heartbeat" LED off
    digitalWrite (13, LOW);
}

---------------------------------

adendum (12/30/16)

the above code did what I thought NYC's code was doing, which was using gates and flashers for each crossing and using 3 blocks.   The flashers would be triggered when the train occupied the outer blocks and the gates come down when the middle block is occupied.

In a private email, Nyc explained that there were two separate crossings: one controlled by a gate and the other by flashers.

 

The code below handles each crossing generically.   Crossing() is passed the state (occupancy) of the two adjacent blocks and controls an output which can be either a gate or flasher and a signal indicator.   A state variable is also maintained to track the direction of the train so that the gate/flasher is disabled after leaving the block it entered first but is still occupying the block on the opposite side.

setup() from the original listing is still needed.  The following code replaces loop().    It demonstrates several coding concepts:

  • the use of sub functions to avoid writing redundant code.  Once a function is written and tested, it can be used multiple times elsewhere in the same code or reused in other Arduino applications.
  • passing parameters to the function, both inputs and outputs.  It show the use of a reference parameter allowing the function to change its value outside the function.
  • the use of a single multi-valued (more than 2) state variable instead of separate variables, which simplifies logic.

 

// -----------------------------------------------
// generic crossing function
//    inputs  are the occupancy of adjacent blocks
//            and a state variable tracking the
//            direction of the train
//    outputs are the variable controlling the
//            gate/flasher and signal
//
//    the outputs are only set when needed to be
//    active.  External code resets them at start
//    of each loop().  This allows multiple
//    crossings (e.g. 2 tracks) to control the
//    same gate/flasher (i.e. logic OR).
//
//    the state variable allows the crossing to
//    be active when the train enters the adjacent
//    block but is cleared when it exits that
//    block but is still occupying the other
//    block.

#define SEQ_CLR     0
#define SEQ_EAST    1
#define SEQ_WEST    2

void crossing (
    int     west,
    int     east,
    int   & seq,
    int   & ctl,
    int   & sig )
{
    if (west == HIGH && east == HIGH)
    {
        seq = SEQ_CLR;
    }

    else if (seq != SEQ_EAST && east == LOW)
    {
        seq = SEQ_WEST;
        ctl = HIGH;
        sig = HIGH;
    }

    else if (seq != SEQ_WEST && west == LOW)
    {
        seq = SEQ_EAST;
        ctl = HIGH;
        sig = HIGH;
    }

    else if (west == LOW || east == LOW)
    {
        sig = HIGH;
    }

}

// --------------------------------------------------------- void loop () { // turn the "heartbeat" LED on digitalWrite (13, HIGH); // read the input values into the variables west_block_1 = digitalRead (west_block_1_pin); mid_block_1 = digitalRead (mid_block_1_pin); east_block_1 = digitalRead (east_block_1_pin); west_block_2 = digitalRead (west_block_2_pin); mid_block_2 = digitalRead (mid_block_2_pin); east_block_2 = digitalRead (east_block_2_pin); // sets deafult values for outputs flashers_on_1 = LOW; gates_down_1 = LOW; signal_block_1 = LOW; signal_block_2 = LOW; // check each crossing crossing (west_block_1, mid_block_1, westbound_sequence_1, gates_down_1, signal_block_1); crossing (mid_block_1, east_block_1, eastbound_sequence_1, flashers_on_1, signal_block_1); crossing (west_block_2, mid_block_2, westbound_sequence_2, gates_down_1, signal_block_2); crossing (mid_block_2, east_block_2, eastbound_sequence_2, flashers_on_1, signal_block_2); // set gate/flasher and signals outputs digitalWrite (flashers_on_pin, flashers_on_1); digitalWrite (gates_down_pin, gates_down_1); digitalWrite (signal_block_1_pin, signal_block_1); digitalWrite (signal_block_2_pin, signal_block_2); // turn the "heartbeat" LED off digitalWrite (13, LOW); }

how can I be sure about this code without running it on an Arduino or the layout?  ... by exercising it within a simulation using the GNU C++ compiler in cygwin on a windows laptiop.

the simulation code implements the digitalRead() and digitalWrite() functions and invokes setup() and loop().

The simulation code reads a file with commands setting the state of an input pin.   digitalRead() returns the value set by the commands from the file.  The value in digitalWrite() is captured to determine the states of the outputs.

The output is shown below.  The '_' indicates that the input is not occupied or that the output is not active.  An occupied block is indicated by '^', an active gate by 'G', flasher by 'F' and a signal by its number 1/2.

^ _ _ _ _ _ G _ 1 _
| | | | | | | | | |
| | | | | | | | | -- signal 2
| | | | | | | | ---- signal 1
| | | | | | | ------ flasher
| | | | | | -------- gate
| | | | |
| | | | | ---------- east block track 2
| | | | ------------ mid  block track 2
| | | -------------- west block track 2
| | |
| | ---------------- east block track 1
| ------------------ mid  block track 1
-------------------- west block track 1

 

---- eastbound block 1

 pinSet: 0 west_block_1_pin
 pinsDump                   ^ _ _ _ _ _ G _ 1 _
 pinsDump                   ^ _ _ _ _ _ G _ 1 _
 pinSet: 0 mid_block_1_pin
 pinsDump                   ^ ^ _ _ _ _ G F 1 _
 pinsDump                   ^ ^ _ _ _ _ G F 1 _
 pinSet: 0 east_block_1_pin
 pinsDump                   ^ ^ ^ _ _ _ G F 1 _
 pinsDump                   ^ ^ ^ _ _ _ G F 1 _
 pinSet: 1 west_block_1_pin
 pinsDump                   _ ^ ^ _ _ _ _ F 1 _
 pinsDump                   _ ^ ^ _ _ _ _ F 1 _
 pinSet: 1 mid_block_1_pin
 pinsDump                   _ _ ^ _ _ _ _ _ 1 _
 pinsDump                   _ _ ^ _ _ _ _ _ 1 _
 pinSet: 1 east_block_1_pin
 pinsDump                   _ _ _ _ _ _ _ _ _ _
 pinSet: 0 east_block_1_pin
 pinsDump                   _ _ ^ _ _ _ _ F 1 _
 pinsDump                   _ _ ^ _ _ _ _ F 1 _
 pinSet: 0 mid_block_1_pin
 pinsDump                   _ ^ ^ _ _ _ G F 1 _
 pinsDump                   _ ^ ^ _ _ _ G F 1 _
 pinSet: 0 west_block_1_pin
 pinsDump                   ^ ^ ^ _ _ _ G F 1 _
 pinsDump                   ^ ^ ^ _ _ _ G F 1 _
 pinSet: 1 east_block_1_pin
 pinsDump                   ^ ^ _ _ _ _ G _ 1 _
 pinsDump                   ^ ^ _ _ _ _ G _ 1 _
 pinSet: 1 mid_block_1_pin
 pinsDump                   ^ _ _ _ _ _ _ _ 1 _
 pinsDump                   ^ _ _ _ _ _ _ _ 1 _
 pinSet: 1 west_block_1_pin
 pinsDump                   _ _ _ _ _ _ _ _ _ _


greg - Philadelphia & Reading / Reading

  • Member since
    January 2010
  • 168 posts
Posted by nycmodel on Sunday, December 25, 2016 7:58 AM

I hesitated to post the actual code because of the line wrap when I pasted it into the forum page. In my Arduino editing environment there is no line wrap and the code is much more readable. Obvious variable names, etc., become important down the road if I need to modify or simply get back up to speed with the application. I have learned the hard way to label things well and reduntantly. There are also a lot of serial.print lines for debugging. No, this is not everyone's "cup of tea". I happen to enjoy it but I understand that others may not.

  • Member since
    February 2002
  • From: Reading, PA
  • 30,002 posts
Posted by rrinker on Saturday, December 24, 2016 7:19 PM

Test posting the code unformatted to it is more readable

/*
 crossing_logic_1
12/24/16
  
                gates_down_1                          flashers_on_1
                |----                                  X
 -----------------| |-----------------------------------| |------------------       signal_block_1 entire length
    west_block_1               mid_block_1                    east_block_1
    
 -----------------| |-----------------------------------| |------------------       signal_block_2 entire length
    west_block_2  ----|        mid_block_2                 X  east_block_2
    
               gates_down_2                           flashers_on_2
               
  All 6 inputs are from Chubb Optimized detectors where HIGH = unoccupied and LOW = occupied
  All 4 outputs will turn on HIGH when the proper inputs are LOW, driving relays via transistors connected to the 12v supply. These relays
  will then activate crossing gates, flashers and block signals.
  
 */
 
// Define pins
const int west_block_1_pin = 2;
const int mid_block_1_pin = 3;
const int east_block_1_pin = 4;
const int west_block_2_pin = 5;
const int mid_block_2_pin = 6;
const int east_block_2_pin = 7;
const int gates_down_pin = 8;
const int flashers_on_pin = 9;
const int signal_block_1_pin = 10;
const int signal_block_2_pin = 11;

// Define and initialize all variables as HIGH (off) except outputs that drive relays through transister switches where HIGH is on.
int west_block_1 = HIGH;
int mid_block_1 = HIGH;
int east_block_1 = HIGH;
int west_block_2 = HIGH;
int mid_block_2 = HIGH;
int east_block_2 = HIGH;
int gates_down_1 = LOW;    //relay active HIGH to show gates down for block 1
int flashers_on_1 = LOW;   //relay active HIGH to show flashers on for block 1
int gates_down_2 = LOW;    //relay active HIGH to show gates down for block 2
int flashers_on_2 = LOW;   //relay active HIGH to show flashers for block 2
int signal_block_1 = LOW;  //relay active HIGH to show block 1 occupied
int signal_block_2 = LOW;  //relay active HIGH to show block 2 occupied
int eastbound_sequence_1 = HIGH; //active HIGH show eastbound train moving through block 1
int westbound_sequence_1 = HIGH; //active HIGH show westbound train moving through block 1
int eastbound_sequence_2 = HIGH; //active HIGH show eastbound train moving through block 2
int westbound_sequence_2 = HIGH; //active HIGH show westbound train moving through block 2
void setup() {
  //start serial connection for debugging
  Serial.begin(9600);
  // initialize digital pin 13 as an output for onboard LED to use as a "heartbeat" for the sketch
  pinMode(13, OUTPUT);
  //configure pins 2 to 7 as an input and enable the internal pull-up resistor
  pinMode(west_block_1_pin, INPUT_PULLUP);   // Unlike pinMode(INPUT), there is no pull-down resistor necessary. An internal
  pinMode(mid_block_1_pin, INPUT_PULLUP);   // 20K-ohm resistor is pulled to 5V. This configuration causes the input to
  pinMode(east_block_1_pin, INPUT_PULLUP);  // read HIGH when the switch is open, and LOW when it is closed.
  pinMode(west_block_2_pin, INPUT_PULLUP);
  pinMode(mid_block_2_pin, INPUT_PULLUP);
  pinMode(east_block_2_pin, INPUT_PULLUP);  
  //configure pins 8 to 11 as outputs. HIGH when not actuated and LOW when actuated ;i.e. action activated
  pinMode(gates_down_pin, OUTPUT);
  pinMode(flashers_on_pin, OUTPUT);
  pinMode(signal_block_1_pin, OUTPUT);
  pinMode(signal_block_2_pin, OUTPUT);
  // Initialize all outputs as off
  digitalWrite(gates_down_pin, LOW); //start with gates up
  digitalWrite(flashers_on_pin, LOW); //start with flashers off
  digitalWrite(signal_block_1_pin, LOW); //start with block 1 unoccupied
  digitalWrite(signal_block_2_pin, LOW); //start with block 2 unoccupied
  
}

void loop() {
  digitalWrite(13, HIGH);   // turn the  "heartbeat" LED on 
  //read the input values into the variables
  west_block_1 = digitalRead(west_block_1_pin);
  mid_block_1 = digitalRead(mid_block_1_pin);
  east_block_1 = digitalRead(east_block_1_pin);
  west_block_2 = digitalRead(west_block_2_pin);
  mid_block_2 = digitalRead(mid_block_2_pin);
  east_block_2 = digitalRead(east_block_2_pin);
  Serial.print("gates_down_1: ");
  Serial.println(gates_down_1);
  Serial.print("flashers on_1: ");
  Serial.println(flashers_on_1);
  Serial.print("gates_down_2: ");
  Serial.println(gates_down_2);
  Serial.print("flashers on_2: ");
  Serial.println(flashers_on_2);
  Serial.print("west_block_1: ");
  Serial.println(west_block_1);
  Serial.print("mid_block_1: ");
  Serial.println(mid_block_1);
  Serial.print("east_block_1: ");
  Serial.println(east_block_1);
  Serial.print("west_block_2: ");
  Serial.println(west_block_2);
  Serial.print("mid_block_2: ");
  Serial.println(mid_block_2);
  Serial.print("east_block_2: ");
  Serial.println(east_block_2);
  Serial.print("eastbound_sequence_1: ");
  Serial.println(eastbound_sequence_1);
  Serial.print("westbound_sequence_1: ");
  Serial.println(westbound_sequence_1);
  Serial.print("eastbound_sequence_2: ");
  Serial.println(eastbound_sequence_2);
  Serial.print("westbound_sequence_2: ");
  Serial.println(westbound_sequence_2);
    
  
  if (west_block_1 == HIGH && mid_block_1 == HIGH && east_block_1 == HIGH  && west_block_2 == HIGH && mid_block_2 == HIGH && east_block_2 == HIGH) {  //blocks 1 and 2 completely clear
  /* ---------------------------------------------- INITIALIZATION  LOGIC ------------------------------------------------------ */ 
    digitalWrite(gates_down_pin, LOW);  //raise gates
    digitalWrite(flashers_on_pin, LOW);  //turn off flashers
    gates_down_1 = LOW;  //show gates up
    flashers_on_1 = LOW; //show flashers off
    gates_down_2 = LOW;  //show gates up
    flashers_on_2 = LOW; //show flashers off
    eastbound_sequence_1 = HIGH;           //initialize block 1 eastbound sequence
    westbound_sequence_1 = HIGH;           //initialize block 1 westbound sequence
    eastbound_sequence_2 = HIGH;           //initialize block 2 eastbound sequence
    westbound_sequence_2 = HIGH;           //initialize block 2 westbound sequence
    digitalWrite(signal_block_1_pin, LOW); //show block 1 clear
    digitalWrite(signal_block_2_pin, LOW); //show block 2 clear
    Serial.println("Initializing block 1");  
    Serial.println("Initializing block 2"); } 
  else if (west_block_1 == HIGH && mid_block_1 == HIGH && east_block_1 == HIGH){  //block 1 completely clear
    eastbound_sequence_1 = HIGH;           //initialize block 1 eastbound sequence
    westbound_sequence_1 = HIGH;           //initialize block 1 westbound sequence
    digitalWrite(signal_block_1_pin, LOW); //show block 1 clear
    Serial.println("Initializing block 1");  }
  else if (west_block_2 == HIGH && mid_block_2 == HIGH && east_block_2 == HIGH){  //block 2 completely clear
    eastbound_sequence_2 = HIGH;           //initialize block 2 eastbound sequence
    westbound_sequence_2 = HIGH;           //initialize block 2 westbound sequence
    digitalWrite(signal_block_2_pin, LOW); //show block 1 clear
    Serial.println("Initializing block 2");  } 
    
  /* ----------------------------------------------------------------------------------------------------------------------------------- */  
  /* If the blocks are not completely clear then proceed with the gate and flasher logic for each block */
  /* --------------------------------------------------------- BLOCK 1 ------------------------------------------------------------------- */
  /* ------------------------------------------------------ CROSSING GATE ------------------------------------------------------------- */
  /* ----------------------------------------------------- EASTBOUND LOGIC ------------------------------------------------------------ */
   if (west_block_1 == LOW && mid_block_1 == HIGH && gates_down_1 == LOW && eastbound_sequence_1 == HIGH && westbound_sequence_1 == HIGH)  {   //initial Eastbound approach gates
    digitalWrite(gates_down_pin, HIGH);  //drop gates
    gates_down_1 = HIGH;                  //show gates down
    eastbound_sequence_1 = LOW;       //set start of eastbound sequence 
    Serial.print("Drop gates initial eastbound"); }
  else if (west_block_1 == HIGH && mid_block_1 == LOW && gates_down_1 == HIGH && eastbound_sequence_1 == LOW && westbound_sequence_1 == HIGH)  { //clearing crossing eastbound gates
    gates_down_1 = LOW;}
  /* --------------------------------------------------- WESTBOUND LOGIC ------------------------------------------------------------ */
  else if (mid_block_1 == LOW && west_block_1 == HIGH && gates_down_1 == LOW && eastbound_sequence_1 == HIGH) {   //&& westbound_sequence_1 == HIGH && eastbound_sequence_1 == HIGH) {   //initial Westbound approach gates
    digitalWrite(gates_down_pin, HIGH);  //drop gates
    gates_down_1 = HIGH;                  //show gates down
    westbound_sequence_1 = LOW;       //set start of westbound sequence 
    Serial.print("Drop gates initial westbound"); }
  else if (west_block_1 == LOW && mid_block_1 == HIGH && gates_down_1 == HIGH && westbound_sequence_1 == LOW) { //clearing crossing westbound gates
    gates_down_1 = LOW;}
    
  /* ------------------------------------------------------__ FLASHER ------------------------------------------------------------- */  
  /* ----------------------------------------------------- EASTBOUND LOGIC ------------------------------------------------------------ */
  else if (mid_block_1 == LOW && east_block_1 == HIGH && flashers_on_1 == LOW && eastbound_sequence_1 == LOW && westbound_sequence_1 == HIGH) {  //initial Eastbound approach flashers
    digitalWrite(flashers_on_pin, HIGH);  //turn on flashers
    flashers_on_1 = HIGH;                  //show flashers on
    eastbound_sequence_1 = LOW;        //set start of eastbound sequence
    Serial.print("Flashers on initial eastbound"); }
  else if (east_block_1 == LOW && mid_block_1 == HIGH && flashers_on_1 == HIGH && eastbound_sequence_1 == LOW) { //clearing crossing eastbound flashers
    flashers_on_1 = LOW;}
  /* ----------------------------------------------------- WESTBOUND LOGIC ------------------------------------------------------------ */
  else if (east_block_1 == LOW && mid_block_1 == HIGH && flashers_on_1 == LOW && westbound_sequence_1 == HIGH && eastbound_sequence_1 == HIGH) {   //initial Westbound approach flashers
    digitalWrite(flashers_on_pin, HIGH);  //turn on flashers
    flashers_on_1 = HIGH;                  //show flashers on
    westbound_sequence_1 = LOW;        //set start of westbound sequence
    Serial.print("Flashers on initial westbound"); }
  else if (east_block_1 == HIGH && mid_block_1 == LOW && flashers_on_1 == HIGH && westbound_sequence_1 == LOW) { //clearing crossing westbound flashers
    flashers_on_1 = LOW;}
    
  /* --------------------------------------------------------- BLOCK 2 ------------------------------------------------------------------- */
  /* ------------------------------------------------------ CROSSING GATE ------------------------------------------------------------- */
  /* ----------------------------------------------------- EASTBOUND LOGIC ------------------------------------------------------------ */
  if (west_block_2 == LOW && mid_block_2 == HIGH && gates_down_2 == LOW && eastbound_sequence_2 == HIGH && westbound_sequence_2 == HIGH)  {   //initial Eastbound approach gates
    digitalWrite(gates_down_pin, HIGH);  //drop gates
    gates_down_2 = HIGH;                  //show gates down
    eastbound_sequence_2 = LOW;       //set start of eastbound sequence 
    Serial.print("Drop gates initial eastbound"); }
  else if (west_block_2 == HIGH && mid_block_2 == LOW && gates_down_2 == HIGH && eastbound_sequence_2 == LOW && westbound_sequence_2 == HIGH) { //clearing crossing eastbound gates
    gates_down_2 = LOW;}
  /* --------------------------------------------------- WESTBOUND LOGIC ------------------------------------------------------------ */
  else if (mid_block_2 == LOW && west_block_2 == HIGH && gates_down_2 == LOW && eastbound_sequence_2 == HIGH) {   //&& westbound_sequence_1 == HIGH && eastbound_sequence_1 == HIGH) {   //initial Westbound approach gates
    digitalWrite(gates_down_pin, HIGH);  //drop gates
    gates_down_2 = HIGH;                  //show gates down
    westbound_sequence_2 = LOW;       //set start of westbound sequence 
    Serial.print("Drop gates initial westbound"); }
  else if (west_block_2 == LOW && mid_block_2 == HIGH && gates_down_2 == HIGH && westbound_sequence_2 == LOW) { //clearing crossing westbound gates
    gates_down_2 = LOW;}
  /* ------------------------------------------------------__ FLASHER ------------------------------------------------------------- */  
  /* ----------------------------------------------------- EASTBOUND LOGIC ------------------------------------------------------------ */
  else if (mid_block_2 == LOW && east_block_2 == HIGH && flashers_on_2 == LOW && eastbound_sequence_2 == LOW && westbound_sequence_2 == HIGH) {  //initial Eastbound approach flashers
    digitalWrite(flashers_on_pin, HIGH);  //turn on flashers
    flashers_on_2 = HIGH;                  //show flashers on
    eastbound_sequence_1 = LOW;        //set start of eastbound sequence
    Serial.print("Flashers on initial eastbound"); }
  else if (east_block_2 == LOW && mid_block_2 == HIGH && flashers_on_2 == HIGH && eastbound_sequence_2 == LOW) { //clearing crossing eastbound flashers
    flashers_on_2 = LOW;}
  /* ----------------------------------------------------- WESTBOUND LOGIC ------------------------------------------------------------ */
  else if (east_block_2 == LOW && mid_block_2 == HIGH && flashers_on_2 == LOW && westbound_sequence_2 == HIGH && eastbound_sequence_2 == HIGH) {   //initial Westbound approach flashers
    digitalWrite(flashers_on_pin, HIGH);  //turn on flashers
    flashers_on_2 = HIGH;                  //show flashers on
    westbound_sequence_2 = LOW;        //set start of westbound sequence
    Serial.print("Flashers on initial westbound"); }
  else if (east_block_2 == HIGH && mid_block_2 == LOW && flashers_on_2 == HIGH && westbound_sequence_2 == LOW) { //clearing crossing westbound flashers
    flashers_on_2 = LOW;}
  
  /* ---------------  Fail safe train blocking crossing --------------------------------------------------- */
  else if (east_block_1 == LOW && mid_block_1 == LOW) { //Flasher crossing is blocked, track 1
    digitalWrite(flashers_on_pin, HIGH);  //turn on flashers
    flashers_on_1 = HIGH;                  //show flashers on
    eastbound_sequence_1 = LOW;        //set start of eastbound sequence
    Serial.print("Flashers on-fail safe"); }
  else if (east_block_2 == LOW && mid_block_2 == LOW) { //Flasher crossing is blocked, track 2
    digitalWrite(flashers_on_pin, HIGH);  //turn on flashers
    flashers_on_2 = HIGH;                  //show flashers on
    eastbound_sequence_2 = LOW;        //set start of eastbound sequence
    Serial.print("Flashers on-fail safe"); }
  else if (west_block_1 == LOW && mid_block_1 == LOW) { //gate crossing is blocked, track 1 
    digitalWrite(gates_down_pin, HIGH);  //drop gates
    gates_down_1 = HIGH;                  //show gates down
    eastbound_sequence_1 = LOW;       //set start of eastbound sequence 
    Serial.print("Drop gates-fail safe"); }
  else if (west_block_2 == LOW && mid_block_2 == LOW) { //gate crossing is blocked, track 2 
    digitalWrite(gates_down_pin, HIGH);  //drop gates
    gates_down_2 = HIGH;                  //show gates down
    eastbound_sequence_2 = LOW;       //set start of eastbound sequence 
    Serial.print("Drop gates-fail safe"); }
  
  /* ------------------- Raise gate logic --------------------------------------------------------------- */
  /* Since there are two tracks we don't want to raise the gates for one track if the other is still down */
  if (gates_down_1 == LOW && gates_down_2 == LOW){   // both tracks are clear of the gate crossing  
    digitalWrite(gates_down_pin, LOW);  //raise gates
    Serial.println("Raise gates"); } 
    
  /* ------------------- Turn off flasher logic ____________________________________________ */ 
  /* Since there are two tracks we don't want to turn off the flashers for one track if the other is still on */
  if (flashers_on_1 == LOW && flashers_on_2 == LOW){   // both tracks are clear of the flasher crossing  
    digitalWrite(flashers_on_pin, LOW);  //turn off flashers
    Serial.println("Turn off flashers"); } 
  
      
  /* ----------------- BLOCK SIGNAL LOGIC ------------------------------------------ */
  Serial.println("");
  if (east_block_1 == LOW || mid_block_1 == LOW || west_block_1 == LOW){ // If any of the 3 blocks for track 1 are occupied set block 1 signals to red
     digitalWrite(signal_block_1_pin, HIGH); //show block 1 occupied}
     Serial.println("Block 1 occupied");} 
  else if (east_block_1 && HIGH && mid_block_1 == HIGH && west_block_1 == HIGH){ //If all 3 blocks for track 1 are clear set block 1 signals to green
    digitalWrite(signal_block_1_pin, LOW); //show block 1 clear
    Serial.println("Block 1 clear");} 
  
  if (east_block_2 == LOW || mid_block_2 == LOW || west_block_2 == LOW){ // If any of the 3 blocks for track 2 are occupied set block 2 signals to red
     digitalWrite(signal_block_2_pin, HIGH);
     Serial.println("Block 2 occupied");} //show block 2 occupied
  else if (east_block_2 && HIGH && mid_block_2 == HIGH && west_block_2 == HIGH){ //If all 3 blocks for track 2 are clear set block 2 signals to green
    digitalWrite(signal_block_2_pin, LOW); //show block 2 clear
    Serial.println("Block 2 clear");} 
  /* ------------------------------------------------------------------------------ */
   // digitalWrite(13, HIGH);   // turn the  "heartbeat" LED on 
   // delay(500);               // wait for a half second
    digitalWrite(13, LOW);    // turn the  "heartbeat" LED off
   //delay(500);               // wait for a half second
    Serial.println("");      //carriage return  
    Serial.println("");      //carriage return 
    Serial.println("");      //carriage return 
  }

Well, that's a little better. Upside of long, obvious variable names - they are obvious. Downside - lots of line wrap when trying to show the code.

                           --Randy

 


Modeling the Reading Railroad in the 1950's

 

Visit my web site at www.readingeastpenn.com for construction updates, DCC Info, and more.

  • Member since
    February 2002
  • From: Reading, PA
  • 30,002 posts
Posted by rrinker on Saturday, December 24, 2016 7:15 PM

 That may be a long wait. Or a huge mess liek you currently see in JMRi for function mapping for Loksound decoders. You can't possibly handle every potential combination of cases with dropdowns. The lack of formatting makes that code look like a complete mess - actually 99% of that is all VERY simple If-Then-Else logic - exactly the way you process the design in your head: If block1 is occupied Then set signal 1 to red Else set signal 1 to green

Say there is an off the shelf board (actually, there arleady are, have been for YEARS - the original CMRI presentation was in MR back in 1984, and the boards were available built up and tested as well as kits) with 16 ports. Either the system artificially limits you to having block detectors connected only to ports 1-8, which allows the software side to limit the dropdown choices to a reasonable number, or you actually utilize the board's capabilities and a graphical configuration becomes every bit as complex as what is effectively 'raw' code, if not more so. You add one more word to If, Then, Else: Unless, and now you've handled just about any case for signal and interlocking logic - this is already implemented in JMRI, you do not need to write a program to use the signal logic, you set up the If-Then-Else cases and off the shelf block sensors feed off the shelf signal controllers. 

 There's another way it's being put these days for the expanding use of home automation: If This, Then That. But really, it's the same thing.

 Bruce Chubb showed 30 years ago how non-computer people could implement a full signal and detection system, and many peopel did deploy his system. Arduino makes it even more approachable - remember Arduino was developed for artists, not computer people - which is why there are probably as many electronics/computer people who hate Arduino as there are who love it. 

                               --Randy

 


Modeling the Reading Railroad in the 1950's

 

Visit my web site at www.readingeastpenn.com for construction updates, DCC Info, and more.

  • Member since
    April 2003
  • 305,205 posts
Posted by Anonymous on Saturday, December 24, 2016 5:41 PM

nycmodel

Finally got my Arduino code worked out on the bench and wired it up to the layout for my crossing gate and flasher logic.

Since I had many circuit boards left over from my original Bruce Chubb CRMI installation on a previous layout, I chose to utilize those cab relays where possible by modifying the boards. The perfboard on the upper right allows the Arduino outputs to drive the relays via the transistors. I added additional Optimized Detectors to cover the 6 inputs.


/*
 crossing_logic_1
12/24/16
 
                gates_down_1                          flashers_on_1
                |----                                  X
 -----------------| |-----------------------------------| |------------------       signal_block_1 entire length
    west_block_1               mid_block_1                    east_block_1
   
 -----------------| |-----------------------------------| |------------------       signal_block_2 entire length
    west_block_2  ----|        mid_block_2                 X  east_block_2
   
               gates_down_2                           flashers_on_2
              
  All 6 inputs are from Chubb Optimized detectors where HIGH = unoccupied and LOW = occupied
  All 4 outputs will turn on HIGH when the proper inputs are LOW, driving relays via transistors connected to the 12v supply. These relays
  will then activate crossing gates, flashers and block signals.
 
 */
 
// Define pins
const int west_block_1_pin = 2;
const int mid_block_1_pin = 3;
const int east_block_1_pin = 4;
const int west_block_2_pin = 5;
const int mid_block_2_pin = 6;
const int east_block_2_pin = 7;
const int gates_down_pin = 8;
const int flashers_on_pin = 9;
const int signal_block_1_pin = 10;
const int signal_block_2_pin = 11;

// Define and initialize all variables as HIGH (off) except outputs that drive relays through transister switches where HIGH is on.
int west_block_1 = HIGH;
int mid_block_1 = HIGH;
int east_block_1 = HIGH;
int west_block_2 = HIGH;
int mid_block_2 = HIGH;
int east_block_2 = HIGH;
int gates_down_1 = LOW;    //relay active HIGH to show gates down for block 1
int flashers_on_1 = LOW;   //relay active HIGH to show flashers on for block 1
int gates_down_2 = LOW;    //relay active HIGH to show gates down for block 2
int flashers_on_2 = LOW;   //relay active HIGH to show flashers for block 2
int signal_block_1 = LOW;  //relay active HIGH to show block 1 occupied
int signal_block_2 = LOW;  //relay active HIGH to show block 2 occupied
int eastbound_sequence_1 = HIGH; //active HIGH show eastbound train moving through block 1
int westbound_sequence_1 = HIGH; //active HIGH show westbound train moving through block 1
int eastbound_sequence_2 = HIGH; //active HIGH show eastbound train moving through block 2
int westbound_sequence_2 = HIGH; //active HIGH show westbound train moving through block 2
void setup() {
  //start serial connection for debugging
  Serial.begin(9600);
  // initialize digital pin 13 as an output for onboard LED to use as a "heartbeat" for the sketch
  pinMode(13, OUTPUT);
  //configure pins 2 to 7 as an input and enable the internal pull-up resistor
  pinMode(west_block_1_pin, INPUT_PULLUP);   // Unlike pinMode(INPUT), there is no pull-down resistor necessary. An internal
  pinMode(mid_block_1_pin, INPUT_PULLUP);   // 20K-ohm resistor is pulled to 5V. This configuration causes the input to
  pinMode(east_block_1_pin, INPUT_PULLUP);  // read HIGH when the switch is open, and LOW when it is closed.
  pinMode(west_block_2_pin, INPUT_PULLUP);
  pinMode(mid_block_2_pin, INPUT_PULLUP);
  pinMode(east_block_2_pin, INPUT_PULLUP); 
  //configure pins 8 to 11 as outputs. HIGH when not actuated and LOW when actuated ;i.e. action activated
  pinMode(gates_down_pin, OUTPUT);
  pinMode(flashers_on_pin, OUTPUT);
  pinMode(signal_block_1_pin, OUTPUT);
  pinMode(signal_block_2_pin, OUTPUT);
  // Initialize all outputs as off
  digitalWrite(gates_down_pin, LOW); //start with gates up
  digitalWrite(flashers_on_pin, LOW); //start with flashers off
  digitalWrite(signal_block_1_pin, LOW); //start with block 1 unoccupied
  digitalWrite(signal_block_2_pin, LOW); //start with block 2 unoccupied
 
}

void loop() {
  digitalWrite(13, HIGH);   // turn the  "heartbeat" LED on
  //read the input values into the variables
  west_block_1 = digitalRead(west_block_1_pin);
  mid_block_1 = digitalRead(mid_block_1_pin);
  east_block_1 = digitalRead(east_block_1_pin);
  west_block_2 = digitalRead(west_block_2_pin);
  mid_block_2 = digitalRead(mid_block_2_pin);
  east_block_2 = digitalRead(east_block_2_pin);
  Serial.print("gates_down_1: ");
  Serial.println(gates_down_1);
  Serial.print("flashers on_1: ");
  Serial.println(flashers_on_1);
  Serial.print("gates_down_2: ");
  Serial.println(gates_down_2);
  Serial.print("flashers on_2: ");
  Serial.println(flashers_on_2);
  Serial.print("west_block_1: ");
  Serial.println(west_block_1);
  Serial.print("mid_block_1: ");
  Serial.println(mid_block_1);
  Serial.print("east_block_1: ");
  Serial.println(east_block_1);
  Serial.print("west_block_2: ");
  Serial.println(west_block_2);
  Serial.print("mid_block_2: ");
  Serial.println(mid_block_2);
  Serial.print("east_block_2: ");
  Serial.println(east_block_2);
  Serial.print("eastbound_sequence_1: ");
  Serial.println(eastbound_sequence_1);
  Serial.print("westbound_sequence_1: ");
  Serial.println(westbound_sequence_1);
  Serial.print("eastbound_sequence_2: ");
  Serial.println(eastbound_sequence_2);
  Serial.print("westbound_sequence_2: ");
  Serial.println(westbound_sequence_2);
   
 
  if (west_block_1 == HIGH && mid_block_1 == HIGH && east_block_1 == HIGH  && west_block_2 == HIGH && mid_block_2 == HIGH && east_block_2 == HIGH) {  //blocks 1 and 2 completely clear
  /* ---------------------------------------------- INITIALIZATION  LOGIC ------------------------------------------------------ */
    digitalWrite(gates_down_pin, LOW);  //raise gates
    digitalWrite(flashers_on_pin, LOW);  //turn off flashers
    gates_down_1 = LOW;  //show gates up
    flashers_on_1 = LOW; //show flashers off
    gates_down_2 = LOW;  //show gates up
    flashers_on_2 = LOW; //show flashers off
    eastbound_sequence_1 = HIGH;           //initialize block 1 eastbound sequence
    westbound_sequence_1 = HIGH;           //initialize block 1 westbound sequence
    eastbound_sequence_2 = HIGH;           //initialize block 2 eastbound sequence
    westbound_sequence_2 = HIGH;           //initialize block 2 westbound sequence
    digitalWrite(signal_block_1_pin, LOW); //show block 1 clear
    digitalWrite(signal_block_2_pin, LOW); //show block 2 clear
    Serial.println("Initializing block 1"); 
    Serial.println("Initializing block 2"); }
  else if (west_block_1 == HIGH && mid_block_1 == HIGH && east_block_1 == HIGH){  //block 1 completely clear
    eastbound_sequence_1 = HIGH;           //initialize block 1 eastbound sequence
    westbound_sequence_1 = HIGH;           //initialize block 1 westbound sequence
    digitalWrite(signal_block_1_pin, LOW); //show block 1 clear
    Serial.println("Initializing block 1");  }
  else if (west_block_2 == HIGH && mid_block_2 == HIGH && east_block_2 == HIGH){  //block 2 completely clear
    eastbound_sequence_2 = HIGH;           //initialize block 2 eastbound sequence
    westbound_sequence_2 = HIGH;           //initialize block 2 westbound sequence
    digitalWrite(signal_block_2_pin, LOW); //show block 1 clear
    Serial.println("Initializing block 2");  }
   
  /* ----------------------------------------------------------------------------------------------------------------------------------- */ 
  /* If the blocks are not completely clear then proceed with the gate and flasher logic for each block */
  /* --------------------------------------------------------- BLOCK 1 ------------------------------------------------------------------- */
  /* ------------------------------------------------------ CROSSING GATE ------------------------------------------------------------- */
  /* ----------------------------------------------------- EASTBOUND LOGIC ------------------------------------------------------------ */
   if (west_block_1 == LOW && mid_block_1 == HIGH && gates_down_1 == LOW && eastbound_sequence_1 == HIGH && westbound_sequence_1 == HIGH)  {   //initial Eastbound approach gates
    digitalWrite(gates_down_pin, HIGH);  //drop gates
    gates_down_1 = HIGH;                  //show gates down
    eastbound_sequence_1 = LOW;       //set start of eastbound sequence
    Serial.print("Drop gates initial eastbound"); }
  else if (west_block_1 == HIGH && mid_block_1 == LOW && gates_down_1 == HIGH && eastbound_sequence_1 == LOW && westbound_sequence_1 == HIGH)  { //clearing crossing eastbound gates
    gates_down_1 = LOW;}
  /* --------------------------------------------------- WESTBOUND LOGIC ------------------------------------------------------------ */
  else if (mid_block_1 == LOW && west_block_1 == HIGH && gates_down_1 == LOW && eastbound_sequence_1 == HIGH) {   //&& westbound_sequence_1 == HIGH && eastbound_sequence_1 == HIGH) {   //initial Westbound approach gates
    digitalWrite(gates_down_pin, HIGH);  //drop gates
    gates_down_1 = HIGH;                  //show gates down
    westbound_sequence_1 = LOW;       //set start of westbound sequence
    Serial.print("Drop gates initial westbound"); }
  else if (west_block_1 == LOW && mid_block_1 == HIGH && gates_down_1 == HIGH && westbound_sequence_1 == LOW) { //clearing crossing westbound gates
    gates_down_1 = LOW;}
   
  /* ------------------------------------------------------__ FLASHER ------------------------------------------------------------- */ 
  /* ----------------------------------------------------- EASTBOUND LOGIC ------------------------------------------------------------ */
  else if (mid_block_1 == LOW && east_block_1 == HIGH && flashers_on_1 == LOW && eastbound_sequence_1 == LOW && westbound_sequence_1 == HIGH) {  //initial Eastbound approach flashers
    digitalWrite(flashers_on_pin, HIGH);  //turn on flashers
    flashers_on_1 = HIGH;                  //show flashers on
    eastbound_sequence_1 = LOW;        //set start of eastbound sequence
    Serial.print("Flashers on initial eastbound"); }
  else if (east_block_1 == LOW && mid_block_1 == HIGH && flashers_on_1 == HIGH && eastbound_sequence_1 == LOW) { //clearing crossing eastbound flashers
    flashers_on_1 = LOW;}
  /* ----------------------------------------------------- WESTBOUND LOGIC ------------------------------------------------------------ */
  else if (east_block_1 == LOW && mid_block_1 == HIGH && flashers_on_1 == LOW && westbound_sequence_1 == HIGH && eastbound_sequence_1 == HIGH) {   //initial Westbound approach flashers
    digitalWrite(flashers_on_pin, HIGH);  //turn on flashers
    flashers_on_1 = HIGH;                  //show flashers on
    westbound_sequence_1 = LOW;        //set start of westbound sequence
    Serial.print("Flashers on initial westbound"); }
  else if (east_block_1 == HIGH && mid_block_1 == LOW && flashers_on_1 == HIGH && westbound_sequence_1 == LOW) { //clearing crossing westbound flashers
    flashers_on_1 = LOW;}
   
  /* --------------------------------------------------------- BLOCK 2 ------------------------------------------------------------------- */
  /* ------------------------------------------------------ CROSSING GATE ------------------------------------------------------------- */
  /* ----------------------------------------------------- EASTBOUND LOGIC ------------------------------------------------------------ */
  if (west_block_2 == LOW && mid_block_2 == HIGH && gates_down_2 == LOW && eastbound_sequence_2 == HIGH && westbound_sequence_2 == HIGH)  {   //initial Eastbound approach gates
    digitalWrite(gates_down_pin, HIGH);  //drop gates
    gates_down_2 = HIGH;                  //show gates down
    eastbound_sequence_2 = LOW;       //set start of eastbound sequence
    Serial.print("Drop gates initial eastbound"); }
  else if (west_block_2 == HIGH && mid_block_2 == LOW && gates_down_2 == HIGH && eastbound_sequence_2 == LOW && westbound_sequence_2 == HIGH) { //clearing crossing eastbound gates
    gates_down_2 = LOW;}
  /* --------------------------------------------------- WESTBOUND LOGIC ------------------------------------------------------------ */
  else if (mid_block_2 == LOW && west_block_2 == HIGH && gates_down_2 == LOW && eastbound_sequence_2 == HIGH) {   //&& westbound_sequence_1 == HIGH && eastbound_sequence_1 == HIGH) {   //initial Westbound approach gates
    digitalWrite(gates_down_pin, HIGH);  //drop gates
    gates_down_2 = HIGH;                  //show gates down
    westbound_sequence_2 = LOW;       //set start of westbound sequence
    Serial.print("Drop gates initial westbound"); }
  else if (west_block_2 == LOW && mid_block_2 == HIGH && gates_down_2 == HIGH && westbound_sequence_2 == LOW) { //clearing crossing westbound gates
    gates_down_2 = LOW;}
  /* ------------------------------------------------------__ FLASHER ------------------------------------------------------------- */ 
  /* ----------------------------------------------------- EASTBOUND LOGIC ------------------------------------------------------------ */
  else if (mid_block_2 == LOW && east_block_2 == HIGH && flashers_on_2 == LOW && eastbound_sequence_2 == LOW && westbound_sequence_2 == HIGH) {  //initial Eastbound approach flashers
    digitalWrite(flashers_on_pin, HIGH);  //turn on flashers
    flashers_on_2 = HIGH;                  //show flashers on
    eastbound_sequence_1 = LOW;        //set start of eastbound sequence
    Serial.print("Flashers on initial eastbound"); }
  else if (east_block_2 == LOW && mid_block_2 == HIGH && flashers_on_2 == HIGH && eastbound_sequence_2 == LOW) { //clearing crossing eastbound flashers
    flashers_on_2 = LOW;}
  /* ----------------------------------------------------- WESTBOUND LOGIC ------------------------------------------------------------ */
  else if (east_block_2 == LOW && mid_block_2 == HIGH && flashers_on_2 == LOW && westbound_sequence_2 == HIGH && eastbound_sequence_2 == HIGH) {   //initial Westbound approach flashers
    digitalWrite(flashers_on_pin, HIGH);  //turn on flashers
    flashers_on_2 = HIGH;                  //show flashers on
    westbound_sequence_2 = LOW;        //set start of westbound sequence
    Serial.print("Flashers on initial westbound"); }
  else if (east_block_2 == HIGH && mid_block_2 == LOW && flashers_on_2 == HIGH && westbound_sequence_2 == LOW) { //clearing crossing westbound flashers
    flashers_on_2 = LOW;}
 
  /* ---------------  Fail safe train blocking crossing --------------------------------------------------- */
  else if (east_block_1 == LOW && mid_block_1 == LOW) { //Flasher crossing is blocked, track 1
    digitalWrite(flashers_on_pin, HIGH);  //turn on flashers
    flashers_on_1 = HIGH;                  //show flashers on
    eastbound_sequence_1 = LOW;        //set start of eastbound sequence
    Serial.print("Flashers on-fail safe"); }
  else if (east_block_2 == LOW && mid_block_2 == LOW) { //Flasher crossing is blocked, track 2
    digitalWrite(flashers_on_pin, HIGH);  //turn on flashers
    flashers_on_2 = HIGH;                  //show flashers on
    eastbound_sequence_2 = LOW;        //set start of eastbound sequence
    Serial.print("Flashers on-fail safe"); }
  else if (west_block_1 == LOW && mid_block_1 == LOW) { //gate crossing is blocked, track 1
    digitalWrite(gates_down_pin, HIGH);  //drop gates
    gates_down_1 = HIGH;                  //show gates down
    eastbound_sequence_1 = LOW;       //set start of eastbound sequence
    Serial.print("Drop gates-fail safe"); }
  else if (west_block_2 == LOW && mid_block_2 == LOW) { //gate crossing is blocked, track 2
    digitalWrite(gates_down_pin, HIGH);  //drop gates
    gates_down_2 = HIGH;                  //show gates down
    eastbound_sequence_2 = LOW;       //set start of eastbound sequence
    Serial.print("Drop gates-fail safe"); }
 
  /* ------------------- Raise gate logic --------------------------------------------------------------- */
  /* Since there are two tracks we don't want to raise the gates for one track if the other is still down */
  if (gates_down_1 == LOW && gates_down_2 == LOW){   // both tracks are clear of the gate crossing 
    digitalWrite(gates_down_pin, LOW);  //raise gates
    Serial.println("Raise gates"); }
   
  /* ------------------- Turn off flasher logic ____________________________________________ */
  /* Since there are two tracks we don't want to turn off the flashers for one track if the other is still on */
  if (flashers_on_1 == LOW && flashers_on_2 == LOW){   // both tracks are clear of the flasher crossing 
    digitalWrite(flashers_on_pin, LOW);  //turn off flashers
    Serial.println("Turn off flashers"); }
 
     
  /* ----------------- BLOCK SIGNAL LOGIC ------------------------------------------ */
  Serial.println("");
  if (east_block_1 == LOW || mid_block_1 == LOW || west_block_1 == LOW){ // If any of the 3 blocks for track 1 are occupied set block 1 signals to red
     digitalWrite(signal_block_1_pin, HIGH); //show block 1 occupied}
     Serial.println("Block 1 occupied");}
  else if (east_block_1 && HIGH && mid_block_1 == HIGH && west_block_1 == HIGH){ //If all 3 blocks for track 1 are clear set block 1 signals to green
    digitalWrite(signal_block_1_pin, LOW); //show block 1 clear
    Serial.println("Block 1 clear");}
 
  if (east_block_2 == LOW || mid_block_2 == LOW || west_block_2 == LOW){ // If any of the 3 blocks for track 2 are occupied set block 2 signals to red
     digitalWrite(signal_block_2_pin, HIGH);
     Serial.println("Block 2 occupied");} //show block 2 occupied
  else if (east_block_2 && HIGH && mid_block_2 == HIGH && west_block_2 == HIGH){ //If all 3 blocks for track 2 are clear set block 2 signals to green
    digitalWrite(signal_block_2_pin, LOW); //show block 2 clear
    Serial.println("Block 2 clear");}
  /* ------------------------------------------------------------------------------ */
   // digitalWrite(13, HIGH);   // turn the  "heartbeat" LED on
   // delay(500);               // wait for a half second
    digitalWrite(13, LOW);    // turn the  "heartbeat" LED off
   //delay(500);               // wait for a half second
    Serial.println("");      //carriage return 
    Serial.println("");      //carriage return
    Serial.println("");      //carriage return
  }

 

This is why Im not really interested in Arduino.  I have no desire to write or debug 100s of lines of code,  I will wait patiently for LCC to become fully fleshed out with a GUI with dropdown boxes and radio buttons to select the functions.  The above lines of code are not my version of fun.  If its yours, great!  I on the other hand would prefer to purchase off the shelf components and not write code.

  • Member since
    January 2010
  • 168 posts
Posted by nycmodel on Saturday, December 24, 2016 5:16 PM

I neglected to add how the crossing gates and flashers are activated by the Arduino driven relays. The flashers are a pair of Tomar H-862 crossing signals connected to a Circuitron FL-2 flasher unit. The relay output is connected to pin C on the FL-2. The relay drives the pin to ground activating the flashers. This may have been possible directly from the Arduino but I like to isolate these two circuits.

The crossing gates are a pair of old style manual gates of unknown origin driven by a pair of Tortoise switch motors wired in parallel. The Arduino relay activates the coil on a 12v dpdt relay configured in the classic reversing pattern. The polarity of the output determines if the gates go up or down.

  • Member since
    January 2010
  • 168 posts
Posted by nycmodel on Saturday, December 24, 2016 1:27 PM

Finally got my Arduino code worked out on the bench and wired it up to the layout for my crossing gate and flasher logic.

Since I had many circuit boards left over from my original Bruce Chubb CRMI installation on a previous layout, I chose to utilize those cab relays where possible by modifying the boards. The perfboard on the upper right allows the Arduino outputs to drive the relays via the transistors. I added additional Optimized Detectors to cover the 6 inputs.


/*
 crossing_logic_1
12/24/16
 
                gates_down_1                          flashers_on_1
                |----                                  X
 -----------------| |-----------------------------------| |------------------       signal_block_1 entire length
    west_block_1               mid_block_1                    east_block_1
   
 -----------------| |-----------------------------------| |------------------       signal_block_2 entire length
    west_block_2  ----|        mid_block_2                 X  east_block_2
   
               gates_down_2                           flashers_on_2
              
  All 6 inputs are from Chubb Optimized detectors where HIGH = unoccupied and LOW = occupied
  All 4 outputs will turn on HIGH when the proper inputs are LOW, driving relays via transistors connected to the 12v supply. These relays
  will then activate crossing gates, flashers and block signals.
 
 */
 
// Define pins
const int west_block_1_pin = 2;
const int mid_block_1_pin = 3;
const int east_block_1_pin = 4;
const int west_block_2_pin = 5;
const int mid_block_2_pin = 6;
const int east_block_2_pin = 7;
const int gates_down_pin = 8;
const int flashers_on_pin = 9;
const int signal_block_1_pin = 10;
const int signal_block_2_pin = 11;

// Define and initialize all variables as HIGH (off) except outputs that drive relays through transister switches where HIGH is on.
int west_block_1 = HIGH;
int mid_block_1 = HIGH;
int east_block_1 = HIGH;
int west_block_2 = HIGH;
int mid_block_2 = HIGH;
int east_block_2 = HIGH;
int gates_down_1 = LOW;    //relay active HIGH to show gates down for block 1
int flashers_on_1 = LOW;   //relay active HIGH to show flashers on for block 1
int gates_down_2 = LOW;    //relay active HIGH to show gates down for block 2
int flashers_on_2 = LOW;   //relay active HIGH to show flashers for block 2
int signal_block_1 = LOW;  //relay active HIGH to show block 1 occupied
int signal_block_2 = LOW;  //relay active HIGH to show block 2 occupied
int eastbound_sequence_1 = HIGH; //active HIGH show eastbound train moving through block 1
int westbound_sequence_1 = HIGH; //active HIGH show westbound train moving through block 1
int eastbound_sequence_2 = HIGH; //active HIGH show eastbound train moving through block 2
int westbound_sequence_2 = HIGH; //active HIGH show westbound train moving through block 2
void setup() {
  //start serial connection for debugging
  Serial.begin(9600);
  // initialize digital pin 13 as an output for onboard LED to use as a "heartbeat" for the sketch
  pinMode(13, OUTPUT);
  //configure pins 2 to 7 as an input and enable the internal pull-up resistor
  pinMode(west_block_1_pin, INPUT_PULLUP);   // Unlike pinMode(INPUT), there is no pull-down resistor necessary. An internal
  pinMode(mid_block_1_pin, INPUT_PULLUP);   // 20K-ohm resistor is pulled to 5V. This configuration causes the input to
  pinMode(east_block_1_pin, INPUT_PULLUP);  // read HIGH when the switch is open, and LOW when it is closed.
  pinMode(west_block_2_pin, INPUT_PULLUP);
  pinMode(mid_block_2_pin, INPUT_PULLUP);
  pinMode(east_block_2_pin, INPUT_PULLUP); 
  //configure pins 8 to 11 as outputs. HIGH when not actuated and LOW when actuated ;i.e. action activated
  pinMode(gates_down_pin, OUTPUT);
  pinMode(flashers_on_pin, OUTPUT);
  pinMode(signal_block_1_pin, OUTPUT);
  pinMode(signal_block_2_pin, OUTPUT);
  // Initialize all outputs as off
  digitalWrite(gates_down_pin, LOW); //start with gates up
  digitalWrite(flashers_on_pin, LOW); //start with flashers off
  digitalWrite(signal_block_1_pin, LOW); //start with block 1 unoccupied
  digitalWrite(signal_block_2_pin, LOW); //start with block 2 unoccupied
 
}

void loop() {
  digitalWrite(13, HIGH);   // turn the  "heartbeat" LED on
  //read the input values into the variables
  west_block_1 = digitalRead(west_block_1_pin);
  mid_block_1 = digitalRead(mid_block_1_pin);
  east_block_1 = digitalRead(east_block_1_pin);
  west_block_2 = digitalRead(west_block_2_pin);
  mid_block_2 = digitalRead(mid_block_2_pin);
  east_block_2 = digitalRead(east_block_2_pin);
  Serial.print("gates_down_1: ");
  Serial.println(gates_down_1);
  Serial.print("flashers on_1: ");
  Serial.println(flashers_on_1);
  Serial.print("gates_down_2: ");
  Serial.println(gates_down_2);
  Serial.print("flashers on_2: ");
  Serial.println(flashers_on_2);
  Serial.print("west_block_1: ");
  Serial.println(west_block_1);
  Serial.print("mid_block_1: ");
  Serial.println(mid_block_1);
  Serial.print("east_block_1: ");
  Serial.println(east_block_1);
  Serial.print("west_block_2: ");
  Serial.println(west_block_2);
  Serial.print("mid_block_2: ");
  Serial.println(mid_block_2);
  Serial.print("east_block_2: ");
  Serial.println(east_block_2);
  Serial.print("eastbound_sequence_1: ");
  Serial.println(eastbound_sequence_1);
  Serial.print("westbound_sequence_1: ");
  Serial.println(westbound_sequence_1);
  Serial.print("eastbound_sequence_2: ");
  Serial.println(eastbound_sequence_2);
  Serial.print("westbound_sequence_2: ");
  Serial.println(westbound_sequence_2);
   
 
  if (west_block_1 == HIGH && mid_block_1 == HIGH && east_block_1 == HIGH  && west_block_2 == HIGH && mid_block_2 == HIGH && east_block_2 == HIGH) {  //blocks 1 and 2 completely clear
  /* ---------------------------------------------- INITIALIZATION  LOGIC ------------------------------------------------------ */
    digitalWrite(gates_down_pin, LOW);  //raise gates
    digitalWrite(flashers_on_pin, LOW);  //turn off flashers
    gates_down_1 = LOW;  //show gates up
    flashers_on_1 = LOW; //show flashers off
    gates_down_2 = LOW;  //show gates up
    flashers_on_2 = LOW; //show flashers off
    eastbound_sequence_1 = HIGH;           //initialize block 1 eastbound sequence
    westbound_sequence_1 = HIGH;           //initialize block 1 westbound sequence
    eastbound_sequence_2 = HIGH;           //initialize block 2 eastbound sequence
    westbound_sequence_2 = HIGH;           //initialize block 2 westbound sequence
    digitalWrite(signal_block_1_pin, LOW); //show block 1 clear
    digitalWrite(signal_block_2_pin, LOW); //show block 2 clear
    Serial.println("Initializing block 1"); 
    Serial.println("Initializing block 2"); }
  else if (west_block_1 == HIGH && mid_block_1 == HIGH && east_block_1 == HIGH){  //block 1 completely clear
    eastbound_sequence_1 = HIGH;           //initialize block 1 eastbound sequence
    westbound_sequence_1 = HIGH;           //initialize block 1 westbound sequence
    digitalWrite(signal_block_1_pin, LOW); //show block 1 clear
    Serial.println("Initializing block 1");  }
  else if (west_block_2 == HIGH && mid_block_2 == HIGH && east_block_2 == HIGH){  //block 2 completely clear
    eastbound_sequence_2 = HIGH;           //initialize block 2 eastbound sequence
    westbound_sequence_2 = HIGH;           //initialize block 2 westbound sequence
    digitalWrite(signal_block_2_pin, LOW); //show block 1 clear
    Serial.println("Initializing block 2");  }
   
  /* ----------------------------------------------------------------------------------------------------------------------------------- */ 
  /* If the blocks are not completely clear then proceed with the gate and flasher logic for each block */
  /* --------------------------------------------------------- BLOCK 1 ------------------------------------------------------------------- */
  /* ------------------------------------------------------ CROSSING GATE ------------------------------------------------------------- */
  /* ----------------------------------------------------- EASTBOUND LOGIC ------------------------------------------------------------ */
   if (west_block_1 == LOW && mid_block_1 == HIGH && gates_down_1 == LOW && eastbound_sequence_1 == HIGH && westbound_sequence_1 == HIGH)  {   //initial Eastbound approach gates
    digitalWrite(gates_down_pin, HIGH);  //drop gates
    gates_down_1 = HIGH;                  //show gates down
    eastbound_sequence_1 = LOW;       //set start of eastbound sequence
    Serial.print("Drop gates initial eastbound"); }
  else if (west_block_1 == HIGH && mid_block_1 == LOW && gates_down_1 == HIGH && eastbound_sequence_1 == LOW && westbound_sequence_1 == HIGH)  { //clearing crossing eastbound gates
    gates_down_1 = LOW;}
  /* --------------------------------------------------- WESTBOUND LOGIC ------------------------------------------------------------ */
  else if (mid_block_1 == LOW && west_block_1 == HIGH && gates_down_1 == LOW && eastbound_sequence_1 == HIGH) {   //&& westbound_sequence_1 == HIGH && eastbound_sequence_1 == HIGH) {   //initial Westbound approach gates
    digitalWrite(gates_down_pin, HIGH);  //drop gates
    gates_down_1 = HIGH;                  //show gates down
    westbound_sequence_1 = LOW;       //set start of westbound sequence
    Serial.print("Drop gates initial westbound"); }
  else if (west_block_1 == LOW && mid_block_1 == HIGH && gates_down_1 == HIGH && westbound_sequence_1 == LOW) { //clearing crossing westbound gates
    gates_down_1 = LOW;}
   
  /* ------------------------------------------------------__ FLASHER ------------------------------------------------------------- */ 
  /* ----------------------------------------------------- EASTBOUND LOGIC ------------------------------------------------------------ */
  else if (mid_block_1 == LOW && east_block_1 == HIGH && flashers_on_1 == LOW && eastbound_sequence_1 == LOW && westbound_sequence_1 == HIGH) {  //initial Eastbound approach flashers
    digitalWrite(flashers_on_pin, HIGH);  //turn on flashers
    flashers_on_1 = HIGH;                  //show flashers on
    eastbound_sequence_1 = LOW;        //set start of eastbound sequence
    Serial.print("Flashers on initial eastbound"); }
  else if (east_block_1 == LOW && mid_block_1 == HIGH && flashers_on_1 == HIGH && eastbound_sequence_1 == LOW) { //clearing crossing eastbound flashers
    flashers_on_1 = LOW;}
  /* ----------------------------------------------------- WESTBOUND LOGIC ------------------------------------------------------------ */
  else if (east_block_1 == LOW && mid_block_1 == HIGH && flashers_on_1 == LOW && westbound_sequence_1 == HIGH && eastbound_sequence_1 == HIGH) {   //initial Westbound approach flashers
    digitalWrite(flashers_on_pin, HIGH);  //turn on flashers
    flashers_on_1 = HIGH;                  //show flashers on
    westbound_sequence_1 = LOW;        //set start of westbound sequence
    Serial.print("Flashers on initial westbound"); }
  else if (east_block_1 == HIGH && mid_block_1 == LOW && flashers_on_1 == HIGH && westbound_sequence_1 == LOW) { //clearing crossing westbound flashers
    flashers_on_1 = LOW;}
   
  /* --------------------------------------------------------- BLOCK 2 ------------------------------------------------------------------- */
  /* ------------------------------------------------------ CROSSING GATE ------------------------------------------------------------- */
  /* ----------------------------------------------------- EASTBOUND LOGIC ------------------------------------------------------------ */
  if (west_block_2 == LOW && mid_block_2 == HIGH && gates_down_2 == LOW && eastbound_sequence_2 == HIGH && westbound_sequence_2 == HIGH)  {   //initial Eastbound approach gates
    digitalWrite(gates_down_pin, HIGH);  //drop gates
    gates_down_2 = HIGH;                  //show gates down
    eastbound_sequence_2 = LOW;       //set start of eastbound sequence
    Serial.print("Drop gates initial eastbound"); }
  else if (west_block_2 == HIGH && mid_block_2 == LOW && gates_down_2 == HIGH && eastbound_sequence_2 == LOW && westbound_sequence_2 == HIGH) { //clearing crossing eastbound gates
    gates_down_2 = LOW;}
  /* --------------------------------------------------- WESTBOUND LOGIC ------------------------------------------------------------ */
  else if (mid_block_2 == LOW && west_block_2 == HIGH && gates_down_2 == LOW && eastbound_sequence_2 == HIGH) {   //&& westbound_sequence_1 == HIGH && eastbound_sequence_1 == HIGH) {   //initial Westbound approach gates
    digitalWrite(gates_down_pin, HIGH);  //drop gates
    gates_down_2 = HIGH;                  //show gates down
    westbound_sequence_2 = LOW;       //set start of westbound sequence
    Serial.print("Drop gates initial westbound"); }
  else if (west_block_2 == LOW && mid_block_2 == HIGH && gates_down_2 == HIGH && westbound_sequence_2 == LOW) { //clearing crossing westbound gates
    gates_down_2 = LOW;}
  /* ------------------------------------------------------__ FLASHER ------------------------------------------------------------- */ 
  /* ----------------------------------------------------- EASTBOUND LOGIC ------------------------------------------------------------ */
  else if (mid_block_2 == LOW && east_block_2 == HIGH && flashers_on_2 == LOW && eastbound_sequence_2 == LOW && westbound_sequence_2 == HIGH) {  //initial Eastbound approach flashers
    digitalWrite(flashers_on_pin, HIGH);  //turn on flashers
    flashers_on_2 = HIGH;                  //show flashers on
    eastbound_sequence_1 = LOW;        //set start of eastbound sequence
    Serial.print("Flashers on initial eastbound"); }
  else if (east_block_2 == LOW && mid_block_2 == HIGH && flashers_on_2 == HIGH && eastbound_sequence_2 == LOW) { //clearing crossing eastbound flashers
    flashers_on_2 = LOW;}
  /* ----------------------------------------------------- WESTBOUND LOGIC ------------------------------------------------------------ */
  else if (east_block_2 == LOW && mid_block_2 == HIGH && flashers_on_2 == LOW && westbound_sequence_2 == HIGH && eastbound_sequence_2 == HIGH) {   //initial Westbound approach flashers
    digitalWrite(flashers_on_pin, HIGH);  //turn on flashers
    flashers_on_2 = HIGH;                  //show flashers on
    westbound_sequence_2 = LOW;        //set start of westbound sequence
    Serial.print("Flashers on initial westbound"); }
  else if (east_block_2 == HIGH && mid_block_2 == LOW && flashers_on_2 == HIGH && westbound_sequence_2 == LOW) { //clearing crossing westbound flashers
    flashers_on_2 = LOW;}
 
  /* ---------------  Fail safe train blocking crossing --------------------------------------------------- */
  else if (east_block_1 == LOW && mid_block_1 == LOW) { //Flasher crossing is blocked, track 1
    digitalWrite(flashers_on_pin, HIGH);  //turn on flashers
    flashers_on_1 = HIGH;                  //show flashers on
    eastbound_sequence_1 = LOW;        //set start of eastbound sequence
    Serial.print("Flashers on-fail safe"); }
  else if (east_block_2 == LOW && mid_block_2 == LOW) { //Flasher crossing is blocked, track 2
    digitalWrite(flashers_on_pin, HIGH);  //turn on flashers
    flashers_on_2 = HIGH;                  //show flashers on
    eastbound_sequence_2 = LOW;        //set start of eastbound sequence
    Serial.print("Flashers on-fail safe"); }
  else if (west_block_1 == LOW && mid_block_1 == LOW) { //gate crossing is blocked, track 1
    digitalWrite(gates_down_pin, HIGH);  //drop gates
    gates_down_1 = HIGH;                  //show gates down
    eastbound_sequence_1 = LOW;       //set start of eastbound sequence
    Serial.print("Drop gates-fail safe"); }
  else if (west_block_2 == LOW && mid_block_2 == LOW) { //gate crossing is blocked, track 2
    digitalWrite(gates_down_pin, HIGH);  //drop gates
    gates_down_2 = HIGH;                  //show gates down
    eastbound_sequence_2 = LOW;       //set start of eastbound sequence
    Serial.print("Drop gates-fail safe"); }
 
  /* ------------------- Raise gate logic --------------------------------------------------------------- */
  /* Since there are two tracks we don't want to raise the gates for one track if the other is still down */
  if (gates_down_1 == LOW && gates_down_2 == LOW){   // both tracks are clear of the gate crossing 
    digitalWrite(gates_down_pin, LOW);  //raise gates
    Serial.println("Raise gates"); }
   
  /* ------------------- Turn off flasher logic ____________________________________________ */
  /* Since there are two tracks we don't want to turn off the flashers for one track if the other is still on */
  if (flashers_on_1 == LOW && flashers_on_2 == LOW){   // both tracks are clear of the flasher crossing 
    digitalWrite(flashers_on_pin, LOW);  //turn off flashers
    Serial.println("Turn off flashers"); }
 
     
  /* ----------------- BLOCK SIGNAL LOGIC ------------------------------------------ */
  Serial.println("");
  if (east_block_1 == LOW || mid_block_1 == LOW || west_block_1 == LOW){ // If any of the 3 blocks for track 1 are occupied set block 1 signals to red
     digitalWrite(signal_block_1_pin, HIGH); //show block 1 occupied}
     Serial.println("Block 1 occupied");}
  else if (east_block_1 && HIGH && mid_block_1 == HIGH && west_block_1 == HIGH){ //If all 3 blocks for track 1 are clear set block 1 signals to green
    digitalWrite(signal_block_1_pin, LOW); //show block 1 clear
    Serial.println("Block 1 clear");}
 
  if (east_block_2 == LOW || mid_block_2 == LOW || west_block_2 == LOW){ // If any of the 3 blocks for track 2 are occupied set block 2 signals to red
     digitalWrite(signal_block_2_pin, HIGH);
     Serial.println("Block 2 occupied");} //show block 2 occupied
  else if (east_block_2 && HIGH && mid_block_2 == HIGH && west_block_2 == HIGH){ //If all 3 blocks for track 2 are clear set block 2 signals to green
    digitalWrite(signal_block_2_pin, LOW); //show block 2 clear
    Serial.println("Block 2 clear");}
  /* ------------------------------------------------------------------------------ */
   // digitalWrite(13, HIGH);   // turn the  "heartbeat" LED on
   // delay(500);               // wait for a half second
    digitalWrite(13, LOW);    // turn the  "heartbeat" LED off
   //delay(500);               // wait for a half second
    Serial.println("");      //carriage return 
    Serial.println("");      //carriage return
    Serial.println("");      //carriage return
  }

  • Member since
    November 2007
  • From: sharon pa
  • 436 posts
Posted by gondola1988 on Tuesday, December 20, 2016 5:57 PM

I did'nt see it mentioned but if you go on u tube there's are a lot of info and projects that the arduino is used for, you must have a few hours to see everything they can do.

  • Member since
    September 2015
  • 34 posts
Posted by Pukka on Monday, December 19, 2016 12:39 PM

An Arduino or any of the other microcomputers consist of a CPU (central processing unit), clock (basically a timer or pulser which may put out 16,000,000 pulses per second), and memory. Memory is where firmware (operating system) and software is placed. Even an Arduino has firmware wich can be replaced with diff firmware; in that case it would no longer be an Arduino. The software section is where your program resides in and makes use of the routines located in firmware. The programming software in your PC or Linux will save your program to the Arduino in machine code, the native code of most cpus. The clock keeps everything humming, if the clock stops, then replace the Arduino.

A local community college or hackerspace may have hands-on classes for the Arduino or PIC, etc. Sparkfun.com has on-line video tutorials. In case one is interested in the technical details.

  • Member since
    August 2003
  • From: Collinwood, Ohio, USA
  • 16,237 posts
Posted by gmpullman on Monday, December 12, 2016 12:49 AM

richg1998
I have seen a fair amount of interest in the MRH forms. Nice device.

Since Rich mentioned MRH I'll point to an excellent, timely, Arduino 101 article there:

 

http://mrhpub.com/2016-12-dec/online/

When the page opens just click on the cover text mentioning the article. There's even a random building lighting project how-to.

hon30critter
All I need now is a good solid kick in the butt!

I'm in the same boat. I have a box full of boards and connectors, etc. just waiting for that magic day when I have several uninterrupted hours that I can devote to "jumping in the deep end" of the Arduino pool.

Regards, Ed

  • Member since
    February 2002
  • From: Reading, PA
  • 30,002 posts
Posted by rrinker on Sunday, December 11, 2016 8:57 PM

 Beyond the forum? Probably not. But beyond the print magazine? Almost certainly.  Several hundred lines of code, plus text describing in detail what it is doing and how it works, would take quite a few pages in the magazine and likely only appeal to a small majority of subscribers. They had similar complaints about many electronic articles in the past. Even when they had the Symposium on Electronics column and cut it down to every other month, people still wrote in and complained about too much of this impossible to understand high tech rubbish!

 Even when it came time to present the software for the CMRI - the examples were highly simplified and only key points highlighted with descriptions. Some other artices that involved both hardware and software, the software was left out of the article completely and you could write in and request a printed copy. This is where a web-based option is probably best, for describing the software that drives these things. The hardware in many cases is fairly simple to explain  - it's the software that does all the real heavy lifting.

                    --Randy

 


Modeling the Reading Railroad in the 1950's

 

Visit my web site at www.readingeastpenn.com for construction updates, DCC Info, and more.

  • Member since
    July 2009
  • From: lavale, md
  • 4,642 posts
Posted by gregc on Sunday, December 11, 2016 7:46 AM

Atchee
but I'm having trouble believing there aren't a bunch of folks that haven't gone crazy with Arduinos out there in model railroad land. Where are they? I don't see much in the forums here.

I believe there are many hobbyist with even limited software skills building projects using pico processors such as Pic or Atmel, as well as Arduinos.  Some have sophisticated systems linking multiple pic-processor modules and PCs with 485 buses for block detection and control of signaling and turnouts.

It seems that most Arduino project articles or forum threads only describe what the Arduino is or can be used for.  None that I've seen in magazines or these forum threads have provided a sufficient description of the software or a code listing.

Recent electronics magazine articles have been fairly simple and often flawed.  This is not to say that they don't work, but not necessarily good examples for other modelers to learn about electronics from.  None compare to the sophistication of Linn Westcott's TAT and early articles nor the detailed explanations of those circuits.

Electronics projects include circuit schematics which describe how the circuit works and aren't too difficult to learn how to read.  But software projects requires a decent description of the software and/or a code listing for a sufficent description.  Would an introduction to C be needed?

I think a lengthy article for someone without a software background would need to describe both the hardware and firmware in even a trivial model railroad project using an Arduino.  Even using one of the many available libraries requires some description of the code integrating the library and hardware.

I think a good introductory article would have to include a description of the integrated developement environment (IDE) used to write, build, simulate and debug the code and most likely some introduction to writing software.  Better yet, it or follow up articles would describe how to build collection of sub-functions and modules that can be used for many purposes.

Unlike Linn Westcott's TAT articles, are software project descriptions beyong the scope of model railroad magazine articles and forum threads?  

I know there are model railroad electronic forums on Yahoo.  Do these include threads on firmware?

greg - Philadelphia & Reading / Reading

  • Member since
    December 2016
  • 9 posts
Posted by KK4HFJ on Thursday, December 8, 2016 4:53 AM

I'm a heavy arduino and raspberry pi user, and use them extensivly in model railroading and my ham radio projects. from scale speedometers to turnout and signal control, to dcc accessory decoders, they make inexpensive easy solutions to sensing and control. See some of my work at http://arduinotronics.blogspot.com . I'm a member of the Charlestown Area Model Railroad Club - http://www.chamrc.com/ . We run JMRI on the Raspberry Pi.

Steve Spence

KK4HFJ (Ham Radio)

http://arduinotronics.blogspot.com

  • Member since
    June 2009
  • From: QLD, Australia
  • 1,111 posts
Posted by tbdanny on Thursday, December 8, 2016 3:14 AM

rrinker

 Do you have any more info on your staging controller? That's a very different approoach, using the ultrasonic modules. I'd like to incorproate something like that in my layout, since running a train in and out of staging is hardly a realistic activity and you can't alwas be sure that someone will be around who is willing to play 'mole' and hid behind the scenes and handle it.

Randy,

I've started a thread outlining how my automated staging works, here: http://cs.trains.com/mrr/f/744/t/259915.aspx.

The Location: Forests of the Pacific Northwest, Oregon
The Year: 1948
The Scale: On30
The Blog: http://bvlcorr.tumblr.com

  • Member since
    September 2003
  • From: Omaha, NE
  • 10,616 posts
Posted by dehusman on Wednesday, December 7, 2016 8:09 AM

I see four different options for automating a crossing.

1.  Schedule :  Set up a fixed schedule of trains on the other road, after xx min the other road occupies the crossing, after YY min the next other train crosses, after ZZ min the next train.

2.  Random interval :   The other road crosses at random length intervals (between some min and max values).  After the other road crosses, the system rests the counter to a new random interval.

3.  Random tests :  At some fixed interval the software makes a test with a random outcome, if true the other railroad occupies the main.  The interval iset at the minimum time between other trains.  Every 7 min the system asks if there is a train, if yes it clears the opposing train.

4.  Request :  Train crew pushes a button/requests a signal and system makes a random test, if true they get the signal.

As for the signals there are options on how they behave:

1.  Home road clear, layout railroad route notmally clear, system knocks down the signals only when the other road "runs".  Option :   Detector circuits on the home road check the approach blocks on the home road and doesn't clear the other road until the home road clears the crossing and approaches.

2.  Manual clear, layout route normally stop, when crew approaches they push a button to get a clear signal, system performs a test, if true clears the signal.

3.  Auto clear, layout home road route normally stop, when train occupies approach circuit, system performs a random test, if true signal is cleared. 

Dave H. Painted side goes up. My website : wnbranch.com

  • Member since
    February 2002
  • From: Reading, PA
  • 30,002 posts
Posted by rrinker on Wednesday, December 7, 2016 7:29 AM

 Another thing you could use an Arduino for is to make simulated fusees for stopped train protection. Yes, contrived because they are fixed in place, but Craig Bisgeier uses them on his Housatonic set in 1892. He uses a premade version, but this would be an ideal spot for one of the ATTiny85's as you need only one output for the light and one input for the pushbutton to activate it.

 Have any crossing gates? Yes, in the era they would be operated by a watchman camped in the little hut alongside the tracks - that just means not using block detectors to automatically drop the gates when the train comes, but rather have them activated manually by a button or toggle on the fascia.

 Controlling the junctions is a great idea. You can program the Arduino to randomly set the signals with a not less than 30 minute period between, or you could program in a strict schedule of the other railroad where you'd start the Arduno at the beginning of an operating session and then per the opposing road's schedule (with a random + factor  - since trains are seldom early. That 12:01 might actually get there at 12:01, or it may not get there until 12:15) or just at random, or different implementations for each crossing. Anything is possible. Even a combination - say the opposing road hs 2 scheduled trains plus 3 extras that would run in between there, sometime during the hours of your op session. Totally doable to generate those two specific signal changes plus 3 random ones. ANd the beauty of it all is that you can start simple - just change the signal every 30 minutes or so, and then gradually add in the other options without having to reqire anything, since it's all just a change of the program running on the Arduino.

                      --Randy

 


Modeling the Reading Railroad in the 1950's

 

Visit my web site at www.readingeastpenn.com for construction updates, DCC Info, and more.

  • Member since
    February 2007
  • From: Christiana, TN
  • 2,134 posts
Posted by CSX Robert on Wednesday, December 7, 2016 4:09 AM

dehusman
How long does the Arduino time things?..

However long you want.  There is a built in millis() function that counts milliseconds since the current program began running that overflows after about 50 days.  There is also a datetime library that will keep track of seconds, hours, minutes, days, months and years.

  • Member since
    September 2003
  • From: Omaha, NE
  • 10,616 posts
Posted by dehusman on Tuesday, December 6, 2016 9:19 PM

How long does the Arduino time things?  For example one application that might be useful is "automating" an interlocking where the every 15-30 actual minutes the signals at the interlocking drop to simulate a train passing through on the conflicting route.  Then a minute or so later the signals would clear.

Possibly another option would be to have the signals at stop on all directions and every so often the conflicting route would get a "clear" for a few minutes.  The crew on my railroad would have to pull up towards the signal and if there were no conflicting routes (other road signals at stop) the signal would be cleared for my route, if the other route was already lined (other road signals cleared) I would have to wait for the conflicting move to "clear' then I would get the signals.

If Arduinos have the ability to time things long duration then that type of a situation might be useful, I have 2 PRR crossings and B&O crossings to model.

As far as lighting, since electric lights would be few and far between in my era and my operating sessions are set in the day, there shouldn't be any interior lights "on".  My engines don't even use their headlights, it wasn't until the 1950's that most roads required headlights to be displayed by day and by night and I'm running about 1903.

Dave H. Painted side goes up. My website : wnbranch.com

  • Member since
    August 2016
  • 42 posts
Posted by wraithe on Tuesday, December 6, 2016 8:11 AM
I am in the process of using an Arduino, shield and stepper motor to drive a turntable... The turntable is home made and 145 scale feet.. The Arduino gives me the added benefit of aligning with tracks no matter where there placed and indexing using the arduino is a little easier rather than having to place track at the exact index place of a commercial turntable..ie over 2,000 steps in 360 degrees... I am also using arduino components for access to my vehicles ecm in real time, so the Arduino is a very useful.. Considering the ancestory of where they come from, its amazing they are only taking off now in the community sector.. The plc/plu's have been in industry for more than 20 years and yet few know about them...
  • Member since
    February 2002
  • From: Reading, PA
  • 30,002 posts
Posted by rrinker on Monday, December 5, 2016 6:20 PM

 What's really cool about it is that you have a universal throttle, that works oin any system. You don't have to try and remember how to use the NCE throttle at one friend's layout, and the Lenze throttle at another friend's layout, plus your Digitrax throttle at home. If they all turn on WiThrottle then you have one common device that works on all DCC systems. And if touch screen control isn;t for you (it's not my favorite way to run trains either. I don't even like potentiometers for knob controls), tht's where the designs like Dave Bodnar's come in to play. Actual knobs to control the train PLUS by using the free open protocol to communicate to JMRI or RocRail, you have the same thing - a universal throttle that will work on any brand DCC system. Powered by Arduino and a wifi interface board - the most common one used by Arduino people is the RF24. Named for the chip - which happens to be the same one Digitrax uses in their Duplex radios.

                                      --Randy

 


Modeling the Reading Railroad in the 1950's

 

Visit my web site at www.readingeastpenn.com for construction updates, DCC Info, and more.

  • Member since
    February 2002
  • From: Reading, PA
  • 30,002 posts
Posted by rrinker on Monday, December 5, 2016 6:13 PM

dehusman
 
fieryturbo
There is no configuration on WiThrottle. You connect to the wireless network the railroad is on, and it broadcasts what locomotives are configured in JMRI. You can also store your personal locomotive IDs in the application - i.e. you can take your phone to any layout that has JMRI and use it. the SRCP based controller is the same, without the convenient broadcast of what locos are part of the layout.

 

The rub is that for me to operate on YOUR railroad, I have to put WiThrottle on MY phone.  If I don't have WiThrottle on my phone I can't operate on your layout.  The interchangeability assumes that everybody will have WiThrottle as an app on their phone.  If your guests don't have a smart phone, don't wish to put the app on their phone or don't have enough battery on their phone then you would have to provide phones for them to use. 

For example I don't have WiThrottle on my phone so if I came to your layout, I would not be able to operate using my phone.

 

 But with $20 no contract Android phones out there, I could buy 6 or more for the price of one DT402, and therefore be able to supply 6 guests with a throttle instead of just one. Even with UT4's, it's about a 4 to 1 ratio. And that's for the wired ones. It's back to 6 to 1 for a UT4D. Although why you would be so against installing a free app that uses no airtime data or minutes I'm not sure.

                                --Randy

 


Modeling the Reading Railroad in the 1950's

 

Visit my web site at www.readingeastpenn.com for construction updates, DCC Info, and more.

  • Member since
    February 2002
  • From: Reading, PA
  • 30,002 posts
Posted by rrinker on Monday, December 5, 2016 6:03 PM

dehusman
 
CentralGulf
Programmable Logic Controller.

 

One of the big barriers to helping people use this technology is all the jargon and acronyms.  Go through the posts in this thread and count how many times a brand name or acronym is used, then count how many times that phrase or code is explained.  To get the technology widely adopted the number of explanations and definitions has to be about the same number.  Otherwide people will be scared off because they won't understand it.

Also the uses have to be expanded.  So far it seems like Arduinos are used for :

  • Lighting automation
  • Controlling switches
  • As some sort of interface in a home made DCC system

I don't really have any of those things (no real building lighting, switches are all manual operation, own an NCE system),  I am trying to figure out what I would want to use an Arduino for.

 

 I do a lot of it on purpose - because it drives me NUTS that every time in MR they say DCC they HAVE to print (Digital Command Control) after it. After 24 years are there REALLY that many people who don;t know what DCC standa for? And even if they don't - do you really NEED to know what it stabnds for? You could forget entirely that it is an acronym for something and it would still carry the same meaning.

 Name brands? People drop name brands all the time with all aspects of the hobby, would you complain that people say "Digitrax" and "NCE" but not have any worries with "Woodland Scenics" or "Atlas"?

 Keep in mind the whole Arduino ecosystem was designed for ARTISTS, not engineers and technical people. In fact on engineering forums there is a not so insignificant subset of users who look down on those using Arduino vs using the native tools for the microcontroller. The biggest complaint is inefficient code - and sure, you cna use native Atmel register control and make the same program you made witht he Arduino IDE and it will run 10x as fast on the same hardware. But do you want to issue commands such as:

DDRA = 0x00;    //Set port a as input
x = PINA;       //Read contents of port a

Or just say

PinMode(1,INPUT)

X=DigitalRead(1)

(It's actually worse than that, depending on some options you may or may not want to use - the Arduino form is way simpler than direct Atmel assembly. The cool thing is though, you actually can embed segments of Atmel code in an Arduino program - but that's advanced stuff that most people using Arduinos would never have to touch.)

  It appears that you don;t do anything that would require any form of control or automation. I get that part of that is your modeled era, but no lights in structures? THat's just something I've always had even back to sectional track days, and now with LEDs it gets even better with multiple lights per structure. And with an Arduino they could be made to all flicker like kerosene and gas lamps in a pre-electricity era.

 As for PLCs - a PLC is common in industrial settings, automating and controlling all sorts of things. A checmical process, a sewer plant, a paint line, these things are often automated with a PLC. The programming is often fairly strightforward and in some cases now graphical - you draw out the flow diagram and set some parameters and the system will know to turn on a drain valve when a tank level gets to a certain percentage, or turn on a chiller if the temperature in the container reaches a certain value. The operational displays on these things is often neat to watch since it graphically depicts what is happening. Industrial PLCs are ruggedized for a harsh environment and have connections suitable for attaching heavy duty motors and solenoids and so forth. And that's almost exactly what an Arduino is - a low power, low voltage PLC. We have a full amuesment park on our club layout, with most of the rides powered and animates. One of the guys works with PLCs and has once of his own, which he is using to control the rides so they all have independent start-run-stop cycles. None of this "they all start together, they all stop together) stuff. He already has the PLC, but the same thing could be done easier and cheaper with an Arduino, IF someone had to also go out and buy the PLC - they aren't cheap.

 

                 --Randy

 

 

Modeling the Reading Railroad in the 1950's

 

Visit my web site at www.readingeastpenn.com for construction updates, DCC Info, and more.

  • Member since
    May 2011
  • 743 posts
Posted by Steven S on Monday, December 5, 2016 5:41 PM

dehusman
If your guests don't have a smart phone, don't wish to put the app on their phone or don't have enough battery on their phone then you would have to provide phones for them to use. For example I don't have WiThrottle on my phone so if I came to your layout, I would not be able to operate using my phone.

This is a rather weak complaint, given that it's true no matter what system a person uses.  If he has a Lenz system, you'll need to go buy a Lenz throttle.  If he has NCE, you'll need to get an NCE one.  At least cell phones are pretty ubiquitous, and there is a free throttle app people can download.  Having to download a free app is small potatos compared to having to shell out hundreds of dollars for proprietary throttles from multiple companies.

  • Member since
    September 2003
  • From: Omaha, NE
  • 10,616 posts
Posted by dehusman on Monday, December 5, 2016 3:46 PM

fieryturbo
There is no configuration on WiThrottle. You connect to the wireless network the railroad is on, and it broadcasts what locomotives are configured in JMRI. You can also store your personal locomotive IDs in the application - i.e. you can take your phone to any layout that has JMRI and use it. the SRCP based controller is the same, without the convenient broadcast of what locos are part of the layout.

The rub is that for me to operate on YOUR railroad, I have to put WiThrottle on MY phone.  If I don't have WiThrottle on my phone I can't operate on your layout.  The interchangeability assumes that everybody will have WiThrottle as an app on their phone.  If your guests don't have a smart phone, don't wish to put the app on their phone or don't have enough battery on their phone then you would have to provide phones for them to use. 

For example I don't have WiThrottle on my phone so if I came to your layout, I would not be able to operate using my phone.

Dave H. Painted side goes up. My website : wnbranch.com

  • Member since
    May 2011
  • 743 posts
Posted by Steven S on Monday, December 5, 2016 2:51 PM

If anyone has a MicroCenter computer store nearby, you can get their house-brand Arduino clones really cheap.  The Uno and Mega boards sell for $6 and $10 respectively.

They only have 25 stores in the U.S.

http://www.microcenter.com/site/stores/default.aspx

(scroll down toward the bottom.)

  • Member since
    May 2011
  • 743 posts
Posted by Steven S on Monday, December 5, 2016 2:40 PM

dehusman
Does it it have controllers that have knobs?

Since it's a DIY system, you'll need to build your own.  Dave Bodnar has come up with a couple of WiFi throttles that have throttle knobs.  Both appear to be run by an Arduino Pro Mini.  The first has a two-line character display and 3x4 keypad.  The other combines the knob with a touch screen display by Nextion.

http://trainelectronics.com/DCC_Arduino/DCC++/Throttle/images/plexUnit1.jpg

http://www.trainelectronics.com/DCC_Arduino/Nextion_LCD/images/TopPic.jpg

 

dehusman
Does the roll your own system have the same capacity and features that the commerical systems

The DCC++ system is open source, which means that people are free to add features to it.  It's potentially better than any commercial system because it's limited only by the creativity of the people contributing to it. 

dehusman
Is it compatible with other systems?

No.  Just as your NCE throttle isn't going to plug into a Digitrax base unit.

dehusman
does the cost if you are running multiple trains include the price of the layout owner buying multiple smart phones to run the trains

No, but they're a lot cheaper than buying multiple throttles from Digitrax. 

 

  • Member since
    February 2007
  • From: Christiana, TN
  • 2,134 posts
Posted by CSX Robert on Monday, December 5, 2016 1:35 PM

I've got several model railroading projects that I'm considering usign an Adrduino on, but there is one that I have done.  For Christmas I like to put an HO scale train in the floor around the tree and an N-scale train actually in the tree.  I use an Arduino with a motor shield to run the trains.  I can control them using an infrarred remote, or I can set them to run automatically.  I can even have them triggered by a motion sensor when someone walks by the tree.

  • Member since
    February 2007
  • From: Christiana, TN
  • 2,134 posts
Posted by CSX Robert on Monday, December 5, 2016 1:08 PM

fieryturbo
There may also be something to do with Arduino posing a serious threat to some of the companies that run ads in the magazine and online.  Digitrax, NCE, EasyDCC come to mind, as you can put together a competing Arduino system for less than $20.

I really don't think DCC manufacturers are too conecerned about Arduino DCC systems.  I would suspect the vast majority of DCC users want something they can take out of the box, plug in, and have it work.  Additionally, when you consider the time it takes to get a home built system working, for many people it's cheaper to purchase a commercial system.  I actually built my own DCC system before there were Arduino's.  I had an MRC Command 2000 at the time and I designed my homemade system to do everything it would do, with plans to extend it's functionality later; however, when Digitrax came out with the Zephyr, it just wasn't worth it to me to keep working on it.

  • Member since
    August 2015
  • 371 posts
Posted by fieryturbo on Monday, December 5, 2016 12:16 PM

dehusman

 

 
fieryturbo
I personally have a SPROG 3, but as soon as I get DCC++ going, I'll probably be selling it.

 

What are the limitations for that system?  Does it allow 10-20 operators to be running trains at the same time?  Does it it have controllers that have knobs?  Will your system/controllers be compatible with other layouts in the area?

 

 
I have a single Uno right now for my turnouts, and bought a bag of sevos from China for $1.25 each - this is 10+ servos for the price of a Tortoise. The old guard of model railroad manufacturers need to watch out.

 

I bought some servos and could never get them to work right, they didn't have enough power to line handliad switches, plus by the time I added auxillary contacts to power the frogs, the price was way over the manual methods or Torti that I had.

 

 
The price ceiling is about to come crashing down on their heads in the next few years.

 

Depends.  Does the roll your own system have the same capacity and features that the commerical systems do and is the set up bullet proof?  Is it compatible with other systems?  Are the other things beyond the Arduino included in the price and set up (I am assuming you have some sort of laptop or other computer running the DCC software, the $20 include a computer).   A lot of these systems seem to relly on wifi and smart phones, does the cost if you are running multiple trains include the price of the layout owner buying multiple smart phones to run the trains (or are you assuming that visiting modelers will be willing to download and configure apps on THEIR phones to run YOUR railroad.)

I'm sure that the DCC market will evolve, but I don't know that the current systems are obsolete.  Most of the objections to DCC are based on hardware and interface, and some of that is user preference.

To call anything in model railroading 'bullet proof' is a stretch, if not an outright error.  There are so many factors that can contribute to something working or not working on a layout.

Any cast off computer made in the last ~10 years will do for running a model railroad.  A suitable one can be had cheap/free.

There are only 2 kinds of phone controllers - WiThrottle and SRCP, the latter of which is supported on both Rocrail and JMRI.

There is no configuration on WiThrottle.  You connect to the wireless network the railroad is on, and it broadcasts what locomotives are configured in JMRI.  You can also store your personal locomotive IDs in the application - i.e. you can take your phone to any layout that has JMRI and use it.  the SRCP based controller is the same, without the convenient broadcast of what locos are part of the layout.

Unless you are firmly entrenched in the older technology, there is no reason to purchase any of these $300-$600 DCC systems on the market.

Just like IBM in computing, eventually these big players of the model railroading world will be toppled from their towers in the face of cheap and free open-standard technology like Arduino and JMRI.

To answer your question of a knob...here is a video of someone using the ESU Mobile Control II with AndRoc to connect to the free RocRail:

 

 

Julian

Modeling Pre-WP merger UP (1974-81)

  • Member since
    September 2003
  • From: Omaha, NE
  • 10,616 posts
Posted by dehusman on Monday, December 5, 2016 11:46 AM

CentralGulf
Programmable Logic Controller.

One of the big barriers to helping people use this technology is all the jargon and acronyms.  Go through the posts in this thread and count how many times a brand name or acronym is used, then count how many times that phrase or code is explained.  To get the technology widely adopted the number of explanations and definitions has to be about the same number.  Otherwide people will be scared off because they won't understand it.

Also the uses have to be expanded.  So far it seems like Arduinos are used for :

  • Lighting automation
  • Controlling switches
  • As some sort of interface in a home made DCC system

I don't really have any of those things (no real building lighting, switches are all manual operation, own an NCE system),  I am trying to figure out what I would want to use an Arduino for.

Dave H. Painted side goes up. My website : wnbranch.com

Subscriber & Member Login

Login, or register today to interact in our online community, comment on articles, receive our newsletter, manage your account online and more!

Users Online

There are no community member online

Search the Community

ADVERTISEMENT
ADVERTISEMENT
ADVERTISEMENT
Model Railroader Newsletter See all
Sign up for our FREE e-newsletter and get model railroad news in your inbox!