L&NC Update; Running Lots of Turnouts

Its been a long stretch where there has been too much going on in real life and little time to write about model railroading. But I’ve been working away on the first module of the L&NC and have made lots of progress, so there’s plenty to write about.

This module has nine turnouts, presenting resource management problems that would arise in any substantial yard or staging facility. If you are familiar with the servo library then you know that you are limited to about 10 servos per microcontroller. With that many servos, your microcontroller will have few resources left to do anything else. This post will focus on a solution that problem, expanding the number of servos and other PWM devices a single microcontroller can manage.

Progress Tour

But first, a quick overview of progress to date.

Here’s the module in its current state:

Progress on the 1st Module as of June 2017. Fascia and dressing up of the edges will be the very last step; its pointless to do that while I’m disturbing things with new features and gear. Wires hanging out the pipe at the bottom of the photo are Anderson Powerpole connectors (track and master power) to the upper level.

As you can see I’ve done quite a bit of detailing. I realized early on that I need to complete all the basic scenicking and detailing of the module before moving on to the next. The big reason is having the module alone on a work table gives the best possible access, especially for electronic or animated items that require access to the underside. That’s not to say detailing will not continue after I move on to the next module, but it will mostly be passive rather than active elements.

By the way, if I were to build a room-sized layout, I’d use a modular (some call it “sectional” because you build it in sections) approach to construction even though the layout would not be portable. After laying mainline and other track spanning sections and cutting gaps between sections, I’d pull each section and do most of the remaining work in a work area under optimal conditions. When ready its just a matter of returning the section to its place in the layout and (literally) plugging it in.

Lets take a quick tour of some details so far.

Roundhouse / Turntable

If you’ve read the previous posts about the Roundhouse and the Turntable, you know these have been long term projects.

The turntable rotating beacon comes on whenever the turntable is in motion.

Then stove fire simulation inside the hut is visible through the door. It’s managed by a little PWM code that will the subject of an upcoming post.

The parking lot side of the Roundhouse has been enhanced with Rix power poles and some EZLINE power cables (which comes in two thicknesses and several colors. I use fine, and chose green–old copper–for its visibility). I fabricated a simple power connection and breaker box for the roundhouse out of a piece of styrene and a brass rod, and an EZLINE cable.

A Woodland Scenics light pole casts a pleasant white-yellow light over the parking lot in night mode. Figures, such as the worker (a Woodland Scenics prepainted figure) at the turntable end of the parking lot breath life into a scene.

Having gone to all the trouble to light the Roundhouse, I’ve started populating the space with some appropriate gear and figures.

A view of the lit Roundhouse interior.

Red Bluffs Yard

The Red Bluffs Yard area has its first structure — a fully lit Yard Office — plus a pickup truck with lighting passing by on the adjacent road.

The Red Bluffs Yard Office is fully lit for night operations. The Woodland Scenics light pole works just fine with my Duino Nodes controlled by an Arduino; treat it like any other 20 mA LED. The truck tailights in the background are from LEDS placed in the rear wheel wells, with the light allowed through tiny holes in the fenders.

Its amazing what a couple of SMD LED headlights can do for a really basic pot metal pickup truck kit from Micro Engineering.

The first of three planned scratch built Signal Bridges has been erected to control one of the approaches to the yard interchange.

And, finally, here is the underside, which is rapidly filling with gear supporting the layout above. This module, with its yard, multiple main tracks and turntable is one of the most electronically “dense” parts of the layout plan, to be exceeded only by the city scene planned for the upper level — that is going to be quite a project and I can hardly wait to finish the lower level and get started on the top!.

The underside of this module is rapidly filling with gear. Obviously overhead soldering is not an issue since I can put the module on its side. That said, I rely primarily on screw terminals and crimped fittings for connections.

PWM Drivers for Turnouts and Other Uses

Pulse Width Modulation (PWM) is used to output a timed pulse, where the output is on only part of the time. The width of the pulses — the percentage of time the pulse in the on state — is used to control external devices like servos or to vary the brightness of an LED.

Some, but not all, Arduino digital outputs are hardware PWM capable. Some of the PWM pins are SPI pins and the two serial pins, leaving only 5 or 6 PWM pins available for unrestricted use depending on the board model. If you want to make extensive use of PWM, that just won’t cut it.

