When the MEGA is the Solution

This is a continuation of High Density DCC Block Detection. In this post I’ll talk about putting all the pieces together to create a unified controller for module 1, lower level of the L&NC.

But first, lets finish out the block detection system.

Modifying Current Sensing Procedures.

I’ve talked extensively about current sensing and block occupancy detection throughout the blog. I started with ACS712 sensors because, in part, they work with both DC and DCC. Their flaw, if you will, is that the sensor produces so much noise that its hard to discern a reading below 20 mA (officially, you can’t); and impossible under 12 mA.  That matters because 1) today’s DCC decoders typically consume 5 mA when idling with all functions off; so to detect an idle DCC locomotive, you have to be able to detect down to 5 mA; and 2) You can take advantage of the fact that DCC track power is always on, and use 10k resistor wheels to make rolling stock detectable. To do that successfully you have to be able to sense 1 mA of current flow at N scale voltage.

Current Transformer

Since the L&NC is a DCC layout I ended up going with Current Transformers, a DCC only solution that, with the right ADC, can detect current flow under 1 mA. With my 14 bit Mayhew Labs Extended ADC Shield, I’ve successfully detected current flow just under 500 µA. The Root Mean Square algorithm for detecting current has been effective with both sensor types, but with CT sensors you can get an accurate read with fewer samples than you would need with the ACS712.

Note that you can successfully read CT sensors with the Arduino built-in ADC; I’ll be doing that in the next module and will share any tweaks needed to get satisfactory detection.

As previously mentioned, the Mayhew Labs Shield and the software library that goes with it is optimized to sequentially read the ports. With this ADC, the ports have to be configured immediately before being read. So, the read function supplied in the library does just that: reads one port and prepares the next. The upshot of that is when you start a read cycle you have to do a throw away read to setup your first port. Hence, reading all the ports you can in a single pass saves time and steps.

The code to do a single read pass in DIFFERENTIAL BIPOLAR mode on three channels:

 // throw away read to prepare channel pair 0-1
 extendedADCShield.analogReadConfigNext(0, DIFFERENTIAL, BIPOLAR, RANGE5V);
 // read prepared and prepare next channel pairs; 2-3, 4-5, 6-7 (unused)
 raw[0] = extendedADCShield.analogReadConfigNext(2, DIFFERENTIAL, BIPOLAR, RANGE5V) - blocks[sensor_map[row][0]].aqv;
 raw[1] = extendedADCShield.analogReadConfigNext(4, DIFFERENTIAL, BIPOLAR, RANGE5V) - blocks[sensor_map[row][1]].aqv;
 raw[2] = extendedADCShield.analogReadConfigNext(6, DIFFERENTIAL, BIPOLAR, RANGE5V) - blocks[sensor_map[row][2]].aqv;

Each command executes in about 5μs, so the read completes in 20μs; doing it this way limits throw away reads and the time they waste.

I created a special version of my current reading function for the Mayhew Labs Extended ADC Shield. The algorithm is identical to the current sensing algorithm I’ve previously posted, except that it works on three inputs at once. The full code (available on the github site) is specific to my situation but it does demonstrate how to adapt the base current sensing method I have posted to different hardware setups.

