TMCC Control With Arduino

Don, that's a lot of questions ;-) 

* I agree, you'll be fine just writing your code in C, no need for C++.  C++ is another steep curve for an old-school programmer (I go back to FORTRAN and punch cards as well) but became necessary due to the complexity of my project.

* I wasn't even aware that there was a web-based IDE for Arduino.  But it sounds like you need to install the traditional Windows-based IDE to help answer your questions about Prof. Chaos's code.  I personally use Microsoft Visual Studio with a plug-in called Visual Micro -- it makes editing and debugging Arduino code significantly easier, but again there is a steep learning curve.  Definitely not necessary for the straightforward project you're describing.

* I've never interfaced directly to TMCC, only to Legacy.  But I doubt you can just connect wires directly between an Arduino and a TMCC base because the Arduino works on 5v signals, and some RS232 signals are higher voltage.  You can buy an "RS232 to TTL Converter Module" on eBay for $5, which allows you to use a regular 9-pin serial cable.  I've attached a photo of the adapter I use to connect to Legacy -- you should be able to plug a 9-pin serial cable directly into your TMCC base unit (you don't need an SER2 unless yo're connecting to Legacy) -- but I'm just guessing.  Note that I needed a couple of in-line adapters to get the right wires talking (the mini adapter swaps RX and TX if I recall correctly? - it's been a long time since I hooked this up and it was just trial and error until I got it working.)

* I don't know what it means to talk to a TMCC controller?  I'm only sending commands to engines via the base unit, and there is no feedback from the base unit.  If you look at the signals that the base unit is sending out, you'll see that it sends everything three times, for good measure I guess.  So if you connect to any base unit that's connected to your track -- I'm not sure what more you would need?  Unless you've got different base units programmed for different trains -- i.e. if only certain base units are able to send commands to certain trains?  My knowledge here is a little fuzzy.  I've got a couple of controllers but only one primary base unit.

* As I mentioned previously, I recommended the ID-20 RFID reader, which will connect directly to your Arduino's serial port (and yes, you should be using a Mega so you'll have four hardware serial ports.)  That was the best unit I could find in 2015 -- there may be something better now.

* I no longer have my proof-of-concept code to blow the whistle, but it was less than 10 lines.  My real code is more complex than what you'll need, as I'm talking to Legacy, and (like Prof. Chaos), I dump my outgoing commands into a circular buffer.  Then I poll the buffer and keep track of the time, to send commands at least 25ms apart, and send each command three times.  These numbers and more are all pre-defined CONSTants.  But here is some info that may help -- with the caveat that it's very hard to read without any formatting!

I start by setting up byte variables to hold each of the up-to-9 bytes of an outgoing command.  For TMCC there are only three outgoing bytes, but Legacy can have up to 9 bytes.  This is done at the top of the code, in setup():

// First define global bytes for each of the 9 possible used in Legacy commands.
// Most commands only use simple 3-byte Legacy (or TMCC) commands.
// Nine-byte commands are for Dialogue, Fx, and Control "extended" commands.
byte legacy11 = 0x00; // F8 for Engine, or F9 for Train (or FE for emergency stop all or TMCC)
byte legacy12 = 0x00;
byte legacy13 = 0x00;
byte legacy21 = 0xFB; // Always FB for 2nd and 3rd "words", when used
byte legacy22 = 0x00;
byte legacy23 = 0x00;
byte legacy31 = 0xFB; // Always FB for 2nd and 3rd "words", when used
byte legacy32 = 0x00;
byte legacy33 = 0x00; // This will hold checksum for "multi-word" commands

Then I populate the bytes as needed for whatever command I want to send.  For example, to send an "Absolute Speed" command here is the code -- please note that it won't make a lot of sense because I'm using a structure with pre-defined values, adding a checksum for Legacy, and other features too deep to go into here:

case 'A': // Set absolute speed
if (actionElement.deviceType == 'E') {  // This is an engine
legacy11 = 0xF8;
} else {  // This is a train
legacy11 = 0xF9;
}
legacy12 = actionElement.deviceNum * 2; // Shift left one bit, fill with zero (deviceNum is train or engine ID)
legacy13 = actionElement.parm1;  // parm1 will be the desired speed i.e. 50
legacyCmdBufEnqueue(legacy11);  // This puts the three-byte command into my outgoing buffer
legacyCmdBufEnqueue(legacy12);
legacyCmdBufEnqueue(legacy13);
legacyCmdBufTransmit(); // attempt immediate execution
break;

Here are the guts of the function that actually sends the data out the serial port.  I only write in bursts of three bytes, even if it's a longer (9-byte) Legacy command:

