Model Railroad Signals

Hi I am using this sketch to control up to 3 sets of signal lights on a model (Marklin) Railway and as my Arduino (C+) program skills are almost none existent , but I used this sketch i found on an YouTube and intend to use 6 more signals with 6 IR sensors to control 6 sections, wth additional IR sensors so the light will stay on Green till the sensor is triggered and a delay so after the Green LED, Yellow and go to RED, how can I daisy-chain, so it will tend to function..i.e. the signal is a RED YELLOW GREEN. I am planning to use it in bi-directional track.
Also as I am a relative novice with Arduino sketches and I am sure there is another way for this sketch but it works for me.

Any assistance/advice will be greatly appreciated.

Please post, "this sketch." - be sure to use the < CODE > button to format the code correctly.

Sorry, Here's the updated sketch,



// Simple 3 Aspect Block Signal Control Paul Matthews 2022
// Set up for three 3 aspect signals. When a train passes signal 1
// it will turn red, when it passes signal 2 it will turn red and
// signal 1 will turn amber, when the train passes signal 3 it will
// turn red, signal 2 will turn amber and signal 1 will turn green
// If the train loops and passes signal 1 again signal 3 will go to
// amber, signal 2 to green and the cycle will start again.
// an overide switch connected to pin A1 will turn all the signals red
// a reset push switch connected to pin A0 will reset all the
// signals back to green

#define train_detection_pin1 2 // Signal 1 train detection sensor is pin 2
#define green1_pin 3 // Signal 1 green LED is pin 3
#define amber1_pin 4 // Signal 1 amber LED is pin 4
#define red1_pin 5 // Signal 1 red LED is pin 5
#define train_detection_pin2 6 // Signal 2 train detection sensor is pin 6
#define green2_pin 7 // Signal 2 green LED is pin 7
#define amber2_pin 8 // Signal 2 amber LED is pin 8
#define red2_pin 9 // Signal 2 red LED is pin 9
#define train_detection_pin3 10 // Signal 3 train detection sensor is pin 10
#define green3_pin 11 // Signal 3 green LED is pin 11
#define amber3_pin 12 // Signal 3 amber LED is pin 12
#define red3_pin 13 // Signal 3 red LED is pin 13
#define reset_switch A0 // Reset switch is pin A0
#define overide_switch A1 // Overide switch is pin A1
byte loop_around = 0;
byte state = 0;

