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!

17 thoughts on “L&NC Update; Running Lots of Turnouts”

  1. Robin.my friend Dave and i are trying to make a stand alone signaling system using arduino and digital IR sensors for block detection and bicolor leds for signal aspects. We have created a sketch
    But are having a hard time keeping the first block active until we clear the second block detector so we can run shorter trains or just an engine between blocks if you send me your email I will send a copy of our sketch and more detail. We are using PWM for IO pins for red brilliance and using bicolor leds for red,green and yellow aspects. Any help will be greatly appreciated.

    Thank you

    Bob Bijeau

    • Hello Robin and Bob,

      I have a demo section using reflective IR sensors one at each end of the block when it clears it goes to green and vice versa. Robin has also helped with using current sensors to keep the block signals active as well. I am sure he will help you on your way as he did me. =-)

  2. Hello, how are you controlling your Turntable?
    I like your drive system.
    I have a Stepper motor.
    Thank you.

    • Hello Jay,

      I’m using the Sparkfun Easy Driver to run the stepper. I’m circling back to that soon and hope to have a blog post with more information, pictures and so on, later this month.

      Best,
      Robin

  3. Hi Rob,
    Following my earlier questions/comments about jerky servos, I am now thinking of PCA9685, as a possible solution. I realize higher resolution means smoother running of servos, but what about sound ? do you see (hear) less noise ?
    Smoother running is good, but sound is a real issue, I think.
    (By the way, cutting power and pulse cables shorter removed my problem with servos going crazy)

    Also, on using this for other things than servos; I need only 5 servos as of now, so there would be capability for more stuff. You described a car with both headlights and rear lights, using two outputs; do I get it right that these two outputs power the actual LED:s directly(with resistors), meaning LED:s with a totally controllable brightness ?
    Best Regards
    Peter

    • Hi Peter,

      The SG90 servos are cheaply made and prone to growling, under load. I would toss any servo that growls when tested with no load. With turnouts, we like to apply some pressure to keep the points in contact with the rails, leading to noise generated by the servo pushing against resistance. Best way to avoid noise is to keep the gauge of the actuator wire thin, so that it bends and stores the energy instead of fighting the servo. A friend produces mounted servos with the right gauge actuator wires: https://www.smarthobbyllc.com/

      You definitely get better resolution with a PCA9695 card — 500 steps across a 180 arc of motion. That helps with smoothness of motion and incremental noise.

      As to the LEDS, yes. Attach the positive lead to the data pin, ground to grd, and ignore the V++ pin required for servos. Put a 270 ohm or greater resistor on either side. Use the PWM level to control brightness, or to create special effects. Play with that a bit… you’ll be pleased.

      R

  4. Hi Rob,
    I have another question, and you may have posted something about this, but I can’t find it;
    I am installing your sketch, specifically the demo sketch for 5 turnouts and shiftregisters. This works fine, but I would rather use switches instead of push buttons, so switches sets input pins to a constant VCC or GND.
    Would appreciate some help with that, or of course a pointer to that other answer I suspect exists.
    Best Regards
    Peter

    • Think of switches as buttons that stay pushed indefinitely. in the case of an SPDT switch. with power to center tap and each leg attached to a pin, its like two buttons that alternate (unless you have only one side live, in which case its a sticky button). So, you wire them like buttons (+5v power, with a pull-down resistor on every pin). The main difference from a logic point of view is that with switches you have to keep track of their state and respond only to state changes. With buttons you can usually get away with blindly responding to presses, but a held button will lead to duplicate actions. In fact, its best to handle all inputs on a state change basis for “bounce” free stability.

      I have an IO board system that I’ve been working on that makes both inputs and digital outputs easier. I’m in the process of bringing them to market … not ready to go fully public yet. If you are interested, let me know and I’ll send you information outside of the blog.

      Best, Rob

  5. Rob,
    I wrote my new question before I saw your reply about servos. Not sure how to post pictures here, but I use a small brass tube, positioned about 10 mm from the switch. in the tube is a pianowire, I think it’s 0,8 mm. Two 90 degrees bends create an actuator. Under the layout the wire can be bent in any suitable direction, and connected to a servo laying on its side. It has a standard servo arm pointing “down” and is simply mounted in a 90 degrees plastic angle(not sure if this is the right expression…). That relatively thin pianowire keeps the turnout tight, while seemingly not stressing the servo. The actuator idea, by the way, is the same as Swithmaster stalling motors use, works fine here too.
    Best Rgds /Peter

  6. Hi Rob,
    I am still installing servos, controlled by your fine coding, as stated before the sound and smoothness is not so fine, but, my PCA9685 should arrive any day now and that item should improve things. This, finally, leads to my question, coding. I’m a bit confused….
    What I use so far is the example with 5 turnouts and shiftregisters for LEDs.
    As I understand it all the coding concerning buttons and turnout movement can stay the same, except for those lines in the end
    #ifdef ADAF_DRIVER
    extern Adafruit_PWMServoDriver pwm;
    pwm.setPWM(pin, 0, data);
    The driver has to know about Arduino output pins and servo positions; so is this “simply” collected by the library and transferred in the I2C signal ?
    I do try to understand your coding, but this is quite complex, so some guidance would be most welcome.
    Best Rgds Peter

    • The standard Servo library uses regular pins. The PCA9685 has its own output “pins” and only interacts with the microcontroller through I2C serial communication. The PCA9685 knows how to use I2C; all it needs is the board’s I2C address to initialize the driver. The Servo library has no knowledge of I2C or PCA9685 boards.

      So you are dealing with two completely different, incompatible ways of running servos. The code you have uses compiler directives (#ifdef ADAF_DRIVER …. #endif) to determine what is included and what is excluded at compile time. The sketch has to contain the correct addressing which ever way you are running servos, and there is no transfer between the different systems.

      Most people are not trying to support multiple methods of accessing servos. Pick one method, write the code for it and skip the compiler directives.

      Best, R

      • Rob,
        You are right of course, no point mixing methods. So, with PCA9685 servos are adressed with the actual driver pins (0-15), and servo movement in “ticks”.
        Is then every servo position defined 0 – 600 (max numbers) ? (i.e. not defined sequentially)
        When my driver arrives I will do some actual testing, and hopefully find out some basics…..
        Best Rgds
        Peter

  7. Hi Rob,
    I have now hooked up and tested the PCA9685, so far only with Adafruit’s tutorial, which
    ran a servo pretty much like the “servo” coding, only faster. Hoping for better results I am now aiming (note:not trying; aiming…) to convert your coding to using this item. This leads to a couple of questions; I have added the ServoDriver library, and created a servo driver object (Adafruit_PWMServoDriver pwm=Adafruit_PWMServodriver();).
    Then I get unsure about “TURNOUT_DATA”; My thoughts are that button pins A0 – A4 should stay the same, but servo pins 2 – 6 will have to change to 0,1,2,3,4 being servo outputs on PCA9685 (?) Is it that simple ? Also, Main and Divergent angles must change to proper “tick” values, and actual servo movement must be initiated, with the correct coding; that is where I’m confused – I see “write(++turnouts…..”,
    but also “setTurnout(i, ALIGN_MAIN);” in both cases arrays of servos are controlled, and I fail to see the difference, and also how to use “pwm.setPWM” .
    Appreciate some hints about that.
    Your coding put all sorts of ideas in my head… The sketch now moves the servos to a predetermined position at program start, so it can define servo position. To avoid that, why not use a sensor to determine servo position, so startup doesn’t initiate a total hickup ?
    Best Rgds
    Peter

    • Hi — This post should get you started: C++ Objects for Layout Control, Part 2 — Turnouts.

      If you use servos, they will twitch when you start up. You have to power up the servo and set the initial positions in order to lock the servo in position (and your turnout as well) — I just have all servos move to default position on startup which puts the power up twitch to good use. RS

      R.

  8. Hi Robin,
    Thanks for your helpful efforts and patience !
    I have looked at the “enchilada”, which to me looks like a whole new concept.
    So, humbly, a basic question: I first set up your 5 servo demo sketch, and got it working.
    Then I have tried to adapt that to PCA9685, thinking this sketch can be used if changing servo addressing; is this correct or am I missing out ?
    If this is doable, please have a look at the sketch
    Trying to compile I get an error “pulselen not declared….” This happens the second time pulselen is visible in the sketch.

    What I’ve changed is this: denoted by “!”

    ! //#include
    ! #include
    ! #include

    ! Adafruit_PWMServoDriver pwm=Adafruit_PWMServoDriver();

    // Definitions

    #define NUMBER_OF_TURNOUTS 4 // Basic parameters; adjust for actual setup

    #define STEP_DELAY 70 // servo movement step delay, in milliseconds

    // Data Structures

    // TURNOUT_DEF holds all configuration information about t-outs and panel LEDS

    typedef struct TURNOUT_DEF {
    uint8_t button_pin; // Digital or analog pin for the button associated with this turnout

    ! uint8_t servo_port;
    // Digital pin for the servo associated with this turnout

    int pos_main;
    // servo position for the MAIN leg, in degrees

    int pos_div;
    // servo position for the DIVERGENT leg, in degrees

    };

    /////////////////////////////////////
    // TURNOUT_DATA is wrapper structure holding
    // both configuration and runtime data for turnout operation
    /////////////////////////////////////
    typedef struct TURNOUT_DATA {
    TURNOUT_DEF data; // configuration
    bool is_moving;
    byte alignment;
    int pos_now;
    int target_pos;
    unsigned long last_move;
    };

    // Alignment state values
    #define ALIGN_NONE 0
    #define ALIGN_MAIN 1
    #define ALIGN_DIVERGENT 2

    //////////////////////////////////////////
    //
    // Global variables
    //
    //////////////////////////////////////////

    // TURNOUT_DATA Array
    // * A0, A1, etc refer to analog pins which are used for buttons in this example
    // * Replace pos_main (93) and pos_div (117) with real values for each turnout

    // Only the TURNOUT_DEF part of the TURNOUT_DATA structure has to be initialized here;
    // The remaining elements are managed internally and are initialized automatically

    TURNOUT_DATA turnouts[NUMBER_OF_TURNOUTS] = {
    ! {A0, 0, 93, 117, 0, 1, 2, 3},
    ! {A1, 1, 93, 117, 4, 5, 6, 7},
    ! {A2, 2, 93, 117, 8, 9, 10, 11},
    ! {A3, 3, 93, 117, 12, 13, 14, 15},
    // {A4, 6, 93, 117, 16, 17, 18, 19}
    };

    // servo objects
    Servo servos[NUMBER_OF_TURNOUTS];

    void setup()
    {
    // initialize each turnout
    for(int i = 0; i < NUMBER_OF_TURNOUTS; i++){ // attach the servo
    servos[i].attach(turnouts[i].data.servo_port); // set the pin mode for the button pin
    pinMode(turnouts[i].data.button_pin, INPUT); // test and position the turnout by moving
    // to divergent then to main positions
    ! //servos[i].write(turnouts[i].data.pos_div);
    ! pulselen = map(turnouts[i].pos_now, 93, 117, SERVOMIN, SERVOMAX);
    ! pwm.setPWM(turnouts[i].data.servo_port, 93, pulselen);

    turnouts[i].pos_now = turnouts[i].data.pos_div;
    setTurnout(i, ALIGN_MAIN);
    }
    } // end of setup

    void loop()
    {

    unsigned long currentMillis = millis(); // get elapsed milliseconds at loop start

    for(int i = 0; i = STEP_DELAY ) { // if sufficient time has elapsed since the last move
    turnouts[i].last_move = currentMillis; // move the turnout one degree
    if (turnouts[i].pos_now < turnouts[i].target_pos) { // if the new angle is higher

    ! pulselen = map(++turnouts[i].pos_now, 93, 117, SERVOMIN, SERVOMAX);
    ! pwm.setPWM(turnouts[i].data.servo_port, 93, pulselen);
    ! //servos[i].write(++turnouts[i].pos_now);
    } else // otherwise the new angle is equal or lower
    if (turnouts[i].pos_now != turnouts[i].target_pos) { // not already at destination
    ! pulselen = map(–turnouts[i].pos_now, 93, 117, SERVOMIN, SERVOMAX);
    ! pwm.setPWM(turnouts[i].data.servo_port, 117, pulselen);
    }
    // servos[i].write(–turnouts[i].pos_now);
    }
    }
    }

    if (turnouts[i].pos_now == turnouts[i].target_pos) { // if target position reached, stop turnout motion
    turnouts[i].is_moving = false;
    turnouts[i].last_move = 0;
    setIndicators(i);
    }
    } else {
    int button_state = digitalRead(turnouts[i].data.button_pin); // if a turnout is NOT in motion, check to see if its button is pressed
    if(button_state == HIGH){

    if(turnouts[i].alignment == ALIGN_MAIN){ // toggle position
    setTurnout(i, ALIGN_DIVERGENT);
    } else {
    setTurnout(i, ALIGN_MAIN);
    }
    }
    }
    }
    } // end of main loop

    ////////////////////////////////////////////////////////////////
    // Supporting Functions
    ////////////////////////////////////////////////////////////////

    void setTurnout(int id, int align){
    // Set indicators to show turnout in motion
    turnouts[id].alignment = ALIGN_NONE;
    setIndicators(id);
    // Set values to trigger motion on next loop iteration
    switch(align){
    case ALIGN_MAIN:
    turnouts[id].is_moving = true;
    turnouts[id].last_move = 0;
    turnouts[id].target_pos = turnouts[id].data.pos_main;
    turnouts[id].alignment = ALIGN_MAIN;
    break;
    case ALIGN_DIVERGENT:
    turnouts[id].is_moving = true;
    turnouts[id].last_move = 0;
    turnouts[id].target_pos = turnouts[id].data.pos_div;
    turnouts[id].alignment = ALIGN_DIVERGENT;
    break;
    }
    }

    void setIndicators(int id){
    switch(turnouts[id].alignment){
    case ALIGN_NONE: // means the turnout is in motion and not aligned
    break;
    case ALIGN_MAIN:
    break;
    case ALIGN_DIVERGENT:
    break;
    }
    }

    I kind of suspect the SetTurnout part should be changed, but….
    Appreciate help.
    Best Regards
    Peter

Leave a Reply

Your email address will not be published. Required fields are marked *