Controlling points with SG90 servos and a PCA9685
Servo-driven turnouts on DCC-EX for a couple of pounds a point — the wiring, the config lines, the paperclip linkage, and the mistake that cost me a board.
Eleven points on the layout, to be thrown by SG90 servos screwed under the baseboard, all driven from one PCA9685 board over I²C. DCC-EX handles the rest.
This approach is well documented in pieces, scattered across forum threads and the DCC-EX documentation. What follows is the whole thing in one place — including the bit that cost me a board and two servos.
The method below is working and the config is real, but the installation is not finished — I am still fitting points, and still experimenting with how the movement should look. Treat it as notes from partway through rather than a finished account. I will update it as it progresses.
Why servos rather than point motors
The commercial options — Cobalt, Tortoise and similar — are good products. They are also sold per point, and the cost multiplies fast when you have eleven of them.
An SG90 is a couple of pounds. A PCA9685 driving sixteen of them costs a few pounds more. For a layout of any size the difference is not marginal, it is most of the budget.
What you give up is a purpose-built product that mounts cleanly and works out of the box. What you get back, apart from the money, is sixteen channels from one board, control that DCC-EX already knows how to speak to, and adjustment done in software instead of by bending things.
My frogs are plastic, so nothing here switches frog polarity. If you have live frogs you need either a microswitch on the linkage or a separate frog juicer — the servo alone will not do it.
What you need
- SG90 servos, one per point. Buy a couple of spares; they are cheap and they do fail.
- A PCA9685 board — sixteen channels, I²C, widely available.
- A separate 5V power supply for the servos. This is not optional, and see the warning below.
- Large paperclips, one per point, for the linkages.
- A bracket per servo to hold it under the baseboard. I printed mine.
The 5V warning, learned the hard way
The PCA9685 has two separate power inputs, and the distinction matters:
- VCC — logic power for the chip itself, from the Arduino
- V+ — the servo power rail, which needs its own supply
V+ must be 5V. I applied 7V to mine, on the reasoning that servos are usually tolerant of a bit extra. They are not. It cost me the board and two servos, and replacing them cost more than the entire rest of the point control system.
SG90s are rated for roughly 4.8–6V. Anything above that is not headroom, it is damage. Use a regulated 5V supply and check it with a meter before connecting anything, because a supply labelled 5V is not always a supply delivering 5V.
Two further wiring points that catch people:
Do not power the servos from the Arduino’s 5V pin. It cannot supply enough current, and eleven servos moving at once will brown out the command station. That looks like random resets and is thoroughly confusing to diagnose.
The servo supply and the Arduino must share a common ground. Without it the I²C signalling has no shared reference and the board behaves erratically or not at all.
Mounting and the linkage
The servo goes under the baseboard in a printed bracket held on with four small screws. A bracket is worth designing properly — the servo needs to be rigid relative to the baseboard, because any flex in the mount comes straight off the throw at the tie bar.
The linkage is a large paperclip cut in half:
- Bend one end round the servo horn so it is captive in one of the holes.
- Run the straight end up through the hole in the baseboard and into the hole in the point’s tie bar.
- Cut the top to length once it is in place.
That is the whole linkage. The spring in the paperclip wire is doing real work here: it absorbs any overtravel between where the servo stops and where the tie bar physically stops, so the servo is not fighting the point at the end of each throw. A rigid linkage would either stall the servo or strain the point.
Getting the wire length right is fiddly and best done in place rather than measured.
Telling DCC-EX about it
This is where the setup is much less work than it looks, because DCC-EX has servo turnout support built in.
You probably do not need a HAL declaration
The documentation shows declaring the board with a HAL(PCA9685, ...) line. On EX-CommandStation
5.4 I did not need one — my myHaldevices.h is empty. DCC-EX scans the I²C bus at startup,
finds a PCA9685 at its default address of 0x40, and assigns it vpins 100 to 115
automatically.
So channel 0 on the board is vpin 100, channel 1 is vpin 101, and so on. A second board at
0x41 picks up the next block.
Worth confirming on your own setup: watch the serial console at startup and see what it reports finding. If it lists the board, you have nothing to declare.
Defining the turnouts
I keep the point definitions in their own file, myPoints.h, included from myAutomation.h.
Splitting them out is not required, but it keeps things readable once there are more than a
handful:
SERVO_TURNOUT(100,100,450,110,Instant,"Point_1")
SERVO_TURNOUT(101,101,450,110,Instant,"Point_2")
SERVO_TURNOUT(102,102,450,110,Instant,"Point_3")
SERVO_TURNOUT(103,103,450,110,Instant,"Point_4")
Included from myAutomation.h with:
#include "myHaldevices.h"
#include "myPoints.h"
AUTOSTART
POWERON
DONE
The arguments are:
| Position | Meaning |
|---|---|
| 1 | Turnout ID — what you refer to it as from a throttle |
| 2 | vpin — which servo channel it drives |
| 3 | Thrown position |
| 4 | Closed position |
| 5 | Movement profile |
| 6 | Description shown in throttle apps |
Keeping the turnout ID and the vpin the same number, as above, is not required but saves a lot of confusion later.
Those position numbers are not degrees
450 and 110 are PWM pulse counts, not angles. The PCA9685 works in counts from 0 to
4095, and the usable range for a typical servo lands roughly between 100 and 500.
There is no point calculating these. Tune them by testing, which is the next section.
Movement profiles
Mine are all on Instant at the moment, so the points snap across. That is a starting point
rather than a decision — getting them moving at all came first. DCC-EX also offers slower
profiles — Fast, Medium, Slow and Bounce — which drive the servo gradually rather than
jumping straight to the target.
Slow-motion movement is one of the genuinely nicer things servos give you over solenoids, and
if you want it, it is a one-word change per line and a re-upload. Bounce overshoots and
settles, which some people like for semaphore signals and most people find distracting on points.
Finding the right positions
Do not guess these, and do not set them by eye with the point disconnected.
EX-Toolbox has a section for testing points that moves the servo while you watch. Work the value up and down until the tie bar moves just enough to swing the blades firmly against the stock rail — and no further.
“Just enough” is the whole trick. Overshoot strains the point, makes the servo buzz as it holds
against a stop it cannot reach, and shortens the life of both. Once both positions are right,
write them into myPoints.h.
Uploading changes
Edit the files, then upload through the Arduino IDE over USB. Every configuration change is a recompile and re-upload, so it is worth getting all the points roughly right in one pass rather than one at a time.
Adding more boards
One PCA9685 gives sixteen channels, so eleven points fit comfortably on a single board. I have daisy-chained a second one anyway, for signals later.
Two boards on one I²C bus need different addresses. The second board’s address jumpers must
be bridged to move it off the default 0x40 — usually to 0x41 — or the two will conflict and
neither will work reliably.
How it is going so far
Early days, but the approach is sound. The cost difference against eleven commercial point motors is substantial, DCC-EX’s built-in support means there is no custom code to maintain, and adjusting a point is editing a number rather than getting under the baseboard with pliers.
The two things I would tell anyone starting: check your 5V supply with a meter before you connect it, and print a bracket that holds the servo genuinely rigid. Everything else is recoverable — as the dead board proves, and that one was avoidable.
Still to do
Kept here honestly rather than written up as though it were finished:
- Realistic movement. Everything is on
Instantat the moment. Working out whatSlowandMediumactually look like on a point, rather than in the documentation, is the next job. - The remaining points. Not all of them are fitted and defined yet.
- Rocrail. Once the points are settled, wiring them into
Rocrail so they can be thrown from the track plan and
used in routes. Rocrail addresses DCC-EX turnouts by the same IDs set in
myPoints.h, so it should be straightforward. - Signals, on the second PCA9685 that is already daisy-chained and waiting.
This page will be updated as those land, rather than left describing a half-finished setup as a finished one.
Where to go next
- 3D-printed accessories for DCC layouts — the servo bracket
- Getting started with DCC-EX