void readSensorRow(int row){
 extern BLOCK_DEF blocks[];
 float accumulator[3];
 float raw[3];
 float delta, rms;
 unsigned int count;
 unsigned long curMicros, prevMicros = micros() - sampleInterval;
 // set the address lines to access the row
 digitalWrite(pinA, addrBits[row].A);
 digitalWrite(pinB, addrBits[row].B);
 digitalWrite(pinC, addrBits[row].C);

 count = 0;
 for(int j = 0; j < 3; j++){
   accumulator[j] = 0;
 }

 while(count < numSamples){
   curMicros = micros();
   if (curMicros - prevMicros >= sampleInterval) {
     prevMicros = curMicros;
     // prepare channel 0
     extendedADCShield.analogReadConfigNext(0, DIFFERENTIAL, BIPOLAR, RANGE5V);
    // read/prepare subsequent 3 channel pairs; 0-1, 2-3, 4-5
    raw[0] = extendedADCShield.analogReadConfigNext(2, DIFFERENTIAL, BIPOLAR, RANGE5V) - blocks[sensor_map[row][0]].aqv;
    raw[1] = extendedADCShield.analogReadConfigNext(4, DIFFERENTIAL, BIPOLAR, RANGE5V) - blocks[sensor_map[row][1]].aqv;
    raw[2] = extendedADCShield.analogReadConfigNext(6, DIFFERENTIAL, BIPOLAR, RANGE5V) - blocks[sensor_map[row][2]].aqv;
    for(int j = 0; j < 3; j++){
      raw[j] /= SENSITIVITY; // convert to amperes
    }
    for(int j = 0; j < 3; j++){
      accumulator[j] += (raw[j] * raw[j]); // sum the squares
    }
    ++count;
   }
 }
 for(int j = 0; j < 3; j++){
   //https://en.wikipedia.org/wiki/Root_mean_square
   rms = sqrt((float)accumulator[j] / (float) numSamples);
   delta = abs(blocks[sensor_map[row][j]].aqc - rms);
   blocks[sensor_map[row][j]].occ = delta > DETECTION_THRESHOLD;
 }
}

The “DETECTION_THRESHOLD” is .0006 (approx 600 µA); the “SENSITIVITY” factor is 3.3. The “sensor_map” associates specific sensors on a “row” of sensors with specific blocks. If you set up the sensors specifically so they will read in block numeric order then you wouldn’t need a “map.” In this case, the sensors are arranged according to the proximity of feeders, not numeric block order.

Constructing an Integrated Controller

So, with working Block Occupancy Detection, 9 turnouts and a few signals installed it was time to create the “controller” sketch for this module/level. In my experience to date, putting all the track functions together on a single controller is the most efficient way to ensure that all the pieces interact in a timely and correct manner. So, starting with an UNO, I built a controller stack with the UNO, an Ethernet shield, the Mayhew Labs Sheild and a connection board (a simple prototyping shield with screw terminals installed at all the pin locations, making connections easy and secure).

In essence, the controller sketch is an improved version (including my turnout class, and a signals class I’m developing) of the test loop sketch; the main difference being that there is more of everything. I compiled the sketch and had about 600 bytes of local memory left. That was pretty much the same as what I got on the test loop, so I thought it would work OK.

At first it seemed to work, but then became erratic and wouldn’t boot consistently. Uh oh. I’ve seen that sort of thing before: Memory problems! Clearly, the increased number of objects (turnouts, signals & blocks) was swamping memory resources at run-time. I’ve always known that 600 bytes remaining for local variables is right on the edge of what you can get away with, and my sketch was jumping the edge and into the void!

Time to Go MEGA

I frequently see people use MEGA’s because of all the additional pins it comes with. Since I use various pin multiplication methods, I don’t find that aspect of the MEGA compelling. But when you need more memory for code or data, the MEGA is unbeatable. When I compile the controller sketch for the MEGA, there are 6700 bytes of local memory remaining.  Plenty!

Arduino MEGA

Needless to say, everything works reliably now. I’ve tested it by letting it run for hours undisturbed, then running a train to see if things work. Perfect.

Module 1, Lower Level Controller Stack

Given that there can be a lot of “pieces” to controlling trackage, its likely that I’ll need a couple more MEGA’s before I’m done. I’ll use UNO’s and NANO’s mostly except key places where the amount of track/turnouts/signals requires a more robust solution.

I’m really done with the knock-offs and am making the effort to buy genuine. Arduino prices are down a little, which helps. This genuine MEGA cost me a little over $30 (its up a little today; prices do fluctuate some) on Amazon which I think is fine for what it is and what I’m able to get out of it (current official list is $38). You still can’t beat the overall cost of the system; off-the shelf “model railroading” equivalents (BOD systems, signals, etc) will run double or more for the amount of track, turnouts and signals involved.

The Challenges of Stacking

Even with a MEGA, stacking shields can present problems because each shield needs to monopolize at least one pin (for SS [Slave Select] on the SPI interface), which means that pin cannot be used for anything else. Further, some shields come with “extras” that require an additional dedicated pin. The Ethernet shields I use, for example, include a MicroSD Card reader; so in addition to taking over pin 10 for the network SS, pin 4 is required for the MicroSD and becomes unusable for anything else. Some shields give you some ability to modify pin assignments.  The Mayhew Labs Extended ADC shield also wants to use pin 10 for its SS, but you can change that and a couple of other pin assignments. Sometimes the change is made by closing a solder jumper. Other times you need a jumper wire to relocate a pin assignment. The Mayhew Labs shield requires a little of both but is fairly adaptable as a consequence.

 

