Signals are a feature of a model railroad that provides animation, and on much larger railroads, is a necessity during normal operation. A table based software signaling system makes it easy to setup and maintain a signalling system without changes to any software. Tables are data structures that can be populated and compiled into the code.
Tables describe
detectors and the blocks the affect
analog input a detector is connected to
signal and the block it signals for
output controlling a particular signal and aspect
The following diagram illustrates a simple point-to-point railroad and various information describing blocks, signals, detectors and outputs controlling the signal LEDs.
// (5) (6)
// (6) (5)
// _____ S2 _____ _____ S6
// _________/____ ______d5_d4______/_____\__________d6_d7_____
// / _______/ S3 B2 S1 B1
// / (3) (4)
// |B3 (8) (4) (3)
// | (7)
// \ __ S5 ______________
// \____d?___\_____d3_d2____/______/______
// S4 B4
// (9)
// (10)
There are three sets of signals, each set is two signals, one for each direction. The signal to the right of the direction of travel provides a signal to a train traveling in that direction. Near the top of the diagram are signals S2 and S3. Each signal has a red and green LED. The red LED is on top. The red LED for signal S2 is controlled by digital output 5. The green LED by output 6. Note that signal S6 to the right also has the same digital outputs, 6 and 5. This will be explained later.
Below the S2 and closer to the track are identifiers for two detectors, d5 and d4. The current signaling systems uses a pair of optical detectors at the signal boundaries to determine block occupancy based on a train entering and exiting a block at the block boundaries. This avoids the needs for resistor wheel sets that draw a small amount of current for occupancy detectors that rely on current being drawn by any car occupying the block. The optical detectors at this block boundary are connected to analog inputs 5 and 4.
The first table to be populated is the detector description table identifying the detectors are each block boundary and the adjacent blocks they affect. The following is the populated table. It has entries for three block boundaries.associated with the three sets of signals. The first two columns identify the blocks to the west (left) and east (right) of the boundary. The next two columns identify the detectors. Likewise, they must be in the corresponding west/east column. The fifth column is not populated and is used during operation.
While the numbers in each column correspond to the block and detector numbers in the diagram, they are embedded in macros that properly format the format as needed. the BLK() macro does nothing but identify the value as a block number. The same if true for the D() macro which identifies the value as a detector. But the D() macro is embedded within the B() macro which translates the detector number into a value bit-field which can be combined with other bit-field values. Each bit in a bit-field represents a unique value.
static DetDesc_t _detDesc [] = {
// blocks detectors
// west east west east
{ BLK(2), BLK(1), B(D(6)), B(D(7)), 0 },
{ BLK(3), BLK(2), B(D(5)), B(D(4)), 0 },
{ BLK(3), BLK(4), B(D(3)), B(D(2)), 0 },
{ 0, 0, 0, 0, 0 },
};
While block numbers need to be unique, they can be arbitrarily assigned, they can start at 100 and don't need to be sequential. Likewise, the detector numbers can also be arbitrary. Values for both blocks and detectors may be chosen for various reason that make them easier to relate to one another or locate on a big layout.
The next table associated a detector number with an analog input on a specific node. Any hardware used to both monitor detectors and drive the signals will have a limited number of input/output (I/O) lines. While there are various approaches to expand the number of I/O, supporting multiple hardware boards on a layout is one approach for dealing with such limitations. They also minimize wiring by locating hardware nodes where a group of detectors and signals are located. Serial communication between nodes to share information will be explained later.
The detector table associates an arbitrary detector number with an analog input on a specific hardware node. The first column is the node, the second the detector and the third the analog input. The fourth column indicates whether the detector output is active high or low. In my case, using room lighting which is blocked by the presence of a car, the output is high to indicate that a car is present. If reflected light were used, a low input would indicate the presence of a car. There are two nodes. Node 1 supports signals 1 and 6 and detectors 6 and 7. Node 2 supports all other signals and detectors. In most cases, the detector and analog input values are the same. They are not the same for node 1
// -----------------------------------------------------------------------------
// identifies node and output(s) controlling a signal
static DetNode_t _detNode [] = {
{ N(2), D(2), A(2), 0 },
{ N(2), D(3), A(3), 0 },
{ N(2), D(4), A(4), 0 },
{ N(2), D(5), A(5), 0 },
{ N(1), D(6), A(5), 0 }, // detector-ID and analog-inp
{ N(1), D(7), A(4), 0 }, // detector-ID and analog-inp
{ 0, 0, 0, 0, },
};
The next table associates a signal with a block. There may be two signals, one at each end of a block. Blocks 1 and 4 both have a single signal.
// ---------------------------------------------------------
// identifies 1+ signals (bitmap) protecting a block
static SigDesc_t _sigDesc [] = {
{ BLK(1), SIG(1) }, // west
{ BLK(2), SIG(6) }, // east
{ BLK(2), SIG(3) }, // west
{ BLK(3), SIG(2) }, // east
{ BLK(3), SIG(5) }, // west
{ BLK(4), SIG(4) }, // west
{ 0, 0, },
};
The last table identifies the digital outputs that control the signal LEDs. There is an entry for each signal and aspect. When controlling a signal all of the LEDs usually need to change either on or off. And like the detectors a signal is controlled by a specific hardware node. The first column specifies the node, the second column the signal. The third columns specifies the aspect, in my case STOP or CLEAR. The fourth column specifies all the outputs used to control the signal. The B() bit-field macro is used. The last column specified which output need to be set (high) to obtain the corresponding aspect, red for STOP and green for CLEAR. This can be expanded for additional aspects such as a yellow APPROACH indication. With some changes, it could probably support semaphores.
//-----------------------------------------------------------------------------
// identifies node and output(s) controlling a signal
static SigNode_t _sigNode [] = {
{ N(1), SIG(1), STOP, B(3)|B(4), B(3) },
{ N(1), SIG(1), CLEAR, B(3)|B(4), B(4) },
{ N(1), SIG(6), STOP, B(5)|B(6), B(5) },
{ N(1), SIG(6), CLEAR, B(5)|B(6), B(6) },
{ N(2), SIG(2), STOP, B(5)|B(6), B(6) },
{ N(2), SIG(2), CLEAR, B(5)|B(6), B(5) },
{ N(2), SIG(3), STOP, B(3)|B(4), B(4) },
{ N(2), SIG(3), CLEAR, B(3)|B(4), B(3) },
{ N(2), SIG(4), STOP, B(9)|B(10), B(9) },
{ N(2), SIG(4), CLEAR, B(9)|B(10), B(10) },
{ N(2), SIG(5), STOP, B(7)|B(8), B(7) },
{ N(2), SIG(5), CLEAR, B(7)|B(8), B(8) },
{ 0, 0, NONE, 0, 0 },
};