After having designed a shield to interface Arduino with a DCC bus, today I’ll show you how to realize a simple accessory decoder to control a couple of leds.
Accessory decoder
The DCC standard (specifically the document S-9.2.1 DCC Extended Packet Formats) defines different types of decoders, which are devices – connected to the DCC bus – that perform actions based on the command they receive.
An accessory decoder is “intended for control of a number of simple functions such as switch machine control or turning on and off lights“.
a DIY decoder by Paco
Addressing
Each decoder is assigned one or more addresses. The document S-9.2.1 linked above explains in details the format of a DCC packet sent to an accessory decoder (basic accessory decoder packet format):
9 bits of the packet (AAAAAAAAA) defines the address of the decoder. The address’ most significative bits (MSB) are the ones in the second bytes, while the least significative bits (LSB) are the ones in the first byte. To make things more complicated, the bits in the second byte are in ones’ complement representation.
Let’s understand it with an example:
Most significative bits are 011. If you ones’ complement them, they become 100.
Least significative bits are 0010011. When you concatenate the two parts, you get 1000010011, therefore the decoder’s address is 267.
2 bit (PP) represents the address of a single port of the decoder. Conventionally, an accessory decoder has 4 ports, each one with a couple of outputs.
Bit O chooses, for the port represented by the PP bits, the output the command is related to. Finally, bit C is the action and defines if that output must be activated (1) or disactivated (0).
Some manufacturers prefert to use a “flat” addressing (not distinguishing between decoder address, port number and output). A good explaination about the different addressing methods (MADA, PADA…) is available on the Rocrail wiki.
Arduino
Before developing the sketch for your accessory decoder, you have to install – using the Library Manager – the NmraDcc library in your Arduino IDE:
Then include it in your sketch and create an instance of the NmraDcc object:
#include "NmraDcc.h" NmraDcc Dcc; |
In the setup() you have to configure and initialize the library. First, call the pin() method to tell the library which Arduino pin the DCC signal is connected to (ExtIntPinNum) and the corresponding interrupt number (ExtIntNum). In addition, you can enable or not the internal pullup resistor:
void pin(uint8_t ExtIntNum, uint8_t ExtIntPinNum, uint8_t EnablePullup); |
The list of suitable pins for each Arduino board is available in the wiki and you can use the digitalPinToInterrupt() method to get the interrupt number that correspond to each pin.
If you’re using the shield I designed with an Arduino Uno, you can choose pin 2 with the pullup resistor enabled:
#define DCC_PIN 2 [...] Dcc.pin(digitalPinToInterrupt(DCC_PIN), DCC_PIN, 1); |
Let’s now configure the library, with the method:
void init(uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, uint8_t OpsModeAddressBaseCV); |
The first two parameters are the manufacturer code and the decoder version; the DCC command station can get those values reading the corresponding CVs (configuration variables). For self-made decoders, the ManufacturedId is set to MAN_ID_DIY.
With the third parameter you can set, in OR, some flags:
For accessory decoder, you have to specify at least the third flag (DCC_ACCESSORY_DECODER).
When the library is correctly configured, in the loop() you have to call as frequently as possible the process() method to let the library handle the incoming messages:
void loop() { Dcc.process(); } |
When the library receives a message for an accessory decoder, it can execute different callback functions, depending on what you defined in your sketch:
- notifyDccAccState()
- notifyDccAccTurnoutBoard()
- notifyDccAccTurnoutOutput()
- notifyDccSigState()
The first 3 methods are called for basic accessory decoder packets, while the last for extended accessory decoder control packets.
For my led decoder, I decided to implement the notifyDccAccState() method:
void notifyDccAccState(uint16_t Addr, uint16_t BoardAddr, uint8_t OutputAddr, uint8_t State) |
The method provides some variables. Let’s see their meaning (remember what you learned in the first part of this article about addressing):
- BoardAddr is the decoder address (9 bits)
- OutputAddr contains the 3 bits PP and O, that are the address for the port and the output
- State is the action (1 = activate, 0 = deactivate)
- Addr is the direct port address (PADA mode)
You can divide the port address (2 bits) from the output address (1 bit) with:
int portAddress = (OutputAddr >> 1) + 1; int outAddress = OutputAddr & 0x01; |
In the code I added 1 to the port address to be able to number the ports from 1 to 4 instead of from 0 to 3.
To keep it simple, the decoder developed for this article has hardcoded in the sketch both the board address and the port address (in a future article I’ll explain how to have the address configurable at runtime):
#define BOARD_ADDRESS 5 #define PORT_ADDRESS 1 |
In the sketch you can verify that the command received is for your decoder with:
if((BoardAddr == BOARD_ADDRESS) && (portAddress == PORT_ADDRESS)) { |
and change the output status of two digital pins (to which I connected the leds) based on the output number and the command:
if(outAddress == 0) { digitalWrite(RED_LED_PIN, HIGH); digitalWrite(GREEN_LED_PIN, LOW); Serial.println("! RED led activated"); } else { digitalWrite(RED_LED_PIN, LOW); digitalWrite(GREEN_LED_PIN, HIGH); Serial.println("! GREEN led activated"); } |
The source code of the decoder is available in my Github repository, here’s a video that shows how it works:
Hello ,Saw you video in You tube great job.
Do you know how to accomplish this without DCC shield that you are using
more I was looking for a bread board example using Uno
Thanks
Hi, you only need to add to your breadboard the same “dcc interface” I’m using in my shield…
Congratulations Luca, you did a really good tutorial.
I’m developing a board to create a decoder for signals. this is composed of an AtMega328 chip, 2 ULN2803 to have an amplified output, with in addition a 5v and 12v power supply and the input for DCC with an optocoupler.
At the moment I program it via serial but I would like to know more about CV so I don’t depend on serial anymore and configure the different aspects of the signals via CV as the QDecoder product does more or less.
I am waiting for more tutorials on DCC topic
Thanks Marcello
Thanks Marcello, I’m working on a series of tutorials about DCC and Arduino… stay tuned 🙂
Hello.
I am from Poland and I found your website. I don’t speak English, I use google translator
I have a question. I made a decoder based on arduino nano. When I send 1AAACDDD instruction to it, the decoder only receives when CDDD = 0010 or 1010 or 0110 or 1110 why?
It does not receive for addr = 1 and 3
Hi Marek. Not sure to have understood your question: are you using the NmraDcc library? The “AAA” bits are the most-significant bits (1-complement) of the address (see S-9.2.1) and the library takes care of “merging” them to the remaining bits to give you the full address in the callback method.
I did not expect you to answer so quickly – thank you. Yes I using NmraDcc. CDDD is your CPPO. I send to the decoder from my arduino Mega and my program. Preanble 16 bits “1” then Pacet start bit “0” then the low byte of the address 1AAAAAAA then bit 0 and then 1AAACDDD (your CPPO) then Pacet start bit “0” then Error detection data then Packet END bit “1”. As in s-9.2.1
I send 10AAAAAA = 10000000 DEC=129, 1AAACDDD(CPPO)=11110010 and the decoder receives Addr = 2 (second pair), Outpower = 0 (becouse C=0), Direction = 0(information that the decoder controls the first output of the pair), and that’s ok,
wen I send 10AAAAAA = 10000000 , 1AAACDDD=11111111 the decoder is not picking, and it should be Addr =4 (fourth pair), Outpower =1 (becouse C=1), Direction = 1(information that the decoder controls the second output of the pair. Unless I get it wrong
The decoder only understands what comes to it when CDDD = 0010 or 1010 or 0110 or 1110
Quite strange… I gave a look at the Library source code and there’s nothing that prevents from receiving CDDD = 1111. Did you set the FLAGS_OUTPUT_ADDRESS_MODE flag (to use the notifyDccAccTurnoutOutput() callback)? If so direction is bit D0, OutputPower is bit C and OutputAddress is made by A bits and D1D2 bits. The only thing is that the library substract 1 when calculating the outputAdress:
OutputAddress = (((BoardAddress - 1) << 2 ) | TurnoutPairIndex) + 1 ;
Maybe you can try to use the alternative callback method (notifyDccAccTurnoutBoard) that gives you separate parameters (
notifyDccAccTurnoutBoard( BoardAddress, TurnoutPairIndex, direction, outputPower );
) to see what’s happening under the hood…I send 10AAAAAA = 10000001 DEC=129 sorry
For three days I thought if I understood everything correctly. Turns out the bug was in sending commands in my base program. But I found hooray. I’m sorry everything is ok. By the way, you are doing a very good job and have the gift of translation. I will be waiting for your next articles. Good luck. Now I am working on a train signal decoder