static unsigned long legacyLastTransmit = millis();
byte b;
if (!legacyCmdBufIsEmpty()) {
if ((millis() - legacyLastTransmit) > LEGACY_MIN_INTERVAL_MS) { // Enough time since last transmit, so okay to transmit again
b = legacyCmdBufDequeue(); // Get first byte of the command
if (b==0xF8 || b==0xF9 || b==0xFB) { // It's a Legacy command
} else if (b == 0xFE) { // It's a TMCC command
} else {
Serial.print("FATAL ERROR. Unexpected data in Legacy buffer: ");
Serial.println(b, HEX);
endWithFlashingLED(7); // Should never hit this!
}
Serial3.write(b); // Write 1st of 3 bytes
b = legacyCmdBufDequeue(); // Get the 2nd byte of 3
Serial3.write(b); // Write 2nd of 3 bytes
b = legacyCmdBufDequeue(); // Get the 3rd byte of 3
Serial3.write(b); // Write 3rd of 3 bytes
legacyLastTransmit = millis();
shortChirp(); // Do a little chirp just so I can hear when a command is sent to Legacy, i.e. in relation to when I see a train hit a sensor, to assess latency.
}
return;
}

At the end of the day, you can see that all you do is put the data in three 1-byte variables, and execute "Serial.write(data);" three times in your code.  So you could accomplish the above in six lines -- three to define the contents of each byte to be broadcast, and three Serial.writes.  Or just three lines if you put the data in the Serial.write() parameter ;-)

Okay Don, there is a lot to get you thinking.  Now I need to get back on track (ha ha) with my own project ;-)

Attachments

Photos (1)

Hi Don - the daisy chain might work, but unless you are reading data from the Base with the Arduino, I think you can just connect the Base RX to the Arduino, and the Base TX to the controllers. (Obviously everyone gets GND). You can also use Legacy SER2 modules to get as many serial ports as you need.

Remember that the Base/SER2/controllers use RS-232 voltages, and the Arduino serial port is TTL. So you will need an RS-232-TTL converter between them. You can make your own with a MAX232 chip, or buy a pre-made one.

For an RFID reader, I've used ID-20 readers. Each one can connect to a TTL serial port on the Arduino, and you can get 125khz tags in all sorts of shapes and sizes.

This thread reminded me to publish some old code to read the serial output of the Command Base and dump it to the console with human read-able text appended to the three hex byte command.  The human read-able part - lots of #defines and text strings - was the grunt work.  The last time I compiled this was on Visual Studio 6 many years ago.  The current Visual Studio doesn't play nicely with old and "untrustworthy" C/C++ code (and life is too short to fuss with it) so building the executable is left to you.  See the README for a link to the multi-threaded TTY I/O code for the files you'll need to build a Windows EXE.  Of course, the Arduino folks will have to adapt the code appropriately for their platform, compilers, and I/O.  The source is .cpp files, the code style is mostly C, so devolving classes to C should not be difficult. 

GitHub project: CmdBaseReader

Command Base Reader example code

I'm currently using C# for development of Legacy tools so I cannot provide a Legacy version of CmdBaseReader using a C/C++ codebase.  I'll publish a Legacy command monitor once I'm satisfied with the code.

Regarding the Command Base serial channel, there is no problem multi-dropping multiple devices that read the output (which is the environment of multiple ICControls devices).  Don't know about potential chaos when transmitting to the Command Base from multiple sources.  I only have one Action Recorder so I haven't stressed writing to the port.

With the LCS devices, each device connects to its neighbors via the PDI cable which is NOT multi-dropped, and I assume that the LCS firmware deals with serial line contention issues.  Accordingly, I would recommend using the SER2 to transmit serial commands into the Legacy Base rather than connecting directly to the Legacy base.

Cheers,

John

 

I noticed the last few post keep mentioning Legacy.  From what I have read, Don is using TMCC only.  Therefore, all of the F8, F9 and FB stuff will not work for him.  He can only use FE.  Also, he cannot use a SER2 with a TMCC base.  They cannot talk to each other.  He has to go directly to the TMCC base.

Thanks Harvy.  Yes I must go directly to the TMCC base from my Arduino, no Legacy involved.  How do I convert the serial signals from TTL to RS-232?  I think that I only have to send commands to the TMCC base not receive and interpret any inputs.  Do I still need a Tx and Rx line?  I'm just acting like a smart Action Recorder.  Where can I buy a pre-built TTL to RS-232 converter and is that just for the Tx line?  Sounds like the ID-20 is the choice as an RFID reader.  Where do I get the reader and the transmitter chips?

