Last Updated: 11/08/2025
![]()
Interlock++ Interlocking signal box and block signalling system for model railways
Projects >> Interlock++ Interlocking signal box and block signalling system for model railways
Latest News
11/08/2025
Version 0.6 released.
0.6 adds 3 new features that make working with block signalling easier.
New Functions:
loadSignalDuelMonitors() function to allow a signal to monitor 2 signal when tracks diverge, written for panel box type block signal control
void listDuelMonitors(void); List all the loadSignalDuelMonitors() rules
byte getItemStatus(uint16_t itemID);//get the items current position status of any itemID
loadSignalMonitors() Error message 12 (see iserGuide.ino) Reduced to a warning but will not flag as error. This is when 2 signals are monitoring the same signal. This is now allowed to happen (with a warning) as it's required for converging track situation.
Contents
I have decided to do seperate tutorials for the different features. This will make it easier to add things as new features come along.
Click on the titles below to jump to the section.
I will try and add video tutorils to each section as I get time.
| Introduction | What is Interlock++ (Video Intro) |
| Hardware requirements | Microprocessor boards and other components (Circuit diagram) |
| How it Works | A short guide on how to use Interlock++ |
| Download the sketch | Download the latest version of Interlock++ or the original 0.1 version |
| Instructions v0.5 or greater | Instructions on how to use interlock++ Main sketch overview and set up instructions. (Includes how to use custom functions to get information out of Interlock++). Input functions (How to send data and instructions to interlock++) |
| How to... | Short tutorials on using different aspects of the Interlock++ system (Usually created in response to information or help requests). Virtual Signals What are they and how do they work? Converging Track Block Signals How to set up block signal monitoring when two tracks come together Diverging Track Block Signals How to set up block signal monitoring at junctions |
| Additional resources | Links to other tutorials that may help with an Interlock++ project sych as how to use servos, various types of shift registers, types of buttons and switches etc. |
Interlock++ - Introduction
Code and circuit diagram download the video (Click to download)
What is Interlock++
I was asked to help with a project that would allow interlocking of signals and points/turnouts in the same way that they work on real railways. Instead of building a project specific to a single layout I decided to build an open system that would be useable with any track plan, big or small.
What is an interlock?
In it's simplest for imagine a straight piece of track with a signal allowing trains onto it at each end. For safety reasons it should be impossible to set a signal to green if the signal in the opposing direction is green.
Another example would be a level crossing. If the signals are set to green the interlock would prevent the signalman from opening the crossing gates to traffic and once opened under a red signal the interlock would prevent te signalman from setting signals to green in either direction.
These of course are simple rules. When it comes to points (turnouts) there may be a number of signals that need to be set to red before the point can be moved and there may need to be a number of points set before signals can be set to green.
Block signalling:
As well as the rules for signal box style interlocking from version 0.5 block signal control was added for routes. This allows the signals to change aspect automatically so for a 3 Aspect signal (red, yellow, green) The yellow or green will display depending on if the next signal is set at red or yellow. Interlock++ v0.5 handles signal types up to 5 aspects and almost unlimited blocks.
Click here for more information on mechanical and electronic interlocking systems.
Hardware Requirements
From version 0.5 interlock only requires an arduino Mega 2560 or an ESP32 Dev Module (Interlock++ will not run on an UNO due to the SRAM requirements).
It is even possible to run a test system without anything else connected using the Serial monitor as an output to the success or failure of commands or blocks.
The user now has more freedom to choose their own screen/button type/touch screen as well as a choice of outputs (direct to servo or solenoid or to DCC EX etc).
This has been done to add as much flexibility as possible for the user to make it work with as many layout set ups as possible rather than trying to force users down a restricted route.
I will add example set ups as time permits. Please email from the link at the bottom of this page or via YouTube to request specific sample set ups.

