Adding Servo-Controlled Turnouts to DCC-EX Using a PCA9685 and EXRAIL
If you're looking for a reliable way to add servo-operated points or turnouts to your DCC-EX layout, the PCA9685 16-channel servo controller is one of the simplest and most cost-effective solutions available.
Combined with DCC-EX EXRAIL, it allows you to control individual points from your throttle while also building automated routes, signalling and interlocking.
In this guide we'll show you how to connect a PCA9685 to your DCC-EX CommandStation, configure the hardware abstraction layer, define servo turnouts and calibrate them for reliable operation.
If you're using the ACI CSB1 Command Station, the Qwiic connector makes connecting a PCA9685 particularly simple, although the same principles also apply to other compatible DCC-EX CommandStations.
For anyone starting from scratch, this is also where a ready-to-run DCC-EX system such as the ACI CSB1 can make life easier. The command station is already assembled and configured, so you can spend more time adding servos, routes and EXRAIL automation rather than building the command station first.
Common ground required: the CommandStation GND, PCA9685 GND and 5V servo power supply GND must all be connected together.
Power warning: Do not power servos directly from the CommandStation. Use a separate regulated 5V power supply connected to the PCA9685 servo power input.
Power up the CommandStation and watch the console log. DCC-EX should list any detected I2C devices, including the PCA9685 address. The default address is commonly 0x40, but always verify this from the log.
If you see:
< No I2C Devices found >
check the SDA/SCL wiring, connector orientation and power connections.
The PCA9685 is declared in myAutomation.h using the HAL(...) macro.
// start vPin, count, I2C address
HAL(PCA9685, 100, 16, 0x40)
The PCA9685 does not appear as normal physical GPIO pins. Instead, DCC-EX assigns each servo channel a virtual pin, known as a vPin. These vPins are then used in your EXRAIL turnout definitions.
If you need more than 16 servo outputs, additional PCA9685 boards can be connected to the same I2C bus. However, each PCA9685 must use a unique I2C address.
A new PCA9685 normally uses the default address:
0x40
You only need to change the address of additional boards - the first PCA9685 can normally be left at its default 0x40 address.
The small A0-A5 solder pads on the PCA9685 are used to change this address. With all of the pads left open, the board remains at 0x40. Bridging one or more pads adds a binary offset to the base address.
| PCA9685 Board | I2C Address | Address Pads to Bridge |
|---|---|---|
| First board | 0x40 |
None |
| Second board | 0x41 |
A0 |
| Third board | 0x42 |
A1 |
| Fourth board | 0x43 |
A0 + A1 |
| Fifth board | 0x44 |
A2 |
| Sixth board | 0x45 |
A2 + A0 |
| Seventh board | 0x46 |
A2 + A1 |
| Eighth board | 0x47 |
A2 + A1 + A0 |
For example, when using two PCA9685 boards, leave the first board unchanged at 0x40 and bridge the A0 solder pad on the second board. This changes the second board to address 0x41.
DCC-EX can then register each controller separately:
// First PCA9685 - vPins 100 to 115
HAL(PCA9685, 100, 16, 0x40)
// Second PCA9685 - vPins 116 to 131
HAL(PCA9685, 116, 16, 0x41)
Both controllers can share the same SDA and SCL connections, but their unique addresses allow DCC-EX to control them independently.
Once the PCA9685 has been registered using HAL(...), each servo-operated turnout must be defined in myAutomation.h using SERVO_TURNOUT(...).
SERVO_TURNOUT(turnout_id, pin, active_angle, inactive_angle, profile [, "description"]|HIDDEN)
HIDDEN with no quotes to hide itWhen initially installing a servo, it is good practice to set both the active_angle and inactive_angle to the same midpoint value.
For example:
// First servo, channel 0, vPin 100
SERVO_TURNOUT(1, 100, 250, 250, Fast, "Turnout 1")
// Second servo, channel 1, vPin 101
SERVO_TURNOUT(2, 101, 250, 250, Fast, "Turnout 2")
Setting both positions to the same midpoint value places the servo at a known central position instead of allowing it to move immediately between two widely separated end positions.
This is particularly useful when fitting the servo horn, linkage or mounting arrangement beneath the turnout, because you can establish the mechanical centre before setting the final closed and thrown positions.
Your initial myAutomation.h configuration therefore contains both the PCA9685 HAL declaration and the centred SERVO_TURNOUT(...) definition.
// Register the PCA9685
HAL(PCA9685, 100, 16, 0x40)
// Define the servo at its initial centre position
SERVO_TURNOUT(1, 100, 250, 250, Fast, "Turnout 1")
Save myAutomation.h, recompile the CommandStation firmware and upload it to your CommandStation.
When the CommandStation restarts, the PCA9685 and turnout definition will be loaded and the servo can be centred before its final travel is calibrated.
Once the servo has been centred and the mechanical linkage is correctly positioned, the next step is to determine the final closed and thrown positions.
The EX-Toolbox Android application includes servo controls that allow you to adjust the servo position interactively without repeatedly editing and recompiling myAutomation.h while searching for the correct values.
Before adding final ballast and scenery around your pointwork, spend time testing each servo and adjusting its travel. It is much easier to correct servo movement, linkage geometry or point alignment while the track is still fully accessible.
250, 250, in myAutomation.h.SERVO_TURNOUT(...) definition in myAutomation.h.myAutomation.h, recompile the CommandStation firmware and upload it again.
The values found using EX-Toolbox are used to determine the correct travel for the individual turnout. Once you are happy with both positions, enter those values into the servo definition in myAutomation.h.
For example, if EX-Toolbox shows that the turnout operates correctly at positions 120 and 400, update the definition to:
SERVO_TURNOUT(1, 100, 120, 400, Fast, "Turnout 1")
Save the file, recompile the CommandStation firmware and upload it again. These calibrated values are then part of the permanent CommandStation configuration and will be restored whenever the CommandStation starts.
You can then test the calibrated turnout directly using turnout commands.
Closed:
<t 1 C>
Thrown:
<t 1 T>
Many throttles and consoles require the angle brackets < > around the turnout commands. Do not omit them.
Once each servo has been centred and calibrated, several turnout definitions can be added to the same PCA9685 configuration.
// PCA9685 mapped at vPins 100 to 115
HAL(PCA9685, 100, 16, 0x40)
// Define four calibrated turnouts
SERVO_TURNOUT(1, 100, 150, 300, Fast, "Turnout 1")
SERVO_TURNOUT(2, 101, 180, 280, Fast, "Turnout 2")
SERVO_TURNOUT(3, 102, 140, 280, Fast, "Turnout 3")
SERVO_TURNOUT(4, 103, 200, 300, Fast, "Turnout 4")
Note: The servo angle values shown above are examples only. Every turnout installation is different, so the final active and inactive angles will vary depending on the type of servo, mounting position, linkage geometry and turnout being used. Use EX-Toolbox to determine the correct values for each turnout.
When using EngineDriver on Android to control turnouts:
Once configured, EngineDriver automatically lists every turnout you've defined, allowing you to operate them directly from your Android device.
This is where things start to get interesting.
ONTHROW and ONCLOSE let you trigger other actions automatically when a turnout moves. This is useful for pairing points, setting signals, building simple interlocking or triggering other layout automation.
These commands are one of the reasons EXRAIL is so powerful, allowing point movements to trigger other automated actions around your layout.
This is exactly the approach used on the Holgate exhibition layout, where EXRAIL automatically sets routes during public demonstrations, helping trains move reliably through complex trackwork.
ONTHROW(1)
THROW(2)
DONE
This will throw turnout 2 automatically when turnout 1 is thrown. You are not limited to just one extra action inside an ONTHROW() block.
ONCLOSE(3)
RED(Signal_ID)
DONE
In this example, a signal is set to red when turnout 3 is closed.
Once you start using ONTHROW and ONCLOSE, they open the door to far more automated operation without needing complex logic.
| Problem | Likely Cause | Solution |
|---|---|---|
| PCA9685 not detected | Wrong wiring or I2C address | Check SDA/SCL wiring and verify the address in the console log |
| Servo not moving | Power issue | Confirm the 5V supply and common ground connection |
| Servo chatters or jitters | Over-travel or weak power supply | Adjust the servo angles and use a stable 5V supply |
| Servo buzzes continuously | End stop is too tight | Reduce the travel angle slightly so the servo is not under constant strain |
| Wrong channel moves | Incorrect vPin mapping | Recheck the vPin to PCA9685 channel mapping |
Once you're comfortable controlling servo turnouts, EXRAIL allows you to:
The same principles can be used on small home layouts or larger exhibition layouts where reliable turnout control and automation are important.
If you would rather begin with a ready-to-run DCC-EX command station, the ACI CSB1 provides a simple starting point for train control, servo-operated turnouts and future EXRAIL automation.
With the PCA9685 configured, your servo turnouts calibrated and EXRAIL handling the automation, your DCC-EX CommandStation is ready to control individual points or fully automated routes with confidence.