Thanks Randy for your long response.  Guess I'll dig in and just proceed a byte at a time.  Going to be an adventure, but a fun one.  Would love just to have a graphic time-line graph (no code) of what a complete packet would look like, data,spaces,data,spaces,etc. for a complete transaction.  Someone of those old engineers has one lying around somewhere! Probably ought to draw one out just to get my head straight.

I don't think I can answer any of those questions, sorry.

One thing I want to bring to your attention.  I believe you said your main purpose is to receive train locations from various sensors and send out TMCC speed commands to the base unit to adjust their speed.  In my opinion Speed commands are the hardest of all TMCC commands to get working and the logic around them.  The TMCC spec gives you two options for speed commands, Relative and Absolute.  Not all engines respond to both.  Some engines will only respond to one or the other.  You may have to experiment.  My software gives users both options, but I believe in your case, since you want it all automated, you might also have to store which speed method each engine needs in your engine array.

Don, you should get an RS232 shield (or similar) to convert the serial comms and voltages to bytes that you read/write to the shield's serial port using an Arduino lib.  Once you are comfortable reading and writing bytes to the shield, then connect it to the Command Base and watch what you receive at 9600 baud 8 bits, no stop bits.  (the cable needs Tx, Rx, and Gnd wired).  You'll receive sets of three bytes beginning with 0xFE.  Then construct and write a 3-byte set to the shield.

I'm working on defining the data structures for my train project first then drop in the code.  I will run up to 4 (out of 20) trains on out 12 block main O-Gauge line, controlled by only TMCC, no Legacy (but we do have some LC+ engines).  I will detect each engine via RFID at only one point on the layout and automatically register the running trains after they all run past the ID-20 a couple of times.  So I need say 25 cheap RFID transmitters that don't look like key fobs and can go in the tenders of steam engines and inside the shells of the diesels and electrics.  Once everything is registered I will identify all trains passing the ID-20 on an LCD screen (Can you recommend a good 4-line LCD with a 12C interface?) with train info, scale speed and number of loops.  After that all works, I'll slow the train in the following block if a train is in the current block by sending TMCC commands to the following engine (already registered and tracked) using the RS-232 serial port on the TMCC base unit, while also using that port to control several other TMCC controllers (TPC,ASC,BPC, etc.) hopefully using one of the additional RS-232 serial ports on the Arduino Mega, so no RS-232 shield needed.  Will start out with sending Engine ID and relative speed commands to TMCC but later more bells and whistles (no pun intended, of course!).  That's enough of a plan to start the data structures and define some common functions (like "readRFID"). 

Since I'm re-learning C right now and never used C++ can I do all this in a year?  I think so with your help.  Thanks for helping so far.  I'll update progress and ask for more help as time goes on.  No back to the Arduino IDE.  I bought a small cheap laptop yesterday so I have a "portable Arduino IDE" now.  I loaded the native Arduino IDE on the laptop but I was using a web-based IDE on my desktop.  I gather that most of you use the native (downloaded) IDE as opposed to the web-based IDE, as the look and feel is different.

 

Don, just a couple of comments:

* Correct, you don't need a RS232 shield; with a Mega you have four on-board serial ports.

* I use "2004" i.e. 20 chars by 4 lines, LCD displays, which are available on eBay for less than $10.  They are available in I2C, SPI, and UART versions.  I've used several brands and they are all the same - just pick your interface and color.  They're also available on Amazon.

* You might want to do a little research on RFID readers, because the ID-20 is out of stock at Sparkfun.  I don't know if that's because it's discontinued or just out of stock.  There might be a better alternative by now.

* I always use absolute speed, never relative speed, because it's easier (and critical) to have certainty about how fast your train is moving (and I have lookup tables so I know that "medium" speed on one loco is often a different absolute speed number than it would be on another loco.)  I just don't see why you would ever use relative speed, because you need to keep track of how fast everything is moving -- if someone inadvertently adjusted the speed of a train, or if there was a bug in your software, then everything would be thrown off.  Also you can set momentum before setting a new absolute speed so the adjustment is more or less gradual.  That said, I know another poster said he'd had trouble with absolute and relative speed with TMCC, depending on the loco -- so something to be confirming as early in this project as possible - what works for your hardware.

