Skip to main content

Reply to "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

Images (1)
  • Arduino Legacy RS232

OGR Publishing, Inc., 1310 Eastside Centre Ct, Suite 6, Mountain Home, AR 72653
800-980-OGRR (6477)
www.ogaugerr.com

×
×
×
×
×