Adafruit PWM Driver

Pin usage issues is one reason why the Adafruit 16 Channel PWM Driver for the turnouts is so important here: it uses the I2C wire interface, totally separate from SPI and the regular pins, so it runs happily along side the other devices without consuming pins. If you look at a recent UNO or MEGA, on the digital pin side after D13 there are a couple of additional slots labeled SCL and SCA; some shields (like the Ethernet shield) don’t even have pins in those locations (see the image of the Controller Stack, above). Consequently, I have to jumper the pins from the Mayhew Labs shield to the connection shield on top of the stack.

This leaves all the regular analog ports  and several regular digital/pwm pins available for other uses, such as connecting to the DuinoNode network that controls lights and animation. On the Mega, there are dozens of remaining pins, both digital and analog should I ever need them.

Timing Issues

My “round robin” sensor reading function for this module reads one “row” of three sensors; there are 8 rows total, so the function is called for each row.

Why did I do it this way? Well, as previously discussed, it takes time to perform Block Occupancy Detection. With the Mayhew Labs shield and a sketch that is optimized for sequential reads, I’ve got the process of reading all 24 blocks down to about 1/2 second.

That’s great all things considered. But unfortunately, it dramatically slows down turnout motion because that big block of busy prevents running the turnout.update() method frequently enough. The first time I ran it without any changes it took an average of 30 seconds to move a turnout. Way too long.

So, I did two things to adjust and speed up turnout motion. 1) I added a movement increment option to the turnout class, so double or triple moves are possible if needed; and 2) I broke the block occupancy detection process down to one row at a time interleaved with turnout updates. The key loop() code looks like this:

 for(int i = 0; i < 8; i++){
   readSensorRow(i);
   for(int j = 0; j < NUM_TURNOUTS; j++){
     turnouts[j]->update(currentMillis);
   }
   currentMillis = millis();
 }

Turnout motion is satisfactory now

Coming Soon

I know quite a few people are interested in the turntable. While I’ve talked about the mechanism, I haven’t gotten into programming and control, so that’s next. That will probably be the last major post exclusively focused the lower level of module 1 as I move onto the adjacent modules and completing the lower level. I’m way over due for a video, too, and I think there’s enough working now to justify a little video tour.

Until then, Happy Railroading.

 

High Density DCC Block Detection

If you have been following along, you already know that the L&NC module 1, lower level is a complex beast. Here’s the state of the accumulated electronics as of today. I’ve talked about quite a bit of it, but there are still key areas to discuss.

Block detection on this first module turned out to be somewhat challenging because of the large number of blocks—24— in a relatively small space.

24 Blocks?

Obviously, the mainline, secondary/yard lead track and a siding are only part of the story. Two big factors in the block count are Red Bluffs Yard (5) and the Roundhouse turntable, tracks and lead in (9). Block arrangement is sometimes dictated by the Peco power routing turnouts I use and the need for insulating gaps; block 11 is ultra small (I’m rethinking the block boundaries here — adding/consolidating feeders and re-gapping is no big deal) because it is at the edge of the module and includes a turnout. The feeder map below will give you some idea of the block layout.

Module 1 LL Feeder Map. Block 1 includes all three turnouts that follow feeder1

Current sensing takes time.

Regardless of the type of sensor you use, the single most important factor is the time it takes for an ADC (analog-to-digital converter) to read the sensor. On an Arduino board with a 10-bit ADC (most boards), it takes 50 μs; if you need to do 100 reads to get an RMS reading, the total read time will be more than 5 ms; add waiting between readings plus calculation time and you need 80 – 100 ms to read a single current sensor. If you are doing only a few, that should present no problems.

But with 24 blocks you are looking at 2+ seconds to do a complete read cycle at that rate. That is an eternity if you are trying to do more than that with the microcontroller. Frankly, its even an eternity just in the context of BOD, because that 2+ seconds for a read cycle limits how fast the system can respond to changes in block occupancy.