* Before you start coding, you should think strategically about how you are going to start each operating session - in terms of how you will run trains past the RFID reader to get them registered into your system, and what happens when you see occupancy for a train that hasn't yet been registered.  And how do you bring things to a controlled stop.  One thing that i do is keep a short block of non-volatile memory updated with the last-known position of each train.  Then, when I start a new operating session I default to that setup.  In my case, I prompt the operator for the identification of each unknown occupied block (before any train starts moving) and default to the last-known train that was in that block -- so startup is typically just a matter of clicking "ok" six times (if there are six trains) for instance -- unless trains have been moved manually since the last automated session or if there was an emergency stop.

I'm sure you can finish this in less than a year.  The fun part is testing every aspect with a small "proof of concept" Arduino sketch and minimal hardware - it's very satisfying to see success of those small steps.  Then you assemble all those parts to get a working whole (not trivial in itself.)  And absolutely no need to use C++ other than to glean useful information from existing code that others have shared.

Thanks Randy for your comments.  I'll look into an alternate to the ID-20.  Where can I buy transmitter "buttons" cheaply as I better get 30?  All engines (Legacy, TMCC, LC+, conventional) will have RFID "buttons".  An array in the Arduino will have static info on each engine and when an new engine is given an RFID I'll add that RFID to an array that maps RFID to engine array index.  So there should never be the case that a unknown engine is encountered. 

All trains have their own siding so there are no trains on our long main line loop at startup.  To start up a session you power up an engine (full train) siding and pull it out on the main line at a random medium speed.  Once that one has cleared you bring out train number two, with enough space to avoid collision until they sync up.  Then start train #3 and so on.  Once train 1 passes the RFID sensor it is identified (RFID sensor in block 2) it's placed in a 12 position "block occupancy" array".  Occupancy is detected in all 12 blocks by detecting current with Hall-Effect current sensors, hence no voltage drop, and an occupancy map is updated in the Arduino.  So when block 3 shows "occupied" the engine index for the train in block 2 is sequenced into block 3, etc. 

So at any given time the Arduino knows which blocks are occupied and by which train.  Any "unidentified" train means a mess-up on my part and a fault is thrown and the TMCC HALT button is thrown by the Arduino.  These are the data structures I'm building up now.  I'll also measure scale speed of each train in block 2 and display it.  If I detect that the train behind the current train in block 2 (hence block 1) is gaining on it and is getting arbitrarily "too close" I want to send the following train (if its TMCC or Legacy) a relative "slow down" command, the duration of which is determined empirically, unique to each engine, and stored in the engine array.  If the next time around it still not slow enough I'll hit it again with a slowdown sequence.  What do you think of this paradigm?

AmeenTrainGuy posted:

I have a cab1 but I am planning to get a legacy command base if I write my program for the cab1 will it be transferable to legacy?

Legacy engines can understand both Legacy and TMCC commands and we run several Legacy engines with our TMCC command base, but we can't use all Legacy features (like quilling horn).  TMCC engines, on the other hand, cannot be operated from a Legacy command base.  TMCC command bases take commands from the Cab-1 via RF or from the RS-232 port on the base.  Legacy command bases take commands from the Cab-2 controller also via RF  but the signals are not comparable.  They also take wired commands but from a different LCS data link, not RS-232.  If you want to talk RS-232 to a Legacy command base you need a serial board LCS SER-2, but the commands must be in Legacy format, not TMCC format.  Hope that this helps.

Don, one of the approaches that I considered for registering trains into the system is similar to what you're describing.  I would manually drive an engine past the RFID sensor, at which time the computer would take over all control of that train -- adjusting the (absolute) speed and directing it on whatever route my algorithm assigned.  The operator (me) would then select a different engine or train on the CAB, and drive that train past the RFID sensor.  And so on.

I'm not sure how your hall effect sensors work -- do they show "occupied" whenever an engine is anywhere inside an electrically isolated block?  How do you do this?  Remember that a long train can extend into a block on either side...  I'd like to see exactly what sensors you are using and how they are connected to the track and an Arduino.  If you could post specific information about your hall effect occupancy sensors,  I'd very much appreciate it.

I'm using old-school isolated rail sections connected to time-delay relays (to avoid bounce/chatter), and have a sensor at each end of every block.  The length of the sections is adjustable as I'm using kapton tape with copper foil tape on top of the rails, rather than cutting the rail to create the electrical isolation.  So I keep a accurate accounting of when trains enter *and completely exit* each block.  Regardless of how long at train is, I know when a block has been completely cleared, and (because I use absolute rather than relative speed adjustments) I know when it is likely to reach the next sensor.  I like that certainty.