How it works
To give an example of how Interlock V0.5+ works I will use the track plan below (Click image for larger version).
At the top of the diagram is a basic point/turnout with 4 signals.
To run the system interlocking rules must be added.
An easy example id the turnout (given an ID of 4). Before the point/turnout can change direction all signals in both directions must be set to Red (danger). Otherwise the point/turnout could be changed under a moving train.
So the following rules would be added to the system using the loadRule() function.
So to turn left (Straight on or 0 in my example all signals must be at danger (stop). Either using the value 0 or the definition SIGST
mySignalBox.loadRule(4, POINTLEFT, 1, SIGST); //Point 4 to move left, check signal 1 is Stopped
mySignalBox.loadRule(4, POINTLEFT, 2, SIGST);
mySignalBox.loadRule(4, 0, 3, 0); //can be written without definitions
mySignalBox.loadRule(4, 0, 5, 0);
The same is true if I want to turn the point right/turnout (direction 1)
mySignalBox.loadRule(4, POINTRIGHT, 1, SIGST); //Point 4 to move right, check signal 1 is Stopped/Danger
mySignalBox.loadRule(4, POINTRIGHT, 2, SIGST);
mySignalBox.loadRule(4, 1, 3, SIGST); //can be written without definitions
mySignalBox.loadRule(4, 1, 5, SIGST);
Rules also need to be set for signals.
As an example, for signal 1 to be set to Green/Clear the following rules would apply
//Signal 1 Go
mySignalBox.loadRule(1, SIGGO, 4, POINTLEFT); //signal 1 to go green, point 4 must be set left
mySignalBox.loadRule(1, SIGGO, 2, SIGST); //signal 1 to go green, signal 2 must be set stop/danger
mySignalBox.loadRule(1, SIGGO, 3, 0); //signal 1 to go green, signal 3 must be set stop/danger
mySignalBox.loadRule(1, SIGGO, 5, 0); //signal 1 to go green, signal 5 must be set stop/danger
Once the rules have been added for all signals and points/turnouts. As the system receives a command each rule will be checked to see if it applies and if it has been broken.
In Trainee mode an alert will be given (error message) to say that a rule has been broken but will allow the operator to continue to set the signal/point/turnout
In Interlock mode an alert (error message) will be given but any command to the track/signalling will NOT be implemented as it breaks an interlock rule.
So for the basic interlock system it is just a case of the operator adding rules, be aware that as track plans become more complex the number of rules increases dramatically just as in the real thing.
Also although the example uses signals and points/turnouts. Other rules can be added for block occupancy, crossings etc.
Block Occupancy Example
In this example we will look at modern panel box signalling that is often semi automatic. Interlock v0.5 or greater can mimic this route setting and automate the signal chnages although it will require some form of block/train detection on the layout.
For the example above I am using 4 aspect UK signal, Red (Stop/danger), Yellow (proceed under caution), Double Yellow (Proceed under Caution), Green (Proceed).
As can be seen Train A is approaching a Green signal (11), as it has passed signal 10 that is set to Red (danger) as train A occupies the track behind it. Signal 9 is set at Yellow warning the driver to expect a red signal at the next block. Signal 8 is double yellow (expect the next signal to be single yellow).
Signal 7 is green and signal 6 is green with Train B approaching it. As Train A moves exits the differnt blocks the signal behind change up one apsect.
In interlock++ this feature is controlled by the loadSignalMonitors() function. This allows a signal to base the colour it displays based on the aspect displayed by the signal in the next block.
So for example in the diagram above the monitorsd would be set as:
mySignalBox.loadSignalMonitors(6, 7);
mySignalBox.loadSignalMonitors(7, 8);
mySignalBox.loadSignalMonitors(8, 9);
mySignalBox.loadSignalMonitors(9, 10);
mySignalBox.loadSignalMonitors(10, 11);
So the first signal 6 will be monitoring the state of signal 7. Signal 7 will monitor the state of signal 8. You can add in as many monitors as required but can also add virtual signals that are monitored.
Virtual signals are signals that are not physically on the layout. So in our example Signals 10 and 11 may not exist on the layout but would be off the layout (in the fiddle yard or staging area).These can be triggered via timers so as the train enters the staging area Signal 10 would go to red and a timer starts. After X amount of time Signal 11 goes to danger (train has virtually reached that block), and so signal 10 would no move to single yellow. This allows full block control of the line ahead even if that section has not been built on the layout.
The Software - The interlock++ sketch
| Version | Changes |
| Future versions |
If you find a bug or have a suggestion for an new feature email me with the address at the bottom of this page. Ability to have outputs as DCC commands sent to DCC-EX or NCE CAB BUS (I only own 2 systems to test with). |
|
Download includes the main sketch and 2 demostration examples, one for converging tracks, the other for diverging tracks. |
New Features interlock++ 0.6 has a few extra functions added to enable block signalling control over converging and diverging tracks. A further function has been opened up to allow users to get the status of any item (signal, turnout, point, block etc) for integration into your own code logic. New Features: void listDuelMonitors(void); List all the loadSignalDuelMonitors() rules byte getItemStatus(uint16_t itemID);//get the items current position status of any itemID loadSignalMonitors() Error message 12 (see iserGuide.ino) Reduced to a warning but will not flag as error. This is when 2 signals are monitoring the same signal. This is now allowed to happen (with a warning) as it's required for converging track situation.
|
interlock V0.5 |
New Features interlock 0.5 has been written as a class and has more standard fuinctions to make using the program easier. This version allows for far greater flexibility on screens, buzzers, buttons etc and now runs on the Mega2560 and ESP32 Dev Module. New Features: |
| interlock V0.1 |
Original version |
Interlock v0.5(or greater) sketch basics and instructions
| Tab Name | Instructions |
Basic System requirements (Default sketch) Sketch set up. |
The Default sketch (as of version 0.5) consists of 6 Tabs interlock.cpp interlock.h interlock_setup.h userGuide.ino |
| interlockV0_6.ino |
The first tab contains the version number in it so may change as new versions are released.
2) Definitions Part 1: These define the different types of item on the layout, Points, Turnouts, Signals, Block, Crossings and Other 1 - Other 3. DO NOT change the definition values but the names of OTHER1 - OTHER3 can be changed to your own names for items I had not thought of. 3) Definitions Part 2: These define a value of 0 or 1 (0 or 1 can be used instead of the definitions) to make it easier to understand what is being set up in the rules. as you become more experienced you may wish to use the values 0 or 1 instead. The examples are: #define POINTLEFT 0 //I define left and right as viewed from the point frog, change definition names to suit 4) setup() The setup() function must contain the following lines: After the Serial monitor the following line is required to initialises interlock++. This cause various arrays to be created in the background ready for rules or monitors to be added. mySignalBox.init(); //This functions sets up the interlock system The loop requires the line: |
| customFunctions.ino |
This tab contains a number of functions that receive information fomr Interlock++ when rules have been processed successfully or not as well as other items that may be useful to drive screen and outputs such as signals, points/turnouts, crossings etc. void interlockModeState(uint8_t modeState) void intErrorTone() This function is called when a rule has been broken and allows the users to trigger an aidible or visual warning for the operator. void intUpdateLayout(uint16_t itemID, uint8_t instruction) This function is called when a successful command has passed the rule test OR when an item has passed or failed the test in trainee mode.
Thi function is used for screens for displaying error messages as well as successes. It could also be used for logging error. This function is called every time a rule is processed successfully or if a rule has been broken. int8_t interlockState: 0 = Success, 1 = interlock rule broken uint16_t itemID: The itemID of the signal/point/turnout/crossing that triggered a rules check. uint8_t instruction: The instruction/direction (0 or 1 for turnouts, 0 - Number of aspects for signals) that was checked against the rules. String itemName: The String of the item processed "Poin" "Turnout" "Signal" etc. uint16_t checkItemID: 0 if rule passed or the Item number of the item that cvaused the rule to be broken uint8_t checkInstruction: The instruction state the item should have been in of 0 if passed rule (Item ID will also be 0) String checkItemName: String of the name of item that failed rule OR Blank if interlock successful. |
interlock.cpp |
These tabs contain the main files that make Interlock++ run. Do not make alterations unless you fully understand the code. |
interlock_setup.h |
This tab contains customisable features of Interlock++ that allow you to make the system best suited to your layout and processor (Mega 2560/ESP32) You will find a number of definitions: #define STARTMODE 0 //0 = Learning Mode, set to 1 for full interlock mode. Sets the start up mode of the system. #define INTPROCSPEED 10 This value sets the time in milliseconds between processing instructions. Can be lowered to 1 for faster processing of increased if you want to run a batch of commands to simulate delays between each item being set... by a slow signalman. #define INTNUMINPUTS 100 The default number iof inputs (Signals, blocks, crossings, turnouts, points) is 100 this value runs OK on the Mega2560 but can be over 400 for the ESP32. The larger the number the more SRAM is used. #define INTNUMINTERLOCKRULES 1000 The default number of rules for the Mega2560 is 1000, much more than this will cause memeory issues on the Mega2560. The ESP32 can handle much larger volumes, I have only tested up to 4000 but it will handle more. #define INTMONITORBLOCKSIGNALS 80 This is the default number if signal monitor rules in the system, increase or decrease to suit your system. The ESP32 handled 400 monitors with ease. Reduce to 1 if not using monitors to save memory. #define INTDUELMONITORBLOCKSIGNALS 40 This is the default number of Duel signal monitors, increase or decrease as required. #define SIGNALMONITORDIAGNOSTICS_ON This line is commented out by default as it slows the system down but is useful for viewing batches of monitoring instructions to see the block sigbnal values changing in the Serial monitor. #define RULECHECKDIAGNOSTICS_ON Displays various diagnostic information to the Serial monitor, cann be commented out for production code once everything is working. |
userGuide.ino |
To save as much memory as possible the code uses very few strings. |
Interlock v0.5 (or greater)sketch input functions
| Function | Instructions |
void loadInput(uint16_t itemID, uint8_t inputTypeID); |
This function is used to load non signal components into interlock. Non signal items include Points/Turnouts, Crossings, Blocks and Other1 - Other 3 uint8_t inputTypeID : This is the item type, see the definitions for the value of the items in the first tab. #define POINT 1
#define TURNOUT 2
#define BLOCK 3
#define CROSSING 4
#define OTHER1 5 //Change definition name as required
#define OTHER2 6 //Change definition name as required
#define OTHER3 7 //Change definition name as required
You can input the definition name or the definition values as follows.
mySignalBox.loadInput(4, POINT); //Item ID = 4 (track plan) Type = 1 POINT from definitions |
void loadSignal(uint16_t itemID, uint8_t aspects); |
This function is used to load signals into Interlock++ uint8_t aspects : This is the number of aspects a signal has. Interlock has a maximum 5 aspects.
mySignalBox.loadSignal(1, 2); //Item ID = 1 (track plan), 2 Aspects = 2 Aspect signal (red/Green) (red/yellow) |
void loadRule(uint16_t itemID, uint8_t itemStatus, uint16_t checkItemID, uint8_t checkItemStatus); |
This function loads a rule into Interlock++ uint8_t itemStatus The directions/state the item will be set to uint16_t checkItemID The item ID of the item that should be checked uint8_t checkItemStatus The state the check item should be in. Examples: mySignalBox.loadRule(4, POINTRIGHT, 1, SIGST); //Point 4 to move right, check signal 1 is Stopped mySignalBox.loadRule(4, POINTRIGHT, 2, SIGST); //Point 4 to move right, check signal 2 is Stopped mySignalBox.loadRule(4, 1, 3, 0); //can be written without definitions Point 4 to move right, check signal 3 is Stopped mySignalBox.loadRule(2, SIGGO, 4, POINTLEFT); //signal 2 to go green/Yellow, point 4 must be set left mySignalBox.loadRule(2, SIGGO, 1, SIGST); //signal 2 to go green/yellow, signal 1 must be set stop |
void loadSignalMonitors(uint16_t itemID, uint16_t monitorID); |
This function is used to load signals monitors into Interlock++ uint16_t itemID ItemID of the signal uint16_t monitorID ItemID of the signal that is being monitored This function MUST be in the setup() AFTER the line mySignalBox.init(); Examples: mySignalBox.loadSignalMonitors(5, 6); Signal 5 is monitoring signal 6 mySignalBox.loadSignalMonitors(6, 7); Signal 6 is monitoring signal 7 mySignalBox.loadSignalMonitors(7, 8); Signal 7 is monitoring signal 8 |
void loadSignalDuelMonitors(uint16_t itemID, uint16_t monitor1ID, uint16_t monitor2ID); |
Version 0.6 or greater uint16_t itemID ItemID of the signal uint16_t monitor1ID ItemID of the first signal that is being monitored uint16_t monitor2ID ItemID of the second signal that is being monitored This function MUST be in the setup() AFTER the line mySignalBox.init(); Examples: mySignalBox.loadSignalDuelMonitors(5, 6, 10); Signal 5 is monitoring signals 6 and 10 |
void listInputTypes(void); |
This function outputs the input types within the Interlock++ data array to the Serial Monitor. mySignalBox.listInputTypes(); |
void listInputs(void); |
This function outputs the list of inputs that have been entered into Interlock++ through the function void loadInput(uint16_t itemID, uint8_t inputTypeID); The output is sent to the Serial monitor. mySignalBox.listInputs(); |
void listRules(void); |
This function lists all the rules that have been added though the function void loadRule(uint16_t itemID, uint8_t itemStatus, uint16_t checkItemID, uint8_t checkItemStatus); |
void listMonitors(void); |
This function lists all the rules that have been added though the function void loadSignalMonitors(uint16_t itemID, uint8_t monitorID);
|
void listDuelMonitors(void); |
Version 0.6 or greater This function lists all the rules that have been added though the function void loadSignalDuelMonitors(uint16_t itemID, uint16_t monitor1ID, uint16_t monitor2ID); |
void setModestate(uint8_t mode); |
This function sets the interlock mode within Interlock++ mySignalBox.setModestate(0); // 0 sets system to trainee mode |
void checkInterlocks(uint16_t itemID, uint8_t checkItemStatus); |
This function that is used to check against the rules. uint16_t itemID The unique Item ID of the item to be checked uint8_t checkItemStatus The direction (turnout/point), signal state etc of the item to be checked. This item is always a 0 or 1 even in multi aspect signals mySignalBox.checkInterlocks(4, POINTLEFT); //Move point ID 4 to position Left (0) mySignalBox.checkInterlocks(4, 0); //Move point ID 4 to position Left (0) |
byte getItemStatus(uint16_t itemID); |
Version 0.6 or greater This function lreturns the current state of an item. For most items the return value will be a Zero or 1 and are related to the values of the definitions below however SIGNAL can have values from Zero to 5 depending on the number of aspects selected. #define POINTLEFT 0 //I define left and right as viewed from the point frog, change definition names to suit uint16_t itemID ItemID of the signal, block, turnout, crossing etc.
|
interlockV0.1 - Instructions
This video shows how to use version 0.1. Click to download the code for the video example (Click to download)
Interlock++ Version 0.1 was written as a normal sketch that could be adapted to each layouts needs while later versions have been written as a class.
The original version has fixed hardware requirements while later version add more flexibility but the 0.1 version is a good platform to learn on as instructions have been kept.
This version ONLY runs on the Mega 2560 (although can be made to run on the ESP32 despite the LCD library warnings).
Hardware Requirements (0.1 Version)
For the Interlock++ system itself the hardware requirements are:
Arduino Mega 2560 (System will not run on an UNO due to SRAM requirements).
I2C 1602 LCD Screen
1 Push button momentary switch (Mode Switch).
4.5K - 10K resistor (button pull down).
Buzzer/Piezo Speaker
For inputs from the layout push to make/ Stud and Probe type connections are best. The system will work with toggle type switches but operators will need to manually reset the switch if an interlock fails.
Outputs can be to solenoids, servos or any other type of driver for signals, crossings and turnouts/points.
Interlock++ Circuit Diagram (0.1 version)
The system can be made to work with DCC systems such as DCC EX and NCE Cab Bus
The circuit diagram below is all that is required for the base Interlock++ code to run.
Other connections will need to be made for inputs from buttons, switches and sensors as well as outputs to servos, solenoids and motor drivers.

