Jump to content
 

Arduino Applications and Programs


Simond
 Share

Recommended Posts

Hi folks, Thanks for yout thoughts... turns out it is a problem with the A0 pin. Moved to A5 and it now works correctly. Should have checked that, but in my defense (!) just about all the sample code I have seen available uses A0.  Leasson learned!

  • Like 1
Link to post
Share on other sites

  • 3 weeks later...

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
Link to post
Share on other sites

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

Link to post
Share on other sites

  • RMweb Premium
7 hours ago, ffayolle said:

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.

 

Keep in touch, Fabrice

 

Hello Fabrice,

 

Thank you for posting your Tokenless Block System update; I will take a look at it and see if I can incorporate it in my Moretonhampstead plan.

 

I am pleased I managed (with the help of my son)  to get your Signalbox Interlocking Program modified and working with my Moretonhampstead Signal Interlocking . In particular  I like the "Printing to Monitor" function where it shows what is happening as you change the Levers.  I was also so impressed with your LocoNet set up that I am now looking at getting the Arduino linked up to the MERG CBUS System using a MCP 2515 CAN BUS Module.

 

 

Edited by Pannier Tank
Link to post
Share on other sites

  • 3 weeks later...
  • RMweb Gold

Hi all, I am just getting interested in Arduinos.  The whine from locos is due to the PWM. I built a controller using op amps some years ago which did the same. I think the H bridge circuits in DCC decoders runs at over 20Hz so you cannot hear any whine. You should be able to do the same with Arduinos. There was also some interesting stuff on a Youtube Channel Dronebot Workshop. I particularly found the bit on using Infra red controls interesting. Controlling a loco with a remote could work well indoors although for outdoors pairing the arduino with A Nrf24l seems a better option.  No sketches at present. The lastime I wrote anything in C  was over 20 years ago.  Neat tidy code is always a good idea. I have seen a lot of coding that has been added to and extended over the years so no one really understands it now. Makes it very hard to change as there are often unexpected effects.

There are some builds on Youtube using an arduino linked to a laptop running JRMI to control DCC and one using an Arduino with an App on an android phone. These offfer very cheap DCC probably from around £50 for the base unit  assuming you already have the laptop or phone to use. However nether seems to offer the possibility of having more than one controller  linked to the same arduino DCC base unit. I think you would probably need somthing like a Pi to do the work of translating the operator inputs into suitable data strings. 

Don

Link to post
Share on other sites

This isn't, strictly, an Arduino question, but as an Arduino is involved, this still seems an appropriate place to ask.

 

The help I received upthread with my Arduino based shuttle unit was invaluable and it's been happily trundling my Hornby J52 up and down 10 feet of Streamline in a shop window display for the past month. 

 

Flushed with the success of this little project I'm contemplating doing something similar with some Lionel 3-rail equipment I have. I'm happy enough to modify the AC loco to operate on DC, so that isn't a problem. However, the honking great motors in Lionel gear can pull up to 3A which rather exceeds the 500mA limit of the typical motor shield. As a cheapskate I'd like to do this without spending much so I've been thinking of possible workarounds. 

 

And so, rather longwindedly to my question. Can I use the PWM output from the motor shield (or even direct from the Arduino) to provide base bias to a robust power transistor (something like a 2N3055 or modern equivalent) to send a high current PWM output to the track? Reversing at each end of the shuttle would be via an extra couple of lines of code to operate a DPDT relay. 

 

Is there any reason why this wouldn't work, and might there be a better (but still cheap) way to do this? 

Link to post
Share on other sites

Pat,

 

yes, you can, but...

 

it'll only go one way!

 

you need an "H-bridge" to get bi-directional control.  I have used these in the past and they will easily handle the current you need;  Polulu VNH5019

 

http://www.hobbytronics.co.uk/vnh5019-motor-driver?utm_source=google&utm_medium=googleshopping&utm_campaign=googlebase&gclid=EAIaIQobChMIx_3xnse25AIViKztCh1fNw2dEAQYASABEgKO4fD_BwE

 

If you go on ebay, they have equivalents that are half the price or less  - this offer is 30A, but I'd expect that there are 5A versions around for lots less again

 

https://www.ebay.co.uk/itm/Vnh5019-Single-Dc-Motor-Drive-Module-Board-30A-High-Current-Self-Voltage-Pr-V0D3/333229954377?_trkparms=aid%3D555018%26algo%3DPL.SIM%26ao%3D2%26asc%3D40733%26meid%3De63bd4c3f00a41378b1485b23b1c86ae%26pid%3D100677%26rk%3D1%26rkt%3D30%26sd%3D283528406730%26itm%3D333229954377%26pmt%3D1%26noa%3D0%26pg%3D2385738&_trksid=p2385738.c100677.m4598

 

 