I know how many inches per second each train is moving at each speed step (I have four pre-defined for each loco - crawl, slow, medium, and fast.)  Using relative speed adjustments is just too arbitrary for my taste ;-)  This is beyond what you'll require, but I've also got tables that indicate rates of deceleration -- so I can calculate when to start slowing a train after hitting a destination siding entry sensor, and attempt to be at crawl speed the moment the train trips the destination siding exit sensor -- giving the software an opportunity to stop the train before it overshoots and fouls the mainline.  Note that entry/exit sensors are identical -- it just depends which direction the train is moving.  I also use these sensors to trip accessories such as crossing gates etc.

I buy virtually all of my parts on eBay, Amazon, and Sparkfun.

Ameentrainguy, I agree with Don's reply to you, but I'm not sure he was clear about one thing: You can write your software to send exclusively TMCC commands out the serial port, and if you upgrade to Legacy hardware and optionally also Legacy locos -- your software will still work fine.  All of the Legacy hardware and all Legacy locos will respond to TMCC commands -- whether from a Lionel TMCC control system or from serial data being spit out of an Arduino serial port.  As Don pointed out, the hardware interface will need to be upgraded for Legacy (to include an SER2), but your software will continue to work exactly as before.

My exclusively-Legacy project would work just fine if all I used were TMCC commands -- and my software would be simpler because the TMCC protocol is simpler.  The incentive to go with the Legacy protocol is that Legacy equipment offers more control -- more speeds, more control over triggering announcements, controlling smoke, etc.  If you look at the plain-English description of the commands in the TMCC protocol versus the Legacy protocol, you'll see what the differences are.

Randy P. posted:

Ameentrainguy, I agree with Don's reply to you, but I'm not sure he was clear about one thing: You can write your software to send exclusively TMCC commands out the serial port, and if you upgrade to Legacy hardware and optionally also Legacy locos -- your software will still work fine.  All of the Legacy hardware and all Legacy locos will respond to TMCC commands -- whether from a Lionel TMCC control system or from serial data being spit out of an Arduino serial port.  As Don pointed out, the hardware interface will need to be upgraded for Legacy (to include an SER2), but your software will continue to work exactly as before.

My exclusively-Legacy project would work just fine if all I used were TMCC commands -- and my software would be simpler because the TMCC protocol is simpler.  The incentive to go with the Legacy protocol is that Legacy equipment offers more control -- more speeds, more control over triggering announcements, controlling smoke, etc.  If you look at the plain-English description of the commands in the TMCC protocol versus the Legacy protocol, you'll see what the differences are.

Randy P. posted:

Don, one of the approaches that I considered for registering trains into the system is similar to what you're describing.  I would manually drive an engine past the RFID sensor, at which time the computer would take over all control of that train -- adjusting the (absolute) speed and directing it on whatever route my algorithm assigned.  The operator (me) would then select a different engine or train on the CAB, and drive that train past the RFID sensor.  And so on.

I'm not sure how your hall effect sensors work -- do they show "occupied" whenever an engine is anywhere inside an electrically isolated block?  How do you do this?  Remember that a long train can extend into a block on either side...  I'd like to see exactly what sensors you are using and how they are connected to the track and an Arduino.  If you could post specific information about your hall effect occupancy sensors,  I'd very much appreciate it.

I'm using old-school isolated rail sections connected to time-delay relays (to avoid bounce/chatter), and have a sensor at each end of every block.  The length of the sections is adjustable as I'm using kapton tape with copper foil tape on top of the rails, rather than cutting the rail to create the electrical isolation.  So I keep a accurate accounting of when trains enter *and completely exit* each block.  Regardless of how long at train is, I know when a block has been completely cleared, and (because I use absolute rather than relative speed adjustments) I know when it is likely to reach the next sensor.  I like that certainty.

I know how many inches per second each train is moving at each speed step (I have four pre-defined for each loco - crawl, slow, medium, and fast.)  Using relative speed adjustments is just too arbitrary for my taste ;-)  This is beyond what you'll require, but I've also got tables that indicate rates of deceleration -- so I can calculate when to start slowing a train after hitting a destination siding entry sensor, and attempt to be at crawl speed the moment the train trips the destination siding exit sensor -- giving the software an opportunity to stop the train before it overshoots and fouls the mainline.  Note that entry/exit sensors are identical -- it just depends which direction the train is moving.  I also use these sensors to trip accessories such as crossing gates etc.

I buy virtually all of my parts on eBay, Amazon, and Sparkfun.

 

Here's the Hall-Effect boards I used from eBay.

https://www.ebay.com/itm/Curre...?hash=item283e9b6153

