|Greg Ciurpita||October 11 2018|
An NCE compatible Arduino cab
is an interesting project and
an inexpensive way of adding extra cabs to the layout.
I built a simple cab, no LCD and without a knob
that uses button presses for controlling speed.
I was initially curious about the NCE cab bus. I bought a USB/RS-485 adapter and tried monitoring the PowerCab's cab bus with my laptop. I just connected to the pair of wires carrying the cab bus. I wrote some code to read, translate and format the cab bus poll messages but wasn't going to see much more without a cab responding to them. The PowerCab messsages did not appear on the cab bus.
I made an attempt to send a response to a poll from the laptop, but wasn't surprised that it wasn't recognized because a response must be within 800 usec of the poll. The Windows OS isn't designed for this without special drivers.
I started using an Arduino Mega with an MAX485  chip to interface to the cab bus. The Arduino Mega supports multiple serial interfaces. Again, the MAX chip was connected to the cab bus wires but Arduino power came from the USB connector as well as supporting the serial monitor. The zeroth interface had to be used for the serial monitor. That meant using one of the others with the MAX chip. I got this all working and could see poll message for various cabs reported through the serial monitor. I then wrote Arduino code that recognizes a poll message for a specifiic hardcoded address and report it on the serial monitor.
The next step was to modify the Arduino code to receive a cab bus response from laptop through the serial monitor, and when one is a available, send it after receiving a poll for the cab address. The initial response has to be the Select Loco and digits for a loco on the layout. Then I could try button zero to turn the headlamp on or off. Getting this to work was a major accomplishment. The next step was to build a button pad and write the Arduino code to monitor it.
I rebuilt all the hardware needed for a cab on an Arduino Uno shield board. This meant I lost the serial monitor available using the Arduino Mega. So I used the laptop and USB/RS-485 adaptor to monitor the cab bus to see the polls to my cab and how it responded. Eventually I got every thing to work.
Final piece was to be able to program the cab address in EEPROM. Code was needed to recognize that a button is held down during power up, then read the address and program it.
I found a box the Arduino and shield fit into. Momentary switches protruded through holes cut in the snap on lid of the box. I added an LED which could be controlled by the PowerCab. The MAX chip and regulator were mounted on the underside of the sield. I cut a notch in the bottom of the box for an RJ-12 connector slide into. A four conductor RJ12 cable connects the cab to the PCP the PowerCab is plugged into. A printed a sheet identifying the buttons that I punched holes into and taped to the lid. I'm sure someone better at fabricating enclosers can do a better job, but I was anxious to road test the cab.
HardwareA cab is connected to a command station (PowerCab in my case) with two pairs of wire. One pair provides power (~13V) and the other pair is an RS-485 interface supporting the Cab Bus.
An RS-485 interface is duplex in that the same pair of wires is used to send data in both directions. A device must disable its transmitter when not transmitting. Data is transitted over both wires, neither is ground. The A lead is more positive than the B lead when a one is transmitted and the reverse for a zero. This support transmission at higher speeds over longer distances.
A MAX-485 chip can be used to interface the Arduino TX and RX pins to the RS-485 bus. A third Arduino pin to enable the transmitter. The MAX chip has a pin to enable its transmitter when high and enable its receiver when low. These can be tied together and to the Arduoino pin so the the Arduino does not receive data it transmits.
The NCE Cab Bus Communications Protocol 
describes how the command station polls a cab,
gives a cab a chance to transmit a response and
the codes sent by the cab in response to the polls.
It also describe commands that can be sent by the command station
after the response from the cab.
For a simple cab,
the cab sends a code when a button is pressed and
may receive a command to turn on/off the LED.
The 16 button switches are arranged in a matrix of four rows and four columns. Each row is connected to an Arduino GPIO pin configured as an output and each column is connected to a GPIO configured as an input with an internal pull-up resistor. The row GPIOs are normally set HIGH and the inputs are normally HIGH because of the pull-up resistors. A button connects a row to a column. When the row GPIO is LOW, the button pulls the column GPIO input LOW. Knowing the row that is LOW and the column pulled LOW identifies the button being pressed.
The cab LED is simply an LED and 1k resistor connected between Vcc and digital I/O pin 11 configured as an output. The LED is on when the pin-11 is driven low.
While the Arduino has a regulator, it's max input voltage is only 12V. I used a 5V regulator to drop the ~13V from the command station to feed 5V to the Arduino. The Arduino doesn't require much current so the regulator shouldn't need to dissipate much heat. I haven't noticed any problems after using the cab for over a year.
FirmwareThe firmware normally monitors for button presses and the next time the cab is polled responds with a two byte message indicating the button and speed if using a knob.
During normal operation, the cab bus code is repeatedly called from the Arduino loop(). It waits for a poll message match the cab address and if there is a pending message it sends it. (This is something the Windows laptop couldn't do and it's ability to respond so quickly is one reason I call it firmware). It also tracks the last polled address and processes any commands sent after being polled. For a simple cab, these are just cursor/LED on/off commands.
Checking for a button press invokes a routine that sequentially drives each row LOW and scans for grounds on the four column inputs as discussed above. The row and column of the button are returned. They are used to index into a table that contains the NCE cab bus code for the button. An NCE button code is passed to the cab bus monitoring code and sent during the next polling cycle.
This approach of getting a row/column and using a table may seem a bit overboard. There are two reasons. The less important one is that a table identifies the physical location of the buttons and is easier to maintain if you wanted a different button layout. The other reason is decimal values are needed when programming the loco address using the Select Loco button. In the Select Loco case, decimal values are sent back to the command station.
Reading decimal values are also needed when programming the cab address. The cab address is read from EEPROM within the Arduino setup(). The NCE cab address can be changed by holding down the Select Loco button when the cab is plugged in. When this is recognized, different functions are called within loop(). A function reads multiple decimal button values to determine the cab address and when Enter is recognized, writes the value to EEPROM and updates the cab bus code. Setting the cab address is handled within the cab. There are no messages sent to the command station during this process. Once a new cab address is updated, the cab firmware continues with normal processing as described earlier.
The posted code is a bit more involved. I'd rather post what's been running reliably than make some hasty last minute changes (an exercise for the student). It includes debug code through the Serial monitor and an option to use a second Serial interface from using the Mega. The code is also divided into four files: Cab12.ino (yes 12 versions), cabbus.cpp, keypad.cpp and Led.cpp. I use object oriented techniques avoiding global variables. For example, a cabbus function is invoked to set the cab address in cabbus.cpp. So these files could be re-used for other applications (even if just looked at). Led.cpp simply blinks the LED indicating that the code is continuing to run.
All files are availble at the link below . These should all be in a format (.ino, .cpp, .h) that are directly compatible with the Arduino IDE.
Some ThoughtsI've been very satisfied with the cab. I like the simplicity of using buttons to control speed and the forward/reverse rather than a direction button. But I have some thoughts on modifications and improvements that those who do build their own controller might like to try.
For those that prefer to add a knob for speed control, I tested the idea with an early version of the code and some code to support a knob is still in the code. But the cab should either support the knob or buttons.
For simplistic operation, I think twelve buttons may be enough When selecting a locomoitive or cab address, twelve buttons are needed. During normal operation, some of those same twelve buttons can have different puposes. Here's one possible set: Select Loco, Fwd, Rev, faster, slower, 5-steps faster, 5-steps slower, Horn, Brake, Headlamp/0, 1, and 8 (sound on/off). The tables make it easy to support different buttons. Of course an additional row of buttons can be added.
A more challenging idea is to determine the speed sent to the command station based on the physics of the train, the throttle setting and use of brakes. Knowing the size of the train would be very helpful. The speed step sent to the command station would gradually increase/decrease.
Having a cab where the firmware can be modified to change its behavior makes many things possible.