And there are plenty of code examples on line too

 

hth

Simon

  • Thanks 1
Link to post
Share on other sites

5 hours ago, PatB said:

However, the honking great motors in Lionel gear can pull up to 3A which rather exceeds the 500mA limit of the typical motor shield.

The Infineon TLE5206 h-bridge can handle 5 amps in a small package.

 

...R

  • Thanks 1
Link to post
Share on other sites

On 15/08/2019 at 19:47, ffayolle said:

// 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};

Both 5th and 11th have a value of 11; do they point to the same decoder address?

Link to post
Share on other sites

  • RMweb Gold
1 hour ago, NinOz said:

Both 5th and 11th have a value of 11; do they point to the same decoder address?

 

It does seem odd  Const int Table_Lever_type give 5 as an FPL and 11 as a point.  It raises the question to me as to why the FPLs would need a decoder address . Have you installed working FPLs or is it simply to ensure the FPLs do have an address for a decoder even if none is needed.

Alternatively I may be totally barking up the wrong tree (it wouldn't be the first time)

 

Don

Link to post
Share on other sites

  • 6 months later...

I am writing a sketch to place DCC electrical signals on the track to control a train with a Bachmann decoder. I am not using a commercially made controller, just an Arduino Uno. Using Arduino I have constructed, what to me, is a packet consisting of a preamble, address, control byte, and XOR check. But still the train does not move in DCC mode. Has a member tried this and is there something else that needs to be done for it to work? The packet I am sending is shown in the attached pdf.

 

The train does move but in DC mode, when I change parameters in the packet like address or direction,..., the train still only moves forward. So I assume my packet is not being decoded by the decoder. I am working with a member of Arduino.cc on this but am unable to get the sketch to work.

Packet Format.pdf

Link to post
Share on other sites

  • RMweb Gold

You say just an arduino but I assume you are using a motor shield too as the arduino itself will not deliver sufficient power. 

I am not really au fait with the DCC packets yet, but what about the sketch sight of that may help. 

 

Don

Link to post
Share on other sites

  • 1 month later...
  • RMweb Gold

Greetings all...

 

Having read this, and other threads, I have succesfully prototyped an arduino static decoder to switch my led lights on and off on the layout under the control of RR&Co which to me is a huge step forward  (and at my age, somewhat unexpected, least of all by me).   

 

What I am wondering is, has anybody built, or designed, an arduino DCC unit for reading the Railcom ID and passing it back on the Lenz feedback bus so that RR&Co can get that info.  I already have the layout linked to RR&Co using LDT modules, so I don't really want to change all that for another decoder type such as the Digikeijs ones  (cost etc), but if someone, somewhere has built a Railcom ID detector with Arduino that can operate in parallel to the LDT's on the Lenz feedback Bus, I would be very interested in getting details :)

 

With thanks in Advance.

 

Graham

Edited by Moria15
spellink
Link to post
Share on other sites

  • 2 months later...

Um, have you guys heard of:

1. DCC++

2. Arcomora

3. Dave Bodnar?

 

@bkkboy  DCC++ sounds like what you want.

 

The Youtube channel DCC++ is a good place to start, it will lead you to the GutHub repositories.  If you search for 'DCC++ commands' on Google, it will take you to the GitHub Wiki page, and you can see what it is capable of.

 

A gentleman by the name of Dave Bodnar has developed a DCC++ Arduino throttle which simply outputs serial commands according to the inputs.  He's also developed a wireless version which uses a HC-12 radio link.  With this, you don't need the software, although I found his coding a little crude.  With a bit of development, the handheld could control individual turnouts, or be customised to select routes.  The way the HC-12s work, fortunately, you can have multiple handhelds transmitting to the basestation, with no errors.  Dave's website is worth exploring from top to toe - hunt down every link, as there are some gems in there, and it's not particularly well organised!

 

Mentioned here in other threads is Arcomora.  If you haven't looked through that, you're missing out.  DCC++ and Arcomora MARDEC/ARSIGDEC allow you to create a complete manually controlled automated layout - points, signals and locos.  Arcomora ARLOCO is a sensor interface which outputs to Loconet, allowing you to automate a layout with block sensing in something like JMRI... as long as you spend a bit of cash on a Loconet to USB interface.

 