You are right that this detection, while simple, does well on block entry but not on block exit.  On exit it may drop out after the engine leaves the block unless you are hauling passenger cars with incandescent lights.  I don't worry about the "overhang" right now as our blocks are long so I concern myself with just entry, not exit.  Time is calculated from entry to entry and I put in a delay before declaring "unoccupied" just to be safe.  A potentiometer allows you to adjust the pickup current but it won't detect cabooses with LED lights very well.  You could drop in a low ohm resistor in each caboose or around each EOT to suck enough current to declare occupancy.  Board outputs are dry 10a SPDT relay contacts to be used any way you choose.  No isolated track sections or overlays.  I detect all current at its source on our control panel so no wires to remote locations, a big plus.

I'm not sure what I'd do with absolute speed as I'd have to determine the sweet spot separately for each engine.  I'd rather set a nominal target speed for the first engine out and let the others queue up behind that one by adjusting their speed incrementally.  Obviously all speculation for now, we'll see how all this works out in a few months.  I'm sure that my scale MPH calculations will not be very precise but good enough for me, I hope.

Don, thanks for the info on your hall-effect board.  So do you just place the coil near the rail, and a train located anywhere on that isolated block of track shows current anywhere along that track?  If so, how do you isolate the block yet use a single power supply over multiple blocks?  Or is it detecting the train passing past the coil?  Dumb questions, I know! ;-)

If you don't want to connect all those relays directly to Arduino inputs, you can use a shift register.  I use a product called Centipede Shield from Macetech (though I don't use it as a shield; I mount it adjacent to my Arduino): http://macetech.com/store/inde...o&products_id=23  Great product and great support from those guys.

Either way, don't forget that relay contacts "bounce" and will register as multiple closures if you poll them too frequently.  Contacts can even "bounce" when released.  It's easily addressed with a bit of code, though some people prefer to debounce using hardware.  Just something to be aware of if your testing shows multiple entries into a block in a matter of a few milliseconds.

Randy P. posted:

Don, thanks for the info on your hall-effect board.  So do you just place the coil near the rail, and a train located anywhere on that isolated block of track shows current anywhere along that track?  If so, how do you isolate the block yet use a single power supply over multiple blocks?  Or is it detecting the train passing past the coil?  Dumb questions, I know! ;-)

If you don't want to connect all those relays directly to Arduino inputs, you can use a shift register.  I use a product called Centipede Shield from Macetech (though I don't use it as a shield; I mount it adjacent to my Arduino): http://macetech.com/store/inde...o&products_id=23  Great product and great support from those guys.

Either way, don't forget that relay contacts "bounce" and will register as multiple closures if you poll them too frequently.  Contacts can even "bounce" when released.  It's easily addressed with a bit of code, though some people prefer to debounce using hardware.  Just something to be aware of if your testing shows multiple entries into a block in a matter of a few milliseconds.

Here's what the current detectors look like on our control panel.  For each block you want to detect, you feed the center rail feed wire two or three times through the Hall-effect coil before connecting it on out to the track.  Each loop results in better current detection.  If the wire is big enough there is no voltage drop with this method of current detection.  Since I don't have my Arduino solution yet the on-board relay switches the center rail voltage of the block you are trying to slow down from 18v to 12v (adjustable), which TMCC boards can tolerate without resetting, and the resulting slowdown is enough to generate the required separation.  Some of the Legacy engines with Odessey Speed Control sluff off this drop and refuse to slow down very much, just doing what they are designed to do, not what I want them to do.  Hence my desire to slow all of them down more legitimately by keeping the voltage at 18v and use TMCC commands to slow the engines.  In that case the relay pulling in would cause an Arduino digital input to go high, causing me to output the slowdown commands to the engine needing slowdown.  I would use software to handle any relay "bounce" as I'm not looking for great accuracy here.  The red LEDs on the right indicate slowdown - unfortunately ON is no slowdown and OFF is slowdown.  Now back to choosing RFID and LCD components!

 

Attachments

Photos (1)

Very interesting, Don -- thanks for the education!

I suggest you consider using your relays as SPST to connect *ground* (low) to an Arduino input when a block is occupied, rather than pulling input high to indicate occupied.  That's because the Arduino has a built-in pull-up resistor on each digital input, and thus it's often easier (and more typical) to trigger an event when a pin gets pulled low.  Then, with the pullup resistor feature turned on, a disconnected pin will read high rather than floating. 

If you use high (+5v) to indicate an event, then you'll need something to pull the inputs low when they are not being tripped, because a disconnected input can "float" and be unpredictable -- it's not necessarily going to be seen as "low" when not connected.  Forgive me if you already know all this -- because if not, it's an important thing to watch out for.

