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:
Often 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:
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:
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.
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:
[checklist]
- extern uint8_t notifyCVWrite( uint16_t CV, uint8_t Value);
- extern void notifyCVChange( uint16_t CV, uint8_t Value);
[/checklist]
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:
The library also defines the numbers of some standard CVs, including those related to the decoder address:
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:
[checklist]
- the working mode (CV10)
- the blink frequency (CV11)
[/checklist]
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); } |
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:
Molto molto interessante. !!
Compliementi Luca, molto interessante.
Vorrei saperne di più sulla configurazione tramite CV.
Sto sviluppando un decoder per segnali configurabile via seriale ma vorrei aggiungere anche l’opzione CV’s
grazie Marcello! Sto sviluppando meglio il tema DCC con dei video su Youtube: i prossimi saranno proprio relativi allo sviluppo di un decoder e alla sua programmazione via CV!