I've some stuff to add to this (like, I don't want to use JMRI or Loconet, however I do want to automate a layout) but I'll post that when I've got all the above stuff working first!  (My bits are in the post.)  :D
 

Edited by FoxUnpopuli
Link to post
Share on other sites

  • RMweb Gold

Yes I have heard of Gregg's DCC++ and built the base station next I will be trying out three different cordless throttles using wifi, bluetooth and 4333MHz to see which copes best. Currently most cordless throttles are unusable at a big show because there is so much wifi etc. going on.

The base station can be used with JMRI on a laptop or with a Raspberry Pi.

For Bluetooth and Wifi I got an Expressif ESP32s  it looked like a big processor I then found in the US someone has ported DCC++ to the ESP32so the base station has built in WIfi capability.

I am also considering porting the DCC++ code to an STM32 Nucleo board a much faster ARM processor and using an IBT-2 Motor driver which can handle more amps  for 0 gauge with sounds.

 

Don

Link to post
Share on other sites

Is anyone aware of a wireless remote sensor transmitter to the Arduino?   To explain, I am using an Arduino for a Station Stop DC based system I have built using hall effect sensors.  I would like to activate the sensor some 40'  from the Arduino.  I could hardwire it but I cannot help thinking there should be a way that the sensor could send a wireless signal to the Arduino but so far i have not found it.  

Link to post
Share on other sites

Generally the sensor will need to be connected to a micro-controller and a wireless device to send messages and the main Arduino will need a compatible wireless receiver.

 

Probably the simplest MCU/wireless combination is the ESP8266 which can transmit as WiFi or using its own ESP-NOW system. The receiving Arduino will also need to be connected to an ESP8266. ESP-NOW demo

 

If you want to use a regular Arduino (such as nano) with the sensor then you could use nRF24L01+ modules for the wireless communication. Simple nRF24L01+ Tutorial

 

...R

  • Thanks 1
Link to post
Share on other sites

  • RMweb Gold

You could replace the Arduinoo with a ESP32S which can be coded using the Arduino IDE. There would be changes needed due to the different pinout but the ESP32 has inbuilt Wifi and Bluetooth capability

Don 

  • Thanks 1
Link to post
Share on other sites

  • 2 weeks later...

Hello, I am new to  the use of Arduino's and having quite a bit of a headache.  I want to simply control my 00 gauge turntable.  I bought a Nema 17 stepper motor and a Dual H Bridge L298N Stepper Motor Driver Controller.

I used a sketch for a small stepper motor with shield (28BYJ-48 Stepper Motor)  A sketch  worked with this stepper motor but not with the Nena 17.  I have used the example sketch in the IDE sketches but the stepper gets very hot when stopped.

I only want to make the turntable go clockwise and anticlockwise and stop at my track on a turntable.  I haven't found a simple sketch to do this, later I would like to programme stopping points

Can anyone help, I am useless at sketch writing and learning is not coming easily to me.

Link to post
Share on other sites

1 hour ago, Warwick said:

I bought a Nema 17 stepper motor and a Dual H Bridge L298N Stepper Motor Driver Controller.

 

An L298N is a very poor choice for a stepper motor driver. You should get a specialised stepper motor driver such as a Pololu DRV8825. A specialised driver has the ability to limit the current to protect the motor and it also takes a lot of the computational load off the Arduino.

 

HOWEVER you need to choose a driver that can comfortably supply the current required by your motor and I don't know what that is - it will be stated in the motor specifications. The DRV8825 is OK for motors that need 1.7 amps or less.

 

...R

Stepper Motor Basics
Simple Stepper Code

also look up the AccelStepper library
 

Link to post
Share on other sites

  • RMweb Premium
1 hour ago, Warwick said:

Hello, I am new to  the use of Arduino's and having quite a bit of a headache.  I want to simply control my 00 gauge turntable.  I bought a Nema 17 stepper motor and a Dual H Bridge L298N Stepper Motor Driver Controller.

I used a sketch for a small stepper motor with shield (28BYJ-48 Stepper Motor)  A sketch  worked with this stepper motor but not with the Nena 17.  I have used the example sketch in the IDE sketches but the stepper gets very hot when stopped.

I only want to make the turntable go clockwise and anticlockwise and stop at my track on a turntable.  I haven't found a simple sketch to do this, later I would like to programme stopping points

Can anyone help, I am useless at sketch writing and learning is not coming easily to me.

The sketch needs to power off the stepper at the end of movement, not all sketches do.

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...