Mayhew Labs Extended ADC Shield

I knew I would need a faster and more capable ADC, so I’ve been working with the Mayhew Labs 14 bit Extended ADC Shield. The Mayhew Labs Extended ADC Shield, which is sometimes available at Amazon, comes in 12, 14 and 16 bit flavors. Bits, in the case of an ADC, determine the smallest detectable voltage out of a given range. An ADC with 14 bits of resolution can divide a 0 – 5 volt range into 16,384 steps (2 ^ 14), making the lowest detectable current (and the detection step interval) 305 μV. In contrast, the 10 bit ADC built into an UNO is capable of only 1024 ( 2 ^ 10) steps, making the lowest detectable current (and step interval) 4.88 mV.  That is a huge difference in detection sensitivity.

Mayhew Labs Extended ADC Shield

In addition to sensitivity, the shield is fast, producing a reading in 5 µs. That is 10 times faster than the conversion rate of a built-in Arduino ADC; in fact, the conversion is completed by the time the UNO is ready to execute the next instruction after triggering the read.

The shield and the supporting Arduino Library are optimized to scan the ports sequentially. The system is at its most efficient when you read all the ports in sequence in a single pass. That requires some adaptation of my current sensing methods, although the detection algorithm itself remains unchanged.

The Mayhew Labs Extended ADC shield has eight ports that can be used in one of two fundamental ways: single ended or differential. Single ended — the same mode used by the Arduino ADC — reads each port individually; a sensor is attached to the port and to ground. Differential mode reads and compares two adjacent ports. With CT sensors, each port of the differential pair connects to one side of the sensor creating a complete circuit. CT sensors produce an AC current at the same frequency as DCC; in single ended mode the ADC only sees the positive phase of the cycle; in DIFFERENTIAL BIPOLAR mode the ADC see both phases of the cycle. Accordingly, reading CT sensors in differential bipolar mode will produce the most accurate results and require the fewest reads for calculating an RMS. However, because the DCC cycle is high frequency (8 Khz), its possible to get reasonably accurate results in single-ended mode because the ADC will see multiple positive DCC phases during each read.

It didn’t take many experiments to convince me to use the Mayhew Labs ADC in differential mode.  That means the shield is limited to 4 sensor connections. Made little difference inasmuch as I already had a problem with 24 blocks to watch from a limited number of ADC ports. I’ll concede that at $35, the Mayhew Labs shield is an expensive board; but given how cheap the CT sensors are, the overall cost of the system for 24 blocks is under $100.  I don’t think it is possible to do the job at this scale with an off-the-shelf BOD system for anything like $100. I anticipate using using the shield in one or two more areas where there are 8 or more blocks to watch.

Muxing/Demuxing to BOD Bliss

I’ve frequently talked about techniques for multiplying digital pins, using shift registers and other devices.  It is also possible to multiply analog connections to an Arduino or an external ADC using a Multiplexer/Demultiplexer IC. A mux/demux is basically a device with a common I/O port and 8 selectable I/O ports, plus 3 address lines to select which port is connected to the common I/O. Think of it as a rotary switch with 8 positions, selectable via the address line. The important part is that each connection is isolated from the others so that only one selectable port can be active and cannot by affected by the other ports.

I use the Texas Instruments CD4051B CMOS Single 8-Channel Analog Multiplexer/Demultiplexer [ Digi-Key ]. This functional diagram of the CD4051B show how it works and includes the address bits required to select an I/O channel. [Note: other CD405XB variants have different port arrangements that are useful in other situations.]

TI CD4051B Functional Diagram

To use these, you have to supply power, ground, three digital address lines and an I/O line back to your microprocessor—two power connections and four logic connections. So you are getting a 8 to 1 improvement in analog capacity, at the cost of three digital address lines, which is OK; with connection sharing (sounds odd, but you’ll see it works), my three address lines can control multiple banks of 8 sensors, further improving pin efficiency.

Round Robin Reads

Since I have 24 blocks, and each CD4051B IC supports 8 channels, three groups of 8 sensors seemed like the optimal approach. The idea is to do an optimized read of all three boards on each pass, using the address lines to select which sensors are read. I think of this as a “round robin” technique. I’ll get into the details of the reading technique in second part of this two part post.

