{"id":661,"date":"2016-02-07T23:56:45","date_gmt":"2016-02-08T07:56:45","guid":{"rendered":"http:\/\/thenscaler.com\/?page_id=661"},"modified":"2016-02-29T23:36:06","modified_gmt":"2016-03-01T07:36:06","slug":"multiple-turnouts-and-other-multitasking","status":"publish","type":"page","link":"https:\/\/thenscaler.com\/?page_id=661","title":{"rendered":"Multiple Turnouts and Other Multitasking"},"content":{"rendered":"<div id=\"attachment_181\" style=\"width: 310px\" class=\"wp-caption alignleft\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-181\" class=\"size-medium wp-image-181\" src=\"https:\/\/thenscaler.com\/wp-content\/uploads\/2014\/11\/Uno-300x199.jpg\" alt=\"Arduino Uno R3\" width=\"300\" height=\"199\" srcset=\"https:\/\/thenscaler.com\/wp-content\/uploads\/2014\/11\/Uno-300x199.jpg 300w, https:\/\/thenscaler.com\/wp-content\/uploads\/2014\/11\/Uno.jpg 600w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><p id=\"caption-attachment-181\" class=\"wp-caption-text\">Arduino Uno R3<\/p><\/div>\n<p>The microcontroller at the heart of your Arduino contains a single processor that can execute one instruction at a time.\u00a0 But it can do that task over and over very quickly &#8212; <a href=\"https:\/\/www.arduino.cc\/en\/Main\/ArduinoBoardUno\" target=\"_blank\">around 16 million times per second on an UNO<\/a>. At that speed, the microcontroller can handle a lot of different tasks, more or less simultaneously.<\/p>\n<p>That said, the processor can only do what you tell it to do, one instruction at a time; it is up to the programmer to structure the code in a way that effectively divvies up processor time between different tasks.<\/p>\n<h2>Avoid Delay()<\/h2>\n<p>The enemy of multitasking is the delay() function, which is frequently used to time actions. The problem with delay() is that it stops the program flow cold; nothing can happen while thousands of cycles are frittered away. In Arduino parlance, delay() is a blocking function.<\/p>\n<p>The classic Arduino example for timing actions without using delay() is the <a href=\"https:\/\/www.arduino.cc\/en\/Tutorial\/BlinkWithoutDelay\" target=\"_blank\">Blink Without Delay sketch<\/a>, where the main loop checks the elapsed time and, if a wait period has passed, turns the LED on or off creating a blinking LED. In between changing the state of the LED, the loop runs and could do other things.<\/p>\n<h2>Looping for Multiple Tasks<\/h2>\n<p>Checking the elapsed time at the start of every iteration of your main loop(), then taking actions based on time, is at the heart of simple multitasking. To make it work effectively, you have to split up tasks that take time to complete, handle complex actions that consume multiple cycles as efficiently as possible and prioritize the tasks so they interact in the ways you want.<\/p>\n<p>For example, the <a href=\"https:\/\/thenscaler.com\/?p=529\" target=\"_blank\">test loop UNO<\/a> has four task groups it has to handle simultaneously:<\/p>\n<ol>\n<li>Block Occupancy Detection &#8212; all five sensors have to be checked on each cycle and the block state updated as necessary.<\/li>\n<li>Turnout Management &#8212; move the turnout when commanded over several seconds for slow turnout motion.<\/li>\n<li>Signal Management &#8212; maintain and change signal states based on turnout position and block occupancy, or in response to a command override.<\/li>\n<li>Communications &#8212; listening for messages, processing and executing commands and transmitting data to other devices.<\/li>\n<\/ol>\n<p>That is quite a bit to do, but well within the capabilities of the UNO. Each of these task groups gets its turn during each iteration of the main loop.<\/p>\n<p>To get a sense of how that works, lets focus in on one core task group: turnout management. Running multiple slow motion turnouts encompasses all the techniques needed for simple Arduino multitasking.<\/p>\n<h2>Slow Motion Turnouts<\/h2>\n<div id=\"attachment_213\" style=\"width: 310px\" class=\"wp-caption alignleft\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-213\" class=\"size-medium wp-image-213\" src=\"https:\/\/thenscaler.com\/wp-content\/uploads\/2014\/11\/servo-installation-complete-top-view-300x200.jpg\" alt=\"Complete servo installation.\" width=\"300\" height=\"200\" srcset=\"https:\/\/thenscaler.com\/wp-content\/uploads\/2014\/11\/servo-installation-complete-top-view-300x200.jpg 300w, https:\/\/thenscaler.com\/wp-content\/uploads\/2014\/11\/servo-installation-complete-top-view.jpg 600w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><p id=\"caption-attachment-213\" class=\"wp-caption-text\">Demo servo installation.<\/p><\/div>\n<p>If you&#8217;ve experimented with using servo motors as turnout motors, or read <a href=\"https:\/\/thenscaler.com\/?page_id=174\">Turnout Control with Arduino and Servos<\/a>, then you know that slowing the motion down requires moving one degree at a time, with a time delay between moves. Using delay() to time the motion, <a href=\"https:\/\/thenscaler.com\/?page_id=174\">as in the basic example<\/a>, renders the microcontroller unable to do anything else while the turnout is moving. Further, with this method only one turnout can be moved at any one time, which would be a problem with multiple trains, turnouts and operators.<\/p>\n<p>Using our multitasking model to handle turnout motion requires keeping track of the turnout&#8217;s current position, whether of not it is currently moving, and its destination if in motion.\u00a0 With that data we can effectively divide its motion into smaller increments, executing the next move whenever a delay period has passed. Scaling to multiple turnouts is primarily a matter of organizing your turnout data appropriately.<\/p>\n<h2>Organizing Turnout Data<\/h2>\n<p>I start with a data structure to represent a turnout:<\/p>\n<pre>typedef struct TURNOUT_DEF {\r\n\u00a0 int pin;\r\n\u00a0 int pos_main;\r\n\u00a0 int pos_div;\r\n};\r\ntypedef struct TURNOUT_DATA {\r\n\u00a0 TURNOUT_DEF data;\r\n\u00a0 bool is_moving;\r\n\u00a0 byte alignment;\r\n\u00a0 int pos_now;\r\n\u00a0 int target_pos;\r\n\u00a0 unsigned long last_move;\r\n};<\/pre>\n<p>The test loop has a single turnout, but I declare the data for the turnout in the form of an array of TURNOUT_DATA structures just as I would with a multi-turnout layout. This way, a single variable <strong><em>turnouts<\/em> <\/strong>represents all turnouts the sketch has to manage:<\/p>\n<pre>TURNOUT_DATA turnouts[NUM_TURNOUTS] = {\r\n\u00a0 {{8, 93, 117}, false, ALIGN_MAIN, 93, 93, 0}\r\n};<\/pre>\n<h2>Methods<\/h2>\n<p>The methods for using the array would be the same no matter how many turnouts there are. For example, in setup, a <em>for<\/em> loop is used to step through the turnout array and initialize the servos ( variable <em>servos<\/em> is an array of pointers to servo objects, declared in this way: servo *servos[NUM_TURNOUTS]. The servo pointer could be included within the TURNOUT_DEF structure, but here I chose to keep the servo pointers in a separate array, index synchronized with <em>turnouts<\/em>.):<\/p>\n<pre>\/\/ initialize turnouts\r\n\u00a0 for(i = 0; i &lt; NUM_TURNOUTS; i++){\r\n\u00a0\u00a0\u00a0 \/\/ create a SERVO instance and store it in the servos array\r\n\u00a0\u00a0\u00a0 servos[i] = new Servo; \r\n\u00a0\u00a0\u00a0 servos[i]-&gt;attach(turnouts[i].data.pin);\r\n\u00a0\u00a0\u00a0 setTurnout(i, ALIGN_MAIN);\u00a0 \/\/ set initial turnout position\r\n\u00a0 }<\/pre>\n<p>The setTurnout() function does exactly what you expect &#8212; sets a turnout position &#8212; but it does not move the turnout. Instead, it initiates motion by setting turnout data elements: assigning a destination to the <em>target_pos<\/em> element, setting the <em>is_moving<\/em> element to true and setting the <em>last_move<\/em> element to zero. ALIGN_MAIN and ALIGN_DIVERGENT are arbitrary integer macro\/constants used to represent those alignment states.<\/p>\n<pre>void setTurnout(int id, int align){\r\n\u00a0\u00a0 switch(align){\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 case ALIGN_MAIN:\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 turnouts[id].is_moving = true;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 turnouts[id].last_move = 0;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 turnouts[id].target_pos = turnout[id].data.pos_main;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 turnouts[id].alignment = ALIGN_MAIN;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 break;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 case ALIGN_DIVERGENT:\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 turnouts[id].is_moving = true;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 turnouts[id].last_move = 0;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 turnouts[id].target_pos = turnout[id].data.pos_div;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 turnouts[id].alignment = ALIGN_DIVERGENT;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 break;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n}<\/pre>\n<p>The command and communications process elsewhere in the test loop sketch uses setTurnout() to trigger turnout moves when it receives appropriate commands.<\/p>\n<h2>Moving Along<\/h2>\n<p>Actual turnout motion is handled within the main loop, beginning with a call to millis() at the beginning of the loop to get current elapsed time in milliseconds. Then the code steps through the turnouts array, checking the <em>is_moving<\/em> element to determine if a turnout is in motion. If <em>is_moving<\/em> is true, the code then checks to see if enough time has elapsed since the last move &#8212; if so, another move is made and the time is recorded.<\/p>\n<p>Movement timing is controlled by by the macro\/constant STEP_DELAY, which is the number of milliseconds (200 on the test loop) to delay between moves. If you wanted to customize the delay for different turnouts, add a member to the data structure to hold the delay period for each turnout, and use that data instead of STEP_DELAY. Every turnout or other animated object can have its own unique delay period.<\/p>\n<pre>\u00a0 \/\/ get elapsed milliseconds at loop start\r\n\u00a0 unsigned long currentMillis = millis();\r\n\r\n\u00a0 \/\/ Turnout Control\r\n\u00a0 for(int i = 0; i &lt; NUM_TURNOUTS; i++){\r\n\u00a0\u00a0\u00a0 if (turnouts[i].is_moving) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 if ( (currentMillis - turnouts[i].last_move) &gt;= STEP_DELAY ) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 turnouts[i].last_move = currentMillis;\r\n        \/\/ if the new angle is higher\r\n        \/\/ and not already at destination\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if (turnouts[i].pos_now &lt; turnouts[i].target_pos) { \r\n          \/\/ increment and write the new position\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 servos[i]-&gt;write(++turnouts[i].pos_now);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 } else {\u00a0 \r\n           \/\/ otherwise the new angle is equal or lower\r\n           \/\/ if not already at destination\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if (turnouts[i].pos_now != turnouts[i].target_pos) { \r\n            \/\/ decrement and write the new position\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 servos[i]-&gt;write(--turnouts[i].pos_now);\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      \/\/ if target position is reached, turn motion off\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 if (turnouts[i].pos_now == turnouts[i].target_pos) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 turnouts[i].is_moving = false;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 }\r\n\u00a0 }<\/pre>\n<p>Using this technique, turnouts start and stop moving asynchronously and can be independently controlled.<\/p>\n<p><a href=\"https:\/\/thenscaler.com\/wp-content\/uploads\/2016\/01\/Station-and-Signal-5-for-slider.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-602 size-full\" src=\"https:\/\/thenscaler.com\/wp-content\/uploads\/2016\/01\/Station-and-Signal-5-for-slider.jpg\" alt=\"Station and Signal 5\" width=\"1200\" height=\"400\" srcset=\"https:\/\/thenscaler.com\/wp-content\/uploads\/2016\/01\/Station-and-Signal-5-for-slider.jpg 1200w, https:\/\/thenscaler.com\/wp-content\/uploads\/2016\/01\/Station-and-Signal-5-for-slider-300x100.jpg 300w, https:\/\/thenscaler.com\/wp-content\/uploads\/2016\/01\/Station-and-Signal-5-for-slider-768x256.jpg 768w, https:\/\/thenscaler.com\/wp-content\/uploads\/2016\/01\/Station-and-Signal-5-for-slider-1024x341.jpg 1024w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><\/a><\/p>\n<h2>Extending the Technique<\/h2>\n<p>Any number of animated objects could be handled in this fashion. The key is to represent your animated object with data, then use the data to trigger and control motion, moving incrementally with a delay. Start each iteration of the main loop by getting the current elapsed time with millis(), then use the time to determine when incremental actions should happen.<\/p>\n<p>Since the Arduino IDE is a C++ platform, you could write the same code as a C++ object encapsulating the data for each turnout,\u00a0 and the methods used, in turnout objects. This kind of problem lends itself well to an Object Oriented approach.<\/p>\n<p>OOP would be semantically elegant, but not necessarily more efficient. This is a matter of personal preference.\u00a0 I love OOP programming, but find the efficiency of straight C coding compelling for many Arduino tasks.\u00a0 Plus, I think, straight C is easier for novices to read and understand, especially if they&#8217;ve been exposed to any other structured procedural language (javascript, for example). So the code here is all C; but OOP would work fine.<\/p>\n<hr \/>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The microcontroller at the heart of your Arduino contains a single processor that can execute one instruction at a time.\u00a0 But it can do that task over and over very quickly &#8212; around 16 million times per second on an UNO. At that speed, the microcontroller can handle a lot of different tasks, more or [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"parent":157,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-661","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/thenscaler.com\/index.php?rest_route=\/wp\/v2\/pages\/661","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/thenscaler.com\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/thenscaler.com\/index.php?rest_route=\/wp\/v2\/types\/page"}],"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=661"}],"version-history":[{"count":18,"href":"https:\/\/thenscaler.com\/index.php?rest_route=\/wp\/v2\/pages\/661\/revisions"}],"predecessor-version":[{"id":703,"href":"https:\/\/thenscaler.com\/index.php?rest_route=\/wp\/v2\/pages\/661\/revisions\/703"}],"up":[{"embeddable":true,"href":"https:\/\/thenscaler.com\/index.php?rest_route=\/wp\/v2\/pages\/157"}],"wp:attachment":[{"href":"https:\/\/thenscaler.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=661"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}