So after playing around with mt Arduino Uno and associated IDE this weekend building a super "Blink" program and re-learning C syntax I'm ready for more challenges.  Want to blow the horn on one of my TMCC engines.  Thanks for the code samples, so I have a head start.  All the other TMCC modules use only the Tx and Gnd wires from the base daisy-chained to Rx and Gnd  on each control module, except the Action Recorder which sends its Tx line back to the TMCC base Rx pin.  I don't have an Action Recorder so can't I just connect the Tx and Gnd from an extra RS-232port on my Arduino Mega to the Rx and Gnd on the TMCC base unit.  Do I even need to connect  the Tx from the base to Rx on the Mega?  That would just add on to the current Rx daisy chain.  I have extra 3-wire RS-232 TMCC cables, can't I just connect one to the Mega and use one out of the TMCC base and cross-connect them appropriately?  Let me know if I have this close to being correct.  Sounds deceptively too easy.  What baud rate do I need to assign?  Thanks again for helping.

I wanted to thank all of you on your help, and report on my progress.  Thanks to your suggestions and code snippets I can successfully control all my Legacy and TMCC engines from my Arduino Mega 2560.  I just use the 3-byte so I can olny do the commands coimmon to both TMCC and legagy, but that's fine for my needs right now.  The latest version of my train control sketch is attached in case you are interested.  My main loop function is a mini task scheduler and as such no subordinate functions are allowed to use the infamous "delay" function, as that defeats the purpose of multitasking.

I'm now ready to code the sub-function dealing with RFID.  Please suggest a suitable RFID reader and preferred serial interface.  Also I need to buy some RFID tags to mount in the first car of each train so I can properly identify and register it.  Can you suggest some suitable, reasonably priced,  tags for this purpose?

Thanks for all your help.

Don

PS: As I look back thru the posts I see that I previously asked about RFID.  Now, however I'm actually ready to go and do it, so I could use your latest suggestions before I start.

Attachments

Files (1)
LATEST TRAIN CONTROL SKETCH for TMCC

Hi Don,

When I was experimenting with RFID, I was using the ID-20: https://www.sparkfun.com/products/11828.  This unit has a very good range and is easy to use with an Arduino.  Here is the Sparkfun hookup guide: https://learn.sparkfun.com/tut...882562561.1510038480

See below for more notes and sample code.  Good luck!

Randy

Here are some raw notes I'll copy and paste from my testing in 2015/2016 which may be helpful:

Only need 3 wires coming off of ID-20LA RFID reader.
Pin 1 (GND) and Pin 7 (FORM) to Ground
Pin 2 (RES) and Pin 11 (VCC) to +5v
Pin 9 (D0) to Arduino serial port Rx. I.e. pin 15 for Serial3 RX
Pin 10 (READ) sets to +5v on RFID read, so good for LED via resistor to ground

TESTING 1/21/16: Connected the RFID reader to an Arduino using just three wires: +5v, ground, and signal (RFID reader pin 9.) It is important to power the RFID reader from the same power as the Arduino for the signal to work reliably. Also connected RFID reader pin 10 to the POSITIVE side of an LED, with a resistor then to ground. LED lights when code is read.