I set out to create sensor boards and the very first question that arose was this: how wise was it to have one side of each CT connected to a shared I/O line, with only the other side of the CT’s isolated through the mux/demux? I’m not an electrical engineer, but I had a feeling that connecting all CT’s together on one side might not be a good idea.

I decided to follow my instincts and build two boards using two 4051B’s to fully isolate each sensor. Here are pictures of that version (annotated as much as possible for those who are interested in building their own):

Top of CT Sensor Board

CT Sensor Board, Top View with ICs

Bottom side of CT Sensor Board.

A couple of notes about this board:

  1. I start with a Busboard Prototype Systems B1 Solderable Breadboard. The board has 6 rails that, with good planning, can cut the number of jumpers you have to use. Use the outer rails for power (+5v on one side, Ground on other); the other two rails on each side can be used to connect sensors to the mux/demux ICs. I didn’t get it quite right on any of them and left some unused rails as a result.
  2. Use high accuracy resistors at the CT’s so that they are essentially identical. I use 100Ω resistors with .1% tolerance [Digi-Key].
  3. I get CT sensors in quantity from Digi-Key. I use universal jumper sets that I get on Amazon.
  4. I have a fetish for power indication LED’s so I know a board is live. I’m starting to get wise about the brightness of today’s LEDs, though, and am using 1K resistors to cut the brightness back (skip the calculations and use a bigger resistor!).
  5. Once I decided on a connection protocol (see the pictures), building the board is a matter of patiently matching connections per the IC pin-outs — like many IC’s this one will make you wish the designers had shown some mercy in arranging the pin-outs! But basically its this simple: connect channel 0 of mux1 to one side of sensor 0, then channel 0 of mux 2 to the other side of sensor 0. Do that for 7 more CT sensors and you’ll have an eight sensor detection board that is as effective as anything out there. Be consistent about which side of the sensors are connected to which mux.

    CD4051B Pin Outs NOTE: INH, VEE and VSS will be grounded; supply +5 volts at VDD. A, B and C are the three address lines. COM OUT/IN is the common I/O channel that connects back to the ADC.

  6. I use 22 gauge wire for feeders. I’ve learned to cut feeder leads for each sensor when I build the board — about 12″ long. The lead needs to wrap 3 times around the sensor, with a tail on each side for splicing to the feeders and connecting to the power distribution board. This makes the installation process a whole lot easier. The “wrap three times” thing is almost a catechism; 2 times will work but the signal is significantly weaker. This is an induction system, so the more times wrapped the better.
  7. I secure the wrapped feeder on the CT coil with a dab of hot glue.
  8. I’m sure you color code your feeders. I recommend that you always read the same rail on all CT sensors.

That 8 sensor board installed on the layout.

How NOT to do it.

But, since part of the point of this exercise is to learn and apply knowledge, I also made a single mux/demux version with one side of the CT’s on a shared line. That would simplify wiring and omit an IC; a good thing, right? Reduces your IC count and the number of connections you have to solder. Initial tests on the bench seemed to show that it worked just fine, thus making me further question my sanity.

But then I installed the whole system, and wouldn’t you know that under load my pessimism was rewarded.  The odd board produced odd readings, clearly off the norms of the other two boards. Lots of cross-talk was evident; occupancy of one block would cause other blocks to show false readings. I’m sure a real electrical engineer would find that pretty funny and predictable.

Here is a picture of the errant board.  I put this here as a warning NOT to do this or you will be profoundly disappointed.

Bad board with all sensors connected to a common line on one side. Looks attractively simple. Don’t do it!

The Interface board.

To consolidate the three boards and their connections, I had to create an interface board.

Interface board linking detector boards to power and microcontroller.

The trick here is that the address lines are shared; address lines set the same address on all three boards at the same time.  So, when a “round robin” read cycle occurs, the same channel is read on each board on each pass: the first pass is channel 0, next channel 1 and so on.

To Be Continued

I had to modify my current sensing algorithm to accommodate the “round robin reads.” That was easy enough. But when I got around to integrating all the functionality on an UNO, I ran into the proverbial brick wall.

More in the companion post that follows.