{"id":514,"date":"2015-11-17T11:07:26","date_gmt":"2015-11-17T19:07:26","guid":{"rendered":"http:\/\/thenscaler.com\/?p=514"},"modified":"2016-01-06T00:10:01","modified_gmt":"2016-01-06T08:10:01","slug":"block-occupancy-detection-for-dc-and-dcc","status":"publish","type":"post","link":"https:\/\/thenscaler.com\/?p=514","title":{"rendered":"Block Occupancy Detection for DC and DCC, Part 1"},"content":{"rendered":"<p>Block occupancy detection is central to advanced layout control. Without it systems like control panels, signals or animation that depend on knowing the position of your trains can&#8217;t function. If my concept of building a layout around a network of Arduino boards and off-the-shelf peripherals is going to work, it has to be able to do block occupancy detection.<\/p>\n<div id=\"attachment_537\" style=\"width: 310px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/thenscaler.com\/wp-content\/uploads\/2015\/11\/JMRI-CornwallPanelLit.gif\" target=\"_blank\" rel=\"http:\/\/jmri.sourceforge.net\/help\/en\/html\/apps\/PanelPro\/PanelPro.shtml\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-537\" class=\"wp-image-537 size-medium\" src=\"https:\/\/thenscaler.com\/wp-content\/uploads\/2015\/11\/JMRI-CornwallPanelLit-300x180.gif\" alt=\"A Control Panel made with JMRI\" width=\"300\" height=\"180\" \/><\/a><p id=\"caption-attachment-537\" class=\"wp-caption-text\">A Control Panel made with <a href=\"http:\/\/jmri.sourceforge.net\/help\/en\/html\/apps\/PanelPro\/PanelPro.shtml\" target=\"_blank\">JMRI PanelPro<\/a><\/p><\/div>\n<p>As it turns out, block occupancy detection is possible and economical (in terms of equipment cost) with an Arduino and some sensors. In this post I&#8217;ll talk about and demonstrate what I&#8217;ve learned so far about the electronics of block occupancy detection.<\/p>\n<hr \/>\n<p>Block occupancy detection can be accomplished a number of ways. The simplest methods involve installing trigger points that detect a passing train using optical or magnetic sensors. These methods generally only work with moving trains, and can&#8217;t detect stationary objects located on the tracks between trigger points.<\/p>\n<h2>Detecting Current Flow<\/h2>\n<p>Enter current sensing, the block detection method most closely tied to the electronic function of the layout. The idea behind sensing current for block occupancy detection is simple enough: current should only flow across the rails in a block that has a locomotive or other device (such as rolling stock with lights or a sound car decoder) on it.\u00a0 If current flows, the block is occupied; if not, it is (or should be) clear.<\/p>\n<h2>How Current is Sensed<\/h2>\n<div id=\"attachment_535\" style=\"width: 310px\" class=\"wp-caption alignleft\"><a href=\"https:\/\/thenscaler.com\/wp-content\/uploads\/2015\/11\/BDL168_jpg_2000x2000_q85.jpg\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-535\" class=\"wp-image-535 size-medium\" src=\"https:\/\/thenscaler.com\/wp-content\/uploads\/2015\/11\/BDL168_jpg_2000x2000_q85-300x300.jpg\" alt=\"Digitrax BDL168 Detection Unit\" width=\"300\" height=\"300\" srcset=\"https:\/\/thenscaler.com\/wp-content\/uploads\/2015\/11\/BDL168_jpg_2000x2000_q85-300x300.jpg 300w, https:\/\/thenscaler.com\/wp-content\/uploads\/2015\/11\/BDL168_jpg_2000x2000_q85-150x150.jpg 150w, https:\/\/thenscaler.com\/wp-content\/uploads\/2015\/11\/BDL168_jpg_2000x2000_q85-50x50.jpg 50w, https:\/\/thenscaler.com\/wp-content\/uploads\/2015\/11\/BDL168_jpg_2000x2000_q85.jpg 750w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-535\" class=\"wp-caption-text\">Digitrax BDL168 Detection Unit<\/p><\/div>\n<p>&nbsp;<\/p>\n<p>Current sensing is generally done indirectly, to avoid drawing on the current (more of a problem in high voltage applications than for a model railroader) and to enable sensors to operate at a different voltage\/current from that being sensed. Early commercial block detection systems used\u00a0 induction coils to sense current passing through wires wrapped around the coil. Newer systems tend to be hybrid or solid-state, reducing size and power consumption. The majority are optimized for DCC and few systems claim to work in DC.<\/p>\n<div id=\"attachment_536\" style=\"width: 282px\" class=\"wp-caption alignright\"><a href=\"https:\/\/thenscaler.com\/wp-content\/uploads\/2015\/11\/RRCirKits_detection_coils.jpg\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-536\" class=\"size-full wp-image-536\" src=\"https:\/\/thenscaler.com\/wp-content\/uploads\/2015\/11\/RRCirKits_detection_coils.jpg\" alt=\"RRCirKits Detection Coils\" width=\"272\" height=\"233\" \/><\/a><p id=\"caption-attachment-536\" class=\"wp-caption-text\">RRCirKits Detection Coils<\/p><\/div>\n<p>To put it another way, there is no block occupancy sensor device on the market with a generic analog or digital output that could be directly read by a general purpose computing device, such as an Arduino. For that matter, I could not identify a single BO system that could easily interconnect with a system from another manufacturer. There aren&#8217;t any real standards in this area.<\/p>\n<h2>Current Sensing, the Arduino Way<\/h2>\n<p>The <a href=\"http:\/\/www.allegromicro.com\/en\/Products\/Part_Numbers\/0712\/0712.pdf\" target=\"_blank\">Allegro ACS712 chip<\/a> is a leading solid state current sensing solution for power feeds up to 30 amps.\u00a0 Its works using a phenomena called the &#8220;hall effect&#8221; to indirectly detect current flow through a silicon chip. The output of the ACS712 connects to an analog port on an Arduino board (or any other microcontroller\/microcomputer that supports analog sensing). Connected to an Arduino, a call to analogRead() gives you the output of the sensor.<\/p>\n<div id=\"attachment_523\" style=\"width: 310px\" class=\"wp-caption alignleft\"><a href=\"https:\/\/thenscaler.com\/wp-content\/uploads\/2015\/11\/ACS712-Board.jpg\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-523\" class=\"size-medium wp-image-523\" src=\"https:\/\/thenscaler.com\/wp-content\/uploads\/2015\/11\/ACS712-Board-300x232.jpg\" alt=\"ACS712 Board\" width=\"300\" height=\"232\" srcset=\"https:\/\/thenscaler.com\/wp-content\/uploads\/2015\/11\/ACS712-Board-300x232.jpg 300w, https:\/\/thenscaler.com\/wp-content\/uploads\/2015\/11\/ACS712-Board.jpg 998w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-523\" class=\"wp-caption-text\">ACS712 Sensor Board<\/p><\/div>\n<p>As is usually the case, the chip requires a few external components to function properly; primarily connectors and a filter capacitor\/resistor combination to fine tune the sensitivity of the device.\u00a0 As it happens there is a robust market for simple ACS712 sensing boards, resulting in prices under $2 per sensor if you buy directly from a Chinese source on Ebay, or Alibaba. Even on Amazon.com, <a href=\"http:\/\/amzn.to\/20sz6Qz\" target=\"_blank\">sensors can be purchased for about $7 each<\/a> with Prime 2-day shipping, which makes them slightly cheaper per block than sensors for the most cost-effective systems specifically marketed for DCC.<\/p>\n<p>The only issue I&#8217;ve seen with these open market sensors is that there is no specific standard for the filter capacitor. Accordingly, different batches of these boards may have a slightly different sensitivity which would have to be accounted for in the software. As you&#8217;ll see, that is not nearly as big a deal as it sounds.<\/p>\n<p>For now I think the optimal strategy for anyone trying to install a block detection system with these components, is to buy all the ones you will need (plus a few extra) in a single batch from a single source. At $2 a pop from China, it makes sense to buy in bulk. In my experience that will guarantee they are all tuned the same way.<\/p>\n<h2>DC and DCC Ready<\/h2>\n<p>It&#8217;s more important for model railroaders that the sensor <strong>functions equally well with alternating and direct current<\/strong>. The only difference in the way the sensors work as a system in DC versus DCC comes from the fact that in DC track power is used to control trains; an occupied block may have no power at all for control reasons. That could throw a block occupancy detection system off.<\/p>\n<p>Any current sensing system for DC has to account this; as should any DCC friendly system that wants to preserve block occupancy information between track power outages (such as caused by a short circuit on the track).\u00a0 We&#8217;ll get to solving that problem in the next post.<\/p>\n<h2>Plug and Read . . . Not!<\/h2>\n<p>I should tell you that if you plug in a sensor and try to determine current state with a single reading&#8230;. well, you&#8217;re going to be disappointed.\u00a0 Individual readings seem to jump around a lot although you can discern trends in either direction.<\/p>\n<p>Alternating current presents the additional problem of sensing a current flow that reverses polarity; at any given moment the power could be anywhere in its cycle, including 0 volts at polarity transition. For AC, multiple samples taken at a frequency that is greater than the frequency of the power cycle is required. Something like 8 &#8211; 12 samples per cycle will allow proper detection of AC current. Multi-sampling helps with DC too.<\/p>\n<h2>First, Calibration<\/h2>\n<p>Different sensor chips will produce slightly different analog readings under the same conditions. Any differences in filter capacitors between boards will also produce different readings, as will any +\/- variation from 5 volts supplied as Vcc to the chip. To deal with these differences and actual power conditions, we have to calibrate each sensor on startup.<\/p>\n<p>What we need to know is the output of each sensor when there is no current flow (no load); we&#8217;ll call this condition <strong>adc-zero<\/strong>. The difference between sensor output and adc-zero represents the current sensed by the device.<\/p>\n<p>Getting an accurate number for adc-zero requires averaging a large number of quick readings &#8212; 5000 seems to be optimal &#8212; like this (we call adc-zero the &#8220;quiescent voltage&#8221; output):<\/p>\n<pre>long VQ = 0;\r\n\/\/read 5000 samples to stabilize value\r\nfor (int i=0; i&lt;5000; i++) {\r\n\u00a0\u00a0 VQ += analogRead(PIN);\r\n\u00a0\u00a0 delay(1);\r\n\u00a0}\r\n\u00a0VQ \/= 5000;\r\n\u00a0return int(VQ);<\/pre>\n<p>With that number calculated for each sensor, we can get\u00a0 consistent readings across multiple sensors.<\/p>\n<h2>Sampling Current<\/h2>\n<p>Reading current consistently requires a multi-sample approach over a defined time span. For this demonstration we&#8217;ll accumulate 250 samples taken over a 100 ms span on each read cycle.<\/p>\n<p>Alternating current drives you to calculate the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Root_mean_square\" target=\"_blank\">Root Mean Square<\/a> of the data to filter polarity shifts and get a very stable mean current value you can work with.\u00a0 Not only does this work well with A\/C, it also is equally helpful in getting stable readings of DC current because the output of the sensor varies over time [for the hardcore math and theory, <a href=\"https:\/\/en.wikipedia.org\/wiki\/Root_mean_square\" target=\"_blank\">see the Wikipedia page<\/a>].<\/p>\n<p>Its an easy formula to translate into a computer algorithm, using a function like this :<\/p>\n<pre>float readCurrent(int PIN, float adc_zero)\r\n{\r\n\u00a0 float currentAcc = 0;\r\n\u00a0 unsigned int count = 0;\r\n\u00a0 unsigned long prevMicros = micros() - sampleInterval ;\r\n\u00a0 while (count &lt; numSamples)\r\n\u00a0 {\r\n\u00a0\u00a0\u00a0 if (micros() - prevMicros &gt;= sampleInterval)\r\n\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 float adc_raw = analogRead(PIN) - adc_zero; \/\/ Electical offset voltage\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 adc_raw \/= SENSITIVITY; \/\/ convert to amperes\r\n      \/\/ accumulate sum of the squares of converted raw data\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 currentAcc += (adc_raw * adc_raw); \r\n\u00a0\u00a0\u00a0\u00a0\u00a0 ++count;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 prevMicros += sampleInterval;\r\n\u00a0\u00a0\u00a0 }\r\n\u00a0 }\r\n  \/\/ calculate the root mean square of the data set\r\n\u00a0 float rms = sqrt((float)currentAcc \/ (float)numSamples);\r\n\u00a0 return rms;\r\n}<\/pre>\n<p>Finally, we have to use the rms values to determine whether or not a block is occupied. For the purposes of this demonstration, I&#8217;m using a hard rms threshold of .0259 (the adc-zero rms is about .021 on the sensor used for the demonstration); I&#8217;ll adopt a different approach when I scale up to a multi-block arrangement.<\/p>\n<p>To further smooth the sketch response, it averages each reading with the previous one. Here is the key code that determines if a block is occupied:<\/p>\n<pre>\u00a0 prev_current = current; \/\/ save last reading\r\n\u00a0 current = readCurrent(currentPin, adc_zero);\r\n\u00a0 avg_current = (current + prev_current)\/2;\r\n\u00a0 occupied = avg_current &gt; occupancy_threshold;<\/pre>\n<p>So lets put the whole journey to this point together in a video demonstration. This demonstrates current sensing in a DC context:<\/p>\n<p><iframe loading=\"lazy\" title=\"Current Sensing and Train Detection Part 1\" width=\"640\" height=\"360\" src=\"https:\/\/www.youtube.com\/embed\/ObjIysigIKU?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe><\/p>\n<p>Here&#8217;s the sketch in its entirety:<\/p>\n<pre>\/\/ Advanced Sensing with ASC712\r\n\/\/ Using Calibration to find sensor 0\r\n\/\/ then sensing by compiling multiple readings to obtain an RMS (Root Mean Square).\r\n\/\/ Derived from these forum posts: https:\/\/forum.arduino.cc\/index.php?topic=179541.0\r\n\/\/ NOTE: Forum posters did not account for device sensitivity\r\n\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\r\nconst int currentPin = 0;\r\n\/\/ Sampling Parameters\r\nconst unsigned long sampleTime = 100000UL; \/\/ sample over 100ms\r\nconst unsigned long numSamples = 250UL; \/\/ the number of samples divides sampleTime exactly, \r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ but low enough for the ADC to keep up\r\nconst unsigned long sampleInterval = sampleTime\/numSamples;\u00a0 \/\/ the sampling interval\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/\u00a0 must be longer than then ADC conversion time\r\n#define SENSITIVITY 185\u00a0 \/\/ from ACS712 data sheet for 5A version, in mV\/A\r\nint adc_zero;\u00a0 \/\/ variable to hold calibrated sensor quiescent voltage\r\n\r\nboolean occupied = false;\r\nfloat occupancy_threshold = .0259;\r\n\r\nfloat current = 0;\r\nfloat prev_current;\r\nvoid setup()\r\n{\r\n\u00a0 Serial.begin(9600);\r\n\u00a0 Serial.println(\"\\nACS712 Current Sensing Basic Demonstration\\nMultiple Readings converted to RMS at 1 second intervals\\nValues for quiescent output are determined by Calibration.\\n\\nCalibrating the sensor:\\n\");\r\n\u00a0 adc_zero = determineVQ(currentPin); \/\/Quiescent output voltage - the average voltage ACS712 shows with no load (0 A)\r\n\u00a0 delay(1000);\r\n}\r\n\r\nvoid loop(){\r\n\u00a0 float avg_current;\r\n\u00a0 prev_current = current;\r\n\u00a0 current = readCurrent(currentPin, adc_zero);\r\n\u00a0 avg_current = (current + prev_current)\/2;\r\n\u00a0 occupied = avg_current &gt; occupancy_threshold;\r\n\u00a0 \r\n\u00a0 Serial.print(\"Current Sensed:\");\r\n\u00a0 Serial.print(current * 1000 ,1);\r\n\u00a0 Serial.print(\" mA\\t\\t\");\r\n\u00a0 Serial.print(\"The block is \");\r\n\u00a0 if(occupied){\r\n\u00a0\u00a0\u00a0 Serial.println(\"occupied\");\r\n\u00a0 } else {\r\n\u00a0\u00a0\u00a0 Serial.println(\"not occupied\");\r\n\u00a0 }\r\n\u00a0 \r\n\u00a0 delay(1000);\r\n}\r\n\r\nint determineVQ(int PIN) {\r\n\u00a0 Serial.print(\"estimating avg. quiscent voltage:\");\r\n\u00a0 long VQ = 0;\r\n\u00a0 \/\/read 5000 samples to stabilize value\r\n\u00a0 for (int i=0; i&lt;5000; i++) {\r\n\u00a0\u00a0\u00a0 VQ += analogRead(PIN);\r\n\u00a0\u00a0\u00a0 delay(1);\/\/depends on sampling (on filter capacitor), can be 1\/80000 (80kHz) max.\r\n\u00a0 }\r\n\u00a0 VQ \/= 5000;\r\n\u00a0 Serial.print(map(VQ, 0, 1023, 0, 5000));\r\n\u00a0 Serial.println(\" mV\");\r\n\u00a0 return int(VQ);\r\n}\r\nfloat readCurrent(int PIN, float adc_zero)\r\n{\r\n\u00a0 float currentAcc = 0;\r\n\u00a0 unsigned int count = 0;\r\n\u00a0 unsigned long prevMicros = micros() - sampleInterval ;\r\n\u00a0 while (count &lt; numSamples)\r\n\u00a0 {\r\n\u00a0\u00a0\u00a0 if (micros() - prevMicros &gt;= sampleInterval)\r\n\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 float adc_raw = analogRead(PIN) - adc_zero; \/\/ Electical offset voltage\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 adc_raw \/= SENSITIVITY; \/\/ convert to amperes\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 currentAcc += (adc_raw * adc_raw);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 ++count;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 prevMicros += sampleInterval;\r\n\u00a0\u00a0\u00a0 }\r\n\u00a0 }\r\n\u00a0 \/\/https:\/\/en.wikipedia.org\/wiki\/Root_mean_square\r\n\u00a0 float rms = sqrt((float)currentAcc \/ (float)numSamples);\r\n\u00a0 return rms;\r\n}<\/pre>\n<p>Next installment, we go to the next level: <strong>multiple blocks<\/strong> and adding <strong>signals<\/strong> to the mix. And I&#8217;ll demonstrate everything in both DC and DCC.<\/p>\n<hr \/>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Block occupancy detection is central to advanced layout control. Without it systems like control panels, signals or animation that depend on knowing the position of your trains can&#8217;t function. If my concept of building a layout around a network of Arduino boards and off-the-shelf peripherals is going to work, it has to be able to [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":523,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[35,21,14],"tags":[46,22,47,40,23],"class_list":["post-514","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-electronics","category-layout-control","category-test-loop","tag-acs712-sensor","tag-arduino","tag-block-occupancy-detection","tag-dc","tag-dcc"],"_links":{"self":[{"href":"https:\/\/thenscaler.com\/index.php?rest_route=\/wp\/v2\/posts\/514","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=514"}],"version-history":[{"count":21,"href":"https:\/\/thenscaler.com\/index.php?rest_route=\/wp\/v2\/posts\/514\/revisions"}],"predecessor-version":[{"id":606,"href":"https:\/\/thenscaler.com\/index.php?rest_route=\/wp\/v2\/posts\/514\/revisions\/606"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/thenscaler.com\/index.php?rest_route=\/wp\/v2\/media\/523"}],"wp:attachment":[{"href":"https:\/\/thenscaler.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=514"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/thenscaler.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=514"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/thenscaler.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=514"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}