fieryturboThere 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
dehusmanIf 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.
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.
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 :
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 inputx = 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.
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.
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.
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.
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.
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.
dehusmanHow 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.
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.
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.
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.
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, OregonThe Year: 1948The Scale: On30The Blog: http://bvlcorr.tumblr.com
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
Atcheebut 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
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.
richg1998I 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.
hon30critterAll 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
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.
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.
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_112/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 pinsconst 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 1int flashers_on_1 = LOW; //relay active HIGH to show flashers on for block 1int gates_down_2 = LOW; //relay active HIGH to show gates down for block 2int flashers_on_2 = LOW; //relay active HIGH to show flashers for block 2int signal_block_1 = LOW; //relay active HIGH to show block 1 occupiedint signal_block_2 = LOW; //relay active HIGH to show block 2 occupiedint eastbound_sequence_1 = HIGH; //active HIGH show eastbound train moving through block 1int westbound_sequence_1 = HIGH; //active HIGH show westbound train moving through block 1int eastbound_sequence_2 = HIGH; //active HIGH show eastbound train moving through block 2int westbound_sequence_2 = HIGH; //active HIGH show westbound train moving through block 2void 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 }
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.
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_112/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 pinsconst 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 1int flashers_on_1 = LOW; //relay active HIGH to show flashers on for block 1int gates_down_2 = LOW; //relay active HIGH to show gates down for block 2int flashers_on_2 = LOW; //relay active HIGH to show flashers for block 2int signal_block_1 = LOW; //relay active HIGH to show block 1 occupiedint signal_block_2 = LOW; //relay active HIGH to show block 2 occupiedint eastbound_sequence_1 = HIGH; //active HIGH show eastbound train moving through block 1int westbound_sequence_1 = HIGH; //active HIGH show westbound train moving through block 1int eastbound_sequence_2 = HIGH; //active HIGH show eastbound train moving through block 2int westbound_sequence_2 = HIGH; //active HIGH show westbound train moving through block 2void 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.
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.
Test posting the code unformatted to it is more readable
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.
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.
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:
// ----------------------------------------------- // 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 _ _ _ _ _ _ _ _ _ _
printf() is available in the Arduino version of C (it's not really C, it's actually derived from Processing), however there is no standard I/O like there is when compiling C code for a PC using any of the common operating systems like Windows, Linux, or OSX. To activate the serial port for debugging you must use the serial library as shown in the otiginal code, there is a handy serial terminal built in to the Arduino IDE that can be used to vew the debugging output and/ot send data to the Arduino.
Yes, the new code is shorter but then all teh initialization steps have been left off as well, with Arduino and other microcontrollers you need to define the I/O pins for th required use - digital output, digital input, digital input with integrated pullup, or analog input.
For a very complex layout (although not even the Arduino Mega has enough I/O pins to handle all the detectors, signals, and other accessories like crossing gates for all that big of a layout, unless you go nuts with shift registers to expand the pin count, in which case the code will need additional functions that resemble in no way at all part of the layout logic to handle the shift register I/O), arranging the logic liek this does make it a bit easier to read, and with a bit more code the CHECK() functions can be configuredto handle blocks that just drive signals as well as ones that have crossing gates and/or signals. And it will run faster, although speed is generally not a problem with railroad stuff. However, for a simpler situation, like just driving two grade crossings, I'd postualte the more inefficient version is more easily understood by the beginner (or non) programmer. Simple IF-THEN constructs, rather than more complex nexted IF-THEN-ELSE IF structures. Again, the second way is definitely more efficient in both processing speed and in use of resources, and is the sort of thing I would expect to see as the final product from someone with a lot of programming experience, however the initial approach is totally correct for the exercise at hand. One other thing, once it is all working, all of the serial calls can be commented out to again speed up the loop speed as well as save resources. ANother way to do some simple debugging, if all the pins aren't used, is to use some leftover pins to light LEDs at specific points in the code. Or a routine can be written to blink the included LED on Pin 13 of all Arduinos at different speeds so you can follow along in the code just by watching the blinkign LED instead of hooking up the serial cable.
Remind me to never post code again. At least not early in a project development. That being said, the code does indeed work for travel in either direction on either track. Remember that all my passenger cars and cabooses pickup track power and are detected by the ODs so a train has to completely clear a block for that input to go HIGH. The system has only been in use for a week and is very much in the debug mode. Indeed I have already found an issue in the unlikely event that I make a westbound or eastbound move and then reverse the direction of the train. The gates and/or flashers then stay on until all 3 blocks are clear. I may or may not try to fix that. My layout is meant for "roundy round" operation and that pleases me so I really don't care. To each his own.
rrinkerArduino version of C (it's not really C, it's actually derived from Processing),
It is C/C++, it's not "derived" from processing (The IDE is deirved from the Processing IDE, and is written in java). It uses the avr-gcc compiler. Processing is an intepreted language based on Java. A limited subset of standard I/O is available to micro controllers in general, as there is no file or operating system. Although there are a lot of atmel/arduino optimized functions provided, most C/C++ commands work just fine. http://www.nongnu.org/avr-libc/user-manual/group__avr__stdio.html