void setup() {
  // put your setup code here, to run once:

  pinMode (green1_pin, OUTPUT);
  pinMode (amber1_pin, OUTPUT);
  pinMode (red1_pin, OUTPUT);
  pinMode (train_detection_pin1, INPUT_PULLUP);
  pinMode (green2_pin, OUTPUT);
  pinMode (amber2_pin, OUTPUT);
  pinMode (red2_pin, OUTPUT);
  pinMode (train_detection_pin2, INPUT_PULLUP);
  pinMode (green3_pin, OUTPUT);
  pinMode (amber3_pin, OUTPUT);
  pinMode (red3_pin, OUTPUT);
  pinMode (train_detection_pin3, INPUT_PULLUP);
  pinMode (reset_switch, INPUT_PULLUP);
  pinMode (overide_switch, INPUT_PULLUP);

  // Begin Lamp Test - each lamp will light in turn for 1 second (1000 ms = 1 second)
  digitalWrite(green1_pin, HIGH);
  digitalWrite(green2_pin, HIGH);
  digitalWrite(green3_pin, HIGH);
  delay(500);
  digitalWrite(green1_pin, LOW);
  digitalWrite(green2_pin, LOW);
  digitalWrite(green3_pin, LOW);
  digitalWrite(amber1_pin, HIGH);
  digitalWrite(amber2_pin, HIGH);
  digitalWrite(amber3_pin, HIGH);
  delay(500);
  digitalWrite(amber1_pin, LOW);
  digitalWrite(amber2_pin, LOW);
  digitalWrite(amber3_pin, LOW);
  digitalWrite(red1_pin, HIGH);
  digitalWrite(red2_pin, HIGH);
  digitalWrite(red3_pin, HIGH);
  delay(500);
  digitalWrite(red1_pin, LOW);
  digitalWrite(red2_pin, LOW);
  digitalWrite(red3_pin, LOW);
  // End Lamp Test

  digitalWrite(green1_pin, HIGH);
  digitalWrite(green2_pin, HIGH);
  digitalWrite(green3_pin, HIGH);
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:

  if (digitalRead(overide_switch) == LOW) {
    state = 1;
  }

  switch (state) {
    case 1: //overide switch
      digitalWrite(green1_pin, LOW);
      digitalWrite(green2_pin, LOW);
      digitalWrite(green3_pin, LOW);
      digitalWrite(amber1_pin, LOW);
      digitalWrite(amber2_pin, LOW);
      digitalWrite(amber3_pin, LOW);
      digitalWrite(red1_pin, HIGH);
      digitalWrite(red2_pin, HIGH);
      digitalWrite(red3_pin, HIGH);
      if (digitalRead(overide_switch) == HIGH) {
        state = 0;
        digitalWrite(green1_pin, HIGH);
        digitalWrite(green2_pin, HIGH);
        digitalWrite(green3_pin, HIGH);
        digitalWrite(amber1_pin, LOW);
        digitalWrite(amber2_pin, LOW);
        digitalWrite(amber3_pin, LOW);
        digitalWrite(red1_pin, LOW);
        digitalWrite(red2_pin, LOW);
        digitalWrite(red3_pin, LOW);
      }
      break;
  }

  if (digitalRead(reset_switch) == LOW) {
    digitalWrite(green1_pin, HIGH);
    digitalWrite(green2_pin, HIGH);
    digitalWrite(green3_pin, HIGH);
    digitalWrite(amber1_pin, LOW);
    digitalWrite(amber2_pin, LOW);
    digitalWrite(amber3_pin, LOW);
    digitalWrite(red1_pin, LOW);
    digitalWrite(red2_pin, LOW);
    digitalWrite(red3_pin, LOW);
  }

  if (digitalRead(train_detection_pin1) == LOW) { //if train is detected at signal 1
    digitalWrite(green1_pin, LOW);
    digitalWrite(green2_pin, HIGH);
    digitalWrite(green3_pin, LOW);
    digitalWrite(amber1_pin, LOW);
    digitalWrite(amber2_pin, LOW);
    digitalWrite(red1_pin, HIGH);
    digitalWrite(red2_pin, LOW);
    digitalWrite(red3_pin, LOW);
    if (loop_around == 1) {
      digitalWrite(amber3_pin, HIGH);
    }
    else {
      digitalWrite(green3_pin, HIGH);
    }
  }
  if (digitalRead(train_detection_pin2) == LOW) { //if train is detected at signal 2
    digitalWrite(green1_pin, LOW);
    digitalWrite(green2_pin, LOW);
    digitalWrite(green3_pin, HIGH);
    digitalWrite(amber1_pin, HIGH);
    digitalWrite(amber2_pin, LOW);
    digitalWrite(amber3_pin, LOW);
    digitalWrite(red1_pin, LOW);
    digitalWrite(red2_pin, HIGH);
    digitalWrite(red3_pin, LOW);

  }
  if (digitalRead(train_detection_pin3) == LOW) { //if train is detected at signal 3
    digitalWrite(green1_pin, HIGH);
    digitalWrite(green2_pin, LOW);
    digitalWrite(green3_pin, LOW);
    digitalWrite(amber1_pin, LOW);
    digitalWrite(amber2_pin, HIGH);
    digitalWrite(amber3_pin, LOW);
    digitalWrite(red1_pin, LOW);
    digitalWrite(red2_pin, LOW);
    digitalWrite(red3_pin, HIGH);
    loop_around = 1;

  }

}Use code tags to format code for the forumis the updated sketch,

