Jump to content
 

ffayolle

Members
  • Posts

    289
  • Joined

  • Last visited

Posts posted by ffayolle

  1. Hi all of you,

    I 've designed a Locoshield version for Arduino Mega.

     

    post-3358-0-33218100-1463578861_thumb.jpg

     

    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

  2. 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

     

  3. 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

    • Like 1
  4. 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

  5. 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

  6. The diagram and matrix concept in Reply #96 looks very neat.

     

    Would you mind explaining (for example) how to "read" or interpret the matrix for example 2. I'm not asking about the computer code - just the signalling system.

     

    ...R

     

    Thanks for your interest!!!

     

    A junction :

     

    post-3358-0-37946300-1461488902.jpg

     

    the interlocking matrix :

     

    post-3358-0-07838700-1461488446.jpg

     

    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

  7. Hi all of you,

     

    I've done a signal box test to verify arduino program and  interlocking system algorithm.

     

    post-3358-0-41056000-1461484573_thumb.jpg

     

    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).

     

    post-3358-0-25149900-1461484729_thumb.jpg

     

    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.

     

    post-3358-0-07026300-1461485597.jpg

     

    Let's go to...

     

    Welcome comments!!!

     

    Be seeing you, Fabrice

    • Like 3
  8. Fabrice, excellent stuff. Starting from basics should help me understand what's involved. I have knocked up a little box with 14 Switches (A0 to A13) and 14 LED's (31 to 44) connected to an Arduino Mega. I will try to use your code etc to see if I can make it work !

     

    Ok, I've copied your code from example 1 into Arduino IDE. I get an "Nb_lever was not declared" error; I did say this code was a bit above my head. I guess I need to sort out what goes in the "set up" and "loop" portions?

     

    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

  9. 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

     

    post-3358-0-60056800-1461155492.jpg

     

    The matrix is :

     

    post-3358-0-73027700-1461155523.jpg

     

    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

     

    post-3358-0-81332400-1461155766.jpg

     

    The matrix is :

     

    post-3358-0-88206100-1461155815.jpg

     

    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

  10. Thanks to Fabrice, and other contributors.

     

    My head is not in this at the moment (See my thread on lasers) but it is really valuable. My intent was (is) to create a signalling Arduino program into which I could load a database of "released by" & "locked by"as some sort of universal table to customise the program to local signalling needs.

     

    Please keep up the good work!

    Simon

     

    Simon,

    That's the same for me. Change just 3 tables to adapt the program  

    Keep in touch, Fabrice

  11. 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

    • Like 2
  12. Fabrice,

     

    I'm just in the process off looking at Signal Interlocking using a Arduino Mega and your example code looks very interesting. I'd not considered looking at Boolean Code before, however it's given me some ideas, although I must admit your code is way above my head at the moment!!!

     

    I don't think you have a "Locked Both Ways" in your code, that's something I require in my application.

     

    Not yet but I can try to manage "Locked Both Ways".

    Please wait!!!

  13. Hi all of you,

     

    Currently, I'm building a Locoshield (Digitrax LocoNet). Cf. John Plocher

     

    post-3358-0-60365700-1460921473.jpg

     

    post-3358-0-20770700-1460921515.jpg

     

    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

  14. I started to weather the WR van last night, and used weathering liquids and powders. wiping most of it all off again. I then got up early this morning and gently blew some frame dirt and black over the body and chassis.

     

    attachicon.gifTal 181.jpg

     

    attachicon.gifTal 182.jpg

     

    I'm going to give it some chalk marks and then matt varnish it all over the weekend. I just hope I don't have the same problems I did with the varnish!!!

     

    Jinty ;)

    Nice weathering. Just enough...

×
×
  • Create New...