One of our customers was trying to build an accessory decoder using our I2C-RELAY16 to drive a bank of relays for high current loads, and they were having a bit of trouble. So, I thought I’d sit down and work through the issues tonight, as I’ve always thought having an accessory decoder with isolated, high current relay outputs might be nice.
The basic hardware is really simple – it’s just an ARD-DCCSHIELD and Arduino Uno connected to an I2C-RELAY16 and a cheap Chinese 16-relay module. I’ve also added a decent (supposedly) UL-listed 2A switching power supply from Amazon, though in reality you likely don’t need 2A. The relay board should only draw ~500mA@12V, and the Arduino maybe another 100mA – you could definitely use one of our high quality Triad 12V 1A switching power supplies. in order to power the whole thing, and a cheap voltmeter module to verify that the supply is on and putting out the right voltage.
The ARD-DCCSHIELD is configured as follows:
- JP1 – both jumpers on to provide I2C pull-ups
- JP2 – connect /IORST to VIO
- JP3 – no jumpers
- JP4 – DCC on D2
- JP5 – no jumpers (isolation between DCC power and local power)
- JP7 – both jumpers on to provide DCC programming ACKs
The I2C-RELAY16 is configured with J5, J6, and J7 all jumpered low.
Both of these sketches require two libraries: Alex Shepherd’s fantastic NmraDcc library (available from the Arduino library manager now) and our Relay16 library. Sorry, ours isn’t available through the Arduino library manager because of some limitations on their part, but the documentation page above describes how to get and install it.
After that, you’ll need the actual application code. I’m storing it as examples in the ARD-DCCSHIELD’s src/ directory. Go download the latest zip file of source and design files from Github, unzip it, and look in the src/ directory. I actually built two different accessory decoders that serve slightly different purposes:
- NmraDccAccessoryDecoder_Relay16 – The first version is just sixteen high current outputs that can be switched on and off. (The relays are rated for 10A at both 125VAC and 30VDC, though those are Chinese amps, so I’d trust it for 3-5A in reality.) This would be great for turning staging tracks (or roundhouse tracks) on and off, or turning lights inside structures on and off. Each one is accessed by a single output address (aka turnout) number, starting with the address programmed into the CVs in the Arduino and going for the next 16. So, for example, if you programmed the decoder to address 32, your outputs would be on accessory addresses 32-47. Setting the “turnout” to normal will turn the relay on, and setting it to “reverse” will turn it off.
- NmraDccAccessoryDecoder_Pulsed_Relay16 – The second one is pulsed, for driving up to eight twin coil switch machines a capacitor discharge supply. Each “turnout” gets two relays, one for normal and one for reverse position. To throw the switch machine, one of the two relays will turn on briefly and then automatically shuts back off, assuring that the coil won’t burn up. In this case, the decoder works on the next eight addresses from whatever is programmed in the CVs of the Arduino.
Just pick the one you want, open up the appropriate sketch in your Arduino environment, and download it into the Arduino. That should be all there is to it to get started. Either sketch will default to accessory address #1, so if you want it somewhere else, you’ll need to set the appropriate CVs with your DCC system.
What CVs are those, you might ask? CV1 and CV9 deal with the accessory decoder address. See below – “A Note on Accessory Addressing” – for the brain pretzel the NMRA has left for us there.
The Relay16 on/off decoder doesn’t have any additional configuration options. The Pulsed example, however, has two additional CVs of interest that can be configured:
- CV2 = Coil activation time in 10 millisecond units. By default, it’s set to 50, which gives you an on time of 500mS or half a second.
- CV3 = Capacitor discharge unit recharge time. If you’re using a CDU to drive your turnout coils, this is the amount of time (in 10mS increments) that it takes to recover from driving a coil. By default, it’s set to 30 (aka 300mS, or roughly a third of a second).
(Alternately, given that you have the source code, you can change the default CVs right in the source and not have to mess with programming them through your DCC system. You’re looking for CV_ACCESSORY_DECODER_ADDRESS_LSB and CV_ACCESSORY_DECODER_ADDRESS_MSB, right near the top of the Arduino sketch.)
A Note on Accessory Addressing
DCC accessory (turnout) addresses are a bit non-standard. At its core, the DCC standard allows for 2044 different accessory addresses, where each one can either be set on or off. (The remaining addresses, 2045-2048, are reserved for broadcasting commands to all accessory decoders.) The gory details of this come from NMRA standard S-9.2.1, section D, “Basic Accessory Decoder Packet Format”.
This is where things get a bit weird. 2048 different addresses are really 11 bits of information (2047 = 0b11111111111). The upper 9 bits of the address are used as the “decoder address”. That means that decoders naturally get groups of 4 accessory outputs. This is sort of wasteful and confusing, but that’s the way it is.
Of that, it even gets weirder. NMRA standard S-9.2.2 defines the standard “configuration value” (aka CV) numbers. For accessory decoders (as defined in table 3), the middle six bits of the 11 bit address go in CV1. The upper 3 bits of the address go in CV9. The lower two bits are just transmitted as part of the packet, and therefore a single 9-bit “decoder address” must deal with up to four accessory addresses.
Confused yet? Yeah, so am I, and I read these sorts of specs all day at work. Here’s the basic address pattern and where the bits go:
0b is just a prefix reminding us that this is a binary number.
UUU is the upper 3 bits. It goes in CV9.
MMMMMM is the middle 6 bits. It goes in CV1.
LL is the lower two bits. It doesn’t go anywhere, as accessory decoders always get 4 accessory addresses.
Let’s work some examples. That’ll make this hopefully a bit clearer.
Let’s say we’re going to give our 16-channel decoder from above accessory addresses 17-32. We always use the starting (lowest) address for the calculation. If we convert 17 to binary (literally type “17 in binary” into Google is probably the easiest way), we get:
Now, make that 11 bits long and line it up with our pattern from above:
That means that for this example, we’d program
See, that wasn’t that bad, right?