PL-11 Turnout Control With JMRI/CRMI

This blog will be showing how I connected up a Peco PL-11 turnout motor to an Arduino to be controlled by JMRI via the CMRI communication.  First this motor runs on 16v ac so we cannot connect this to the Arduino and run it directly, we will have to use a relay. Fortunately Arduino provides relay boards which have the optical isolation components already on the boards to prevent damage to the Arduino from back EMF. These boards come in a number of different setups. The one I am using runs on 5 v dc so will be run from my computer power supply on the layout which provides the 5 v dc line. To control the relays we use a digital input to the IN pins. I will be using IN1 and IN2 as I will need two relays one for the closing of the turnout and one for the throw of the turnout. These will be connected to pins 11 and 12 of the Arduino.

On the relay terminal side I will have the centre connection joined to each other and to one side of the 16v power supply. Then each normally open contacts will have one side of the PL-11. So the Pl-11 will only be powered on when one of the relays is operated.

You need to remember the PL-11 must not be powered of for too long as this will burn out the motor winding. For this setup we will only be pulsing the relays on for 0.5 seconds.

Now I have it all connected up we will look at the Arduino code. I will show two setups one with the 'IF' statement which works very well but is more code than needed, some people might find this better to follow and prefer to use which is ok as it works. The second code will be very short and makes coding quicker.

Below is the IF statement code



#include <CMRI.h>
#include <Auto485.h>    // this is for the 485 if you are using this 

#define CMRI_ADDR 1
#define DE_PIN 2  // this again it for the 465 connection

int T1C = 11; //turnout 1 close relay
int T1T = 12; //turnout 1 throw relay

int T1_bit_close;  // two variable to store the incoming bits from JMRI
int T1_bit_throw;

Auto485 bus(DE_PIN); // Arduino pin 2 -> MAX485 DE and RE pins  again for 485 only
CMRI cmri(CMRI_ADDR, 24, 48, bus); // defaults to a SMINI with address 0. SMINI = 24 inputs, 48 outputs

void setup() {
pinMode (T1C, OUTPUT);
pinMode (T1T, OUTPUT);
digitalWrite(T1C, HIGH); // I have relays which when the Arduino output is set to HIGH the realys will be off. 
digitalWrite(T1T, HIGH); // I set them HIGH at the setup to ensure all relays start in he off state.
bus.begin(9600);  // for the 485 connection only
}

void loop() {

cmri.process();
T1_bit_close = (cmri.get_bit(0)); //1001
T1_bit_throw = (cmri.get_bit(1)); //1002

if (T1_bit_close == 1 ){
    digitalWrite(T1C, LOW);  // turn a relay on to throw the turnout
} else {
    digitalWrite(T1C, HIGH); // turn it off again after 1 second
}
if (T1_bit_throw ==1){
    digitalWrite(T1T, LOW);  // turn the close relay on to close the turnout
} else {
    digitalWrite(T1T, HIGH);  // turn it off again after 1 second
}

}
            

Below is the second code. For this I am setting the relays to the bit state that is coming in from JMRI. If you remember JMRI will be setting a bit to either 1 or 0. And to turn and relay on and off we set it to either HIGH or LOW, HIGH being a 1 state and LOW being a 0 state. So we could just write

 digitalWrite(T1C, T1_bit_close);
and with this the relay will be the same sate as the bit coming in.

The one thing with my relay boards is they are reverse logic meaning a HIGH will be off I need to invert the incoming bit so a 1 bit coming in will be 0 and vice versa. For this I place a ! exclamation mark in front of the incoming bit which is logic NOT for the Arduino code. This will be stored in T1_bit_close or throw like this.

 
T1_bit_close = !(cmri.get_bit(0));
T1_bit_throw = !(cmri.get_bit(1)); //1002
            

Hopefully this all makes sense. Below is the full code.



#include <CMRI.h>
#include <Auto485.h>

#define CMRI_ADDR 1
#define DE_PIN 2

int T1C = 11; //turnout 1 close relay
int T1T = 12; //turnout 1 throw relay

int T1_bit_close;
int T1_bit_throw;

Auto485 bus(DE_PIN); // Arduino pin 2 -> MAX485 DE and RE pins
CMRI cmri(CMRI_ADDR, 24, 48, bus); // defaults to a SMINI with address 0. SMINI = 24 inputs, 48 outputs

