-
Posts
289 -
Joined
-
Last visited
Content Type
Profiles
Forums
Blogs
Gallery
Events
Exhibition Layout Details
Store
Everything posted by ffayolle
-
Hi all of you, Happy new year from France. I hope you're well. In progress, Rue d'Abbeville, a French switching layout Trackplan : Rules : Coulisse = Fiddle yard Building : I built tracks (ME code 125 and code 100) and pointworks with some "Fast Tracks" tools. I used L-Girder tips for my benchwork. Some pictures I took during building. Comments welcome!!! To be continued... Fabrice
- 68 replies
-
- 34
-
Hi all of you, I 've designed a Locoshield version for Arduino Mega. I've more possibilities with the Arduino Mega. I've added on my program Loconet Throttle management. I can now command my turntable by my Arduino program. // SignalBox-Mega // Signal box (terminus station with single track) with "Nb_lever" levers // Signal box integrated interlocking system and tokenless block system (single track / terminus station) // Sound system -> Wtv020 // Digitrax LocoNet -> LocoNet // Tokenless Block System -> Tokenless // DCC Throttle -> Throttle // Fabrice Fayolle, May 2016 // Version 2.0 for Arduino Mega // Signal box with "NB_lever" const int Nb_lever = 20; // Arduino Mega // Pin -> Use for -> Connect to // 0 // 1 // 2 -> Tokenless Up (IN) -> Tokenless PCB (Pin 12) // 3 -> Tokenless Signal Up (OUT) -> Tokenless PCB (Pin 13) // 4 // 5 -> Tokenless Signal Down (OUT) -> Tokenless PCB // 6 -> Default -> Red LED (with a 220 ohms resistor) // 9 -> Emergency Stop -> Push button // 10 -> Turntable direction -> SPDT ON-ON // 11 -> Turntable stop -> Push button // 12 -> Turntable low speed -> Push button // 13 -> Turntable mid speed -> Push button // 14 -> Loco start/stop -> Push button // 15 -> Loco whistle -> Push button // 22 to 45 -> Lever Input -> SPDT ON-ON // 46 -> LocoNet Transmit pin -> Locoshield PCB Tx pin // 48 -> LocoNet Receive pin -> Locoshield PCB Rx pin // 47, 49, 51 and 53 -> Wtv020 -> Wtv020 PCB // INPUT // Emergency Stop : Push button // 1 -> Emergency Stop Pin // 2 -> GND // Lever : SPDT ON-ON // 1 -> 5V -> Normal position // Common point -> Lever Input Pin // 2 -> GND -> Reverse position // OUTPUT // Digitrax LocoNet message // Global constants and variables const boolean Activated = LOW; // Type lever table // 0 -> Not use // 1 -> Point // 2 -> FPL // 3 -> Signal // 4 -> Block signal const int Table_lever_type[Nb_lever] = {4, 4, 1, 2, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Locking lever table to initialize each lock lever // 0 -> No lock // 1 -> 1 lock // x -> x lock const int Table_lever_lock[Nb_lever] = {1, 2, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Interlocking system table (locking rules) // It's not necessary to simplify locking rules like with mechanical locking bars // 0 -> Nothing // 1 -> Lock // -1 -> Release // 1xx -> LWxxN (Lock When xx Normal) // -1xx -> RWxxN (Release When xx Normal) // 2xx -> LWxxR (Lock When xx Reverse) // -2xx -> RWxxR (Release When xx Reverse) const int Table_interlocking[Nb_lever * Nb_lever] = { // Lever 1 (signal) // 1 locks 2, 3, 4, 5 and 6 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Lever 2 (signal) // 2 locks 1, 3, 4, 5 and 6 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Lever 3 (point) // 3 locks 1,2 and 4 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Lever 4 (FPL) // 4 releases 1 and 2. 4 locks 3 -1, -1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Lever 5 (point) // 5 locks 1 and 2. 5 releases 6 1, 1, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Lever 6 (signal) // 6 locks 1, 2, 3(BW) and 5 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Lever 7 (-) // not used 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Lever 8 (-) // not used 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Lever 9 (-) // not used 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Lever 10 (-) // not used 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Lever 11 (-) // not used 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Lever 12 (-) // not used 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Lever 13 (-) // not used 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Lever 14 (-) // not used 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Lever 15 (-) // not used 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Lever 16 (-) // not used 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Lever 17 (-) // not used 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Lever 18 (-) // not used 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Lever 19 (-) // not used 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Lever 20 (-) // not used 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // Visual management for interlocking system // Default lever position const int DefaultPin = 6; // Tokenless block system // Rail directions : Up Line -> To London / Down Line -> To the station boolean Block_ON = false; // Define which Arduino UNO Pin you use for communicate with the Tokenless PCB const int upBlock_request_PIN = 2; const int upBlock_signalopen_PIN = 3; const int downBlock_signalopen_PIN = 5; // Define which lever type you use for Block signal const int Block_signal = 4; // Define which lever you use to control signal const int upSignal_lever = 1; const int downSignal_lever = 0; boolean upBlock_request = false; boolean upBlock_blocked = false; // Throttle // Define the address of the turntable DCC decoder const int ADR_Turntable = 101; // Define constants and variables for the turntable boolean TT_dir = false; const int TT_dir_PIN = 10; const int TT_stop_PIN = 11; const int TT_lowspeed_PIN = 12; const int TT_midspeed_PIN = 13; // Define the address of the loco DCC decoder const int ADR_Loco = 22; // Define constants and variables for the loco boolean Loco_On = false; const int Loco_start_stop_PIN = 14; const int Loco_whistle_PIN = 15; // LocoNet #include <LocoNet.h> // Define LocoNet Transmit Pin #define LN_TX_PIN 46 // Emergency Stop const int Emergency_PIN = 9; // Pointer to a received LocoNet packet lnMsg *LnPacket; // DDC Address of stationary decoder const int Table_lever_dcc[Nb_lever] = {1, 2, 3, 4, 11, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}; 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 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; } // Wtv020 #include <Wtv020sd16p.h> const int clockPin = 47; // CLOCK 7 const int resetPin = 49; // RESET 1 const int diPin = 51; // DATAIN 10 const int busyPin = 53; // BUSY 15 Wtv020sd16p wtv020sd16p(resetPin, clockPin, diPin, busyPin); // 0000.AD4 -> 0 -> Lever change // 0001.AD4 -> 1 -> Block system code bell // 0002.AD4 -> 2 -> People talking // 0003.AD4 -> 3 -> Steam engine // 0004.AD4 -> 4 -> People talking (2) // Wait the end of the sound before to do something else void Wtv020_wait() { delay(250); while (digitalRead(busyPin) == HIGH) { } return; } void Play_random_sound() { unsigned long time = millis(); if (time % random(1, 1000) == random(0, 10)) { Serial.println("Sound system"); wtv020sd16p.asyncPlayVoice(random(2, 5)); Wtv020_wait(); } 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); delay(250); LnPacket = LocoNet.receive() ; while ((LnPacket->data[0] != 0xE7) | (LnPacket->data[4] != SADR)) { sendOPC_xxx(OPC_LOCO_ADR, 0, SADR); delay(250); 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"); 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("Direction:"); if ((DIRF & 0x20) == 0) Serial.print(" <-"); else Serial.print("->"); Serial.print("/Lights:"); if ((DIRF & 0x10) == 0) Serial.print("Off"); else Serial.print("On"); Serial.print("/F1:"); if ((DIRF & 0x01) == 0) Serial.print("Off"); else Serial.print("On"); Serial.print("/F2:"); if ((DIRF & 0x02) == 0) Serial.print("Off"); else Serial.print("On"); Serial.print("/F3:"); if ((DIRF & 0x04) == 0) Serial.println("Off"); else Serial.println("On"); Serial.print("DIRF: "); Serial.println(DIRF); sendOPC_xxx(OPC_LOCO_DIRF, SLOT, DIRF); } void Throttle::Change_SND(int SSND) { SND = SND + SSND; Serial.print("SND: "); Serial.println(SND); sendOPC_xxx(OPC_LOCO_DIRF, SLOT, SND); } Throttle TT_Throttle, Loco_Throttle; // Object "Lever" class Lever { private: int Lever_input; int Lever_type; int Lever_state; int Lever_lock; int Lever_dcc; public: void Setup(int SLever_input); boolean State_of_lever(); int Type_of_lever(); boolean Change_asking(); boolean Change_is_possible(); void Change(); void Change_lever_lock(int SChange); } ; void Lever::Setup(int SLever_input) { // Input : from pin 22 to pin 45 Lever_input = SLever_input + 22; pinMode (Lever_input, INPUT); digitalWrite(Lever_input, HIGH); boolean Normal = true; boolean Reverse = false; if (digitalRead(Lever_input) == Reverse) { delay(100); if (digitalRead(Lever_input) == Reverse) { digitalWrite(DefaultPin, HIGH); while (digitalRead(Lever_input) == Reverse) { } digitalWrite(DefaultPin, LOW); } } Lever_type = Table_lever_type[SLever_input]; // State of the lever // true -> Normal(by default) // false -> Reverse Lever_state = true; Lever_lock = Table_lever_lock[SLever_input]; Lever_dcc = Table_lever_dcc[SLever_input]; LocoNet_Message_For_Lever(Lever_dcc, Lever_type, true); return; } boolean Lever::Change_asking() { boolean result = false; if (digitalRead(Lever_input) != Lever_state) { delay(100); if (digitalRead(Lever_input) != Lever_state) { result = true; } } return result; } boolean Lever::Change_is_possible() { boolean result = false; result = (Lever_lock == 0); if (result == false) { while (digitalRead(Lever_input) != Lever_state) { digitalWrite(DefaultPin, HIGH); } digitalWrite(DefaultPin, LOW); } return result; } void Lever::Change() { Lever_state = !Lever_state; // Wtv020 wtv020sd16p.asyncPlayVoice(0); // LocoNet LocoNet_Message_For_Lever(Lever_dcc, Lever_type, Lever_state); // Wtv020 // Wtv020_wait(); return; } boolean Lever::State_of_lever() { boolean result = true; result = Lever_state; return result; } int Lever::Type_of_lever() { int result = 0; result = Lever_type; return result; } void Lever::Change_lever_lock(int SChange) { Lever_lock += SChange; return; } Lever L[Nb_lever]; void setup() { // Initialize Serial Port USB at 57600 baud Serial.begin(57600); Serial.println("Monitor"); // LocoNet LocoNet.init(LN_TX_PIN); pinMode(Emergency_PIN, INPUT); digitalWrite(Emergency_PIN, HIGH); DCC_On(); // Throttle // Initialize turntable INPUT pinMode(TT_dir_PIN, INPUT); digitalWrite(TT_dir_PIN, HIGH); pinMode(TT_stop_PIN, INPUT); digitalWrite(TT_stop_PIN, HIGH); pinMode(TT_lowspeed_PIN, INPUT); digitalWrite(TT_lowspeed_PIN, HIGH); pinMode(TT_midspeed_PIN, INPUT); digitalWrite(TT_midspeed_PIN, HIGH); // Initialize Turntable Throttle TT_Throttle.Setup(ADR_Turntable); // Initialize loco INPUT pinMode(Loco_start_stop_PIN, INPUT); digitalWrite(Loco_start_stop_PIN, HIGH); pinMode(Loco_whistle_PIN, INPUT); digitalWrite(Loco_whistle_PIN, HIGH); // Initialize Loco Throttle Loco_Throttle.Setup(ADR_Loco); // Wtv020 wtv020sd16p.reset(); delay(500); // If you want to adjust the speaker volume, modify on Wtv020sd16p library (Wtv020sd16p.cpp) // this constant VOLUME_MAX = 0xFFF7 (from 0xFFF0 to 0xFFF7) and use the unmute() function wtv020sd16p.unmute(); delay(500); // Visual management pinMode(DefaultPin, OUTPUT); digitalWrite(DefaultPin, LOW); // Levers setup for (int i = 0; i < Nb_lever; i++) { L[i].Setup(i); } Serial.println("Ready to use"); // Tokenless pinMode(upBlock_request_PIN, INPUT); pinMode(upBlock_signalopen_PIN, OUTPUT); digitalWrite(upBlock_request_PIN, HIGH); digitalWrite(upBlock_signalopen_PIN, HIGH); pinMode(downBlock_signalopen_PIN, OUTPUT); digitalWrite(downBlock_signalopen_PIN, HIGH); Serial.println("Tokenless Block System : Normal"); } void loop() { for (int i = 0; i < Nb_lever; i++) { if (L[i].Change_asking()) { if (L[i].Change_is_possible()) { // Interlocking rules Locking_rules(i); // Change the state of the lever L[i].Change(); Serial.print("Lever "); Serial.print(i + 1); Serial.print(" changed to "); if (L[i].State_of_lever()) { Serial.print("Normal"); } else { Serial.print("Reverse"); } Serial.println(); // Tokenless if ( i == upSignal_lever) { if (L[i].State_of_lever()) { if (Block_ON) { L[i].Change_lever_lock(1); Serial.print("Block System locks "); Serial.println(i + 1); upBlock_blocked = true; digitalWrite(upBlock_signalopen_PIN, HIGH); } } else { if (Block_ON) { digitalWrite(upBlock_signalopen_PIN, LOW); Serial.println("Tokenless Block System : UP -> Blocked"); } } } if ( i == downSignal_lever) { if (L[i].State_of_lever()) { digitalWrite(downBlock_signalopen_PIN, HIGH); Serial.println("Signal DOWN Line -> Closed"); } else { digitalWrite(downBlock_signalopen_PIN, LOW); Serial.println("Signal DOWN Line -> Open"); } } } } } // Loconet Emergency(); // Tokenless upBlock_accept(); upBlock_normal(); // Wtv020 //Play_random_sound(); // Throttle (turntable) Turntable_Order(); // Throttle (loco) Loco_Order(); } void Locking_rules(int Slever) // Apply interlocking system rules when you move a lever { int Shift = Slever * Nb_lever; if (L[Slever].State_of_lever()) // Lever on "Normal" position { for (int i = 0; i < Nb_lever; i++) { switch (Table_interlocking[Shift + i]) { case 0 : break; case -1 : // Releases L[i].Change_lever_lock(Table_interlocking[Shift + i]); Serial.print(Slever + 1); Serial.print(" releases "); Serial.println(i + 1); break; case 1 : // Locks L[i].Change_lever_lock(Table_interlocking[Shift + i]); Serial.print(Slever + 1); Serial.print(" locks "); Serial.println(i + 1); break; default : // Locks/Releases when xx Normal/Reverse L[i].Change_lever_lock(Conditionnal_locking(Table_interlocking[Shift + i])); ; } } } else // Lever on "Reverse" position { for (int i = 0; i < Nb_lever; i++) { switch (Table_interlocking[Shift + i]) { case 0 : break; case -1 : // Locks L[i].Change_lever_lock(-Table_interlocking[Shift + i]); Serial.print(Slever + 1); Serial.print(" locks "); Serial.println(i + 1); break; case 1 : // Releases L[i].Change_lever_lock(-Table_interlocking[Shift + i]); Serial.print(Slever + 1); Serial.print(" releases "); Serial.println(i + 1); break; default : // Releases/Locks when xx Normal/Reverse L[i].Change_lever_lock(-Conditionnal_locking(Table_interlocking[Shift + i])); ; } } } return; } int Conditionnal_locking (int Locking) // Verify lever position if interlocking system rule is Locks/Releases When Lever xx is Normal/Reverse { int Hundred = Locking / 100; int result = 0; switch (Hundred) { case 1 : // Locks when Lever (Locking-(HundredX100)) is Normal if (L[Locking - 101].State_of_lever()) { result = 1; } break; case -1 : // Releases when Lever (Locking-(HundredX100)) is Normal if (L[-Locking - 101].State_of_lever()) { result = -1; } break; case 2 : // Locks when Lever (Locking-(HundredX100)) is Reverse if (!L[Locking - 201].State_of_lever()) { result = 1; } break; case -2 : // Releases when Lever (Locking-(HundredX100)) is Reverse if (!L[-Locking - 201].State_of_lever()) { result = -1; } break; } return result; } void upBlock_accept() // Tokenless Block System on "Accepted" for Up Line { upBlock_request = digitalRead(upBlock_request_PIN); delay(100); if ( !upBlock_request && (!Block_ON && L[downSignal_lever].State_of_lever())) { // Wtv020 wtv020sd16p.asyncPlayVoice(1); Wtv020_wait(); Serial.println("Tokenless Block System : UP Line -> Accepted"); L[upSignal_lever].Change_lever_lock(-1); Serial.print("Block System releases "); Serial.println(upSignal_lever + 1); L[downSignal_lever].Change_lever_lock(1); Serial.print("Block System locks "); Serial.println(downSignal_lever + 1); Block_ON = true; } return; } void upBlock_normal() // Tokenless Block System on "Normal" { upBlock_request = digitalRead(upBlock_request_PIN); delay(100); if (L[upSignal_lever].State_of_lever()) { if ( upBlock_request && Block_ON ) { if (!upBlock_blocked) { L[upSignal_lever].Change_lever_lock(1); Serial.print("Block System locks "); Serial.println(upSignal_lever + 1); } upBlock_blocked = false; Block_ON = false; L[downSignal_lever].Change_lever_lock(-1); Serial.print("Block System releases "); Serial.println(downSignal_lever + 1); Serial.println("Tokenless Block System : Normal"); //Wtv020 wtv020sd16p.asyncPlayVoice(2); Wtv020_wait(); } } return; } void DCC_On() // DCC Power ON { sendOPC_x(OPC_GPOFF); sendOPC_x(OPC_GPON); return; } void Emergency() // Emergency Stop required { if (digitalRead(Emergency_PIN) == Activated) { delay(100); if (digitalRead(Emergency_PIN) == Activated) { while (digitalRead(Emergency_PIN) == Activated) { digitalWrite(DefaultPin, HIGH); delay(100); digitalWrite(DefaultPin, LOW); delay(100); } sendOPC_x(OPC_IDLE); } } return; } void Turntable_Order() // Control Turntable Direction by a SPDT ON-ON (Input: TT_dir_PIN) // and Turntable Speed by some Push buttons (Input: TT_stop_PIN, TT_lowspeed_PIN, TT_midspeed_PIN) { if ((digitalRead(TT_dir_PIN) == HIGH) && TT_dir) { TT_dir = false; TT_Throttle.Change_DIRF(-0x20); } if ((digitalRead(TT_dir_PIN) == LOW) && !TT_dir) { TT_dir = true; TT_Throttle.Change_DIRF(0x20); } if (digitalRead(TT_stop_PIN) == Activated) { delay(100); if (digitalRead(TT_stop_PIN) == Activated) { while (digitalRead(TT_stop_PIN) == Activated) { digitalWrite(DefaultPin, HIGH); delay(100); digitalWrite(DefaultPin, LOW); delay(100); } Serial.print("Turntable "); TT_Throttle.Change_SPD(0x00); } } if (digitalRead(TT_lowspeed_PIN) == Activated) { delay(100); if (digitalRead(TT_lowspeed_PIN) == Activated) { while (digitalRead(TT_lowspeed_PIN) == Activated) { digitalWrite(DefaultPin, HIGH); delay(100); digitalWrite(DefaultPin, LOW); delay(100); } Serial.print("Turntable "); TT_Throttle.Change_SPD(0x03); } } if (digitalRead(TT_midspeed_PIN) == Activated) { delay(100); if (digitalRead(TT_midspeed_PIN) == Activated) { while (digitalRead(TT_midspeed_PIN) == Activated) { digitalWrite(DefaultPin, HIGH); delay(100); digitalWrite(DefaultPin, LOW); delay(100); } Serial.print("Turntable "); TT_Throttle.Change_SPD(0x40); } } return; } void Loco_Order() // Control LOCO Sound by some Push buttons (Input: Loco_start_stop_PIN, Loco_whistle_PIN) { if ((digitalRead(Loco_start_stop_PIN) == LOW) && Loco_On == false) { delay(100); if ((digitalRead(Loco_start_stop_PIN) == LOW) && Loco_On == false) { while (digitalRead(Loco_start_stop_PIN) == LOW) { digitalWrite(DefaultPin, HIGH); delay(100); digitalWrite(DefaultPin, LOW); delay(100); } Loco_Throttle.Change_DIRF(0x10); delay(500); Loco_Throttle.Change_DIRF(0x08); delay(500); Loco_Throttle.Change_DIRF(-0x08); delay(3000); Loco_Throttle.Change_SND(0x01); delay(1500); Loco_Throttle.Change_SND(-0x01); delay(500); Loco_Throttle.Change_DIRF(0x01); delay(500); Loco_Throttle.Change_DIRF(-0x01); delay(500); Loco_Throttle.Change_DIRF(0x01); Loco_On = true; } } if ((digitalRead(Loco_start_stop_PIN) == LOW) && Loco_On == true) { delay(100); if ((digitalRead(Loco_start_stop_PIN) == LOW) && Loco_On == true) { while (digitalRead(Loco_start_stop_PIN) == LOW) { digitalWrite(DefaultPin, HIGH); delay(100); digitalWrite(DefaultPin, LOW); delay(100); } Loco_Throttle.Change_DIRF(-0x01); delay(1000); Loco_Throttle.Change_DIRF(-0x10); Loco_On = false; } } if ((digitalRead(Loco_whistle_PIN) == LOW) && Loco_On == true) { delay(100); if ((digitalRead(Loco_whistle_PIN) == LOW) && Loco_On == true) { while (digitalRead(Loco_whistle_PIN) == LOW) { digitalWrite(DefaultPin, HIGH); delay(100); digitalWrite(DefaultPin, LOW); delay(100); } Loco_Throttle.Change_DIRF(0x04); delay(250); Loco_Throttle.Change_DIRF(-0x04); } } return; } To be continued... Fabrice
-
Hi all of you, I tested my Tokenless Block System Arduino Program with the other program. I use another Arduino Uno. The code is : // Tokenless //Tokenless block system for single track // A -> B : Down Line // B -> A : Up Line // Rail directions : Up Line -> To London / Down Line -> To the station // Fabrice Fayolle, May 2016 // Version 1.0 for Arduino Uno // Arduino Uno // Pin -> Use for -> Connect to // 0 // 1 // 2 -> A:Normal -> Orange LED (with a 470 ohms resistor) // 3 -> A:Blocked -> Red LED // 4 -> A:Accepted -> Green LED // 5 -> B:Normal -> Orange LED (with a 470 ohms resistor) // 6 -> B:Blocked -> Red LED // 7 -> B:Accepted -> Green LED // 8 // 9 // 10 -> Up (OUT) -> Locoshield PCB // 11 -> Signal Up (IN) -> Locoshield PCB // 12 -> Down (OUT) -> Locoshield PCB (Uno -> Pin 2 / Mega -> Pin x) // 13 -> Signal Down (IN) -> Locoshield PCB (Uno -> Pin 3 / Mega -> Pin x) // 14 -> Lever input (B:Arrived) -> SPDT ON-(ON) // 15 -> Lever input (B:Offer) -> SPDT ON-(ON) // 16 -> Lever input (B:Normal/Accept)-> SPDT ON-ON // 17 -> Lever input (A:Arrived) -> SPDT ON-(ON) // 18 -> Lever input (A:Offer) -> SPDT ON-(ON) // 19 -> Lever input (A:Normal/Accept)-> SPDT ON-ON // INPUT // SPDT ON-ON // 1 -> 5V -> Normal // Common point // 2 -> GND -> Accept int A_Normal_Accept = 19; // SPDT ON-MON // 1 -> 5V // Common point // 2 -> GND -> Offer int A_Offer = 18; // SPDT ON-MON // 1 -> 5V // Common point // 2 -> GND -> Offer int A_Arrived = 17; // int B_Normal_Accept = 16; int B_Offer = 15; int B_Arrived = 14; // int A_Block_OK = 10; int A_Signal_Open = 11; int B_Block_OK = 12; int B_Signal_Open = 13; // Orange LED int A_Normal = 2; // Red LED int A_Blocked = 3; // Green LED int A_Accepted = 4; // int B_Normal = 5; int B_Blocked = 6; int B_Accepted = 7; void setup() { pinMode(A_Normal, OUTPUT); digitalWrite(A_Normal, HIGH); pinMode(B_Normal, OUTPUT); digitalWrite(B_Normal, HIGH); pinMode(A_Blocked, OUTPUT); digitalWrite(A_Blocked, HIGH); pinMode(B_Blocked, OUTPUT); digitalWrite(B_Blocked, HIGH); pinMode(A_Accepted, OUTPUT); digitalWrite(A_Accepted, HIGH); pinMode(B_Accepted, OUTPUT); digitalWrite(B_Accepted, HIGH); pinMode(A_Normal_Accept, INPUT); digitalWrite(A_Normal_Accept, HIGH); pinMode(B_Normal_Accept, INPUT); digitalWrite(B_Normal_Accept, HIGH); pinMode(A_Offer, INPUT); digitalWrite(A_Offer, HIGH); pinMode(B_Offer, INPUT); digitalWrite(B_Offer, HIGH); pinMode(A_Arrived, INPUT); digitalWrite(A_Arrived, HIGH); pinMode(B_Arrived, INPUT); digitalWrite(B_Arrived, HIGH); pinMode(A_Signal_Open, INPUT); digitalWrite(A_Signal_Open, HIGH); pinMode(A_Block_OK, OUTPUT); digitalWrite(A_Block_OK, HIGH); pinMode(B_Signal_Open, INPUT); digitalWrite(B_Signal_Open, HIGH); pinMode(B_Block_OK, OUTPUT); digitalWrite(B_Block_OK, HIGH); // Normalisation of the block // Each signalman sets his block instrument to “Accept” (A_Normal_Accept and B_Normal_Accept to HIGH), provided no shunting is taking place into the block section. // The block status is indicated to both signalmen as “Normal” (A_Normal and B_Normal to LOW) while (digitalRead(A_Normal_Accept) == LOW || digitalRead(B_Normal_Accept) == LOW) { digitalWrite(A_Blocked, LOW); digitalWrite(B_Blocked, LOW); delay(250); digitalWrite(A_Blocked, HIGH); digitalWrite(B_Blocked, HIGH); delay(250); } digitalWrite(A_Normal, LOW); digitalWrite(B_Normal, LOW); } void loop() { while (digitalRead(A_Normal_Accept) == LOW && digitalRead(B_Normal_Accept) == LOW) { if ((digitalRead(A_Offer) == LOW) || (digitalRead(B_Offer) == LOW)) { for (int i = 0; i < 4; i++) { digitalWrite(A_Normal, HIGH); digitalWrite(B_Normal, HIGH); delay(250); digitalWrite(A_Normal, LOW); digitalWrite(B_Normal, LOW); delay(250); } } } // When the next train arrives at A, the A signalman sets his block instrument to “Normal” (A_Normal_Accept to LOW). // He then presses “Offer” (A_Offer to LOW). // At B, automatic acceptance of the train occurs, and the block status is indicated as “Blocked” (B_Blocked to LOW). // If the B instrument were not set to “Accept” (B_Normal_Accept at HIGH), this would not occur, and an blinking warning would be given // When acceptance has occurred, the block status at A is indicated as “Accepted” (A_Accepted to LOW). if ((digitalRead(A_Normal_Accept) == LOW) && (digitalRead(B_Normal_Accept) == HIGH) && (digitalRead(A_Offer) == LOW)) { digitalWrite(A_Normal, HIGH); digitalWrite(A_Accepted, LOW); digitalWrite(B_Normal, HIGH); digitalWrite(B_Blocked, LOW); digitalWrite(A_Block_OK, LOW); delay(250); // The A signalman may now, at any time, release his starting signal towards B. // As the signal clears (A_Signal_Open at LOW), the block status changes to “Blocked” (A_Blocked to LOW). while (digitalRead(A_Signal_Open) != LOW) { } digitalWrite(A_Accepted, HIGH); digitalWrite(A_Blocked, LOW); delay(250); // The A signalman replaces his starting signal to danger once the train has departed from A (A_Signal_Open at HIGH). while (digitalRead(A_Signal_Open) == LOW) { } delay(250); // The B signalman checks that the train has arrived complete at B (tail lamp) and then sets his block instrument to “Normal” (B_Normal to LOW). // He presses “Train Arrived” (B_Arrived to LOW). // The block section status is then restored to “Normal” (A_Normal and B_Normal at LOW) and indicated to both signalmen by “Normal” indications (A_Normal and B_Normal to LOW). while ((digitalRead(B_Arrived) != LOW) || (digitalRead(B_Normal_Accept) != LOW)) { } digitalWrite(A_Blocked, HIGH); digitalWrite(B_Blocked, HIGH); digitalWrite(A_Normal, LOW); digitalWrite(B_Normal, LOW); digitalWrite(A_Block_OK, HIGH); } // When the next train arrives at B, the B signalman sets his block instrument to “Normal” (B_Normal_Accept to LOW). // He then presses “Offer” (B_Offer to LOW). // ... if ((digitalRead(B_Normal_Accept) == LOW) && (digitalRead(A_Normal_Accept) == HIGH) && (digitalRead(B_Offer) == LOW)) { digitalWrite(B_Normal, HIGH); digitalWrite(B_Accepted, LOW); digitalWrite(A_Normal, HIGH); digitalWrite(A_Blocked, LOW); digitalWrite(B_Block_OK, LOW); delay(250); while (digitalRead(B_Signal_Open) != LOW) { } digitalWrite(B_Accepted, HIGH); digitalWrite(B_Blocked, LOW); delay(250); while (digitalRead(B_Signal_Open) == LOW) { } delay(250); while ((digitalRead(A_Arrived) != LOW) || (digitalRead(A_Normal_Accept) != LOW)) { } digitalWrite(B_Blocked, HIGH); digitalWrite(A_Blocked, HIGH); digitalWrite(B_Normal, LOW); digitalWrite(A_Normal, LOW); digitalWrite(B_Block_OK, HIGH); } } To be continued... Fabrice
-
Hi all of you, I've received my locoshield PCB (LocoNet Interface for Arduino). I've tested my Arduino Program. And it is APPROVEDDDDDDDDDDDDDDDD I can control now directly turnouts by my interface. The program code is : [code] // SignalBox // Signal box (terminus station with single track) with "Nb_lever" levers // Signal box integrated interlocking system and tokenless block system (single track / terminus station) // Sound system -> Wtv020 // Digitrax LocoNet -> LocoNet // Tokenless Block System -> Tokenless // Fabrice Fayolle, May 2016 // Version 1.0 for Arduino Uno // Signal box with "NB_lever" const int Nb_lever = 7; // Arduino Uno // Pin -> Use for -> Connect to // 0 // 1 // 2 -> Tokenless Up (IN) -> Tokenless PCB // 3 -> Tokenless Signal Up (OUT) -> Tokenless PCB // 4 // 5 -> Tokenless Signal Down (OUT) -> Tokenless PCB // 6 -> Default -> Red LED (with a 220 ohms resistor) // 7 -> LocoNet Transmit pin -> Locoshield PCB Tx pin // 8 -> LocoNet Receive pin -> Locoshield PCB Rx pin // 9 to 15 -> Lever Input -> SPDT ON-ON // 16 to 19 -> Wtv020 -> Wtv020 PCB // INPUT // Lever : SPDT ON-ON // 1 -> 5V -> Normal position // Common point -> Lever Input // 2 -> GND -> Reverse position // OUTPUT // Digitrax LocoNet message // Type lever table // 0 -> Not use // 1 -> Point // 2 -> FPL // 3 -> Signal // 4 -> Block signal const int Table_lever_type[Nb_lever] = {4, 4, 1, 2, 1, 3, 0}; // Locking lever table to initialize each lock lever // 0 -> No lock // 1 -> 1 lock // x -> x lock const int Table_lever_lock[Nb_lever] = {1, 2, 0, 0, 0, 1, 1}; // Interlocking system table (locking rules) // It's not necessary to simplify locking rules like with mechanical locking bars // 0 -> Nothing // 1 -> Lock // -1 -> Release // 1xx -> LWxxN (Lock When xx Normal) // -1xx -> RWxxN (Release When xx Normal) // 2xx -> LWxxR (Lock When xx Reverse) // -2xx -> RWxxR (Release When xx Reverse) const int Table_interlocking[Nb_lever * Nb_lever] = { // Lever 1 (signal) // 1 locks 2, 3, 4, 5 and 6 0, 1, 1, 1, 1, 1, 0, // Lever 2 (signal) // 2 locks 1, 3, 4, 5 and 6 1, 0, 1, 1, 1, 1, 0, // Lever 3 (point) // 3 locks 1,2 and 4 1, 1, 0, 1, 0, 0, 0, // Lever 4 (FPL) // 4 releases 1 and 2. 4 locks 3 -1, -1, 1, 0, 0, 0, 0, // Lever 5 (point) // 5 locks 1 and 2. 5 releases 6 1, 1, 0, 0, 0, -1, 0, // Lever 6 (signal) // 6 locks 1, 2, 3(BW) and 5 1, 1, 1, 0, 1, 0, 0, // Lever 7 (-) // not used 0, 0, 0, 0, 0, 0, 0 }; // Visual management for interlocking system // Default lever position const int DefaultPin = 6; // Tokenless block system // Rail directions : Up Line -> To London / Down Line -> To the station boolean Block_ON = false; // Define which Arduino UNO Pin you use for communicate with the Tokenless PCB const int upBlock_request_PIN = 2; const int upBlock_signalopen_PIN = 3; const int downBlock_signalopen_PIN = 5; // Define which lever type you use for Block signal const int Block_signal = 4; // Define which lever you use to control signal const int upSignal_lever = 1; const int downSignal_lever = 0; boolean upBlock_request = false; boolean upBlock_blocked = false; // LocoNet #include <LocoNet.h> // LocoNet Transmit pin #define LN_TX_PIN 7 // Pointer to a received LocoNet packet lnMsg *LnPacket; // DDC Address of stationary decoder const int Table_lever_dcc[Nb_lever] = {1, 2, 3, 4, 11, 6, 7}; void sendOPC_SW_REQ(int SLever_dcc, boolean SLever_state, boolean On) { lnMsg SendPacket ; int sw2 = 0x00; if (SLever_state) { sw2 |= B00100000; } if (On) { sw2 |= B00010000; } sw2 |= (SLever_dcc >> 7) & 0x0F; SendPacket.data[ 0 ] = OPC_SW_REQ ; SendPacket.data[ 1 ] = SLever_dcc & 0x7F ; SendPacket.data[ 2 ] = sw2 ; LocoNet.send( &SendPacket ); Serial.print ("DCC address "); Serial.print(SLever_dcc+1); Serial.println(" : OPC_SW_REQ LocoNet message sent"); } void LocoNet_Message(int SLever_dcc, int SLever_type, boolean SLever_state) { switch (SLever_type) { case 0 : // 0 -> Not use break; case 1 : // 1 -> Point sendOPC_SW_REQ(SLever_dcc - 1, SLever_state, true); sendOPC_SW_REQ(SLever_dcc - 1, SLever_state, false); break; case 2: // 2 -> FPL break; case 3: // 3 -> Signal break; case 4: // 4 -> Block signal break; default: break; } } // Wtv020 #include <Wtv020sd16p.h> const int clockPin = 16; // CLOCK 7 const int resetPin = 17; // RESET 1 const int diPin = 18; // DATAIN 10 const int busyPin = 19; // BUSY 15 Wtv020sd16p wtv020sd16p(resetPin, clockPin, diPin, busyPin); // 0000.AD4 -> 0 -> Lever change // 0001.AD4 -> 1 -> Block system code bell // 0002.AD4 -> 2 -> People talking // 0003.AD4 -> 3 -> Steam engine // 0004.AD4 -> 4 -> People talking (2) // Wait the end of the sound before to do something else void Wtv020_wait() { delay(250); while (digitalRead(busyPin) == HIGH) { } } void Play_random_sound() { unsigned long time = millis(); if (time % random(1, 1000) == random(0, 10)) { Serial.println("Sound system"); wtv020sd16p.asyncPlayVoice(random(2, 5)); Wtv020_wait(); } } // Object "Lever" class Lever { private: int Lever_input; int Lever_type; int Lever_state; int Lever_lock; int Lever_dcc; public: void Setup(int SLever_input); boolean State_of_lever(); int Type_of_lever(); boolean Change_asking(); boolean Change_is_possible(); void Change(); void Change_lever_lock(int SChange); } ; void Lever::Setup(int SLever_input) { // Input : from pin 9 to pin 15 Lever_input = SLever_input + 9; pinMode (Lever_input, INPUT); digitalWrite(Lever_input, HIGH); boolean Normal = true; boolean Reverse = false; if (digitalRead(Lever_input) == Reverse) { delay(100); if (digitalRead(Lever_input) == Reverse) { digitalWrite(DefaultPin, HIGH); while (digitalRead(Lever_input) == Reverse) { } digitalWrite(DefaultPin, LOW); } } Lever_type = Table_lever_type[SLever_input]; // State of the lever // true -> Normal(by default) // false -> Reverse Lever_state = true; Lever_lock = Table_lever_lock[SLever_input]; Lever_dcc = Table_lever_dcc[SLever_input]; LocoNet_Message(Lever_dcc, Lever_type, true); } boolean Lever::Change_asking() { boolean result = false; if (digitalRead(Lever_input) != Lever_state) { delay(100); if (digitalRead(Lever_input) != Lever_state) { result = true; } } return result; } boolean Lever::Change_is_possible() { boolean result = false; result = (Lever_lock == 0); if (result == false) { while (digitalRead(Lever_input) != Lever_state) { digitalWrite(DefaultPin, HIGH); } digitalWrite(DefaultPin, LOW); } return result; } void Lever::Change() { Lever_state = !Lever_state; // Wtv020 wtv020sd16p.asyncPlayVoice(0); // LocoNet LocoNet_Message(Lever_dcc, Lever_type, Lever_state); // Wtv020 Wtv020_wait(); } boolean Lever::State_of_lever() { boolean result = true; result = Lever_state; return result; } int Lever::Type_of_lever() { int result = 0; result = Lever_type; return result; } void Lever::Change_lever_lock(int SChange) { Lever_lock += SChange; } Lever L[Nb_lever]; void setup() { // Initialize Serial Port USB at 57600 baud Serial.begin(57600); Serial.println("Monitor"); // LocoNet LocoNet.init(LN_TX_PIN); // Wtv020 wtv020sd16p.reset(); delay(500); // If you want to adjust the speaker volume, modify on Wtv020sd16p library (Wtv020sd16p.cpp) // this constant VOLUME_MAX = 0xFFF7 (from 0xFFF0 to 0xFFF7) and use the unmute() function wtv020sd16p.unmute(); delay(500); // Visual management pinMode(DefaultPin, OUTPUT); digitalWrite(DefaultPin, LOW); // Levers setup for (int i = 0; i < Nb_lever; i++) { L[i].Setup(i); } Serial.println("Ready to use"); // Tokenless pinMode(upBlock_request_PIN, INPUT); pinMode(upBlock_signalopen_PIN, OUTPUT); digitalWrite(upBlock_request_PIN, HIGH); digitalWrite(upBlock_signalopen_PIN, HIGH); pinMode(downBlock_signalopen_PIN, OUTPUT); digitalWrite(downBlock_signalopen_PIN, HIGH); Serial.println("Tokenless Block System : Normal"); } void loop() { for (int i = 0; i < Nb_lever; i++) { if (L[i].Change_asking()) { if (L[i].Change_is_possible()) { // Interlocking rules Locking_rules(i); // Change the state of the lever L[i].Change(); Serial.print("Lever "); Serial.print(i + 1); Serial.print(" changed to "); if (L[i].State_of_lever()) { Serial.print("Normal"); } else { Serial.print("Reverse"); } Serial.println(); // Tokenless if ( i == upSignal_lever) { if (L[i].State_of_lever()) { if (Block_ON) { L[i].Change_lever_lock(1); Serial.print("Block System locks "); Serial.println(i + 1); upBlock_blocked = true; digitalWrite(upBlock_signalopen_PIN, HIGH); } } else { if (Block_ON) { digitalWrite(upBlock_signalopen_PIN, LOW); Serial.println("Tokenless Block System : UP -> Blocked"); } } } if ( i == downSignal_lever) { if (L[i].State_of_lever()) { digitalWrite(downBlock_signalopen_PIN, HIGH); Serial.println("Signal DOWN Line -> Closed"); } else { digitalWrite(downBlock_signalopen_PIN, LOW); Serial.println("Signal DOWN Line -> Open"); } } } } } // Tokenless upBlock_accept(); upBlock_normal(); // Wtv020 Play_random_sound(); } void Locking_rules(int Slever) // Apply interlocking system rules when you move a lever { int Shift = Slever * Nb_lever; if (L[Slever].State_of_lever()) // Lever on "Normal" position { for (int i = 0; i < Nb_lever; i++) { switch (Table_interlocking[Shift + i]) { case 0 : break; case -1 : // Releases L[i].Change_lever_lock(Table_interlocking[Shift + i]); Serial.print(Slever + 1); Serial.print(" releases "); Serial.println(i + 1); break; case 1 : // Locks L[i].Change_lever_lock(Table_interlocking[Shift + i]); Serial.print(Slever + 1); Serial.print(" locks "); Serial.println(i + 1); break; default : // Locks/Releases when xx Normal/Reverse L[i].Change_lever_lock(Conditionnal_locking(Table_interlocking[Shift + i])); ; } } } else // Lever on "Reverse" position { for (int i = 0; i < Nb_lever; i++) { switch (Table_interlocking[Shift + i]) { case 0 : break; case -1 : // Locks L[i].Change_lever_lock(-Table_interlocking[Shift + i]); Serial.print(Slever + 1); Serial.print(" locks "); Serial.println(i + 1); break; case 1 : // Releases L[i].Change_lever_lock(-Table_interlocking[Shift + i]); Serial.print(Slever + 1); Serial.print(" releases "); Serial.println(i + 1); break; default : // Releases/Locks when xx Normal/Reverse L[i].Change_lever_lock(-Conditionnal_locking(Table_interlocking[Shift + i])); ; } } } } int Conditionnal_locking (int Locking) // Verify lever position if interlocking system rule is Locks/Releases When Lever xx is Normal/Reverse { int Hundred = Locking / 100; int result = 0; switch (Hundred) { case 1 : // Locks when Lever (Locking-(HundredX100)) is Normal if (L[Locking - 101].State_of_lever()) { result = 1; } break; case -1 : // Releases when Lever (Locking-(HundredX100)) is Normal if (L[-Locking - 101].State_of_lever()) { result = -1; } break; case 2 : // Locks when Lever (Locking-(HundredX100)) is Reverse if (!L[Locking - 201].State_of_lever()) { result = 1; } break; case -2 : // Releases when Lever (Locking-(HundredX100)) is Reverse if (!L[-Locking - 201].State_of_lever()) { result = -1; } break; } return result; } void upBlock_accept() // Tokenless Block System on "Accepted" for Up Line { upBlock_request = digitalRead(upBlock_request_PIN); delay(100); if ( !upBlock_request && (!Block_ON && L[downSignal_lever].State_of_lever())) { // Wtv020 wtv020sd16p.asyncPlayVoice(1); Wtv020_wait(); Serial.println("Tokenless Block System : UP Line -> Accepted"); L[upSignal_lever].Change_lever_lock(-1); Serial.print("Block System releases "); Serial.println(upSignal_lever + 1); L[downSignal_lever].Change_lever_lock(1); Serial.print("Block System locks "); Serial.println(downSignal_lever + 1); Block_ON = true; } } void upBlock_normal() // Tokenless Block System on "Normal" { upBlock_request = digitalRead(upBlock_request_PIN); delay(100); if (L[upSignal_lever].State_of_lever()) { if ( upBlock_request && Block_ON ) { if (!upBlock_blocked) { L[upSignal_lever].Change_lever_lock(1); Serial.print("Block System locks "); Serial.println(upSignal_lever + 1); } upBlock_blocked = false; Block_ON = false; L[downSignal_lever].Change_lever_lock(-1); Serial.print("Block System releases "); Serial.println(downSignal_lever + 1); Serial.println("Tokenless Block System : Normal"); } } } [/code] If you are interesting by my Locoshield PCB, don't hesitate to contact me. Comments welcome!!! Keep in touch, Fabrice
-
David, I've added some messages for Arduino Serial Monitor to see what's happen. // SignalBox // Signal box with "Nb_lever" levers with interlocking system //Fabrice Fayolle, April 2016 //Beta Version for Arduino Uno //Signal box with "NB_lever" const int Nb_lever = 7; // Arduino Uno // Pin -> Use for -> Connect to // 0 // 1 // 2 // 3 // 4 -> Default -> Red LED (with a 220 ohms resistor) // 5 // 6 -> LocoNet Transmit pin -> Locoshield PCB Tx pin // 7 // 8 -> LocoNet Receive pin -> Locoshield PCB Rx pin // 9 to 15 -> Lever Input -> SPDT ON-ON // 16 to 19 -> Wtv020 -> Wtv020 PCB // INPUT // Lever : SPDT ON-ON // 1 -> 5V -> Normal position // Common point -> Lever Input // 2 -> GND -> Reverse position // OUTPUT // Digitrax LocoNet message // Type lever table // 0 -> Not use // 1 -> Point // 2 -> FPL // 3 -> Signal const int Table_lever_type[Nb_lever] = {3, 3, 1, 2, 1, 3, 0}; // Locking lever table to initialize each lock lever // 0 -> No lock // 1 -> 1 lock // x -> x lock const int Table_lever_lock[Nb_lever] = {1, 1, 0, 0, 0, 1, 1}; // Interlocking system table (locking rules) // 0 -> Nothing // 1 -> Lock // -1 -> Release // 1xx -> LW (Lock When Normal) // -1xx -> RW (Release When Normal) // 2xx -> LW (Lock When Reverse) // -2xx -> RW (Release When Reverse) const int Table_interlocking[Nb_lever * Nb_lever] = { // Lever 1 (signal) // 1 locks 2, 4 and 5 0, 1, 0, 1, 1, 0, 0, // Lever 2 (signal) // 2 locks 1, 4 and 5 1, 0, 0, 1, 1, 0, 0, // Lever 3 (point) // 3 locks 4 0, 0, 0, 1, 0, 0, 0, // Lever 4 (FPL) // 4 releases 1 and 2. 4 locks 3 -1, -1, 1, 0, 0, 0, 0, // Lever 5 (point) // 5 locks 1 and 2. 5 releases 6 1, 1, 0, 0, 0, -1, 0, // Lever 6 (signal) // 6 locks 3 and 5 0, 0, 1, 0, 1, 0, 0, // Lever 7 (-) // not used 0, 0, 0, 0, 0, 0, 0, }; // Visual management for interlocking system // Default lever position const int DefaultPin = 4; //LocoNet #include <LocoNet.h> // LocoNet Transmit pin #define LNtxPin 6 // pointer to a received LocoNet packet lnMsg *LnPacket; //DDC Address of stationary decoder const int Table_lever_dcc[Nb_lever] = {1, 2, 3, 4, 5, 6, 7}; void sendOPC_SW_REQ(int SLever_dcc, boolean SLever_state, boolean On) { lnMsg SendPacket ; int sw2 = 0x00; if (SLever_state) { sw2 |= B00100000; } if (On) { sw2 |= B00010000; } sw2 |= (SLever_dcc >> 7) & 0x0F; SendPacket.data[ 0 ] = OPC_SW_REQ ; SendPacket.data[ 1 ] = SLever_dcc & 0x7F ; SendPacket.data[ 2 ] = sw2 ; LocoNet.send( &SendPacket ); //Serial.println("OPC_SW_REQ LocoNet message sent"); } void LocoNet_Message(int SLever_dcc, int SLever_type, boolean SLever_state) { switch (SLever_type) { case 0 : // 0 -> Not use break; case 1 : // 1 -> Point sendOPC_SW_REQ(SLever_dcc, SLever_state, true); sendOPC_SW_REQ(SLever_dcc, SLever_state, false); break; case 2: // 2 -> FPL break; case 3: // 3 -> Signal break; default: break; } } //Wtv020 #include <Wtv020sd16p.h> const int clockPin = 16; // CLOCK 7 const int resetPin = 17; // RESET 1 const int diPin = 18; // DATAIN 10 const int busyPin = 19; // BUSY 15 Wtv020sd16p wtv020sd16p(resetPin, clockPin, diPin, busyPin); // 0000.AD4 -> 0 -> Lever change // 0001.AD4 -> 1 -> Signal box is ready to use //Wait the end of the sound before to do void Wtv020_wait() { delay(250); while (digitalRead(busyPin) == HIGH) { } } //Object "Lever" class Lever { private: int Lever_input; int Lever_type; int Lever_state; int Lever_lock; int Lever_dcc; public: void Setup(int SLever_input); boolean State_of_lever(); boolean Change_asking(); boolean Change_is_possible(); void Change(); void Change_lever_lock(int SChange); } ; void Lever::Setup(int SLever_input) { // Input : from pin 9 to pin 15 Lever_input = SLever_input + 9; pinMode (Lever_input, INPUT); digitalWrite(Lever_input, HIGH); boolean Normal = true; boolean Reverse = false; if (digitalRead(Lever_input) == Reverse) { delay(100); if (digitalRead(Lever_input) == Reverse) { digitalWrite(DefaultPin, HIGH); while (digitalRead(Lever_input) == Reverse) { } digitalWrite(DefaultPin, LOW); } } Lever_type = Table_lever_type[SLever_input]; // State of the lever // true -> Normal(by default) // false -> Reverse Lever_state = true; Lever_lock = Table_lever_lock[SLever_input]; Lever_dcc = Table_lever_dcc[SLever_input]; LocoNet_Message(Lever_dcc, Lever_type, true); } boolean Lever::Change_asking() { boolean result = false; if (digitalRead(Lever_input) != Lever_state) { delay(100); if (digitalRead(Lever_input) != Lever_state) { result = true; } } return result; } boolean Lever::Change_is_possible() { boolean result = false; result = (Lever_lock == 0); if (result == false) { while (digitalRead(Lever_input) != Lever_state) { digitalWrite(DefaultPin, HIGH); } digitalWrite(DefaultPin, LOW); } return result; } void Lever::Change() { Lever_state = !Lever_state; // Wtv020 wtv020sd16p.asyncPlayVoice(0); //DCC LocoNet_Message(Lever_dcc, Lever_type, Lever_state); //Wtv020 Wtv020_wait(); } boolean Lever::State_of_lever() { boolean result = true; result = Lever_state; return result; } void Lever::Change_lever_lock(int SChange) { Lever_lock += SChange; } Lever L[Nb_lever]; void setup() { // Initialize Serial Port USB at 57600 baud Serial.begin(57600); Serial.println("Monitor"); // Wtv020 wtv020sd16p.reset(); delay(250); // Visual management pinMode(DefaultPin, OUTPUT); digitalWrite(DefaultPin, LOW); // Levers setup for (int i = 0; i < Nb_lever; i++) { L[i].Setup(i); } // Wtv020 wtv020sd16p.asyncPlayVoice(1); //Wtv020_wait(); Serial.println("Ready to use"); } void loop() { for (int i = 0; i < Nb_lever; i++) { if (L[i].Change_asking()) { if (L[i].Change_is_possible()) { // Interlocking rules Locking_rules(i); // Change the state of the lever L[i].Change(); Serial.print("Lever "); Serial.print(i + 1); Serial.print(" changed to "); if (L[i].State_of_lever()) { Serial.print("Normal"); } else { Serial.print("Reverse"); } Serial.println(); } } } } void Locking_rules(int Slever) { int Shift = Slever * Nb_lever; if (L[Slever].State_of_lever()) // Lever on "Normal" position { for (int i = 0; i < Nb_lever; i++) { switch (Table_interlocking[Shift + i]) { case 0 : break; case -1 : L[i].Change_lever_lock(Table_interlocking[Shift + i]); Serial.print(Slever+1); Serial.print(" releases "); Serial.println(i+1); break; case 1 : L[i].Change_lever_lock(Table_interlocking[Shift + i]); Serial.print(Slever+1); Serial.print(" locks "); Serial.println(i+1); break; default : L[i].Change_lever_lock(Conditionnal_locking(Table_interlocking[Shift + i])); ; } } } else // Lever on "Reverse" position { for (int i = 0; i < Nb_lever; i++) { switch (Table_interlocking[Shift + i]) { case 0 : break; case -1 : L[i].Change_lever_lock(-Table_interlocking[Shift + i]); Serial.print(Slever+1); Serial.print(" locks "); Serial.println(i+1); break; case 1 : L[i].Change_lever_lock(-Table_interlocking[Shift + i]); Serial.print(Slever+1); Serial.print(" releases "); Serial.println(i+1); break; default : L[i].Change_lever_lock(-Conditionnal_locking(Table_interlocking[Shift + i])); ; } } } } int Conditionnal_locking (int Locking) { int Hundred = Locking / 100; int result = 0; switch (Hundred) { case 1 : if (L[Locking - 101].State_of_lever()) { result = 1; } break; case -1 : if (L[-Locking - 101].State_of_lever()) { result = -1; } break; case 2 : if (!L[Locking - 201].State_of_lever()) { result = 1; } break; case -2 : if (!L[-Locking - 201].State_of_lever()) { result = -1; } break; } return result; } Just copy the code and enjoy!!! Fabrice
-
David, Arduino program algorithm : Begin Do you move a lever ? -> No, I do nothing. Go to begin ->Yes, I do. Can I move this lever ? Is it lock (lever_table_lock) ? -> Yes, it is. That's wrong. the default led switchs on. You can do nothing while you move again this lever. The default led switchs off. Go to the begin -> No, it isn't. Apply the interlocking matrix (table_interlocking) Play the sound of the lever Send a loconet message Go to the begin To be continued... Fabrice
-
Thanks for your interest!!! A junction : the interlocking matrix : row 1 : 1 locks 2, 3 and 4. If you reverse lever 1, you can't reverse after lever 2, lever 3 and lever 4 (2, 3 and 4 lock by 1) row 2 : 2 locks 1, 3 and 4. row 3 : 3 locks 1, 2 and 4. row 4 : 4 locks 2, releases 3. If you reverse lever 4, you can reverse after lever 3 (3 releases by 4). Don't hesitate to contact me for further informations Fabrice
-
Hi all of you, I've done a signal box test to verify arduino program and interlocking system algorithm. I use some SPDT On-On (Radiospares #734-7062P) and switch cap lever cover (Radiospares #297-415). You can see another PCB. I use it for the lever sound (Wtv020). I use brass rod to connect all SPDT (5V and GND). I'm just waiting for my Locoshield PCB to do some tests with my Digitrax system!!! I define a trackplan and I use 6 of 7 levers. Let's go to... Welcome comments!!! Be seeing you, Fabrice
-
David, Just copy this code (Cf. http://www.rmweb.co.uk/community/index.php?/topic/82978-arduino-applications-and-programs/?p=2276967) and modify tables like you want Fabrice
-
Hi all of you, some examples you can manage with my Arduino program. I you don't use DCC, you can declare some output pins for using with 5V relays. Example 1 The matrix is : Just replace L by 1 Tables are : // Type lever table // 0 -> Not use // 1 -> Point // 2 -> FPL // 3 -> Signal const int Table_lever_type[Nb_lever] = {3, 3, 0, 0, 0, 0, 0}; // Locking lever table to initialize each lock lever // 0 -> No lock // 1 -> 1 lock // x -> x lock const int Table_lever_lock[Nb_lever] = {0, 0, 0, 0, 0, 0, 0}; // Interlocking system table (locking rules) // 0 -> Nothing // 1 -> Lock // -1 -> Release // 1xx -> LW (Lock When Normal) // -1xx -> RW (Release When Normal) // 2xx -> LW (Lock When Reverse) // -2xx -> RW (Release When Reverse) const int Table_interlocking[Nb_lever * Nb_lever] = { // Lever 1 0, 1, 0, 0, 0, 0, 0, // Lever 2 1, 0, 0, 0, 0, 0, 0, // Lever 3 0, 0,0, 0, 0, 0, 0, // Lever 4 ... Example 2 The matrix is : Replace L by 1 Replace R by -1 Tables are: // Type lever table // 0 -> Not use // 1 -> Point // 2 -> FPL // 3 -> Signal const int Table_lever_type[Nb_lever] = {3, 3, 3, 1, 0, 0, 0}; // Locking lever table to initialize each lock lever // 0 -> No lock // 1 -> 1 lock // x -> x lock const int Table_lever_lock[Nb_lever] = {0, 0, 1, 0, 0, 0, 0}; // Interlocking system table (locking rules) // 0 -> Nothing // 1 -> Lock // -1 -> Release // 1xx -> LW (Lock When Normal) // -1xx -> RW (Release When Normal) // 2xx -> LW (Lock When Reverse) // -2xx -> RW (Release When Reverse) const int Table_interlocking[Nb_lever * Nb_lever] = { // Lever 1 0, 1, 1, 1, 0, 0, 0, // Lever 2 1, 0, 1, 1, 0, 0, 0, // Lever 3 1, 1, 0, 1, 0, 0, 0, // Lever 4 0, 1, -1, 0, 0, 0, 0, ... Comments welcome!!! Fabrice
-
Simon, That's the same for me. Change just 3 tables to adapt the program Keep in touch, Fabrice
-
Hi all of you, I apologize but you can manage "Locked Both Way". Replace LW3R by 203 Replace BW by 1 You obtain these tables : // Type lever table // 0 -> Not use // 1 -> Point // 2 -> FPL // 3 -> Signal const int Table_lever_type[Nb_lever] = {3, 3, 1, 0, 0, 0, 0}; // Locking lever table to initialize each lock lever // 0 -> No lock // 1 -> 1 lock // x -> x lock const int Table_lever_lock[Nb_lever] = {0, 0, 0, 0, 0, 0, 0}; // Interlocking system table (locking rules) // 0 -> Nothing // 1 -> Lock // -1 -> Release // 1xx -> LW (Lock When Normal) // -1xx -> RW (Release When Normal) // 2xx -> LW (Lock When Reverse) // -2xx -> RW (Release When Reverse) const int Table_interlocking[Nb_lever * Nb_lever] = { // Lever 1 0, 203, 1, 0, 0, 0, 0, // Lever 2 203, 0, 1, 0, 0, 0, 0, // Lever 3 0, 0,0, 0, 0, 0, 0, // Lever 4 0, 0, 0, 0, 0, 0, 0, // Lever 5 0, 0, 0, 0, 0, 0, 0, // Lever 6 0, 0, 0, 0, 0, 0, 0, // Lever 7 0, 0, 0, 0, 0, 0, 0, }; Comments welcome!!! Keep in touch, Fabrice
-
-
Not yet but I can try to manage "Locked Both Ways". Please wait!!!
-
It's done.
-
Hi all of you, Currently, I'm building a Locoshield (Digitrax LocoNet). Cf. John Plocher I've modified the code of my signal box with interlocking system arduino program to include Digitrax LocoNet command. // SignalBox // Signal box with "Nb_lever" levers with interlocking system //Fabrice Fayolle, May 2016 //Version 1.0 for Arduino Uno // Arduino Uno // Pin -> Use for -> Connect to // 0 // 1 // 2 // 3 // 4 -> Default -> Red LED (with a 220 ohms resistor) // 5 // 6 -> LocoNet Transmit pin -> Locoshield Tx pin // 7 // 8 -> LocoNet Receive pin -> Locoshield Rx pin // 9 to 15 -> Lever Input -> SPDT ON-ON // 16 to 19 -> Wtv020 -> Wtv020 PCB //Signal box with "NB_lever" const int Nb_lever = 7; // INPUT // Lever : SPDT ON-ON // 1 -> 5V -> Normal position // Common point -> Lever Input // 2 -> GND -> Reverse position // OUTPUT // Digitrax LocoNet // Type lever table // 0 -> Not use // 1 -> Point // 2 -> FPL // 3 -> Signal const int Table_lever_type[Nb_lever] = {1, 1, 1, 1, 1, 0, 0}; // Locking lever table to initialize each lock lever // 0 -> No lock // 1 -> 1 lock // x -> x lock const int Table_lever_lock[Nb_lever] = {0, 1, 0, 0, 0, 0, 0}; // Interlocking system table (locking rules) // 0 -> Nothing // 1 -> Lock // -1 -> Release // 1xx -> LW (Lock When Normal) // -1xx -> RW (Release When Normal) // 2xx -> LW (Lock When Reverse) // -2xx -> RW (Release When Reverse) const int Table_interlocking[Nb_lever * Nb_lever] = { // Lever 1 0, -1, 1, 0, 0, 0, 0, // Lever 2 1, 0, 0, 0, 0, 0, 0, // Lever 3 1, 0, 0, 0, 0, 0, 0, // Lever 4 0, 0, 0, 0, 0, 0, 0, // Lever 5 0, 0, 0, 0, 0, 0, 0, // Lever 6 0, 0, 0, 0, 0, 0, 0, // Lever 7 0, 0, 0, 0, 0, 0, 0, }; // Visual management for interlocking system // Default lever position const int DefaultPin = 4; //LocoNet #include <LocoNet.h> // LocoNet Transmit pin #define LNtxPin 6 // pointer to a received LocoNet packet lnMsg *LnPacket; //DDC Address of stationary decoder const int Table_lever_dcc[Nb_lever] = {1, 2, 3, 4, 5, 6, 7}; void sendOPC_SW_REQ(int SLever_dcc, boolean SLever_state, boolean On) { lnMsg SendPacket ; int sw2 = 0x00; if (SLever_state) { sw2 |= B00100000; } if (On) { sw2 |= B00010000; } sw2 |= (SLever_dcc >> 7) & 0x0F; SendPacket.data[ 0 ] = OPC_SW_REQ ; SendPacket.data[ 1 ] = SLever_dcc & 0x7F ; SendPacket.data[ 2 ] = sw2 ; LocoNet.send( &SendPacket ); Serial.println("OPC_SW_REQ LocoNet packet sent"); } void LocoNet_Order(int SLever_dcc, int SLever_type, boolean SLever_state) { switch (SLever_type) { case 0 : // 0 -> Not use break; case 1 : // 1 -> Point sendOPC_SW_REQ(SLever_dcc, SLever_state, true); sendOPC_SW_REQ(SLever_dcc, SLever_state, false); break; case 2: // 2 -> FPL break; case 3: // 3 -> Signal break; default: break; } } //Wtv020 #include <Wtv020sd16p.h> const int clockPin = 16; // CLOCK 7 const int resetPin = 17; // RESET 1 const int diPin = 18; // DATAIN 10 const int busyPin = 19; // BUSY 15 Wtv020sd16p wtv020sd16p(resetPin, clockPin, diPin, busyPin); // 0000.AD4 -> 0 -> Lever change // 0001.AD4 -> 1 -> Signal box is ready to use //Object "Lever" class Lever { private: int Lever_input; int Lever_type; int Lever_state; int Lever_lock; int Lever_dcc; public: void Setup(int SLever_input); boolean State_of_lever(); boolean Change_asking(); boolean Change_is_possible(); void Change(); void Change_lever_lock(int SChange); } ; void Lever::Setup(int SLever_input) { // Input : from pin 9 to pin 15 Lever_input = SLever_input + 9; pinMode (Lever_input, INPUT); digitalWrite(Lever_input, HIGH); boolean Normal = true; boolean Reverse = false; if ( digitalRead(Lever_input) == Reverse ) { delay(100); if (digitalRead(Lever_input) == Reverse) { digitalWrite(DefaultPin, HIGH); while (digitalRead(Lever_input) == Reverse) { } digitalWrite(DefaultPin, LOW); } } Lever_type = Table_lever_type[SLever_input]; // State of the lever // true -> Normal(by default) // false -> Reverse Lever_state = true; Lever_lock = Table_lever_lock[SLever_input]; Lever_dcc = Table_lever_dcc[SLever_input]; LocoNet_Order(Lever_dcc, Lever_type, true); } boolean Lever::Change_asking() { boolean result = false; // Type lever // 0 -> Not use if (Lever_type != 0) { if (digitalRead(Lever_input) != Lever_state) { delay(100); if (digitalRead(Lever_input) != Lever_state) { result = true; } } } return result; } boolean Lever::Change_is_possible() { boolean result = false; result = (Lever_lock == 0); if (result == false) { while (digitalRead(Lever_input) != Lever_state) { digitalWrite(DefaultPin, HIGH); } digitalWrite(DefaultPin, LOW); } return result; } void Lever::Change() { Lever_state = !Lever_state; // Wtv020 wtv020sd16p.asyncPlayVoice(0); //DCC LocoNet_Order(Lever_dcc, Lever_type, Lever_state); } boolean Lever::State_of_lever() { boolean result = true; result = Lever_state; return result; } void Lever::Change_lever_lock(int SChange) { Lever_lock += SChange; } lever L[Nb_lever]; void setup() { // Initialize Serial Port USB at 57600 baud Serial.begin(57600); Serial.println("Monitor"); // Wtv020 wtv020sd16p.reset(); // Visual management pinMode(DefaultPin, OUTPUT); digitalWrite(DefaultPin, LOW); // Levers setup for (int i = 0; i < Nb_lever; i++) { L[i].Setup(i); } // Wtv020 wtv020sd16p.asyncPlayVoice(1); } void loop() { for (int i = 0; i < Nb_lever; i++) { if (L[i].Change_asking()) { if (L[i].Change_is_possible()) { // Interlocking rules Locking_rules(i); // Change the state of the lever L[i].Change(); Serial.print("Lever "); Serial.print(i + 1); Serial.print(" changed to "); if (L[i].State_of_lever()) { Serial.print("Normal"); } else { Serial.print("Reverse"); } Serial.println(); } } } } void Locking_rules(int Slever) { int Shift = Slever * Nb_lever; if (L[Slever].State_of_lever()) // Lever on "Normal" position { for (int i = 0; i < Nb_lever; i++) { switch (Table_interlocking[Shift + i]) { case 0 : break; case -1 : L[i].Change_lever_lock(Table_interlocking[Shift + i]); break; case 1 : L[i].Change_lever_lock(Table_interlocking[Shift + i]); break; default : L[i].Change_lever_lock(Conditionnal_locking(Table_interlocking[Shift + i])); ; } } } else // Lever on "Reverse" position { for (int i = 0; i < Nb_lever; i++) { switch (Table_interlocking[Shift + i]) { case 0 : break; case -1 : L[i].Change_lever_lock(-Table_interlocking[Shift + i]); break; case 1 : L[i].Change_lever_lock(-Table_interlocking[Shift + i]); break; default : L[i].Change_lever_lock(-Conditionnal_locking(Table_interlocking[Shift + i])); ; } } } } int Conditionnal_locking (int Locking) { int Hundred = Locking / 100; int result = 0; switch (Hundred) { case 1 : if (L[Locking - 101].State_of_lever()) { result = 1; } break; case -1 : if (L[-Locking - 101].State_of_lever()) { result = -1; } break; case 2 : if (!L[Locking - 201].State_of_lever()) { result = 1; } break; case -2 : if (!L[-Locking - 201].State_of_lever()) { result = -1; } break; } return result; } Comments welcome!!! Fabrice
-
Nice project!!! Thanks for sharing
-
Hi, I've discovered your exhibition layout. Amazing good shed!!! Have a G'day, Fabrice
-
It sounds great!!!
-
Talyllyn Junction. The Station area has been started!
ffayolle replied to Jintyman's topic in 7mm+ modelling
Nice weathering. Just enough... -
Nice project!!!
-
New French narrow gauge layout in 1/32 scale
ffayolle replied to kirtleypete's topic in French Railways
Very nice layout!!! I like it -
Scotland for ever Nice atmosphere!!!
-
Nice trackplan Thanks for this sharing