{"id":971,"date":"2016-11-13T15:44:41","date_gmt":"2016-11-13T23:44:41","guid":{"rendered":"http:\/\/thenscaler.com\/?p=971"},"modified":"2016-12-01T18:02:17","modified_gmt":"2016-12-02T02:02:17","slug":"basic-signaling-for-a-small-layout","status":"publish","type":"post","link":"https:\/\/thenscaler.com\/?p=971","title":{"rendered":"Basic Signaling for a Small Layout"},"content":{"rendered":"<p>Continuing with the <a href=\"https:\/\/thenscaler.com\/?p=942\">theme of controlling a small layout with an UNO<\/a>, I thought I&#8217;d accept my own challenge from the last post and talk about how one might implement signals on a small layout as I did on the Test Loop.<\/p>\n<h4>Signals on the Test Loop<\/h4>\n<p>While I was actively testing block occupancy detection on the test loop, I set up three sets of signals as part of that effort. I wanted to both test some off-the-shelf signals from Tomar and take a crack at building my own searchlight signals using BLMA unlit signal heads. The former turned out to work well, but because they are wired for common anode, they sent me on a <a href=\"https:\/\/thenscaler.com\/?p=684\">quest to tame common anode wiring<\/a>. The latter also came out well, once I learned how to reliably solder magnet wire to SMD micro LEDS!<\/p>\n<div id=\"attachment_974\" style=\"width: 620px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/thenscaler.com\/wp-content\/uploads\/2016\/11\/Test-Loop-block-and-signal-layout.jpg\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-974\" class=\"wp-image-974 size-large\" src=\"https:\/\/thenscaler.com\/wp-content\/uploads\/2016\/11\/Test-Loop-block-and-signal-layout-1024x672.jpg\" alt=\"Block and Signal layout on the Test Loop\" width=\"610\" height=\"400\" srcset=\"https:\/\/thenscaler.com\/wp-content\/uploads\/2016\/11\/Test-Loop-block-and-signal-layout-1024x672.jpg 1024w, https:\/\/thenscaler.com\/wp-content\/uploads\/2016\/11\/Test-Loop-block-and-signal-layout-300x197.jpg 300w, https:\/\/thenscaler.com\/wp-content\/uploads\/2016\/11\/Test-Loop-block-and-signal-layout-768x504.jpg 768w, https:\/\/thenscaler.com\/wp-content\/uploads\/2016\/11\/Test-Loop-block-and-signal-layout.jpg 1800w\" sizes=\"auto, (max-width: 610px) 100vw, 610px\" \/><\/a><p id=\"caption-attachment-974\" class=\"wp-caption-text\">Block and Signal layout on the Test Loop<\/p><\/div>\n<p>I did this primarily to see the block detection system working (block detection is also shown on on my programmable <a href=\"https:\/\/thenscaler.com\/?p=414\">control panel<\/a>, but that is another story) as a train moves around the track.\u00a0 You can see it in action in this video\u2014note that only the locomotive is detectable by the block occupancy detection system; the rolling stock is not set up for detection.<\/p>\n<p><iframe loading=\"lazy\" src=\"https:\/\/www.youtube.com\/embed\/KleQt1fx2Go\" width=\"480\" height=\"270\" frameborder=\"0\" allowfullscreen=\"allowfullscreen\"><\/iframe><\/p>\n<p>Since the test loop is just an oval with a single turnout and siding, the system is fairly simple.\u00a0 As you watch the train go around the oval you will see signals change state as the train moves in and out of blocks, and as the turnout changes state.\u00a0 The logic is imperfect in a few cases but good enough to show the various parts of the system working as a whole under the control of an UNO, which was the point of the exercise.<\/p>\n<h4>A Framework for ABS<\/h4>\n<p><a href=\"https:\/\/en.wikipedia.org\/wiki\/Automatic_block_signaling\" target=\"_blank\">Automatic Block Signalling<\/a> (ABS) is straight forward and prototypical for late nineteenth \/ early twentieth century railroads. ABS signals are autonomous and react to block occupancy and turnout status; the red (next block obstructed), yellow (a subsequent block is obstructed) and green (no obstruction) indicators are near universal. Typical implementations handle blocks in groups of three or four, depending on how far ahead the system sensors extend.<\/p>\n<p>Because each signal is an autonomous object that responds to specific environmental conditions, ABS lends itself well to a data-oriented approach. As with turnouts in the previous post, the best starting point is to devise a data structure that will encapsulate and represent everything that needs to be known about each signal in your system. Here is what I came up with for the test loop (see <a href=\"https:\/\/thenscaler.com\/?p=684\">this post for an explanation of my Duino nodes<\/a>, and <a href=\"https:\/\/thenscaler.com\/?p=756\">this post for the addressing system in use<\/a>).<\/p>\n<pre>typedef struct SIGNAL_DEF {\r\n  byte type; \/\/ bit mask indicating available signal aspects\r\n             \/\/ bit 1 = Red; bit 2 = Green; bit 3 = Yellow)\r\n\u00a0            \/\/ 1=R; 2=G; 3=RG; 4=Y; 5=RY; 6=GY; 7=RGY\r\n             \/\/ 3 and 7 are the two types on the test loop\r\n\u00a0 nodeAddress addr; \/\/ base address of the Duino node for this signal\r\n\u00a0 byte R_ID; \/\/ pin\/bit id for red indication\r\n\u00a0 byte G_ID; \/\/ pin\/bit id for green indication\r\n\u00a0 byte Y_ID; \/\/ pin\/bit id for yellow indication\r\n\u00a0 \r\n\u00a0 \/\/ data elements for running the signal\r\n  byte state; \/\/ current signal state\r\n\u00a0 T_ALIGN *turnouts; \/\/ these turnouts must be aligned as defined to get SIGNAL_GREEN\r\n\u00a0 byte numTurnouts;\r\n\u00a0 byte *following; \/\/ additional blocks ahead (depends on direction signal faces)\r\n  \/\/ watched for occupancy resulting in SIGNAL_YELLOW caution \u00a0 \r\n  byte numFollowing; \r\n};<\/pre>\n<p>By now you should recognize that this is my preferred approach to dealing with complex, interactive objects in the system.\u00a0 As always, I define a compound\u00a0data structure (the structure contains other structures as elements, in this case the T_ALIGN and NODEADDRESS types)\u00a0 to collect all relevant data for each signal. Feel free to reinvent any of this &#8212; the point is to collect all necessary data in one place for each signal.<\/p>\n<p>&#8220;T_ALIGN *turnouts&#8221;\u00a0 is an example of pointer notation which allows for an array of zero (empty array) or more of the respective types; the &#8220;numTurnouts&#8221; element indicates how many items the T_ALIGN array contains. The &#8220;byte *following&#8221; and &#8220;numFollowing&#8221; do the same thing for a list of subsequent block IDs that are watched for occupancy.<\/p>\n<p>Here is the declaration of a signals array from the Test Loop encapsulating all the signals in use, using the data types discussed. Notice how structures and arrays within the SIGNAL_DEF structure are defined inside their own curly braces:<\/p>\n<pre>\/\/ Signals definitions and data\r\nSIGNAL_DEF signals[NUM_SIGNALS] = {\r\n\u00a0 {3, {3, 0}, 0, 1, -1, SIGNAL_OFF,{0, ALIGN_DIVERGENT}, 1,{2}, 1 },\r\n\u00a0 {3, {3, 0}, 2, 3, -1, SIGNAL_OFF,{0, ALIGN_MAIN}, 1, {2}, 1 },\r\n\u00a0 {7, {1, 0}, 0, 1,  2, SIGNAL_OFF,{}, 0, {0}, 1},\r\n\u00a0 {7, {0, 0}, 0, 1,  2, SIGNAL_OFF,{0, ALIGN_DIVERGENT}, 1, {}, 0 },\r\n\u00a0 {3, {0, 0}, 3, 4, -1, SIGNAL_OFF,{0, ALIGN_MAIN}, 1,{2}, 1}\r\n};<\/pre>\n<p>Signal logic is handled by one function that gets called at the end of each loop cycle on the UNO, after block occupancy has been established.<\/p>\n<pre>void refreshSignals() {\r\n\u00a0 \/\/ First pass, set Stop (RED) state; default is GREEN\r\n\u00a0 \/\/ from block occupancy or turnout states\r\n\u00a0 for(int i = 0; i &lt; NUM_SIGNALS; i++){ \/\/ for each signal\r\n\u00a0\u00a0\u00a0 \/\/ default state\r\n\u00a0\u00a0\u00a0 int state = SIGNAL_GREEN;\r\n\u00a0\u00a0\u00a0 SIGNAL_DEF sig = signals[i];\r\n\u00a0\u00a0\u00a0 for(int j = 0; j &lt; max(sig.numTurnouts, sig.numBlocks); j++){ \r\n\u00a0\u00a0\u00a0\u00a0\u00a0 if(j &lt; sig.numTurnouts){\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ if the turnout is in motion OR \r\n        \/\/ if turnout alignment does not equal the required alignment\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(turnout[sig.turnouts[j].id].is_moving || \r\n            turnout[sig.turnouts[j].id].alignment != sig.turnouts[j].align){\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 state = SIGNAL_RED;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 } \r\n\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 if(j &lt; sig.numBlocks){ \/\/ for each linked block in the SIGNAL_DEF\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(blocks[sig.blocks[j]].occ){ \/\/ if occupied\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 state = SIGNAL_RED;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 setSignalBits(i, state);\r\n\u00a0 }\r\n\u00a0 \r\n\u00a0 \/\/ Second pass to set caution states on\r\n\u00a0 \/\/ signals that support it and are currently set to GREEN\r\n\u00a0 \r\n\u00a0 for(int i = 0; i &lt; NUM_SIGNALS; i++){ \/\/ for each signal\r\n\u00a0\u00a0\u00a0 SIGNAL_DEF sig = signals[i];\r\n\u00a0\u00a0\u00a0 if(bitRead(sig.type, 2)){ \/\/ if the signal supports the caution state\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 if(sig.numFollowing &gt; 0 &amp;&amp; sig.state == SIGNAL_GREEN){\r\n        \/\/ check occupancy of following block(s) if any\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 for(int j = 0; j &lt; sig.numFollowing; j++){\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(blocks[sig.following[j]].occ){\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 setSignalBits(i, SIGNAL_YELLOW);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 }\r\n\u00a0 }\r\n\u00a0 \/\/ Refresh the nodes to show signals in their updated state\r\n\u00a0 nodeRefresh();\r\n}\r\n\r\nvoid setSignalBits(int signalID, byte signalState) {\r\n\u00a0 SIGNAL_DEF sig = signals[signalID];\r\n\u00a0 if (sig.state != signalState) {\r\n\u00a0\u00a0\u00a0 signals[signalID].state = signalState;\r\n\u00a0\u00a0\u00a0 byte nodeBits = nodeGet(sig.addr);\r\n\u00a0\u00a0\u00a0 switch (signalState) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 case SIGNAL_OFF:\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(bitRead(sig.type, 0)) bitWrite(nodeBits, sig.R_ID, LOW);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(bitRead(sig.type, 1)) bitWrite(nodeBits, sig.G_ID, LOW);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(bitRead(sig.type, 2)) bitWrite(nodeBits, sig.Y_ID, LOW);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 break;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 case SIGNAL_RED:\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(bitRead(sig.type, 0)) bitWrite(nodeBits, sig.R_ID, HIGH);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(bitRead(sig.type, 1)) bitWrite(nodeBits, sig.G_ID, LOW);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(bitRead(sig.type, 2)) bitWrite(nodeBits, sig.Y_ID, LOW);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 break;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 case SIGNAL_GREEN:\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(bitRead(sig.type, 0)) bitWrite(nodeBits, sig.R_ID, LOW);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(bitRead(sig.type, 1)) bitWrite(nodeBits, sig.G_ID, HIGH);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(bitRead(sig.type, 2)) bitWrite(nodeBits, sig.Y_ID, LOW);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 break;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 case SIGNAL_YELLOW:\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(bitRead(sig.type, 0)) bitWrite(nodeBits, sig.R_ID, LOW);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(bitRead(sig.type, 1)) bitWrite(nodeBits, sig.G_ID, LOW);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(bitRead(sig.type, 2)) bitWrite(nodeBits, sig.Y_ID, HIGH);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 break;\r\n\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 nodeSet(sig.addr, nodeBits);\r\n\u00a0 }\r\n\u00a0 return;\r\n}\r\n\r\nvoid setSignal(int signalID, byte signalState) {\r\n\u00a0 setSignalBits(signalID, signalState);\r\n\u00a0 nodeRefresh();\r\n}<\/pre>\n<p>For more in-depth discussion of my Duino Node devices, node functions and how they are used,\u00a0 see <a href=\"https:\/\/thenscaler.com\/?p=684\" target=\"_blank\">Adding Signals to the Test Loop<\/a> and <a href=\"https:\/\/thenscaler.com\/?p=756\" target=\"_blank\">Adding Signals to the Test Loop Part 2<\/a>.<\/p>\n<p>The idea here is that the logic of the signal system is executed in the refreshSignals() function. That function, in turn, calls setSignalBits() to interface with the hardware, using the hardware specific Duino Node functions to\u00a0 drive the hardware.<\/p>\n<h4>How to Integrate Signals into Your Small Layout<\/h4>\n<p>Adding signals to your small layout consists of two basic steps: 1) setup your signal hardware so that it can be turned on and off in some way; either by direct connection to your UNO or using a shift register chain along similar lines to what I do with Duino Nodes. The choice of common anode vs. common cathode wiring is yours to make, but will depend on how your signal gear is wired. 2) Integrate signal handling in the sketch using turnout state and (if you have it) block occupancy data. Call your signal logic function at the end of each iteration of the main loop, and let that function interact with the signal hardware. Your signal logic should be in one place, and should be written as an &#8220;abstraction&#8221; that doesn&#8217;t know how to change signal display, but relies on other hardware specific functions to do that job.<\/p>\n<div id=\"attachment_686\" style=\"width: 571px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/thenscaler.com\/wp-content\/uploads\/2016\/02\/Tomar-Signals.jpg\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-686\" class=\"size-full wp-image-686\" src=\"https:\/\/thenscaler.com\/wp-content\/uploads\/2016\/02\/Tomar-Signals.jpg\" alt=\"Tomar Signals\" width=\"561\" height=\"851\" srcset=\"https:\/\/thenscaler.com\/wp-content\/uploads\/2016\/02\/Tomar-Signals.jpg 561w, https:\/\/thenscaler.com\/wp-content\/uploads\/2016\/02\/Tomar-Signals-198x300.jpg 198w\" sizes=\"auto, (max-width: 561px) 100vw, 561px\" \/><\/a><p id=\"caption-attachment-686\" class=\"wp-caption-text\">Tomar N Scale Signals<\/p><\/div>\n<p>I consider signals to be the most basic form of animation you can add to your layout to bring it to life. Its a little bit of trouble, but I hope you can see its really not hard.\u00a0 The advantage of the Arduino approach over a hardware\/hardwired approach (eg, connecting signals to the outputs of a stand alone block occupancy device) is the flexibility you gain in implementing signals while keeping wiring to an absolute minimum. Adding <strong>Absolute Permissive Block<\/strong> signalling is just as matter of additional logic to the sketch for the stretch of track you are trying to protect. Even full CTC functionality can be readily supported by responding to messages from a CTC control panel or system.<\/p>\n<p>You&#8217;ll be amazed that how much work a single UNO can actually do for you.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Continuing with the theme of controlling a small layout with an UNO, I thought I&#8217;d accept my own challenge from the last post and talk about how one might implement signals on a small layout as I did on the Test Loop. Signals on the Test Loop While I was actively testing block occupancy detection [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":658,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[35,21,34,14],"tags":[22,39,57],"class_list":["post-971","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-electronics","category-layout-control","category-lighting-and-animation","category-test-loop","tag-arduino","tag-programming","tag-signals"],"_links":{"self":[{"href":"https:\/\/thenscaler.com\/index.php?rest_route=\/wp\/v2\/posts\/971","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/thenscaler.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/thenscaler.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/thenscaler.com\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/thenscaler.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=971"}],"version-history":[{"count":13,"href":"https:\/\/thenscaler.com\/index.php?rest_route=\/wp\/v2\/posts\/971\/revisions"}],"predecessor-version":[{"id":1040,"href":"https:\/\/thenscaler.com\/index.php?rest_route=\/wp\/v2\/posts\/971\/revisions\/1040"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/thenscaler.com\/index.php?rest_route=\/wp\/v2\/media\/658"}],"wp:attachment":[{"href":"https:\/\/thenscaler.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=971"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/thenscaler.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=971"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/thenscaler.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=971"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}