From the IDE, use the auto-format tool, then copy and paste AS CODE
(add annotation to say you’ve ’Edit: Added code tags’

You can erase and replace the text block in your first post - then you don’t need to create a lengthy mess.

Fix your code format. Here is a simulation. Press 1, then press 2, then press 3... the lights will change.

Hi, As a first timer, Pardon my ignorance, how do I paste as code?

any advice will be greatly appreciated,

Regards
J

Added Code tags for OP

// Simple 3 Aspect Block Signal Control Paul Matthews 2022
// Set up for three 3 aspect signals. When a train passes signal 1
// it will turn red, when it passes signal 2 it will turn red and
// signal 1 will turn amber, when the train passes signal 3 it will
// turn red, signal 2 will turn amber and signal 1 will turn green
// If the train loops and passes signal 1 again signal 3 will go to
// amber, signal 2 to green and the cycle will start again.
// an overide switch connected to pin A1 will turn all the signals red
// a reset push switch connected to pin A0 will reset all the
// signals back to green

#define train_detection_pin1 2 // Signal 1 train detection sensor is pin 2
#define green1_pin 3 // Signal 1 green LED is pin 3
#define amber1_pin 4 // Signal 1 amber LED is pin 4
#define red1_pin 5 // Signal 1 red LED is pin 5
#define train_detection_pin2 6 // Signal 2 train detection sensor is pin 6
#define green2_pin 7 // Signal 2 green LED is pin 7
#define amber2_pin 8 // Signal 2 amber LED is pin 8
#define red2_pin 9 // Signal 2 red LED is pin 9
#define train_detection_pin3 10 // Signal 3 train detection sensor is pin 10
#define green3_pin 11 // Signal 3 green LED is pin 11
#define amber3_pin 12 // Signal 3 amber LED is pin 12
#define red3_pin 13 // Signal 3 red LED is pin 13
#define reset_switch A0 // Reset switch is pin A0
#define overide_switch A1 // Overide switch is pin A1
byte loop_around = 0;
byte state = 0;

void setup() {
  // put your setup code here, to run once:

  pinMode(green1_pin, OUTPUT);
  pinMode(amber1_pin, OUTPUT);
  pinMode(red1_pin, OUTPUT);
  pinMode(train_detection_pin1, INPUT_PULLUP);
  pinMode(green2_pin, OUTPUT);
  pinMode(amber2_pin, OUTPUT);
  pinMode(red2_pin, OUTPUT);
  pinMode(train_detection_pin2, INPUT_PULLUP);
  pinMode(green3_pin, OUTPUT);
  pinMode(amber3_pin, OUTPUT);
  pinMode(red3_pin, OUTPUT);
  pinMode(train_detection_pin3, INPUT_PULLUP);
  pinMode(reset_switch, INPUT_PULLUP);
  pinMode(overide_switch, INPUT_PULLUP);

  // Begin Lamp Test - each lamp will light in turn for 1 second (1000 ms = 1 second)
  digitalWrite(green1_pin, HIGH);
  digitalWrite(green2_pin, HIGH);
  digitalWrite(green3_pin, HIGH);
  delay(500);
  digitalWrite(green1_pin, LOW);
  digitalWrite(green2_pin, LOW);
  digitalWrite(green3_pin, LOW);
  digitalWrite(amber1_pin, HIGH);
  digitalWrite(amber2_pin, HIGH);
  digitalWrite(amber3_pin, HIGH);
  delay(500);
  digitalWrite(amber1_pin, LOW);
  digitalWrite(amber2_pin, LOW);
  digitalWrite(amber3_pin, LOW);
  digitalWrite(red1_pin, HIGH);
  digitalWrite(red2_pin, HIGH);
  digitalWrite(red3_pin, HIGH);
  delay(500);
  digitalWrite(red1_pin, LOW);
  digitalWrite(red2_pin, LOW);
  digitalWrite(red3_pin, LOW);
  // End Lamp Test

  digitalWrite(green1_pin, HIGH);
  digitalWrite(green2_pin, HIGH);
  digitalWrite(green3_pin, HIGH);
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:

  if (digitalRead(overide_switch) == LOW) {
    state = 1;
  }

  switch (state) {
  case 1: //overide switch
    digitalWrite(green1_pin, LOW);
    digitalWrite(green2_pin, LOW);
    digitalWrite(green3_pin, LOW);
    digitalWrite(amber1_pin, LOW);
    digitalWrite(amber2_pin, LOW);
    digitalWrite(amber3_pin, LOW);
    digitalWrite(red1_pin, HIGH);
    digitalWrite(red2_pin, HIGH);
    digitalWrite(red3_pin, HIGH);
    if (digitalRead(overide_switch) == HIGH) {
      state = 0;
      digitalWrite(green1_pin, HIGH);
      digitalWrite(green2_pin, HIGH);
      digitalWrite(green3_pin, HIGH);
      digitalWrite(amber1_pin, LOW);
      digitalWrite(amber2_pin, LOW);
      digitalWrite(amber3_pin, LOW);
      digitalWrite(red1_pin, LOW);
      digitalWrite(red2_pin, LOW);
      digitalWrite(red3_pin, LOW);
    }
    break;
  }

  if (digitalRead(reset_switch) == LOW) {
    digitalWrite(green1_pin, HIGH);
    digitalWrite(green2_pin, HIGH);
    digitalWrite(green3_pin, HIGH);
    digitalWrite(amber1_pin, LOW);
    digitalWrite(amber2_pin, LOW);
    digitalWrite(amber3_pin, LOW);
    digitalWrite(red1_pin, LOW);
    digitalWrite(red2_pin, LOW);
    digitalWrite(red3_pin, LOW);
  }

  if (digitalRead(train_detection_pin1) == LOW) { //if train is detected at signal 1
    digitalWrite(green1_pin, LOW);
    digitalWrite(green2_pin, HIGH);
    digitalWrite(green3_pin, LOW);
    digitalWrite(amber1_pin, LOW);
    digitalWrite(amber2_pin, LOW);
    digitalWrite(red1_pin, HIGH);
    digitalWrite(red2_pin, LOW);
    digitalWrite(red3_pin, LOW);
    if (loop_around == 1) {
      digitalWrite(amber3_pin, HIGH);
    } else {
      digitalWrite(green3_pin, HIGH);
    }
  }
  if (digitalRead(train_detection_pin2) == LOW) { //if train is detected at signal 2
    digitalWrite(green1_pin, LOW);
    digitalWrite(green2_pin, LOW);
    digitalWrite(green3_pin, HIGH);
    digitalWrite(amber1_pin, HIGH);
    digitalWrite(amber2_pin, LOW);
    digitalWrite(amber3_pin, LOW);
    digitalWrite(red1_pin, LOW);
    digitalWrite(red2_pin, HIGH);
    digitalWrite(red3_pin, LOW);

  }
  if (digitalRead(train_detection_pin3) == LOW) { //if train is detected at signal 3
    digitalWrite(green1_pin, HIGH);
    digitalWrite(green2_pin, LOW);
    digitalWrite(green3_pin, LOW);
    digitalWrite(amber1_pin, LOW);
    digitalWrite(amber2_pin, HIGH);
    digitalWrite(amber3_pin, LOW);
    digitalWrite(red1_pin, LOW);
    digitalWrite(red2_pin, LOW);
    digitalWrite(red3_pin, HIGH);
    loop_around = 1;

  }

}

I understand for a bi-directional track you'd need two IR proximity sensors for each signal.
Placed on the either side?

Next time !

Hi Anshu, that's correct, that's the plan/intention.

Cheers
jay

Thanks, will do.

Sample sketch, one signal controlled by two bidirectional sensors

#define train_detection_pin1 2 // Signal 1 train detection sensor is pin 2
#define train_detection_pin2 6 // Signal 2 train detection sensor is pin 6
#define green1_pin 3 // Signal 1 green LED is pin 3
#define amber1_pin 4 // Signal 1 amber LED is pin 4
#define red1_pin 5 // Signal 1 red LED is pin 5

void setup() {
  pinMode(green1_pin, OUTPUT);
  pinMode(amber1_pin, OUTPUT);
  pinMode(red1_pin, OUTPUT);
  pinMode(train_detection_pin1, INPUT_PULLUP);
  pinMode(train_detection_pin2, INPUT_PULLUP);
  greenSignal();  //Default green signal on track
}

void loop() {
  if (digitalRead(train_detection_pin1) == LOW) {
    trainCrossingSeq();
    //Wait for train to clear off the second signal also?
  }
  if (digitalRead(train_detection_pin2) == LOW) {
    trainCrossingSeq();
    //Wait for train to clear off the first signal also?
  }
}
void trainCrossingSeq(){
  //Train detected, turn from green to amber, then red and finally green again after appropriate delay
    amberSignal();delay(1000);
    redSignal();delay(5000);
    greenSignal();
}
void redSignal(){
  digitalWrite(red1_pin, HIGH);
  digitalWrite(amber1_pin, LOW);
  digitalWrite(green1_pin, LOW);
}
void amberSignal(){
  digitalWrite(red1_pin, LOW);
  digitalWrite(amber1_pin, HIGH);
  digitalWrite(green1_pin, LOW);
}
void greenSignal(){
  digitalWrite(red1_pin, LOW);
  digitalWrite(amber1_pin, LOW);
  digitalWrite(green1_pin, HIGH);
}

having done this on a large layout with multiple esp32 connected thru WiFi i suggest you structure the code

  • by blocks,
  • which blocks are adjacent to one another,
  • whether a block is occupied and
  • which signal protects each block

the processing

  • updates the occupancy for each block
  • determines the signal state for each block depending on the occupancy of the block and the "next" block
  • sets the Leds to reflect that state

you can use arrays or arrays of structures to manage blocks

pseudo code

    for each block {
        if (OCCUPIED == block)
            sigState = STOP;
        else if (OCCUPIED == blockNext)
            sigState = APPROACH;
        else
            sigState = CLEAR;

        turn all LEDs off

        switch (sigState)  {
        case CLEAR:
            turn Green Led on
        case APPROACH:
            turn Yellow Led on
        case STOP:
            turn Red Led on
            break;
        }
    }

Thanks, gcjr, my programming skills are not that great! I will try and give it a go, to include the code to the original code, but I might have to ask your help if I get ‘stuck’.
Cheers
Jay

Thanks, anshu, I will try to incorporate it into my code, however, if I need further help to expand the number of sensors/signals I may need your help.
Cheers for now,
Jay

look this over

// model RR signals

struct BlkSig {
    byte    Id;
    byte    IdNext;

    byte    PinDet;

    byte    PinGreen;
    byte    PinAmber;
    byte    PinRed;

    byte    blkOcc;
    byte    sigState;
    BlkSig *next;
};

//       ,_____._____,_____,_____,_____,
//               101   102   103

BlkSig blkSig [] = {
    { 101, 102,  2,  3,  4,  5 },
    { 102, 103,  6,  7,  8,  9 },
    { 103,   0, 10, 11, 12, 13 },
};
const int NblkSig = sizeof (blkSig) / sizeof(BlkSig);

enum { STOP, APPROACH, CLEAR };
enum { Off = HIGH, On = LOW };

// -----------------------------------------------------------------------------
void setSignal (
    BlkSig  *p)
{
    digitalWrite (p->PinGreen, Off);
    digitalWrite (p->PinAmber, Off);
    digitalWrite (p->PinRed,   Off);

    if (STOP == p->sigState)
        digitalWrite (p->PinRed,   On);
    else if (APPROACH == p->sigState)
        digitalWrite (p->PinAmber, On);
    else
        digitalWrite (p->PinGreen, On);
}

// -----------------------------------------------------------------------------
void loop ()
{
    // update block status
    BlkSig *p = blkSig;
    for (int n = 0; n < NblkSig; n++, p++) {
        p->blkOcc = ! digitalRead (p->PinDet);   // active low
    }

    // update aignal
    p = blkSig;
    for (int n = 0; n < NblkSig; n++, p++) {
        if (p->blkOcc)
            p->sigState = STOP;
        else if (p->next->blkOcc)
            p->sigState = APPROACH;
        else
            p->sigState = CLEAR;

        setSignal (p);
    }
}

// -----------------------------------------------------------------------------
void setup ()
{
    Serial.begin (9600);

    BlkSig *p = blkSig;
    for (int n = 0; n < NblkSig; n++, p++) {
        pinMode (p->PinDet, INPUT_PULLUP);

        pinMode (p->PinGreen, OUTPUT);
        pinMode (p->PinAmber, OUTPUT);
        pinMode (p->PinRed,   OUTPUT);

        digitalWrite (p->PinGreen, Off);
        digitalWrite (p->PinAmber, Off);
        digitalWrite (p->PinRed,   Off);

        // find next
        for (int n = 0; n < NblkSig; n++) {
            if (blkSig [n].Id == p->IdNext)
                p->next = & blkSig [n];
        }
    }
}

Hi bcjr,

thanks for your response/effort in compiling the code! due to my lack of knowledge in C++ programming, where do i insert this portion of the code.
Thanking you in advance,

Cheers,
Jay

the code i posted replaces yours

it defines a structure describing block ID and block ID of the next block and the detection and signal pins (see chap 6 in The C Programming Language)

struct BlkSig {
    byte    Id;
    byte    IdNext;

    byte    PinDet;

    byte    PinGreen;
    byte    PinAmber;
    byte    PinRed;

    byte    blkOcc;
    byte    sigState;
    BlkSig *next;
};

there's a table describing all the blocks with an ASCII diagram showing the blocks and their IDs. Hopefully you see that additional blocks could be easily added if there were sufficient pins.

//       ,_____._____,_____,_____,_____,
//               101   102   103

BlkSig blkSig [] = {
    { 101, 102,  2,  3,  4,  5 },
    { 102, 103,  6,  7,  8,  9 },
    { 103,   0, 10, 11, 12, 13 },
};
const int NblkSig = sizeof (blkSig) / sizeof(BlkSig);

enums define unique values for the signals and On/Off states (see pg 39)

enum { STOP, APPROACH, CLEAR };
enum { Off = HIGH, On = LOW };

setSignal() is passed a pointer to the block entry in the table and sets the signal to the current signal state

// -----------------------------------------------------------------------------
void setSignal (
    BlkSig  *p)
{
    digitalWrite (p->PinGreen, Off);
    digitalWrite (p->PinAmber, Off);
    digitalWrite (p->PinRed,   Off);

    if (STOP == p->sigState)
        digitalWrite (p->PinRed,   On);
    else if (APPROACH == p->sigState)
        digitalWrite (p->PinAmber, On);
    else
        digitalWrite (p->PinGreen, On);
}

loop() first reads the block detection pin and updates the blkOcc field for the block structure (see chap 5 for pointers)

// -----------------------------------------------------------------------------
void loop ()
{
    // update block status
    BlkSig *p = blkSig;
    for (int n = 0; n < NblkSig; n++, p++) {
        p->blkOcc = ! digitalRead (p->PinDet);   // active low
    }

loop() then sets the signal state depending on the block occupancy state of the block and next block

    // update aignal
    p = blkSig;
    for (int n = 0; n < NblkSig; n++, p++) {
        if (p->blkOcc)
            p->sigState = STOP;
        else if (p->next->blkOcc)
            p->sigState = APPROACH;
        else
            p->sigState = CLEAR;

        setSignal (p);
    }
}

setup() configures and initializes each pin

// -----------------------------------------------------------------------------
void setup ()
{
    Serial.begin (9600);

    BlkSig *p = blkSig;
    for (int n = 0; n < NblkSig; n++, p++) {
        pinMode (p->PinDet, INPUT_PULLUP);

        pinMode (p->PinGreen, OUTPUT);
        pinMode (p->PinAmber, OUTPUT);
        pinMode (p->PinRed,   OUTPUT);

        digitalWrite (p->PinGreen, Off);
        digitalWrite (p->PinAmber, Off);
        digitalWrite (p->PinRed,   Off);

and then searches the block structure table for the entry matching the next block ID and sets the next block pointer to the entry. using a next block pointer avoids search the table each time (again, see chap 5 about pointers)

        // find next
        for (int n = 0; n < NblkSig; n++) {
            if (blkSig [n].Id == p->IdNext)
                p->next = & blkSig [n];
        }
    }
}

Hi gcjr,
Excellent, thank you very much, much appreciated for pointing me in the right direction! Rest assured i will be spending the few day in getting in right.

once again a big thank you,

Cheers
Jay

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.