/* Library: DCC_Decoder Original file: DCC_Basic_acc_Decoder File: DCC_Decoder-Switch2_16 Descr: DCC-switch for 16 outputs By: Ed den Ouden 2022 DCC-Edd (public domain) Remark: In order to function properly this library needs an active DCC locomotive to be on the track | Green | Red ----------+-----------+--------- Arduino | Pin x | Pin x+1 DCC | Disable | Enable Rocrail | Off | On DCC addresses decoder: 17 to 32 (16 switches), adjustable Available extra pins: A5 on Uno, A5-A7 on Nano, many more on Mega Rocrail settings: Protocol: default, Parameter: 0, Value: 0, Pulsetime: OFF, Accessory: ON Address-port: 5-1, 5-2, 5-3, 5-4, 6-1, etc */ #include // library to use for this sketch #define kDCC_INTERRUPT 0 #define DEBUGGER // debugger, uncomment to use debugger, comment to ignore debugger #define SERIALMON // serial monitor, comment/uncomment to switch off/onn const int pulseTime = 0; // pulsetime in ms for the accessory, 0 is switch on/off without pulse // pulsing is only usefull when using a push button in modeltrainsoftware (no toggle switch!) const byte numAccessories = 16; // nr of accessories/channels const boolean outputIdle = LOW; // output state when idle const boolean outputActive = HIGH; // output state when activated const byte startPinArduino = 3; // first output pin on Arduino const int dccStartAddress = 17; // first DCC address to respond to typedef struct { // internal datafields for the DCC-decoder int address; // address to respond to byte output; // state of (active) output 1=on, 0=off int outputPin; // arduino output pin to drive boolean isDigital; // true=digital, false=analog. If analog must also set analogValue field boolean isFlasher; // true=flash output, false=no time, no flash. (this parameter flashes the signal a few times) byte analogValue; // value to use with analog type (PWM 0-255) int durationMilli; // milliseconds to leave output on for. 0 means don't auto off unsigned long onMilli; // used internally for timing unsigned long offMilli; // used internally for timing } DCCAccessoryAddress; DCCAccessoryAddress gAddresses[numAccessories]; // array size void ConfigureDecoder() { /* set DCC-addresses, outputpins and behaviour: copy & paste as many times as you have accessories check NUMACCESSORIES and increment accessory[x] init can also be done in a for-next loop DCC signal pin: 2, control led: pin 19(A5) 16 pins Uno/Nano: 3-18 (8 turnouts or 16 accessories) 32 pins Mega: 22-53 (16 turnouts or 32 accessories) */ gAddresses[0].address = dccStartAddress; gAddresses[0].output = 1; gAddresses[0].outputPin = startPinArduino; gAddresses[0].isDigital = true; gAddresses[0].isFlasher = false; gAddresses[0].analogValue = 0; gAddresses[0].durationMilli = pulseTime; gAddresses[1].address = dccStartAddress + 1; gAddresses[1].output = 1; gAddresses[1].outputPin = startPinArduino + 1; gAddresses[1].isDigital = true; gAddresses[1].isFlasher = false; gAddresses[1].analogValue = 0; gAddresses[1].durationMilli = pulseTime; gAddresses[2].address = dccStartAddress + 2; gAddresses[2].output = 1; gAddresses[2].outputPin = startPinArduino + 2; gAddresses[2].isDigital = true; gAddresses[2].isFlasher = false; gAddresses[2].analogValue = 0; gAddresses[2].durationMilli = pulseTime; gAddresses[3].address = dccStartAddress + 3; gAddresses[3].output = 1; gAddresses[3].outputPin = startPinArduino + 3; gAddresses[3].isDigital = true; gAddresses[3].isFlasher = false; gAddresses[3].analogValue = 0; gAddresses[3].durationMilli = pulseTime; gAddresses[4].address = dccStartAddress + 4; gAddresses[4].output = 1; gAddresses[4].outputPin = startPinArduino + 4; gAddresses[4].isDigital = true; gAddresses[4].isFlasher = false; gAddresses[4].analogValue = 0; gAddresses[4].durationMilli = pulseTime; gAddresses[5].address = dccStartAddress + 5; gAddresses[5].output = 1; gAddresses[5].outputPin = startPinArduino + 5; gAddresses[5].isDigital = true; gAddresses[5].isFlasher = false; gAddresses[5].analogValue = 0; gAddresses[5].durationMilli = pulseTime; gAddresses[6].address = dccStartAddress + 6; gAddresses[6].output = 1; gAddresses[6].outputPin = startPinArduino + 6; gAddresses[6].isDigital = true; gAddresses[6].isFlasher = false; gAddresses[6].analogValue = 0; gAddresses[6].durationMilli = pulseTime; gAddresses[7].address = dccStartAddress + 7; gAddresses[7].output = 1; gAddresses[7].outputPin = startPinArduino + 7; gAddresses[7].isDigital = true; gAddresses[7].isFlasher = false; gAddresses[7].analogValue = 0; gAddresses[7].durationMilli = pulseTime; gAddresses[8].address = dccStartAddress + 8; gAddresses[8].output = 1; gAddresses[8].outputPin = startPinArduino + 8; gAddresses[8].isDigital = true; gAddresses[8].isFlasher = false; gAddresses[8].analogValue = 0; gAddresses[8].durationMilli = pulseTime; gAddresses[9].address = dccStartAddress + 9; gAddresses[9].output = 1; gAddresses[9].outputPin = startPinArduino + 9; gAddresses[9].isDigital = true; gAddresses[9].isFlasher = false; gAddresses[9].analogValue = 0; gAddresses[9].durationMilli = pulseTime; gAddresses[10].address = dccStartAddress + 10; gAddresses[10].output = 1; gAddresses[10].outputPin = startPinArduino + 10; gAddresses[10].isDigital = true; gAddresses[10].isFlasher = false; gAddresses[10].analogValue = 0; gAddresses[10].durationMilli = pulseTime; gAddresses[11].address = dccStartAddress + 11; gAddresses[11].output = 1; gAddresses[11].outputPin = startPinArduino + 11; gAddresses[11].isDigital = true; gAddresses[11].isFlasher = false; gAddresses[11].analogValue = 0; gAddresses[11].durationMilli = pulseTime; gAddresses[12].address = dccStartAddress + 12; gAddresses[12].output = 1; gAddresses[12].outputPin = startPinArduino + 12; gAddresses[12].isDigital = true; gAddresses[12].isFlasher = false; gAddresses[12].analogValue = 0; gAddresses[12].durationMilli = pulseTime; gAddresses[13].address = dccStartAddress + 13; gAddresses[13].output = 1; gAddresses[13].outputPin = startPinArduino + 13; gAddresses[13].isDigital = true; gAddresses[13].isFlasher = false; gAddresses[13].analogValue = 0; gAddresses[13].durationMilli = pulseTime; gAddresses[14].address = dccStartAddress + 14; gAddresses[14].output = 1; gAddresses[14].outputPin = startPinArduino + 14; gAddresses[14].isDigital = true; gAddresses[14].isFlasher = false; gAddresses[14].analogValue = 0; gAddresses[14].durationMilli = pulseTime; gAddresses[15].address = dccStartAddress + 15; gAddresses[15].output = 1; gAddresses[15].outputPin = startPinArduino + 15; gAddresses[15].isDigital = true; gAddresses[15].isFlasher = false; gAddresses[15].analogValue = 0; gAddresses[15].durationMilli = pulseTime; // setup output pins for (int i = 0; i < numAccessories; i++) { if ( gAddresses[i].outputPin ) { pinMode( gAddresses[i].outputPin, OUTPUT ); } gAddresses[i].onMilli = 0; gAddresses[i].offMilli = 0; } } // the DCC library calls this function in a loop to set / reset accessories void BasicAccDecoderPacket_Handler(int address, boolean activate, byte data) { address -= 1; address *= 4; address += 1; address += (data & 0x06) >> 1; boolean enable = (data & 0x01) ? 1 : 0; // converts bits to a decimal DCC-address for (int i = 0; i < numAccessories; i++) { if ( address == gAddresses[i].address ) // this line checks if the DCC-address belongs to a channel on this decoder { #ifdef SERIALMON Serial.print("DCC packet (address, enable, data): "); Serial.print(address, DEC); Serial.print(", "); Serial.print(enable); Serial.print(", "); Serial.print(data); #endif if ( enable ) // this routine checks the DCC-instruction: 1/enable/on or 0/disable/off, and prepares output to 1 or 0 and sets timers for pulse { gAddresses[i].output = 1; // prepare for 1/enable/on gAddresses[i].onMilli = millis(); gAddresses[i].offMilli = 0; } else { gAddresses[i].output = 0; // prepare for 0/disable/off gAddresses[i].onMilli = 0; gAddresses[i].offMilli = millis(); } } // the actual switching is done in void loop() } } void setup() { #ifdef SERIALMON Serial.begin(115200); Serial.println("DCC decoder switch v2 for accessories (on-off, blink, digital-analog), Ed den Ouden 2022 (lib: DCC_Decoder)"); Serial.println(""); Serial.print("First DCC-address: "); Serial.println(dccStartAddress); Serial.print("Nr of accessories: "); Serial.println(numAccessories); Serial.print("Outputs when idle: "); Serial.println(outputIdle); Serial.print("Outputs when activated: "); Serial.println(outputActive); Serial.println(""); Serial.println("Ready/idle, waiting for DCC-packets..."); Serial.println(""); #endif DCC.SetBasicAccessoryDecoderPacketHandler(BasicAccDecoderPacket_Handler, true); ConfigureDecoder(); DCC.SetupDecoder( 0x00, 0x00, kDCC_INTERRUPT ); pinMode(A5, OUTPUT); // control led pin A5 digitalWrite(A5, HIGH); // switch control led ON } void loop() { static int addr = 0; DCC.loop(); // bump to next address to test if ( ++addr >= numAccessories ) { addr = 0; } // turn off output 0/off if ( gAddresses[addr].offMilli && gAddresses[addr].offMilli < millis() ) { // clear off time gAddresses[addr].offMilli = 0; // disable output 0/OFF if ( gAddresses[addr].isDigital ) { digitalWrite( gAddresses[addr].outputPin, outputIdle); #ifdef DEBUGGER Serial.println(" (switched off)"); // debug code #endif } else { analogWrite( gAddresses[addr].outputPin, 0); } // if still enabled and a flash type, set on time if ( gAddresses[addr].output && gAddresses[addr].isFlasher) { gAddresses[addr].onMilli = millis() + gAddresses[addr].durationMilli; } else { gAddresses[addr].output = 0; } return; } // turn on output 1/on if ( gAddresses[addr].onMilli && gAddresses[addr].onMilli <= millis() ) { // clear off time gAddresses[addr].onMilli = 0; // enable output 1/ON if ( gAddresses[addr].isDigital ) { digitalWrite( gAddresses[addr].outputPin, outputActive); #ifdef DEBUGGER Serial.println(" (switched on)"); // debug code #endif } else { analogWrite( gAddresses[addr].outputPin, gAddresses[addr].analogValue); } // if still enabled and a flash type, set off time if ( gAddresses[addr].durationMilli ) { gAddresses[addr].offMilli = millis() + gAddresses[addr].durationMilli; } return; } }