PWM can also be synthesized with timed interrupts on any pin, which is how the servo library works and why it does not require you to attach to PWM pins. Unlike hardware PWM pins, PWM synthesized with interrupts represents a hidden load on your board that can affect the performance of your sketch.

External PWM Boards or “Drivers”

External PWM drivers allow  you to greatly expand the number of PWM devices a single Arduino can manage. PWM is used extensively in robotics, so PWM drivers are fairly ubiquitous and inexpensive. Aside from expanding the number of PWM devices you can control, PWM drivers allow you to off-load all of the PWM overhead and timing routines to the external device, freeing your Arduino for other tasks.

I decided to try Adafruit 16-Channel 12-bit PWM/Servo Driver for servo control and a couple of lighting applications on this module. Adafruit also sells a similar device in shield form.

Adafruit 16-Channel 12-bit PWM/Servo Driver, assembled with original terminal block (blue) that did not hold up to use. I eventually soldered leads to the underside of the board.

I chose the independent board rather than the shield because it has a number of advantages, not least of which it is chain-able with up to 61 additional boards, for a total 992 PWM outputs. A single chain of these can handle the servo needs of most club and museum sized layouts! A more modest layout could use these for both turnout servos and all lights and lighting effects, effectively centralizing and simplifying control of all connected devices. It uses the shared I2C interface for fast communication without using any regular pins on your Arduino. For more details, and a tutorial, see the Adafruit product page.

Assembling the board was straight forward, though there are a lot of pins to solder. The terminal block in the center provides independent power for the servo outputs (V+ center pin on outputs) per standard servo wiring; independent power is required by servos because of their substantial current draw. LED’s and other devices that draw their power from the PWM signal itself will not use the independent power.  Be warned: the terminal block Adafruit supplies is poor quality—substitute a better quality part or solder power leads directly to the board. The headers on the sides are for input and output, transferring both data and power to subsequent boards in a chain.

Adafruit 12 bit 16 Channel PWM Driver installed and connected to servos and lighting.

Connecting servos is just a matter of having a male->female servo extension the right size, or combining multiple extensions for longer runs. Any robotics supply store should have an assortment of extensions; as does Amazon. I have three different sizes to work with, which has worked well so far.

On the board positions 0 through 8 (1st 2 banks of four, plus the first pin of bank 3) are attached to the 9 turnout servos. Positions 9 and 10 are for headlights and taillights on the pickup truck. Using PWM I can have the headlights go between low beam and high beam, or have the taillights brighten as if the brakes have been applied. I have some thoughts about an animated animal crossing in front of the truck from time to time….

Using Adafruit’s PWM Driver Software

Adafruit’s software library for this device is available from their GitHub site. Using the software you create an object that you then use to control the board outputs:

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();

Creating the Adafruit_PWMServoDriver object without arguments uses the base SPI address to access the board; any different address has to be specified as an argument (and the appropriate jumpers on the board have to be closed). With multiple boards, you create a pwm object for each board using its unique SPI address.

From there, the PWM pulse is set on any output by calling the setPWM() member function:

pwm.setPWM(pin, on, off);

where pin is the board output (a number between 0 and 15), on sets the point in the cycle when the signal goes from low to high (usually 0, the beginning of the cycle, but it can be another value) and 0ff is a number between 0  and 4095 setting the point in the cycle when the signal transitions from high to low.

With the Adafruit driver board you do not use degrees to set a servo’s position. Instead we use timing “tick” values that control the signal transitions from low to high and back. There are 4096 “ticks” (12 bits of resolution) during each cycle. That turns out to be a good thing. For servos, the correct off tick values (assuming the on tick is 0) range from about 150 (the minimum or 0 degree position) to 600 (maximum position, 180 degrees).

Directly setting the cycle through ticks at 12 bits of resolution confers highly granular control and extra smooth servo motion.  Using degrees for position, as the standard servo library does, results in jerkier motion since a degree represents a lower resolution–between 8 and 9 bits–than the 12 bit resolution of the Adafruit board. For LEDS and other lighting, you can vary brightness from off to full on in 4096 steps, allowing fine control of lighting effects.