How to use Version 0.1
To give an example of how Interlock++ works I will use the track plan below (Click image for larger version).
The first thing that needs to be set up is the number of item types.
The default number is 5, OTHER, POINT (Swap to TURNOUT as required), TSIGNAL, BLOCK, CROSSING
If more item types are added just change the value of
Then add or change the associated labels in the array:
The defaults are:
Next add in your list of point/turnouts, signals, blocks, crossings, Others. These are the items you want to have included in your interlocking system.
For the track plan above I have 8 signals and 3 points/Turnouts
Each item must have a unique reference number (I have used 1-11). Once you have listed all your items make sure you update the line
#define NUMINPUTS 11 to make it the same as the number if items you have added.
Next you can add or edit the status definitions, the defaults are:
Try not to make the names too long as it's easier to make error.
If you add extra definitions they must be in pairs, with a value of Zero and 1.
Once all of that is done you can enter your rules into the InterlockRules array as shown in the example below.
After adding your rules count them up and update the line
The rules above show what interlocks must be in place for Point/Turnout 9 in the diagram above to turn to direction 0.
So it requires signals 1,2,5 & 6 to have a status of
SIGSTOP (Zero) that emans the signal is set at red.
Setting the rules can take some time and the bigger the layout the more rules you will need. It will give you a real appreciation of the railway engineers who build mechanical and electronic signal boxes.
These are the rules that will be checked before the system allows a movement to take place. In reality the point/turnout could not be thrown until all the interlocks had been passed.
Thankfully Interlock++ has 2 modes.
Trainee mode (mode 0) will show you the errors you have made, but still allow the point change/signal change.... you atke responsibility for the accident.
Interlock Mode (mode 1) will display the error and reject the command, preventing you in real life from setting a dangerous set of signals/turnouts.
How To tutorials
These tutorials are added insresponse to information or help requests on how to use specific features or work with different components/systems.
If you have a specific request please email me from the link at the bottom of the page or add a comment below the You Tube video.
Additional Resources
Comments
For help or suggestions on new projects, please email the address in this image:
and use Interlock++ Interlocking signal box and block signalling system for model railways as a reference.















