Jump to content
 

ffayolle

Members
  • Posts

    289
  • Joined

  • Last visited

Posts posted by ffayolle

  1. Hi everybody,

     

    I'm back...

    😉😉😉

     

    I've bought this layout but currently I'm building a new 7mm scale layout with the similar trackplan.

     

    Infographie.jpg.5555447bd9b45a9dd9e4f537cf92db29.jpg

    I've built tracks and pointworks with some 3D items from FAB432. Cf. https://www.shapeways.com/shops/fab432

    FAB432 is my new cottage industry.

    EPV2-1.jpg.b20a9e3856f3f8a5f8828192704b4cd5.jpg

     

    Br3voies.jpg.ea7ad5bc39c4584a174fc2dfc83477ce.jpg

     

    I've designed a manual pointwork control (lasercut).

    EPV2-5.jpg.e0d6a07ba44b6041f9c764f804f4182f.jpg

    And I've designed some typical buildings like the signal box of Longroy Gamaches (Somme/Seine-Maritime), a 6 meters Saxby signal box, the goodshed of Miraumont (Somme) and the foot bridge of Amiens (Somme) station.

    EPV2-2023-13.jpg.9f78921195ade43de2b78c60c8ce0b5b.jpg

     

    Saxby signal box from the start (kit) to the end

    20230225_110705_resized.jpg.6c6e109fbad97a6bb78fb372a098cef2.jpg

    20230226_160320_resized.jpg.9e795b90e76abe30c15406847c15ded6.jpg

     

    20230319_111618_resized.jpg.fd4c29aac8d9346fd92d4456ccf45e37.jpg

     

    To be continued...

     

    Best regards from France, Fabrice

     

    • Like 13
    • Craftsmanship/clever 6
  2. Hi all of you,

     

    I've modified the Tokenless Block System program. I've added some comments to improve the understanding.

    This program includes IN&OUT datas for interlocking system.

     

    // Name
    #define NAME  "Tokenless-Blocksystem"
    #define NAME2 "Tokenless block system for single track"
    #define NAME3 "with IN&OUT datas for interlocking system"
    
    // Version & Copyright
    #define VERSION "Version 2.0 (Arduino Uno)"
    #define COPYRIGHT "Copyright Fabrice Fayolle, August 2019"
    
    // A -> B : Down Line
    // B -> A : Up Line
    // Rail directions : Up Line -> To London / Down Line -> To the station
    
    // Arduino Uno
    // Pin      -> Use for                      -> Connect to
    // 0
    // 1
    // 2        -> A:Normal                     -> Orange LED (Common - with a 470 ohms resistor)
    // 3        -> A:Blocked                    -> Red LED
    // 4        -> A:Accepted                   -> Green LED
    // 5        -> B:Normal                     -> Orange LED (Common - with a 470 ohms resistor)
    // 6        -> B:Blocked                    -> Red LED
    // 7        -> B:Accepted                   -> Green LED
    // 8
    // 9
    // 10       -> Block A->B (OUT)             -> Interlocking System PCB
    // 11       -> Signal A (IN)                -> Interlocking System PCB
    // 12       -> Block B->A (OUT)             -> Interlocking System PCB
    // 13       -> Signal B (IN)                -> Interlocking System PCB
    // 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 -> Accept
    // Common point
    // 2 -> GND -> Normal
    int A_Normal_Accept = 19;
    int B_Normal_Accept = 16;
    // SPDT ON-MON
    // 1 -> 5V
    // Common point
    // 2 -> GND -> Offer
    int A_Offer = 18;
    int B_Offer = 15;
    // SPDT ON-MON
    // 1 -> 5V
    // Common point
    // 2 -> GND -> Arrived
    int A_Arrived = 17;
    int B_Arrived = 14;
    //
    int A_Block_NOK = 10;
    int A_Signal_Not_Open = 11;
    int B_Block_NOK = 12;
    int B_Signal_Not_Open = 13;
    // Orange LED
    int A_Normal = 2;
    int B_Normal = 5;
    // Red LED
    int A_Blocked = 3;
    int B_Blocked = 6;
    // Green LED
    int A_Accepted = 4;
    int B_Accepted = 7;
    
    void setup()
    {
      Serial.begin(9600);
      Serial.println(NAME);
      Serial.println(NAME2);
      Serial.println(NAME3);
      Serial.println("-------------------------------------------------------------------------- -");
      Serial.print(VERSION); Serial.print(", "); Serial.println(COPYRIGHT);
      Serial.println("---------------------------------------------------------------------------");
      Serial.println("");
      pinMode(A_Normal, OUTPUT);
      digitalWrite(A_Normal, LOW);
      pinMode(B_Normal, OUTPUT);
      digitalWrite(B_Normal, LOW);
      pinMode(A_Blocked, OUTPUT);
      digitalWrite(A_Blocked, LOW);
      pinMode(B_Blocked, OUTPUT);
      digitalWrite(B_Blocked, LOW);
      pinMode(A_Accepted, OUTPUT);
      digitalWrite(A_Accepted, LOW);
      pinMode(B_Accepted, OUTPUT);
      digitalWrite(B_Accepted, LOW);
      pinMode(A_Normal_Accept, INPUT_PULLUP);
      pinMode(B_Normal_Accept, INPUT_PULLUP);
      pinMode(A_Offer, INPUT_PULLUP);
      pinMode(B_Offer, INPUT_PULLUP);
      pinMode(A_Arrived, INPUT_PULLUP);
      pinMode(B_Arrived, INPUT_PULLUP);
      pinMode(A_Signal_Not_Open, INPUT_PULLUP);
      pinMode(A_Block_NOK, OUTPUT);
      digitalWrite(A_Block_NOK, HIGH);
      pinMode(B_Signal_Not_Open, INPUT_PULLUP);
      pinMode(B_Block_NOK, OUTPUT);
      digitalWrite(B_Block_NOK, HIGH);
      // Normalization 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)
      if (digitalRead(A_Normal_Accept) == LOW || digitalRead(B_Normal_Accept) == LOW)
      {
        Serial.println("Error: To initialize the block system, please set both block instrument to Accept");
      }
      while (digitalRead(A_Normal_Accept) == LOW || digitalRead(B_Normal_Accept) == LOW)
      {
        digitalWrite(A_Blocked, HIGH);
        digitalWrite(B_Blocked, HIGH);
        delay(250);
        digitalWrite(A_Blocked, LOW);
        digitalWrite(B_Blocked, LOW);
        delay(250);
      }
      digitalWrite(A_Normal, HIGH);
      digitalWrite(B_Normal, HIGH);
      Serial.println("Block system: OK");
    }
    
    void loop()
    {
      while (digitalRead(A_Normal_Accept) == LOW && digitalRead(B_Normal_Accept) == LOW)
      {
        if (digitalRead(A_Offer) == LOW)
        {
          Serial.println("Error: A can't take the B offer because B is not set to Accept. Maybe shunting is taking place into the block section");
          for (int i = 0; i < 4; i++)
          {
            digitalWrite(A_Normal, LOW);
            delay(250);
            digitalWrite(A_Normal, HIGH);
            delay(250);
          }
        }
        if (digitalRead(B_Offer) == LOW)
        {
          Serial.println("Error: B can't take the A offer because A is not set to Accept. Maybe shunting is taking place into the block section");
          for (int i = 0; i < 4; i++)
          {
            digitalWrite(B_Normal, LOW);
            delay(250);
            digitalWrite(B_Normal, HIGH);
            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 HIGH).
      // If the B instrument were not set to “Accept” (B_Normal_Accept at HIGH), this would not occur, and a blinking warning would be given
      // When acceptance has occurred, the block status at A is indicated as “Accepted” (A_Accepted to HIGH).
      if ((digitalRead(A_Normal_Accept) == LOW) && (digitalRead(B_Normal_Accept) == HIGH) && (digitalRead(A_Offer) == LOW))
      {
        Serial.println("Block system: A->B (accepting)");
        digitalWrite(A_Normal, LOW);
        digitalWrite(A_Accepted, HIGH);
        digitalWrite(B_Normal, LOW);
        digitalWrite(B_Blocked, HIGH);
        // Block System releases the starting signal lever towards B (interlocking system).
        Serial.println("Block system: A->B (starting signal towards B interlocking system releasing)");
        digitalWrite(A_Block_NOK, LOW);
        // Block System don't release the B starting signal lever towards A (interlocking system).
        digitalWrite(B_Block_NOK, HIGH);
        delay(250);
        // The A signalman may now, at any time, release his starting signal towards B.
        // As the signal clears (A_Signal_Not_Open at LOW), the block status changes to “Blocked” (A_Blocked to HIGH).
        Serial.println("Block system: A->B (waiting to open the starting signal towards B)");
        while (digitalRead(A_Signal_Not_Open))
        {
        }
        Serial.println("Block system: A->B (starting signal towards B opening)");
        digitalWrite(A_Accepted, LOW);
        digitalWrite(A_Blocked, HIGH);
        delay(250);
        // The A signalman replaces his starting signal to danger once the train has departed from A (A_Signal_Not_Open at HIGH).
        Serial.println("Block system: A->B (waiting to close the starting signal towards B)");
        while (!digitalRead(A_Signal_Not_Open))
        {
        }
        Serial.println("Block system: A->B (starting signal towards B closing)");
        // Block System cancels the releasing of the starting signal lever towards B (interlocking system).
        digitalWrite(A_Block_NOK, HIGH);
        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))
        {
        }
        Serial.println("Block system: A->B (train arriving complete)");
        Serial.println("Block system: OK");
        digitalWrite(A_Blocked, LOW);
        digitalWrite(B_Blocked, LOW);
        digitalWrite(A_Normal, HIGH);
        digitalWrite(B_Normal, 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, LOW);
        digitalWrite(B_Accepted, HIGH);
        digitalWrite(A_Normal, LOW);
        digitalWrite(A_Blocked, HIGH);
        digitalWrite(B_Block_NOK, HIGH);
        digitalWrite(A_Block_NOK, LOW);
        delay(250);
        while (digitalRead(B_Signal_Not_Open))
        {
        }
        digitalWrite(B_Accepted, LOW);
        digitalWrite(B_Blocked, HIGH);
        delay(250);
        while (!digitalRead(B_Signal_Not_Open))
        {
        }
        digitalWrite(B_Block_NOK, LOW);
        delay(250);
        while ((digitalRead(A_Arrived) != LOW) || (digitalRead(A_Normal_Accept) != LOW))
        {
        }
        digitalWrite(B_Blocked, LOW);
        digitalWrite(A_Blocked, LOW);
        digitalWrite(B_Normal, HIGH);
        digitalWrite(A_Normal, HIGH);
    
      }
    }
    

     

    Keep in touch, Fabrice

  3. Hi all of you,

    Some news about my Arduino interlocking system:

     

    controlpanel.jpg.291c51ae6fbc1b2b3e846cd051581a1c.jpg

     

    tco.jpg.e70713081e7da3befbdb6501c416f45f.jpg

     

    // 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
    // Accessories -> Accessories
    
    // Fabrice Fayolle, August 2016
    // Version 2.1 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
    // 16 to 21             -> Accessories                  -> SPDT ON-ON
    // 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, 3, 2, 1, 2, 1, 4, 3, 0, 3, 1, 3, 1, 1, 3, 3, 0, 0, 1, 3};
    
    // Locking lever table to initialize each lock lever
    // 0 -> No lock
    // 1 -> 1 lock
    // x -> x locks
    // 99 -> not used lever
    const int Table_lever_lock[Nb_lever] = {0, 2, 0, 1, 0, 1, 1, 2, 99, 2, 0, 1, 0, 1, 0, 0, 99, 99, 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)
    // Special locking rules
    // 300 -> Lock When 4 is Normal and 6 is Normal
    // 400 -> Lock When 4 is Reverse and 19 is Reverse
    // -500 -> Release When 4 is Reverse and 11 is Reverse
    // -600 -> Release When 11 is Reverse and 19 is Reverse
    const int Table_interlocking[Nb_lever * Nb_lever] = {
      // Lever 1 (block signal)
      // locks 3, 5, 7, 11 // releases
      0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      // Lever 2 (signal)
      // locks 3, 4, 5, 6, 8(6N), 10(6R), 11(4N&6N), 13 (4R), 14(4R), 15(4R), 19(4R), 20(4R&19R) // releases
      0, 0, 1, 1, 1, 1, 0, 106, 0, 206, 300, 0, 204, 204, 204, 0, 0, 0, 204, 400,
      // Lever 3 (FPL)
      // locks 1, 7 // releases 2, 4, 8, 10
      1, -1, 0, -1, 0, 0, 1, -1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      // Lever 4 (point)
      // locks 3, 6, 8, 16 // releases 2(11R&19R)
      0, -600, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
      // Lever 5 (FPL)
      // locks 1, 7 // releases 2, 6, 8
      1, -1, 0, 0, 0, -1, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      // Lever 6 (point)
      // locks 4, 5, 8 // releases 2(11R), 10
      0, -211, 0, 1, 1, 0, 0, 1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      // Lever 7 ( block signal)
      // locks 1, 3, 5, 11 // releases
      1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      // Lever 8 (signal)
      // locks 2, 3, 4, 5, 6 // releases
      0, 1, 1, 1, 1, 1, 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 (signal)
      // locks 2, 3, 4, 5, 6 // releases
      0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      // Lever 11 (point)
      // locks 1, 2(4N&6N), 7 // releases 12
      1, 300, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0,
      // Lever 12 (signal)
      // locks 11, 16(14N) // releases
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 114, 0, 0, 0, 0,
      // Lever 13 (point)
      // locks // releases
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      // Lever 14 (point)
      // locks 19 // releases 16(12R)
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -212, 0, 0, 1, 0,
      // Lever 15 (signal)
      // locks 11, 12(11R), 13, 14, 16 , 19// releases
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 211, 1, 1, 0, 1, 0, 0, 1, 0,
      // Lever 16 (signal)
      // locks 2 (4R), 4, 11(14N), 12 (14N), 13, 14, 15, 19, 20(19R) // releases
      0, 204, 0, 1, 0, 0, 0, 0, 0, 0, 114, 114, 1, 1, 1, 0, 0, 0, 1, 219,
      // 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 (point)
      // locks 15 // releases 14, 2(4R&11R)
      0, -500, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 1, 0, 0, 0, 0, 0,
      // Lever 20 (signal)
      // locks 2 (4R&19R),4 (19R), 13 (19R), 14 (19R), 15(19R), 16(19R), 19 // releases
      0, 400, 0, 219, 0, 0, 0, 0, 0, 0, 0, 0, 219, 219, 219, 219, 0, 0, 1, 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 (0 to xx) you use to control signal
    const int upSignal_lever = 6;
    const int downSignal_lever = 0;
    boolean upBlock_request = false;
    boolean upBlock_blocked = false;
    
    // Throttle
    // Define used slots table
    int used_SLOT[120];
    // 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;
    }
    
    // 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 = 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)
    {
      boolean Normal = true;
      boolean Reverse = false;
      // Input : from pin 22 to pin 45
      Lever_input = SLever_input + 22;
      pinMode (Lever_input, INPUT);
      digitalWrite(Lever_input, HIGH);
      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 = Normal;
      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("SignalBox-Mega Monitor");
      // LocoNet
      LocoNet.init(LN_TX_PIN);
      pinMode(Emergency_PIN, INPUT);
      digitalWrite(Emergency_PIN, HIGH);
      DCC_On();
      // Throttle
      // Initialize used_SLOT
      for (int i = 0; i < 120; i++)
      {
        used_SLOT[i] = 0;
      }
      // 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);
      // Accessories
    
      // Levers setup
      for (int i = 0; i < Nb_lever; i++)
      {
        L[i].Setup(i);
      }
      Serial.println("Ready to use");
      // Wtv020
      wtv020sd16p.asyncPlayVoice(2);
      // 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 ");
            Serial.println(L[i].State_of_lever() ? "Normal" : "Reverse");
            // 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");
              }
            }
          }
          else
          {
            Serial.print("Lever ");
            Serial.print(i + 1);
            Serial.println(" is locked");
          }
        }
      }
      // Loconet
      Emergency();
      // Tokenless
      upBlock_accept();
      upBlock_normal();
      // 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
              Serial.print(Slever + 1);
              Serial.print(" locks/releases ");
              Serial.print(i + 1);
              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
              Serial.print(Slever + 1);
              Serial.print(" releases/locks ");
              Serial.print(i + 1);
              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(s) xx is(are) 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())
          {
            Serial.print(" because ");
            Serial.print(Locking - 100);
            Serial.println(" is Normal");
            result = 1;
          }
          else
          {
            Serial.println(" -NA-");
          }
          break;
        case -1 :
          // Releases when Lever (Locking-(HundredX100)) is Normal
          if (L[-Locking - 101].State_of_lever())
          {
            Serial.print(" because ");
            Serial.print(-Locking - 100);
            Serial.println(" is Normal");
            result = -1;
          }
          else
          {
            Serial.println(" -NA-");
          }
          break;
        case 2 :
          // Locks when Lever (Locking-(HundredX100)) is Reverse
          if (!L[Locking - 201].State_of_lever())
          {
            Serial.print(" because ");
            Serial.print(Locking - 200);
            Serial.println(" is Reverse");
            result = 1;
          }
          else
          {
            Serial.println(" -NA-");
          }
          break;
        case -2 :
          // Releases when Lever (Locking-(HundredX100)) is Reverse
          if (!L[-Locking - 201].State_of_lever())
          {
            Serial.print(" because ");
            Serial.print(-Locking - 200);
            Serial.println(" is Reverse");
            result = -1;
          }
          else
          {
            Serial.println(" -NA-");
          }
          break;
        case 3 :
          // Locks when Lever 4 is Normal and Lever 6 is Normal
          if (L[4 - 1].State_of_lever() && L[6 - 1].State_of_lever())
          {
            Serial.println(" because 4 is Normal and 6 is Normal");
            result = 1;
          }
          else
          {
            Serial.println(" -NA-");
          }
          break;
        case 4 :
          // Locks when Lever 4 is Reverse and Lever 19 is Reverse
          if (!L[4 - 1].State_of_lever() && !L[19 - 1].State_of_lever())
          {
            Serial.println(" because 4 is Reverse and 19 is Reverse");
            result = 1;
          }
          else
          {
            Serial.println(" -NA-");
          }
          break;
        case -5 :
          // Releases when Lever 4 is Reverse and Lever 11 is Reverse
          if (!L[4 - 1].State_of_lever() && !L[11 - 1].State_of_lever())
          {
            Serial.println(" because 4 is Reverse and 11 is Reverse");
            result = -1;
          }
          else
          {
            Serial.println(" -NA-");
          }
          break;
        case -6 :
          // Releases when Lever 11 is Reverse and Lever 19 is Reverse
          if (!L[11 - 1].State_of_lever() && !L[19 - 1].State_of_lever())
          {
            Serial.println(" because 11 is Reverse and 19 is Reverse");
            result = -1;
          }
          else
          {
            Serial.println(" -NA-");
          }
          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
    {
      int i = 0;
      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);
          }
          Serial.println("Emergency STOP!!!");
          //sendOPC_x(OPC_IDLE);
          while (used_SLOT[i] != 0)
          {
            sendOPC_xxx(OPC_LOCO_SPD, used_SLOT[i], 0x00);
            i = i + 1;
          }
        }
      }
      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;
    }
    

     

    // 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 (Common + with a 470 ohms resistor)
    // 3        -> A:Blocked                    -> Red LED
    // 4        -> A:Accepted                   -> Green LED
    // 5        -> B:Normal                     -> Orange LED (Common + 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 a 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);
      }
    }
    

     

    wtv020-pcb.jpg.b8b6dcc65a958ebdd4675d62d88649ae.jpg

     

    wtv020.jpg.eb7cb6416f2066a78c4e6a8eacc377d6.jpg

     

    locoshield-mega-pcb.jpg.883089bb0f9f12bea2c4107d757dad1d.jpg

     

    locoshield-mega.jpg.bb2a5c87235b509c5696fa4ee468cfc5.jpg

    • Like 1
    • Craftsmanship/clever 2
  4. Hi all of you,

     

    Happy new year from France

     

    So, I've finished some detail parts on the bed track.

     

    post-3358-0-69725400-1547974004_thumb.jpg

     

    And currently, I've building a CdZ (the French Gauge O Guild) U-60 kit.

     

    post-3358-0-44808900-1547974092_thumb.jpg

     

    post-3358-0-00802000-1547974110_thumb.jpg

     

    To be continued...

     

    Fabrice

    • Like 12
  5. Hi all of you,

     

    I've modified the trackplan.

     

    post-3358-0-35713900-1541322335.jpg

     

    I've finished the baseboard design (2 sheets of 5mm plywood).

     

    post-3358-0-97862600-1541322261.jpg

     

    post-3358-0-38304700-1541322277.jpg

     

    What do you think abour this design? is it enough strong?

     

    I'm waiting for turnout kits to verify the baseboard design.

     

    To be continued...

     

    Fabrice

    • Like 2
×
×
  • Create New...