// DCC-ARC-System // DCC Digitrax automatic reverse circuit // Detection with DIODE // Fabrice Fayolle, March 2017 // Version 2.2 for Arduino Uno // Arduino Uno // Pin -> Use for -> Connect to // 0 // 1 // 2 // 3 -> ACR On -> SPDT // 4 -> DCC Sound -> SPDT // 5 -> DCC Decoder Model -> SPDT // 6 -> ACR Indicator -> Red LED (with a 220 ohms resistor) // 7 -> LocoNet Transmit pin -> Locoshield PCB Tx pin // 8 -> LocoNet Receive pin -> Locoshield PCB Rx pin // 9 // 10 // 11 // 12 // 13 // 14 to 15 -> Detection Input (West) -> DIODE // 16 -> ADR Loco selection -> Potentiometer // 17 to 18 -> Detection Input (East) -> DIODE // 19 -> Speed Loco selection -> Potentiometer // INPUT // ACR // ACR On -> SPDT ON-ON // 5V -> Not activated / 0V -> Activated // DCC // DCC Sound -> SPDT ON-ON // 5V -> Not activated / 0V -> Activated // DCC Decoder model -> SPDT ON-ON // 5V -> ESU type / 0V -> Soundtraxx type // Speed Loco selection -> Potentiometer 10K // ADR Loco selection -> Potentiometer 10K // Detection : DIODE // Not detected -> HIGH // Detected -> LOW // OUTPUT // Digitrax LocoNet message // Global constants and variables const int ACR_On_Pin = 3; const int Sound_On_Pin = 4; boolean Sound_Activated; const boolean Not_Activated = true; const boolean Activated = !(Not_Activated); const int Min_Time = 6000; const int Max_Time = 12000; const int ACR_Indicator = 6; const boolean On = true; const boolean Off = !(On); // DIODE // Define constants const int Detect_West1_PIN = 14; const int Detect_West2_PIN = 15; const int Detect_East1_PIN = 17; const int Detect_East2_PIN = 18; const int Detect_Time = 1000; // Throttle // Define used slots table int used_SLOT[120]; // Define the DCC decoder model const int Decoder_Model_Pin = 5; boolean Decoder_Model = true; // Define the address of the loco DCC decoder you want to use for ACR system const int ADR_Loco_Pin = 16; const int ADR_Loco_Tab[4] = {12, 3, 11, 13}; int ADR_Loco = 12; // Define the loco speed const int Speed_Loco_Pin = 19; const int Speed_Loco_Tab[4] = {0x1B, 0x03, 0x0F, 0x29}; int Speed_Loco = 0x1B; // LocoNet #include // Define LocoNet Transmit Pin #define LN_TX_PIN 7 // Pointer to a received LocoNet packet lnMsg *LnPacket; void slot_Init() // Initiliaze all slots { for (int slot = 0; slot < 120; slot++) { sendOPC_xxx(OPC_RQ_SL_DATA, slot, 0); } } void sendOPC_x(int OPC_Type) // OPC_GPOFF - OPC_GPON - OPC_IDLE LocoNet Message { lnMsg SendPacket; SendPacket.data[0] = OPC_Type; LocoNet.send( &SendPacket ); switch (OPC_Type) { case OPC_GPOFF: // OPC_GPOFF -> Power OFF Serial.print("OPC_GPOFF"); break; case OPC_GPON: // OPC_GPON -> Power ON Serial.print("OPC_GPON"); break; case OPC_IDLE: // OPC_IDLE -> Emergency Stop_Time Serial.print("OPC_IDLE"); break; } Serial.println(" LocoNet message sent"); delay(250); return; } void sendOPC_xxx(int OPC_Type, int Arg1, int Arg2) // OPC_LOCO_SPD - OPC_LOCO_DIRF - OPC_LOCO_SND - OPC_SW_REQ - OPC_MOVE_SLOTS - OPC_RQ_SL_DATA - OPC_LOCO_ADR LocoNet Message { lnMsg SendPacket; SendPacket.data[0] = OPC_Type; SendPacket.data[1] = Arg1; SendPacket.data[2] = Arg2; LocoNet.send( &SendPacket ); switch (OPC_Type) { case OPC_LOCO_SPD: // OPC_LOCO_SPD Serial.print ("SLOT "); Serial.print(Arg1); Serial.print(" : OPC_LOCO_SPD"); break; case OPC_LOCO_DIRF: // OPC_LOCO_DIRF Serial.print ("SLOT "); Serial.print(Arg1); Serial.print(" : OPC_LOCO_DIRF"); break; case OPC_LOCO_SND: // OPC_LOCO_SND Serial.print ("SLOT "); Serial.print(Arg1); Serial.print(" : OPC_LOCO_SND"); break; case OPC_SW_REQ: // OPC_SW_REQ Serial.print ("Turnout Adr "); Serial.print(Arg1 + 1); Serial.print(" : OPC_SW_REQ"); break; case OPC_MOVE_SLOTS: // OPC_MOVE_SLOTS Serial.print ("SLOT "); Serial.print(Arg1); Serial.print(" : OPC_MOVE_SLOTS"); break; case OPC_RQ_SL_DATA: // OPC_RQ_SL_DATA Serial.print ("SLOT "); Serial.print(Arg1); Serial.print(" : OPC_RQ_SL_DATA"); break; case OPC_LOCO_ADR: // OPC_LOCO_ADR Serial.print ("DCC Adr "); Serial.print(Arg2); Serial.print(" : OPC_LOCO_ADR"); break; } Serial.println(" LocoNet message sent"); delay(250); return; } void LocoNet_Message_For_Lever(int SLever_dcc, int SLever_type, boolean SLever_state) { int sw1 = 0x00; int sw2 = 0x00; switch (SLever_type) { case 0 : // 0 -> Not use break; case 1 : // 1 -> Point SLever_dcc = SLever_dcc - 1; if (SLever_state) { sw1 |= B00100000; sw2 |= B00100000; } sw1 |= B00010000; sw1 |= (SLever_dcc >> 7) & 0x0F; sw2 |= (SLever_dcc >> 7) & 0x0F; sendOPC_xxx(OPC_SW_REQ, SLever_dcc & 0x7F, sw1); sendOPC_xxx(OPC_SW_REQ, SLever_dcc & 0x7F, sw2); break; case 2: // 2 -> FPL break; case 3: // 3 -> Signal break; case 4: // 4 -> Block signal break; } return; } // Object "Throttle" class Throttle { private: int SLOT; int ADR; int SPD; int DIRF; int SND; public: void Setup(int SADR); void Change_SPD(int SSPD); void Change_DIRF(int SDIRF); void Change_SND(int SSND); } ; void Throttle::Setup(int SADR) { sendOPC_xxx(OPC_LOCO_ADR, 0, SADR); LnPacket = LocoNet.receive() ; while ((LnPacket->data[0] != 0xE7) | (LnPacket->data[4] != SADR)) { sendOPC_xxx(OPC_LOCO_ADR, 0, SADR); LnPacket = LocoNet.receive(); } Serial.print(LnPacket->data[0], HEX); Serial.print(" LocoNet Message Received -> DCC Adr "); Serial.print(LnPacket->data[4]); Serial.print(" was found in SLOT "); Serial.println(LnPacket->data[2]); SLOT = LnPacket->data[2]; ADR = LnPacket->data[4]; SPD = 0x00; DIRF = 0x00; SND = 0x00; //sendOPC_xxx(OPC_MOVE_SLOTS, SLOT, SLOT); //sendOPC_xxx(OPC_LOCO_DIRF, SLOT, DIRF); //sendOPC_xxx(OPC_LOCO_SPD, SLOT, SPD); //sendOPC_xxx(OPC_LOCO_SND, SLOT, SND); Serial.println("Communication initialized"); int i = 0; while (used_SLOT[i] != 0) { i = i + 1; } used_SLOT[i] = SLOT; return; } void Throttle::Change_SPD(int SSPD) { SPD = SSPD; Serial.print("SPD at "); Serial.print((SPD * 100) / 128); Serial.println("%"); sendOPC_xxx(OPC_LOCO_SPD, SLOT, SPD); } void Throttle::Change_DIRF(int SDIRF) { DIRF = DIRF + SDIRF; Serial.print("DIRF:"); Serial.print(DIRF); Serial.print("\t\tDirection: "); Serial.print(((DIRF & 0x20) == 0) ? "<-" : "->"); Serial.print("\tFunction(s): "); Serial.print(((DIRF & 0x10) == 0) ? "" : "F0 "); Serial.print(((DIRF & 0x01) == 0) ? "" : "F1 "); Serial.print(((DIRF & 0x02) == 0) ? "" : "F2 "); Serial.println(((DIRF & 0x04) == 0) ? "" : "F3"); sendOPC_xxx(OPC_LOCO_DIRF, SLOT, DIRF); } void Throttle::Change_SND(int SSND) { SND = SSND; Serial.print("SND:"); Serial.println(SND); sendOPC_xxx(OPC_LOCO_SND, SLOT, SND); } Throttle Loco_Throttle; void setup() { // Initialize Serial Port USB at 57600 baud Serial.begin(57600); Serial.println("Monitor ready"); // LocoNet LocoNet.init(LN_TX_PIN); // DCC DCC_On(); // ACR pinMode(ACR_On_Pin, INPUT); digitalWrite(ACR_On_Pin, HIGH); // DCC Sound pinMode(Sound_On_Pin, INPUT); digitalWrite(Sound_On_Pin, HIGH); // Decoder Model pinMode(Decoder_Model_Pin, INPUT); digitalWrite(Decoder_Model_Pin, HIGH); // Speed Loco pinMode(Speed_Loco_Pin, INPUT); // ADR Loco pinMode(ADR_Loco_Pin, INPUT); // Initialize Detection Input pinMode(Detect_West1_PIN, INPUT); digitalWrite(Detect_West1_PIN, Not_Activated); pinMode(Detect_West2_PIN, INPUT); digitalWrite(Detect_West2_PIN, Not_Activated); pinMode(Detect_East1_PIN, INPUT); digitalWrite(Detect_East1_PIN, Not_Activated); pinMode(Detect_East2_PIN, INPUT); digitalWrite(Detect_East2_PIN, Not_Activated); // ACR Indicator pinMode(ACR_Indicator, OUTPUT); digitalWrite(ACR_Indicator, Off); // Initialize Loco Throttle // Loco_Throttle.Setup(ADR_Loco); // Initialize Random Stop_Time Time Variable randomSeed(1000); // System initialized Serial.println("System ACR initialized"); } int ind_tab_read(int pin, int gap) { int value; int k = gap; int i = 0; value = analogRead(pin); while (value > k) { k = k + gap; i++; } return (i); } void ACR_Detection_Indicated(int delay_time, int nb) { for (int x = 0; x < nb; x++) { digitalWrite(ACR_Indicator, Off); delay(delay_time); digitalWrite(ACR_Indicator, On); delay(delay_time); } return; } void Stop_Time(int delay_time) { Serial.print("Stop during "); Serial.print(delay_time); Serial.println(" ms"); delay(delay_time); return; } void Whistle(int i) { for (int x = 0; x < i; x++) { Loco_Throttle.Change_DIRF(0x02); Loco_Throttle.Change_DIRF(-0x02); } return; } void ACR_loop() { while ((digitalRead(Detect_West1_PIN) == Not_Activated) && (digitalRead(Detect_West2_PIN) == Not_Activated)) { delay(100); } Serial.println("Loco on West detector"); ACR_Detection_Indicated(250, 2); delay(Detect_Time); Loco_Throttle.Change_SPD(0x00); Stop_Time(random(Min_Time, Max_Time)); Loco_Throttle.Change_DIRF(-0x20); delay(1000); if (Sound_Activated) { Whistle(1); delay(2500); } Loco_Throttle.Change_SPD(Speed_Loco); while ((digitalRead(Detect_East1_PIN) == Not_Activated) && (digitalRead(Detect_East2_PIN) == Not_Activated)) { delay(100); } Serial.println("Loco on East detector"); ACR_Detection_Indicated(250, 2); delay(Detect_Time); Loco_Throttle.Change_SPD(0x00); Stop_Time(random(Min_Time, Max_Time)); if (digitalRead(ACR_On_Pin) == Activated) { Loco_Throttle.Change_DIRF(0x20); delay(1000); if (Sound_Activated) { Whistle(2); delay(2500); } Loco_Throttle.Change_SPD(Speed_Loco); ACR_loop(); } return; } void loop() { while (digitalRead(ACR_On_Pin) == Not_Activated) { } // Initialize Automatic Circuit Reverse // Loco must have activated West 1 detector to initialize the system ADR_Loco = ADR_Loco_Tab[ind_tab_read(ADR_Loco_Pin, 256)]; Speed_Loco = Speed_Loco_Tab[ind_tab_read(Speed_Loco_Pin, 256)]; if (digitalRead(Decoder_Model_Pin) == HIGH) { Decoder_Model = true; } else { Decoder_Model = false; } Serial.print("Waiting for System ACR activation on West 1 detector with ADR_LOCO "); Serial.println(ADR_Loco); Serial.println("Parameters :"); Serial.print("- Loco speed defined at "); Serial.print((Speed_Loco * 100) / 128); Serial.println("%"); // Sound if (digitalRead(Sound_On_Pin) == Activated) { Sound_Activated = HIGH; Serial.println("- Sound"); Serial.print("- DCC Decoder Model: "); if (Decoder_Model) { Serial.println("ESU type"); } else { Serial.println("Soundtraxx type"); } } else { Sound_Activated = LOW; Serial.println("- No sound"); } while (digitalRead (Detect_West1_PIN) == Not_Activated) { ACR_Detection_Indicated(100, 1); } // System initialized Serial.println("System ACR activited"); digitalWrite(ACR_Indicator, On); // Initialize Loco Throttle with ADR Loco Selected Loco_Throttle.Setup(ADR_Loco); // Speed Loco selected Serial.print("Loco ADR "); Serial.print(ADR_Loco); Serial.println(" -> USED"); delay(500); Loco_Throttle.Change_SPD(0x00); delay(2500); Loco_Throttle.Change_DIRF(0x30); if (Sound_Activated) { delay(1000); if (Decoder_Model) { Loco_Throttle.Change_DIRF(0x01); } else { Loco_Throttle.Change_SND(0x00); } delay(5000); } ACR_loop(); Serial.println("System ACR stopped"); if (Sound_Activated) { delay(1000); if (Decoder_Model) { Loco_Throttle.Change_DIRF(-0x01); } else { Loco_Throttle.Change_SND(0x08); } delay(5000); } Loco_Throttle.Change_DIRF(-0x10); Serial.print("Loco ADR "); Serial.print(ADR_Loco); Serial.println(" -> NOT USED"); digitalWrite(ACR_Indicator, Off); } void DCC_On() // DCC Power ON { sendOPC_x(OPC_GPOFF); delay(500); sendOPC_x(OPC_GPON); return; }