void setup() {
pinMode (T1C, OUTPUT);
pinMode (T1T, OUTPUT);
digitalWrite(T1C, HIGH);
digitalWrite(T1T, HIGH);
bus.begin(9600);
}

// using this code you will need to set JMRI turnout to address 1001 and 2 bit with a pulse output. This will take up address 1001 and 1002 in the JMRI registery

void loop() {
cmri.process();
T1_bit_close = !(cmri.get_bit(0)); //1001
T1_bit_throw = !(cmri.get_bit(1)); //1002
digitalWrite(T1C, T1_bit_close);  // bit code coming in will be a 1 or 0 so will turn relay on when 1 and off when 0
digitalWrite(T1T, T1_bit_throw);
}
            

Now we just need to setup the JMRI a little different on the turn out table.

One thing with this setup I notice, when starting JMRI the relays will chatter on pin 13 as the Arduino communicates with the computer. If you have the 16v ac power supply switched on this will probably chatter your turnouts so best to avoid using pin 13.

When adding a turnout to JMRI for this Arduino code the JMRI turnout address will be 1001 as shown below, but because we will be using two bits for one turnout control the addresses used wittin JMRI will be 1001 and 1002. So each turnout will use two addresses. Therefore a second turnout will be address 1003.



img

The next step will be to select 2 bit and then pulse output so JMRI will only pulse a 1 state output for 1 second, normal state will always be 0.

Once you have this setup and the 16v ac power switched on, you should now be able to control you PL-11 with JMRI.



Finally below is a way of controlling the PL-11 with one address from JMRI using a flag variable to store when the JMRI is set to 1 or 0. This worked OK and the end of the code has a little add on for reading inputs from a microswitch on the turnout to be used as a feedback to JMRI. This will set a sensor bit address on JMRI to active or inactive when the turnout is operated. Again this was working OK but I was getting in consistency with the operation of the PL-11 which was not due to programming or JMRI interface but I believe was due to the torque of the PL-11. I found these not guaranteed to operate so will be turning back to the servo control turnouts.

#include &lt;CMRI.h&gt;
#include &lt;Auto485.h&gt;
#define CMRI_ADDR 1
#define DE_PIN 2
Auto485 bus(DE_PIN); // Arduino pin 2 -> MAX485 DE and RE pins
CMRI cmri(CMRI_ADDR, 24, 48, bus); // defaults to a SMINI with address 0. SMINI = 24 inputs, 48 outputs

int numberRelays = 4;
int Tbit[2];
int Tout[] = {8, 9, 10, 11};
boolean Tstatus[2];
boolean flag = false;

int TF1_CLOSE = 53;
int TF1_THROW = 52;
int TF1_CLOSE_READING;
int TF1_THROW_READING;

void setup() {
pinMode(TF1_CLOSE, INPUT_PULLUP);
pinMode(TF1_THROW, INPUT_PULLUP);
for (int i=0; i<numberRelays; i++){
pinMode(Tout[i], OUTPUT);
digitalWrite(Tout[i], HIGH);
}
for (int i=0; i<2; i++){
Tstatus[i] = false;
}
bus.begin(9600);
}

void loop(){
cmri.process();
Tbit[0] = (cmri.get_bit(0));
Tbit[1] = (cmri.get_bit(1));

if (Tbit[0] == 1 && flag == false){
digitalWrite(Tout[0], Tstatus[0]);
digitalWrite(Tout[1], !Tstatus[0]);
Tstatus[0] = !Tstatus[0];
flag = true;
delay(500);
}

if (Tbit[1] == 1 && flag == false){
digitalWrite(Tout[2], Tstatus[1]);
digitalWrite(Tout[3], !Tstatus[1]);
Tstatus[1] = !Tstatus[1];
flag = true;
}

if (Tbit[0] == 0 && Tbit[1] == 0 ){
for (int i=0; i<numberRelays; i++){
digitalWrite(Tout[i], HIGH);
}
flag = false;
}

// Below was the micro switch on turnout 1 as a feedback into pins 52 and 53. This was used as a feedback to JMRI to show the status of the turnout
TF1_CLOSE_READING = digitalRead(TF1_CLOSE);
TF1_THROW_READING = digitalRead(TF1_THROW);

cmri.set_bit(0, TF1_CLOSE_READING);
cmri.set_bit(1, TF1_THROW_READING);
}
            

Comments



12