CABLE LENGTH 1/21/16: Tested the RFID reader connected to Arduino via a 62-foot-long 4-conductor 22awg cable (I have 1000' of this, very inexpensive.) looked at signal quality at each end of the cable and it was excellent both ways. So standard serial connection (one wire) even over such a long distance works no problem! No need for RS-485 due to length of wire between RFID reader and Arduino.

Important: 1/21/16: If the power does not have a common ground and common +5v between the RFID reader and the Arduino, the RFID reader may APPEAR to work correctly, however each reading for a given tag is likely to be wrong and possibly different each time. A power reset must be done to correct this.
FOR THIS REASON, because it can spit out random incorrect IDs, we should probably track more than three digits of each code -- to minimize the possibility of a random (bad) code matching an actual code. We can at least do four digits with an integer value.

Finally, here is some test code I wrote a few years ago -- it might help you get started:

// Rev 06-29-2015 by RDP

#define RFIDBUFSIZE 16 // Size of data sent by RFID reader
#define RFIDSTX 2
#define RFIDCR 13
#define RFIDLF 10
#define RFIDETX 3

void setup() { // Set up code called once on start-up
Serial.begin(9600); // Serial monitor
Serial.println("RFID Card Reader Test");

Serial3.begin(9600); // RFID reader will connect to Serial3
while (Serial3.read() > 0) { // Clear out any incoming RFID junk
delay(1);
}
}

void loop() { // Main code, to run repeatedly

if (Serial3.available()) {
byte rfidCode = getRFID();
Serial.print("Scanned RFID: ");
Serial.println(rfidCode);
// check to see if we already have registered this code, etc.

}

Serial.print(".");
delay(2000); // kill some time

}

////////////////////////////////
/// Define various functions ///
////////////////////////////////

byte getRFID() {
/* Gets called if there is at least a byte of data waiting in the incoming
serial buffer for the RFID reader. This routine reads the entire RFID,
does some basic confirming that it's a good read, and returns a byte 0..255
based on the last two hex digits of the scanned RFID code.
We could use all 10 hex digits, but totally unnecessary. We simply need
to be sure that we don't use two RFID tags that share the same last two
digits. There are 256 possible values, so not much chance of dup tags.
Assumes using the ID-20LA RFID reader.

Connect RFID reader pins as follows:
Pin 1 and Pin 7 to Ground
Pin 2 and Pin 11 to +5v
Pin 9 to Arduino serial port Rx. I.e. pin 15 for Serial3 RX

RFID reader sends 16 bytes each time it is activated:
Byte 1 = STX (0x02)
Byte 2..11 = 10 bytes of data, the unique ID. ASCII '0'..'9', 'A'..'F'
Byte 12..13 = 2 bytes of checksum
Byte 14..15 = CR + LF (decimal 13 and 10)
Byte 16 = ETX (0x03)
A typical string coming from the serial interface, in decimal, might be:
2, 54, 56, 48, 48, 57, 51, 55, 53, 67, 55, 52, 57, 13, 10, 3
We would look at 67, 55 -- which is 'C' '7' in ASCII read as hex, which is 199 decimal.
This is calculated as (12 * 16) + 7 = 199.
*/

byte rfidData[RFIDBUFSIZE]; // Buffer for incoming data

// Wait for all 16 bytes to be reading in serial buffer
do {} while (Serial3.available() < RFIDBUFSIZE);

for (int RFIDByte = 0; RFIDByte < RFIDBUFSIZE; RFIDByte++) {
rfidData[RFIDByte] = Serial3.read();
// Serial.println(rfidData[RFIDByte]);
}

if ((rfidData[0] != RFIDSTX) || (rfidData[13] != RFIDCR) || (rfidData[14] != RFIDLF) || (rfidData[15] != RFIDETX)) {
Serial.println("Houston, we have a problem!");
} else {
// Serial.println("All internal bytes look okay!");
}

// We will take the last two digits of the RFID code.
// ASCII '0'..'9' is 48..57. Subtract 48 from ASCII value to get value 0..9.
// ASCII 'A'..'F' is 65..70. Subtract 55 from ASCII value to get value 10..15.
// Then multiply by 1 or 16, depending on the position.

byte rfidCode = 0; // 00..FF = 0..255
if (rfidData[9] <= 57) { // Must be ASCII '0'..'9'
rfidCode = (rfidData[9] - 48) * 16;
} else { // Must be ASCII 'A'..'F'
rfidCode = (rfidData[9] - 55) * 16;
}
if (rfidData[10] <= 57) {
rfidCode = rfidCode + (rfidData[10] - 48);
} else {
rfidCode = rfidCode + (rfidData[10] - 55);
}

return rfidCode;
}

 

Hi Randy,

Thanks again for your help.  I copied your code and "Furgersonized" it (as my matching braces must line up vertically - a nit) and have ordered the RFID reader (and a breakout board) that you suggested.  I'll use it as a test and then integrate it into my overall project.

I was going to read all 16 characters and compare them but I'm only using 30 tags so hopefully the two characters you chose will be unique.  And makes comparison faster.  I'll let you know how I make out in a few weeks.

Then to connect a 4-line LCD panel via I2C to output results and I'll be pretty much done.  A fun project.  When my project is done what is the best way to share it in the public domain, as everyone seems to do?

Thanks again,

Don

Hi Don,

I always format my code -- the tabs were stripped when I pasted into the post ;-)  Since I opted not to use RFID to identify my locos (my Arduino prompts the operator for loco starting locations, defaulting to last-known, so it's a lot faster) I didn't have an .ino file to post -- just some clipped text saved in my OneNote notebook where I keep track of test results and such.

If you'd like to share your code, in my experience the most typical way would be to post it on GitHub.  A bit of a learning curve to set up an account and learn how to check in projects, but well worth it for the version control it provides.  It's free as long as your code is public, so that's a win-win.  Of course, you'll need to post a link to it where people will see it, such as here.

Randy

Add Reply



OGR Publishing, Inc. PO Box 218, Hilliard, OH 43026 330-757-3020
www.ogaugerr.com
×
×
×
×
×