If you ask me, the smooth motion you can achieve with this board makes its $14.95 price more than worthwhile.

The only difference in your code between working with the standard servo library and the Adafruit driver, is in the object and member function you use to cause the servo to move. Every other aspect of your code and logic should remain the same.

What’s Next?

More coding, and I promise I won’t make you wait long. In the next installment I’m going to introduce you to simplified Object Oriented Programming (OOP) in C++ with the Arduino IDE. I’ll demonstrate a different way to code that, I think, improves several aspects of working with multiple turnouts, and makes the intent and flow of your code easier to understand and maintain. We’ve done it procedurally; we are going to take what we’ve learned and create some OOP code to do the work using either the Adafruit driver or the standard servo driver (Hint: we’ll use a compiler directive to select which driver gets implemented, making the object itself agnostic on the issue and universally usable around the layout).

Until then, happy railroading!

Basic Signaling for a Small Layout

Continuing with the theme of controlling a small layout with an UNO, I thought I’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 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 quest to tame common anode wiring. The latter also came out well, once I learned how to reliably solder magnet wire to SMD micro LEDS!

Block and Signal layout on the Test Loop

Block and Signal layout on the Test Loop

I did this primarily to see the block detection system working (block detection is also shown on on my programmable control panel, but that is another story) as a train moves around the track.  You can see it in action in this video—note that only the locomotive is detectable by the block occupancy detection system; the rolling stock is not set up for detection.

Since the test loop is just an oval with a single turnout and siding, the system is fairly simple.  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.  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.

A Framework for ABS

Automatic Block Signalling (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.

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 this post for an explanation of my Duino nodes, and this post for the addressing system in use).

typedef struct SIGNAL_DEF {
  byte type; // bit mask indicating available signal aspects
             // bit 1 = Red; bit 2 = Green; bit 3 = Yellow)
             // 1=R; 2=G; 3=RG; 4=Y; 5=RY; 6=GY; 7=RGY
             // 3 and 7 are the two types on the test loop
  nodeAddress addr; // base address of the Duino node for this signal
  byte R_ID; // pin/bit id for red indication
  byte G_ID; // pin/bit id for green indication
  byte Y_ID; // pin/bit id for yellow indication
  
  // data elements for running the signal
  byte state; // current signal state
  T_ALIGN *turnouts; // these turnouts must be aligned as defined to get SIGNAL_GREEN
  byte numTurnouts;
  byte *following; // additional blocks ahead (depends on direction signal faces)
  // watched for occupancy resulting in SIGNAL_YELLOW caution   
  byte numFollowing; 
};

By now you should recognize that this is my preferred approach to dealing with complex, interactive objects in the system.  As always, I define a compound data structure (the structure contains other structures as elements, in this case the T_ALIGN and NODEADDRESS types)  to collect all relevant data for each signal. Feel free to reinvent any of this — the point is to collect all necessary data in one place for each signal.

“T_ALIGN *turnouts”  is an example of pointer notation which allows for an array of zero (empty array) or more of the respective types; the “numTurnouts” element indicates how many items the T_ALIGN array contains. The “byte *following” and “numFollowing” do the same thing for a list of subsequent block IDs that are watched for occupancy.

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:

// Signals definitions and data
SIGNAL_DEF signals[NUM_SIGNALS] = {
  {3, {3, 0}, 0, 1, -1, SIGNAL_OFF,{0, ALIGN_DIVERGENT}, 1,{2}, 1 },
  {3, {3, 0}, 2, 3, -1, SIGNAL_OFF,{0, ALIGN_MAIN}, 1, {2}, 1 },
  {7, {1, 0}, 0, 1,  2, SIGNAL_OFF,{}, 0, {0}, 1},
  {7, {0, 0}, 0, 1,  2, SIGNAL_OFF,{0, ALIGN_DIVERGENT}, 1, {}, 0 },
  {3, {0, 0}, 3, 4, -1, SIGNAL_OFF,{0, ALIGN_MAIN}, 1,{2}, 1}
};

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.

void refreshSignals() {
  // First pass, set Stop (RED) state; default is GREEN
  // from block occupancy or turnout states
  for(int i = 0; i < NUM_SIGNALS; i++){ // for each signal
    // default state
    int state = SIGNAL_GREEN;
    SIGNAL_DEF sig = signals[i];
    for(int j = 0; j < max(sig.numTurnouts, sig.numBlocks); j++){ 
      if(j < sig.numTurnouts){
        // if the turnout is in motion OR 
        // if turnout alignment does not equal the required alignment
        if(turnout[sig.turnouts[j].id].is_moving || 
            turnout[sig.turnouts[j].id].alignment != sig.turnouts[j].align){
         state = SIGNAL_RED;
        } 
      }
      if(j < sig.numBlocks){ // for each linked block in the SIGNAL_DEF
        if(blocks[sig.blocks[j]].occ){ // if occupied
          state = SIGNAL_RED;
        }
      }
    }
    setSignalBits(i, state);
  }
  
  // Second pass to set caution states on
  // signals that support it and are currently set to GREEN
  
  for(int i = 0; i < NUM_SIGNALS; i++){ // for each signal
    SIGNAL_DEF sig = signals[i];
    if(bitRead(sig.type, 2)){ // if the signal supports the caution state
      if(sig.numFollowing > 0 && sig.state == SIGNAL_GREEN){
        // check occupancy of following block(s) if any
        for(int j = 0; j < sig.numFollowing; j++){
          if(blocks[sig.following[j]].occ){
            setSignalBits(i, SIGNAL_YELLOW);
          }
        }
      }
    }
  }
  // Refresh the nodes to show signals in their updated state
  nodeRefresh();
}

void setSignalBits(int signalID, byte signalState) {
  SIGNAL_DEF sig = signals[signalID];
  if (sig.state != signalState) {
    signals[signalID].state = signalState;
    byte nodeBits = nodeGet(sig.addr);
    switch (signalState) {
      case SIGNAL_OFF:
        if(bitRead(sig.type, 0)) bitWrite(nodeBits, sig.R_ID, LOW);
        if(bitRead(sig.type, 1)) bitWrite(nodeBits, sig.G_ID, LOW);
        if(bitRead(sig.type, 2)) bitWrite(nodeBits, sig.Y_ID, LOW);
        break;
      case SIGNAL_RED:
        if(bitRead(sig.type, 0)) bitWrite(nodeBits, sig.R_ID, HIGH);
        if(bitRead(sig.type, 1)) bitWrite(nodeBits, sig.G_ID, LOW);
        if(bitRead(sig.type, 2)) bitWrite(nodeBits, sig.Y_ID, LOW);
        break;
      case SIGNAL_GREEN:
        if(bitRead(sig.type, 0)) bitWrite(nodeBits, sig.R_ID, LOW);
        if(bitRead(sig.type, 1)) bitWrite(nodeBits, sig.G_ID, HIGH);
        if(bitRead(sig.type, 2)) bitWrite(nodeBits, sig.Y_ID, LOW);
        break;
      case SIGNAL_YELLOW:
        if(bitRead(sig.type, 0)) bitWrite(nodeBits, sig.R_ID, LOW);
        if(bitRead(sig.type, 1)) bitWrite(nodeBits, sig.G_ID, LOW);
        if(bitRead(sig.type, 2)) bitWrite(nodeBits, sig.Y_ID, HIGH);
        break;
    }
    nodeSet(sig.addr, nodeBits);
  }
  return;
}

void setSignal(int signalID, byte signalState) {
  setSignalBits(signalID, signalState);
  nodeRefresh();
}

For more in-depth discussion of my Duino Node devices, node functions and how they are used,  see Adding Signals to the Test Loop and Adding Signals to the Test Loop Part 2.

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  drive the hardware.

How to Integrate Signals into Your Small Layout

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 “abstraction” that doesn’t know how to change signal display, but relies on other hardware specific functions to do that job.

Tomar Signals

Tomar N Scale Signals

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.  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 Absolute Permissive Block 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.

You’ll be amazed that how much work a single UNO can actually do for you.

Adding Signals to the Test Loop, Part 2

Using shift register/darlington driver nodes for attaching lighting (and other devices) to the layout is easily justified by the conservation of precious Arduino pins. But that’s not necessarily the most important benefit of this approach.

Duino node for station lighting.

Duino nodes for station signal and lighting on the Test Loop.

The principal advantage of organizing lighting control into chains of shift registers—with or without Darlington Drivers to sink instead of source current (at this level it doesn’t matter what the shift register is attached to)—is to create an addressable architecture for lighting and animation.

What do I mean?

An addressable architecture is a system where individual objects can be identified by an “address” that provides sufficient information, in proper context, to allow the object to be accessed (my definition). An “address” should be a direct access mechanism and should not require conversion to a different form in order to use it. However, an address does not necessarily have to be complete if the context within which it is used has “knowledge” of the missing address information.

By that definition a pin number is a valid address. But, since we want to control more objects that we have pins, we need to think on a larger scale.

Arrays

Going beyond pin numbers, an address could be an offset (index) into an array. This works for objects attached to a shift register because you need to have memory set aside to represent state of the shift register; the values in memory are used to generate the stream of bits that sets the shift register state.

An 8-bit shift register could be represented in memory as an array of 8 bytes, either as plain 8-bit values or as boolean values (boolean is the better idea), occupying one byte for each bit on the register:

byte MyRegister[8];
or
bool MyRegister[8];

Having done that, every object on the shift register can be referred to by its position in the MyRegister array, a number from 0 to 7.  When you want to know or change the state of a particular bit on the shift register, you access the bit via its array index, making the index an address.

What about the Pins?

I am cheating in a sense. I’m relying on the fact that the first shift register in a chain has to be attached to three pins, you need to know which pin is which, and we can embed that information in the method that pushes bits out to the shift register. With that housekeeping squared away, all we need to do to turn shift register outputs on or off is maintain the array representing the shift register in memory, using the array indices as addresses for specific objects.

Here is an example method (derived from the Arduino Shift-Out tutorials, and used in the Roundhouse to run stall lamps) that writes to shift registers, using a static byte array to hold the register state information:

const int clockPIN = 3;
const int latchPIN = 4;
const int dataPIN = 5;
void registerWrite(int output, int state) {
  static byte outputStates[8];
  byte bitsToSend = 0;

  // update the outputStates array
  outputStates[output] = state;
  
 // Set bits according to the outputStates Array
 for(int i = 0; i < 8; i++){
     bitWrite(bitsToSend, i, outputStates[i]);
 } 
 // turn off the output while shifting bits
 digitalWrite(latchPIN, LOW);
 // shift the bits out
 shiftOut(dataPIN, clockPIN, MSBFIRST, bitsToSend);
 // turn on the output
 digitalWrite(latchPIN, HIGH);
}

This is what I mean by proper context. Here the context is a known “base address” for the shift register that never changes: the LatchPin, ClockPin and DataPin used to access it. Once context is established with a method that knows the “base address” and writes to the shift register, we no longer need to worry about that aspect of the problem and can supply the unique part of the address — the position of the output we wish to change — as an argument to the registerWrite method. In this instance, the address supplied to the method should be considered a relative address, because it is relative to the static portion of the address stored in the method code.

This simple approach works great for a shift register or two, but gets unwieldy when trying to manage the state of multiple shift register outputs with a single array. Further, memory is a precious resource and using a byte of memory to represent the state of a single bit on a shift register is inefficient at best. For a layout with hundreds of controllable lights for signals, crossings, street lights, building lighting and whatever else you can think of, something more sophisticated is called for.

The Data

So lets start with a better way to hold the values of the 8 bits of a shift register; I call shift registers “nodes”:

typedef struct nodeState {
  byte pins;
};

The 8 bits constituting a single byte of memory represent the 8 bits of a node. This is a big improvement in both memory efficiency and execution efficiency, since the pins byte can be sent directly to the target register without any processing.

Next we need a type to hold all information relevant to a chain of nodes:

typedef struct nodeGroup {
  byte clockPin;
  byte latchPin;
  byte dataPin;
  byte numNodes;
  nodeState *nodes;
};

Now we have all the data we need to handle a chain of shift register nodes in one place. Notice how the nodeState type is used. Once instantiated,  the *nodes element will point to an array of bytes, one for each node.

Notice something else: the door is now open to run multiple, distinct chains of nodes easily by creating a nodeGroup data instance for each chain. This confers even more freedom to structure resources for the convenience and logic of the sketch.

What’s The Address?

So that is how the nodes are attached and accessed. Now we’re ready for an address form that will work with this architecture:

typedef struct nodeAddress { 
  byte chain;
  byte node;
  byte pin;
};

With all node data organized into nodeGroup structures, the nodeAddress type provides what amounts to an absolute address to any resource defined in this way. An absolute address contains all necessary parts of an address, and is processed by methods without having any hidden address elements embedded in the method code.

Station and Signal 5

Practical Application

The test loop has one chain of four nodes.  For that sketch, a single node group is instantiated this way:

nodeState ndata0[] = {0,0,0,0};
nodeGroup nodeGroups[] = {{7, 6, 5, 4, ndata0}};

Here I’ve defined an array of nodeGroups with one member. The node group uses pins 5, 6 and 7 on the Uno, and has four member nodes. An array of nodeState structures, one for each node, is defined as ndata0[], and incorporated into the nodeGroup via the ndata0 pointer.

Signals on the test loop are defined by a different data structure that includes this element

nodeAddress addr;

capturing the base address of a particular signal. When the sketch is ready to set the state of a signal, it passes the nodeAddress to the node setting method, like this:

nodeSet(signal.addr, nodeBits);

All manipulation of the nodes can be accomplished with 4 methods:

void nodeWrite(struct nodeAddress addr, byte state) {
  // write the state to the pin bit defined by nodeAddress addr
  bitWrite(nodeGroups[addr.chain].nodes[addr.node].pins, addr.pin, state);
  nodeRefresh();
}
void nodeSet(struct nodeAddress addr, byte state) {
  // set the pins element with a byte value
  nodeGroups[addr.chain].nodes[addr.node].pins = state;
  nodeRefresh();
}
byte nodeGet(struct nodeAddress addr){
  // get the pins element for a node
  return nodeGroups[addr.chain].nodes[addr.node].pins;
}
void nodeRefresh(){
  // Shift out current node data, one node group at a time
  for(int i = (NODE_GROUPS - 1); i>=0; i--) {
    // shift all bits out in MSB order (last node first):
    // Prepare to shift by turning off the output
    digitalWrite(nodeGroups[i].latchPin, LOW);
    // for each node in the group
    for(int j = (nodeGroups[i].numNodes - 1); j>=0; j--) {
      shiftOut(nodeGroups[i].dataPin, nodeGroups[i].clockPin, MSBFIRST, nodeGroups[i].nodes[j].pins);
    }
    // turn on the output to activate
    digitalWrite(nodeGroups[i].latchPin, HIGH);
  }
}

The nodeSet method used by the signal system—primarily because a signal requires an output for each aspect (red, green, yellow)—sets the pins byte for a random node in one operation.

NodeWrite sets a single output bit on any random node.

NodeGet retrieves the current pin byte for a random node. The signal system uses this method to retrieve the bits on a signal node, before modifying the values and applying them with nodeSet.

NodeRefresh writes the pins bytes to the nodes.

There is, of course, more going on with the signals on the test loop. Deciding what aspect a signal should show at any given time is a matter of logic and data. In the next post in this series I’ll do a deep dive into my first take on making signals operational within an Arduino environment.

Until then, happy railroading!

Adding Signals to the Test Loop

Tomar Signals

Tomar Signals

With block occupancy detection running on the test loop, it was time  to start experimenting with signals. Automated signaling is an important part of the plan for the L&NC.

Signaling potentially involves a great deal of wiring. It isn’t particularly difficult when done the Arduino way, but it does force you to deal with common capacity issues that come into play whenever you want to control a lot of lights or other kinds of devices.

I think of signals as the simplest form of animation a layout can and should have. Creating a signaling system is a good starting point for developing more complex automation and animation for the layout.

Initially, I bought a couple of Tomar Industries n scale signals to try out something off-the-shelf. These particular ones are nicely made and fully wired with red and green LEDS. A three aspect (red, yellow, green) version is also available.

With two outputs required to power each signal, it became clear that directly powering an entire signal system from a microcontroller would be impractical because there aren’t enough pins to work with. Further, The Tomar signals turned out be wired in the common anode configuration, which had additional implications. Common anode wiring is not unusual in off-the-shelf accessories.

Current Limitations

Arduino boards have design limitations when it comes to powering and controlling external devices. The amount of current an Arduino can source or sink on any one pin is limited to 40 mA, enough to light a couple of LEDS. The ground pins can sink a maximum of 400 mA. Accordingly, an UNO could power a maximum of 10 pins with no more than two 20 mA LEDS on each. The I/O pins cannot handle enough current to run most solenoids, relays or motors.

Common Anode Devices

Common anode devices present further problems because Arduinos are common ground devices. Even through the IO pins can sink current, you can’t switch them into a ground state the way you can switch them into a high voltage state.

Most of us are used to common ground — or common cathode — wiring style to control multiple lights or other electronic objects with a minimum number of wires. There, the positive feeds are isolated from each other so individual devices can be controlled, but they all connect to a common ground.

Common anode wiring is the reverse, where multiple devices share a power feed, but their ground sides are isolated from each other for control. Where resistors are needed to control current, as with LEDS, they are put on the cathode side instead of the anode side.

Extending the Pins

Running signals and other animation to any significant extent requires a strategy to multiply the outputs an Arduino can control in a way that is broadly compatible with available devices. Fortunately, there are readily available components that can significantly extend the I/O and current handling capabilities of an Arduino Board and allow control of common anode devices.

Shift Registers

HC595 Shift Registers (I use the 74HC595 variant) are logic ICs offering a basic way to multiply the digital output pins the Arduino can control. Using just three digital pins, an Arduino can control 8 output pins on the shift register.  Better still multiple shift registers can be serially connected, without any additional connections to the microcontroller, to extend the number of outputs under control to span many shift registers. I’ve previously written about shift registers in the Roundhouse rebuild.

To control a shift register, a byte of memory is needed to represent and manage the state of the register’s 8 pins. Accordingly, the maximum number of outputs you can control in a single chain of shift registers is primarily limited by the amount of memory available for managing them.

Consider, though, that a 100 bytes of memory allows you to control 800 digital outputs using just three Arduino pins. Let that sink in for a minute.

Shift registers are serial input, parallel output devices; the Arduino writes the bits serially, then all connected registers output on all data pins at the same time when the write is finished. Shift register outputs maintain their state–on or off–indefinitely until changed.

Darlington Drivers

Another basic tool is a ULN2803APG Darlington Driver, an IC that can switch higher voltages and higher current than an Arduino or shift register can handle. These are current sinking transistor devices, using NPN Darlington pairs; the addition of internal clamping diodes allow them to handle inductive loads such as relays, solenoids and motor driver applications like running unipolar stepper motors. LEDS and incandescent lights work well in common anode configuration controlled through a darlington driver.

Darlingtons allow a logic system (the microcontroller and attached Shift Registers) to control a set of outputs that are powered independently. So, for example, a 5 volt logic system can switch a 12 volt load (up to 50 volt max).  Very useful, yes? For current up to 500 mA per channel, Darlington Drivers can be a viable alternative to expensive relays.

The Good

An HC595 Shift Register plus a Darlington Driver extends the number of digital outputs a single Arduino board can control, using a common anode wiring solution that can handle virtually any kind of layout animation or control device. Using using Shift Register/Darlington Driver pairs and going to common anode wiring as my standard makes all connection and access methods uniform across a variety of devices.

The Bad and the Ugly

In theory, you just connect the output of the shift register to the corresponding inputs of the darlington driver, connect the darlington outputs to your devices, and you are good to go. Trouble is, the chip designers didn’t get the memo on that! The chips are a little difficult to interconnect: you can’t place the chips side by side and have respective outputs and inputs facing each other in the correct order. In fact, the HC595 connections are arranged a little oddly, requiring at least some connections between it and the darlington to cross over each other no matter what.

My first try was the roundhouse controller board. My problem then was trying to run some low voltage incandescent bulbs from 3.3 volt power. The darlington driver allowed me to do that with the 5 volt shift register.

Roundhouse Controller with an Uno

Roundhouse Controller with an Uno

See all those green jumper wires? That was my first solution to the interconnect problem on a single layer board; it works but it is awkward, bulky and hard to replicate. The additional interface for the addressable LEDS further complicates the board.

For signals on the test loop, I decided to try to shrink the board size, incorporate the necessary resistors (they are on a separate board in the roundhouse) and take a two sided approach using my favorite two sided prototyping boards.

Node Prototype 2

Node Prototype 2

As I was planning to record the block occupancy demo, I decided I needed more signals on the test loop to show the block occupancy system in action. I still had four available outputs on the board running the two Tomar signals– except those could not help me with a signal on the other side of the layout [the signals all have short lengths of thin magnet wire].

The HC595 can connect to another shift register serially, but I had not provisioned that on the boards built so far. It takes three connections to link to the next shift register: the two shared timing lines and the data output from the shift register.

Time to build board #3.

Node Prototype 3

Node Prototype 3

The board works fine. But creating these boards with prototyping materials is painful because of the number of jumpers to route and connections to make. As useful as these were going to be, it was going to take a long time to churn out a lot of them accurately. On the L&NC I expect to be controlling hundreds of lights and other devices around the layout. There had to be a better way.

Duino Nodes, Type A, Version 1

I was using fritzing, my favorite circuit design tool, to work on the circuit design for the next board and it became obvious that what I really needed to do was have a printed circuit board produced to my specs. Since I would need several boards for the test loop alone, and I was only just getting going, a small trial run of boards made sense.

I’m glad I did.  Here is the first version of what I call a Type A Duino Node.

Duino Node Type A

Duino Node Type A

These are double sided pc boards, pre-drilled and ready for components, with copper ground fill on both sides for emf control. It takes about 10 minutes to assemble a board for use. The prototypes took over an hour each to build, so the practical time savings for the dozens I’ll need on the L&NC is substantial.

They are 88 mm x 52mm; about 30% bigger than the smallest prototype I built. There are a couple of reasons for that: first, the board has Arduino-style mounting holes arranged to facilitate board stacking. Second, since these boards will run lights and other devices with varying current limits. generous space is available to mount an appropriate resistor for each cathode. A jumper can be used if a resistor is not needed, as might be the case when running a relay, motor or solenoid through a position.

On the right side are three control connectors: power, serial in and and serial out. When stacked, only short three wire jumpers are needed to chain from one board to the next. A terminal block is mounted on the opposite, output side of the board. There are nine positions: the common anode (power) plus eight switchable cathodes.

Deployment

There are four boards deployed on the test loop. One is dedicated to the two Tomar signals above.

Another board runs the dual signals below. The top searchlight is three aspect (red, yellow, green) and the bottom is two aspect (red, green), requiring a total of 5 outputs.

Dual Searchlight Signals, Scratch Made with BLMA Signal Heads.

Dual Searchlight Signals, Scratch Made with BLMA Signal Heads.

The last two Duino Nodes are used together for the station / signal complex.

The Station, and Signal #5

The Station, and Signal #5

The signal is three aspect (red, green, yellow). The station has a total of 8 individually switchable LEDS for internal lighting.  You may notice that the two sections of the depot have distinctly different lighting: the passenger station side has warmer, incandescent lighting (achieved with a mix of led colors); and the freight side has a cooler fluorescent lighting style (cool white LEDS).

One node is for the station signal that connects via four strands of magnet wire.

Duino Node controlling Station Signal

Duino Node controlling Station Signal

The second node mounts on top of the first, and it controls the Station internal lighting. The bottom node receives the incoming serial connection from elsewhere; then a cable connects the bottom serial out with the top serial in. There is no limit to the number of boards that can be chained and/or stacked together.

The Duino node for station lighting is stacked on top another node that runs the signal.

The Duino node for station lighting is stacked on top another node that runs the signal.

Here is a view of the test loop from below with all the Duino nodes in place.

Test loop from below.

Test loop from below.

It may be hard to tell, but the overall effect of using the Duino nodes is to simplify and compartmentalize wiring.

Using the Nodes

The real advantage of this system comes from adopting a uniform addressing system that makes it easy to randomly access and change any element on any node at any time.  I’ll go into that in the next post.