DCC, configure the decoder with CVs

In my previous tutorial I explained how to build a simple DCC accessory decoder with Arduino.

To keep the sketch simple, all the configuration parameters for the decoder (in particular its address) were defined as constants:

dcc-cv-001Often it can be useful to change the configuration of the decoder without re-programming its firmware (= the sketch that runs on the ATMega microcontroller). The DCC standard includes a document (S-9.2.2 DCC Configuration Variables) that introduces – for the decoders – the idea of Configuration Variables (CV).

CVs are parameters, stored in a non-volatile way (it means that the values are kept also when the power is off) in the decoders, which can define the behavior of the decoder itself.

NMRA defines, in the same S-9.2.2 document, a list of CVs – numbered from 1 to 1024 – and a meaning for each of them. For example, for an accessory decoder it is established that its address (9 bits) is contained in CV513 and 521:

dcc-cv-002

When you buy a commercial decoder, in the manual it is always given a table that indicates, for each supported CV, the meaning. For example, the manual of the ESU SwitchPilot Servo decoder, a decoder able to command servos to activate turnouts or other movements, lists the CVs that control position and speed of the 4 servos:

dcc-cv-003

Many digital stations support the service mode (document S-9.2.3), that is, they are able to program the values of the CVs of a decoder connected to a particular output, called the programming track. The model maker can therefore adjust the behavior of the decoders installed in the locomotives or that control accessories simply positioning the locomotives (or connecting the accessory decoders) on the programming track and programming the correct values in the CVs.

CV1 vs CV513

The NMRA standard provides that the CVs that can be used by accessory decoders are those from 513 to 1024. In fact, many manufacturers, in order not to create confusion or differences between the different types of decoders, have decided to use “low” CV numbers for accessory decoders. This is the reason why many accessory decoders use CVs 1 and 9 to configure their address.

NmraDcc

Once you have learned the meaning of the CVs, let’s see how to manage their programming in our sketch.

We have to store the CV values in a memory that is not erased every time we turn off the decoder. The ATMega328 processor used by the Arduino Uno has a non-volatile memory of 1024 bytes and, thanks to the Arduino’s EEPROM library, we can store and read values from this memory.

The NmraDcc library can independently manage the CVs or – alternatively – leave it completely to the external program.

For example, in the case of writing a CV (the command station that writes the value of a CV in the decoder), the NmraDcc library offers two callback functions:

  • extern uint8_t notifyCVWrite( uint16_t CV, uint8_t Value);
  • extern void notifyCVChange( uint16_t CV, uint8_t Value);

The first substitutes the standard CV handling of the library, while the second one is called to notify that a CV changed its value. If you inspect the source code of the library, the difference is clear:

dcc-cv-004

The library also defines the numbers of some standard CVs, including those related to the decoder address:

dcc-cv-005

LED decoder

Let’s go back to the sketch of the LED decoder of the previous tutorial and add the CV management.

Define two CVs:

  • the working mode (CV10)
  • the blink frequency (CV11)

Thanks to CV10 it will be possible to choose whether the activated LED will be stready on (CV value = 0) or blinking (CV value = 1). In this second case, the blink frequency will be configured via the CV11 (from value 1 = 5 seconds to value 100 = 50ms).

First, let’s define a constant for each CV and its number:

#define CV_ACCESSORY_DECODER_MODE        10
#define CV_ACCESSORY_DECODER_BLINK_FREQ  11

You also need some variables that will contain the runtime value for those CVs to avoid continuously reading the EEPROM (which, according to the datasheet, has a life cycle of about 100,000 reads/writes):

int decoderMode;
int blinkFrequency;

In the setup() method, you have to read the values of the CVs from the EEPROM and store them in the variables:

decoderMode = Dcc.getCV(CV_ACCESSORY_DECODER_MODE);
blinkFrequency = Dcc.getCV(CV_ACCESSORY_DECODER_BLINK_FREQ);

Let the library handle the CVs. To be informed about when a value changes and update the variables accordingly, implement the notifyCVChange() method:

void notifyCVChange(uint16_t CV, uint8_t Value) {
 
  if(CV == CV_ACCESSORY_DECODER_MODE) decoderMode = Value;
  else if(CV == CV_ACCESSORY_DECODER_BLINK_FREQ) blinkFrequency = Value;
}

Blink

To make the led blink, you can use the Timer1 library which allows to trigger a callback function periodically:

This callback function simply changes the led status:

void blinkLED() {
  digitalWrite(ledActive, !digitalRead(ledActive));
}

If the decoder is in blink mode, when the output status is changed, the active led is identified and the callback function is scheduled according to the frequency configured in the appropriate CV. Otherwise the led is simply switched on:

if(outputInPair == 0) {      
  digitalWrite(GREEN_LED_PIN, LOW);
  ledActive = RED_LED_PIN;
} else {
  digitalWrite(RED_LED_PIN, LOW);
  ledActive = GREEN_LED_PIN;    
}
if(decoderMode == 1) {
  Timer1.setPeriod(5000000 / blinkFrequency);
  Timer1.attachInterrupt(blinkLED);
} 
else digitalWrite(ledActive, HIGH);
}

lettura CV

The DCC standard also includes the possibility – for a command station – to read the value of a CV of a decoder connected to the programming track. In a next article you’ll learn how to implement this feature!

Demo

Here’s a video that shows how it is possible to modify the behavior of the led decoder programming its CVs using a laptop that runs the Rocrail application connected to a SPROG command station:

DCC, led accessory decoder

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.

dccled-101 dccled-100

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“.

dcc2servo2relay

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):

dcc-001

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:

dcc-003a

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).

dcc-002

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 (MADAPADA…) 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:

dcc-acc-001

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:

dcc-acc-002

For accessory decoder, you have to specify at least the third flag (DCC_ACCESSORY_DECODER).

FLAG_MY_ADDRESS_ONLY

The first flag tells the library to filter the DCC messages it receives and to process only the ones sent to the decoder address. I usually do not set that flag, because I want my sketch to be able to “see” each incoming message. In my code I then decide which messages to process and which not.

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: