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.

 

C++ Objects for Layout Control, Part 2 — Turnouts

In C++ Objects for Layout Control, Part 1 I did an introduction to C++ objects, and demonstrated the basics of OOP with a simple “fire” object that I use to run an LED to simulate a fire in the turntable operator’s hut on the L&NC. In the demo sketch I did two instances of the fire object with two LED’s to demonstrate object independence and scalability.

In this post, we’ll take the basics of C++ objects for animation and extend them to the most common animated object on our layouts: turnouts.

L&NC Progress

Before I go hardcore programmer on you, I thought’ I’d share a few progress notes & pictures. Last weekend we rejoined the three lower level modules together for the first time in over a year.

All Three lower level sections reassembled.

Crossing from module 1 to module 2 for the first time.

Red Bluffs Yard

Another view of Modules ! & 2

I was especially pleased that the three sections came back together perfectly, even though they have been apart for over a year. I attribute the stability of the structures and the accuracy of the alignment to the style of framing, use of hardwoods and the McMaster-Carr alignment pins I use to assure the sections align correctly when put back to together. It all looked good on paper; but until proven you can’t be completely sure. Now I’m confident I can take the layout apart and put it back together again reliably.

I’m getting the basic wiring and interconnections with modules 2 & 3 completed, following the same basic methods as on module 1 (the main difference from where I began is I’ve moved to current transformers for occupancy detection). Once the basic wiring is in and the turnouts are mated with servos, I’ll start in with scenicking. I’ll cover and further shape the mountain with plaster cloth and sculptamold. Since I am doing more roads (using the Woodland Scenics road system which I think works pretty well; more about that at the end of this previous post) on both modules 2 & 3, I’ll pour those first. I find it best to get the roads in before applying any paint or other material. All that should keep me busy for a while.

Turnout Issues & Requirements

Like the fire object, the turnout object requires certain basic parameters to run, and has to be able to keep track of its own state. The turnout object has to respond to positioning commands and manage the turnouts’ motion to achieve a slow, scale appropriate movement from one alignment to the other.

The added complication is that the turnout class will need to deal with different hardware configurations and interfaces in different situations. For example, on the L&NC module I’ve been working on there are 9 turnouts and I’m using the Adafruit PWM Driver to run the servos and additional PWM devices, which I discussed in L&NC Update: Running Lots of Turnouts. The adjacent module has only two turnouts, so I’ll use the standard servo library and a couple of pins there. But regardless of the hardware interface, I want to use the same software objects in both places so they perform the same way.

Properties

The turnout class is a little more complicated than the fire class. Its basic properties include the pin (in the case of the Adafruit driver, the channel) the servo is attached to, the current alignment of the turnout, the default alignment and the servo settings for the main and divergent positions (pos_main and pos_divergent). Then, to facilitate motion, there are a variety of properties that work together: delay between moves, the increment (1 or more units) of each move, the current position, the target position and the target alignment.

class turnout
{
private:
 // object properties
 int pin;
 int pos_main;
 int pos_div;
 int align_default;
 int alignment;
 
 // motion data
 bool is_moving;
 int move_delay;
 int increment;
 int pos_now;
 int target_pos;
 int target_alignment;
 unsigned long last_move;
}

I mentioned that I am using the turnout class in different situations where the hardware that runs the servos could be a standard pin, or an external driver board such as the Adafruit PWM Driver. To complicate matters, while the standard Servo Library positions by degrees, the Adafruit PWM Driver positions by the “tick” value of the desired PWM setting.  These are different units and different scales; but it doesn’t matter. So long the turnout class is initialized with a correct set of values (in the same units) for the hardware interface in question, it works consistently.

Methods

For the turnout class I have created an interface of public methods for interacting with the class. Additionally, the class has a private hardware interface for interacting with the hardware environment.

The Constructor

The constructor is straight forward with one twist. Because a lot of arguments are required to set up the object, I’m using a data structure to pass most of the arguments.

typedef struct TURNOUT_PARAMS {
 int pin;
 int pos_main;
 int pos_div;
 int align_default;
 int move_delay;
};

On the L&NC Module1, Lower Level the array of turnout parameters looks like this (STANDARD_DELAY is 20):

TURNOUT_PARAMS tdef[NUM_TURNOUTS] = {
 {0, 375, 310, ALIGN_MAIN, STANDARD_DELAY},
 {1, 335, 408, ALIGN_MAIN, STANDARD_DELAY},
 {2, 330, 370, ALIGN_MAIN, 30},
 {3, 284, 345, ALIGN_MAIN, STANDARD_DELAY},
 {4, 355, 415, ALIGN_MAIN, STANDARD_DELAY},
 {5, 291, 390, ALIGN_MAIN, STANDARD_DELAY},
 {6, 285, 373, ALIGN_MAIN, STANDARD_DELAY},
 {7, 355, 285, ALIGN_MAIN, STANDARD_DELAY},
 {8, 305, 372, ALIGN_MAIN, STANDARD_DELAY}
 };

The constructor takes a pointer to a TURNOUT_PARAMS variable, plus an optional movement_increment argument. I added the movement increment parameter (and the corresponding class property) as an additional factor in turnout motion after encountering problems integrating multiple processes on the Uno on Module 1. Manipulating both delay and increment values improves control over the movement of servos in different situations. On the L&NC Module 1, Lower Level there are both a lot of turnouts and a lot of block detectors. I’ll discuss the issues more in an upcoming post, but the resources required for block detection make it difficult to move the servos fast enough without adjusting the movement increment.

// Constructor
 turnout(TURNOUT_PARAMS *parameters, int movement_increment = 1){
   pin = parameters->pin;
   pos_main = parameters->pos_main;
   pos_div = parameters->pos_div;
   align_default = parameters->align_default;
   move_delay = parameters->move_delay;
   is_moving = false;
   increment = movement_increment;
   init_servo();
 }

After capturing the parameter values, the constructor invokes the private init_servo() method which I will talk about below in the hardware interface section.

Using the array of parameters above, the turnout objects are instantiated with this bit of code in setup, taking the default value for movement_increment (turnouts is global, declared before setup):

 turnout *turnouts[NUM_TURNOUTS];
 for(int i = 0; i < NUM_TURNOUTS; i++){
   turnouts[i] = new turnout(&tdef[i]);
 }

What’s all this Pointer Stuff?

If you’ve been working with Arduino for a while you’ve likely dealt with pointers before. For those who are unfamiliar with the term (and it is a complex subject, worth learning), pointers are the memory address of a variable. Pointers are another way to access variables unique to the C/C++ languages. In the case of the Constructor, passing a pointer to the parameters array grants access to a complex data structure without having to copy it first. The Reference (&) operator returns a pointer to an variable (&tdef[i]); the dereference (*) operator declares a pointer variable used to access a pointer’s value — such as TURNOUT_PARAMS *parameters in the Constructor. With pointers to data structures or objects, use “->” to access members instead of “.”; eg: params->data instead of params.data or object->method() instead of object.method().

The Public Interface

The public interface is comprised for four methods. The getAlignment() method is a good example of the correct way to share the value of an internal property with an external process. Its correct because it outputs the value of the alignment property without breaking its protection as a private property (and thus exposing it to being changed externally).

 int getAlignment(){
   return alignment;
 }

Another simple method provides a way to toggle the position of the turnout back and forth.

void toggle(){
   if(alignment == ALIGN_MAIN){
     set(ALIGN_DIVERGENT);
   } else {
     set(ALIGN_MAIN);
   }
 }

The alignment can be one of three values at any given time.

#define ALIGN_NONE 0
#define ALIGN_MAIN 1
#define ALIGN_DIVERGENT 2

As you’ll see, ALIGN_NONE indicates that the turnout is in motion.

The heart of the logic for moving the turnout is in the set() and update() methods.

void set(int align){
 if(align != alignment){
   is_moving = true;
   last_move = 0;
   target_alignment = align;
   alignment = ALIGN_NONE;
   switch(align){
     case ALIGN_MAIN:
       target_pos = pos_main;
       break;
     case ALIGN_DIVERGENT:
       target_pos = pos_div;
       break;
     }
   }
 }

 void update(unsigned long curMillis) {
   if(is_moving){ 
     if((curMillis - last_move) >= move_delay){
       last_move = curMillis;
       if (pos_now < target_pos) { // if the new position is higher
         pos_now = min(pos_now + increment, target_pos);
         setServo(pos_now);
       } else { // otherwise the new position is equal or lower
         if (pos_now != target_pos) { // not already at destination
           pos_now = max(pos_now - increment, target_pos);
           setServo(pos_now);
         }
       }
       if (pos_now == target_pos) {
          is_moving = false;
          last_move = 0;
          alignment = target_alignment;
       }
     }
   }
 }

Motion works by setting motion variables in the set() method then calling update() repeatedly to execute the motion. Update() is intended to be called continually as part of the main loop() while the sketch is running—my multitasking model is to get the current time (millis()) at the start of every iteration of the main loop(), then pass that value to every object that uses time to manage its own state. It is very important that the very first logic test within the update() method is whether or not the turnout is in motion; if not in motion the method exits immediately. With nine turnouts on the lower lever of module 1, efficiency is necessary or the sketch bogs down.

Hardware Interface

Up to this point there has been no direct interaction with hardware. Instead there have been calls to two private methods: init_servo() and setServo(). These two private methods interact directly with the servo hardware.

I should emphasize that any motor type can be used with appropriate connections, even 12 volt stall motors. I like servos because they are cheap and easily supported in the Arduino world. But don’t feel that just because you have a different motor type you can’t run them with an Arduino. The point of creating a private, protected hardware interface is to isolate all the hardware specific stuff in one place while presenting a more general public interface. That makes it much easier to drive a wide variety of hardware while behaving consistently.

private:
 void init_servo(){
   int data;
   switch(align_default){
     case ALIGN_MAIN:
     data = pos_main;
     break;
   case ALIGN_DIVERGENT:
     data = pos_div;
     break;
   }
   setServo(data);
   is_moving = false;
   pos_now = data;
   alignment = align_default;
 }
 // hardware interface 
 void setServo(int data){
   // use compiler directives to determine which
   // method is used to drive the actual hardware
   #ifdef ADAF_DRIVER
   extern Adafruit_PWMServoDriver pwm;
   pwm.setPWM(pin, 0, data);
   #endif
   #ifdef SERVO_LIB
   extern servo servos;
   servos[pin]->write(data);
   #endif
 }

Init_servo() is for doing whatever your motor needs to set up. Here the method sets the default alignment and commands the servo to move to that position immediately. This is private so that it can never be called from outside a turnout object.

setServo() moves the servo to a specific position. I use a compiler directive — by defining either ADAF_DRIVER or SERVO_LIB (but not both)— to determine which hardware system is in use. In some cases I’ll be using the Adafruit 16 Channel PWM Driver and its library; in other cases regular pins with the native servo library.

The Whole Enchilada

I’m posting the turnout class on the github site.

This time I’m posting it in the form of a header — *.h — file. To use it, copy it into your sketch directory. The Arduino IDE will recognize and allow you to edit the “h” file, but it won’t automatically include it in your build.  To in include it in the build you must explicitly include it near the top of your main sketch this way:

#include "turnout.h"

Why do it this way? Because the class definition has to be seen by the compiler before it can be used to create run-time objects. Therefore, in a single file sketch you’d have to put the class definition at the top of the sketch. If you have a lot of header material of that sort (class definitions, typdefs, etc.), the top of your sketch can get long and the whole thing harder to maintain.  By putting the class definition in a header file you can segregate different elements of your sketch, control exactly when the compiler sees it during the build process, ensure all dependencies are satisfied and keep your main sketch file clean and uncluttered. The bigger your sketch gets the more important this becomes.

Coming Soon

Back to block occupancy detection with 24 blocks and one Arduino to rule them all! To say scale started to be a problem would be an understatement. More about that in the next post.

Until then, Happy Railroading!

 

 

Wiring the L&NC — Adding Servos

This it the second installment in a series (Part 1) about the build out of Module 1, lower level of the L&NC. This series covers all the basic steps I’m following to install all wiring, electronics and mechanical objects throughout the layout, so in subsequent phases of this project I can focus on the unique aspects of other modules. Step 1 was to install the basic wiring trunks and connection points, create track power distribution nodes then connect track feeders to the nodes.

Step 2

I tested the track with one of my most challenging locomotives—a Broadway Limited EMD E8A DCC with Sound. I say “challenging” because it has two 3-axle trucks, a long wheelbase and no big capacitors to buffer track power, making it susceptible to derailment or operational problems with faulty track.  If this loco can run a stretch of track problem-free, it’s good track!  Naturally I found and fixed a few (cough …. ) places where there were problems.

Correcting track problems for flawless running.

Correcting track problems for flawless running.

After tinkering with the problem zones, I realized that the “course of least resistance” was to rip out and re-lay a few short strips of track. The caulk adhesive track laying method makes this a piece of cake: after cutting the rails, run a long bladed knife under the track section you want to remove and its free in seconds. The ease of fixes really adds incentive to be fussy at this stage and get it right. I had everything in good order in a couple of hours including drying time for fresh caulk adhesive.

Step 3

The next step is to mount the servos running the 9 turnouts on this level. Installing them now is the best way to ensure they have the space they need for normal operation and maintenance.

test loop servo 1 in place

Servo mount on the Test Loop.

I’ve previously done a basic demo of the mounting method I used on the Test Loop. I chose that particular mounting method because it simplified the connection between the servo and the rod connected to the turnout; the rod fits easily but snugly through the hole in the horn. In this mounting method the base plate of the mount provides the fulcrum or pivot point for the rod.

Turnout operation on the Test Loop continues to be 100% reliable. The only issue with that mounting method is that noise is transmitted to the layout through the mount, so servo operation is noisier than it should be.

A New Low Noise, Low Profile Servo Mount

In addition to noise control, the equipment space beneath the layout level is about 1 3/4 deep, the width of a 1″ x 2″ frame member. For both protection and aesthetics, I need all equipment to fit inside that space. The old servo mounting method requires more space than that, and would stick out below the edge of the frame.

The solution is to mount the servo on its side, allowing the horn to rock a rod back and forth setting the turnout points. Several obvious ways to do that came to mind.  But I also wanted to make sure that alignment of the servo is easy and foolproof.

Layout Prep

I drilled holes for the turnout rods when I laid the track. To make installation and alignment of the servo easy, I  drilled the holes so that the rod would be in the 90 degree position (perpendicular to the plywood base) with the points aligned one direction or the other. I tried to keep the holes through the plywood small to serve as fulcrums  (mostly succeeded), then widened the holes in the foam and roadbed beneath the track so the rod can swing between the two positions of the points.

I inserted a 3″ rod cut from 1/16″ music wire (you need a hard wire cutter for this stuff) and made sure the positioning of the rod was correct. The advantage of the music wire is that it can flex without deforming, allowing you to apply pressure to the points.

Fulcrum pad for turnout 5

Fulcrum pad for turnout 5

That said, 1/16″ music wire I’m using is fairly stiff in the short lengths needed here. It is stiffer than the wire typically use with stall motor turnouts, stiff enough that it easily overcomes resistance from the built in springs in the Peco turnouts I’m using on this layout. Many people recommend removing the positioning springs in Peco and similar turnouts, since they can cause turnout movement to pause while overcoming resistance of the spring. Using 1/16″ music wire the servo is able to move a sprung turnout smoothly. I took a few springs out before I realized it was completely unnecessary.

 

At this point I made sure the rods could move the points properly.  In a few cases, the fulcrum hole was a little too large because of sloppy drilling; the easy solution is to fit a plywood plate with a fulcrum hole in the right diameter over the old one.

Servo Prep

Preparing the servo requires testing and setting it to the 90 degree position. Then with the case on its side, orient the output shaft to either the right or left (which ever you need for a given situation) and install a standard single arm horn pointing up, perpendicular to the case.

The offset shaft allows you to select the right orientation. In either case, 0 degrees is 1/4 turn to the right of center, 180 degrees is 1/4 turn left of center.

The offset shaft allows you to select the right orientation. In either case, 0 degrees is 1/4 turn to the right of center, 180 degrees is 1/4 turn left of center.

Here’s a sketch to test the servo by running it from 0 to 180 degrees (the travel of a typical micro servo), then to the required mid-point position:

#include <Servo.h> 
 
Servo myservo;  // global servo object
int midpoint = 90;  // in degrees
int pin = 6; // control pin

void setup() 
{ 
  int i;
  myservo.attach(pin);
  myservo.write(0);
  delay(1000);
  for(i = 1; i <= 180; i++){
    myservo.write(i);
    delay(50);
    }
  delay(1000);
  for(i = 180; i > midpoint; i--){
    myservo.write(i);
    delay(50);
    }
  delay(1000);
  myservo.write(midpoint);
} 
 
void loop() 
{ 
  
}

Mr. Hot Glue Strikes Again

If only micro servos came with side mounting tabs instead of just the ones on top. They do not. To do a side mount like I’m doing you need only fabricate two parts: 1) a strip of .080″ styrene, cut to about 2″ x .5″ and predrilled with holes at each end to accommodate mounting screws; and 2) a piece of 1/32″ brass wire with a loop (a little over 1/16″ inside diameter), a short straight section (about equal to the thickness of a servo horn) leading to a 90 degree bend and a longer straight section.

These two parts allow you to side mount a micro servo, and connect its horn to a rod.

These two parts allow you to side mount a micro servo, and connect its horn to a rod.

First I remove any labels on the side of the servo that attaches to the mount, then I put a dab of hot glue on the servo and press the styrene strip against it, centering and aligning the strip with the built in fins. Then I put a bead of hot glue down each side of the servo where it joins the mounting strip. Its probably overkill, but I want the servos mounted solidly and resistant to torsional stress.

Micro servo glued to a side mounting strip.

Micro servo glued to a side mounting strip.

The brass wire is threaded through the top hole of the horn, with the long leg aligned along the length of the back side of the horn and the loop parallel to the base. Apply dabs of hot glue to adhere the wire to the horn.

Here you can see how the brass wire is glued to the horn, and the turnout rod is threaded through the loop.

Here you can see how the brass wire is threaded through the top hole glued to the horn, and the turnout rod is threaded through the loop. Note the clearance between the rod and the horn.

Installation

Here a servo has been aligned to the motion of the rod, marked on the plywood.

Here a servo has been aligned to the motion of the rod, marked on the plywood.

First it is necessary to determine the plane along which each rod moves; that will depend on the angle of the turnout relative to the rest of the layout.

With the plane of motion marked and the rod set to its 90 degree position, I slip the rod through the loop glued to the horn and place the servo next to the rod, parallel to the plane of the rod. Placing just a little tension on the rod and maintaining even clearance between the horn and rod, I mark and drill mounting holes for the servo. Sometimes its easiest to do one mounting hole, attach the servo at that hole then—after adjusting positioning—drilling the second hole and completing the mount.

After a test fitting, I remove the servo and apply a strip of 3/4″ Rubber Splicing Electrical Tape (Scotch #2242) to the bottom of the mount to inhibit noise transmission. I remount the servo in its final position.

Servos 1, 2 and 9 mount in their final positions.

Servos 1, 2 and 9 in their final positions.

Problems at Turnout 4

The location for turnout 4's servo.

The location for turnout 4’s servo.

Turnout 4’s rod comes down at an awkward spot, close to a frame cross member, the edge of the layout, the main wiring bundle and three feeder sets. The feeders are the main problem; I should have located them further from the turnout. While moving the feeders is an option, I also realized that the fulcrum hole was too large so I was going to have to put in a new fulcrum plate anyway.

The solution I came up with was to fabricate a mounting plate from a couple of pieces of scrap plywood, that would provide a new fulcrum and cantilever over the feeders. Everything screws down so that it is removable and repairable.

Mounting solution for Turnout 4.

Mounting solution for Turnout 4.

 

Turnout 4 Servo Mounted. The horn swings UP in this photo, so the wire bundle below the servo does not interfere.

Turnout 4 Servo Mounted. The horn swings UP to change the position of the points (as oriented in this photo), so the wire bundle below the servo does not interfere. Its snug but effective.

Gathering Servo Positioning Data

At this point it makes sense to test each servo and determine the positions for each point setting.  Each servo installation is different so each one will have unique settings for turnout positions. The size or “looseness” of the fulcrum hole and the length of the rod are the main factors affect servo positioning

On this module and level it takes approximately 20 – 30 degrees movement of the servo to change the points. Once I determined that, it was easy to calculate initial positions that could then be fine tuned for individual installations.

I'm using CadRail's layers to record information. Here I've recorded feeder positions (in red) and turnout servo positioning data.

I’m using CadRail’s layers to record information. Here I’ve recorded feeder positions (in red; turntable area not yet built) and turnout servo positioning data (green). The two positions are “S”, straight or Mainline; and “D”, divergent.

The goal is to have the points firmly pressed against the rails at each end of their travel, without making the servo work so hard it gets noisy. A light hum while the servo is holding a position is OK; but it should not become a loud buzz and the servo should not feel “buzzy” to the touch. Try moving the turnout manually – you should get resistance to moving the points against the servo, but the flex of the music wire should still be evident. Tinker with this for a while and you’ll start to get a feel for it.

Next step is a big one: install the turntable mechanism, install the Roundhouse base, lay track, and so on. Until then, happy railroading!