Note: Reposted from my entry in germaN87AC blog. The title and terms have been corrected. Paired turnout is correctly called single crossover turnout in this context.
I wanted to use servos to control the turnouts on my planned RailAdventure H0-scale module (total 19 turnouts) and N-scale Timesaver (total 4 turnouts). Each turnout will have 2 LED indicators to indicate the straight and divergent route. These turnouts and LED will be mounted on a separate control panel for the respective module. It was always been my wish to learn how to create a control panel for such purposes. Finally, after watching some videos and researching, I created and optimised the sketches and am happy with the prototyping results.
Single Turnout Servo Control with 2 LED and 1 Push Button
For a single turnout with 2 LED indicators (straight/divergent), the sketch is pretty simple. You can tweak the code const int servoAngles1[] = {0, 45}; to set the movement of the servo arm based on your scale and turnout.
I am using Elegoo Uno shield for prototyping but you can compile and upload to any Arduino or clone shield e.g. Mega. On each button press (Button Pin 2), the turnout will switch to divergent route (as indicated with a red LED in my case; Pin 13) and to straight route (as indicated with a yellow LED; Pin 12).
For each single turnout, you can replicate the same codes and change the pin number accordingly. As I am using Uno, there will be a limited number of pins that I can use for servos and LED. Since my Uno is used for prototyping, I am fine with this limitation.
// Single Servo Turnout with 2 LED (straight/divergent) and a push button
// Created by Jimmy Low (germaN87AC.com) on 15th April 2025
// Version 1.0 (Beta Test)
#include <Bounce2.h>
#include <Servo.h>
// Turnout 1 configuration
const int buttonPin1 = 2;
const int straightLedPin1 = 12;
const int divergentLedPin1 = 13;
const int servoPin1 = 9;
const int servoAngles1[] = {0, 45};
// States for Turnout 1
int straightLed1State = HIGH;
int divergentLed1State = LOW;
int servoState1 = 0;
int servoPosition1 = servoAngles1[0];
int servoTarget1 = servoAngles1[0];
// Bounce buttons and servos
Bounce2::Button button1 = Bounce2::Button();
Servo switch1;
void setup() {
// LED setup
pinMode(straightLedPin1, OUTPUT);
pinMode(divergentLedPin1, OUTPUT);
digitalWrite(straightLedPin1, straightLed1State);
digitalWrite(divergentLedPin1, divergentLed1State);
// Button setup
button1.attach(buttonPin1, INPUT_PULLUP);
button1.interval(10);
button1.setPressedState(LOW);
// Servo setup
switch1.attach(servoPin1, 440, 2400);
switch1.write(servoPosition1);
}
void loop() {
button1.update();
if (button1.pressed()) {
straightLed1State = !straightLed1State;
divergentLed1State = !divergentLed1State;
digitalWrite(straightLedPin1, straightLed1State);
digitalWrite(divergentLedPin1, divergentLed1State);
servoState1 = !servoState1;
servoTarget1 = servoAngles1[servoState1];
moveServoSmoothly(switch1, servoPosition1, servoTarget1);
servoPosition1 = servoTarget1;
}
}
void moveServoSmoothly(Servo &servo, int from, int to) {
int step = (from < to) ? 1 : -1;
for (int pos = from; pos != to; pos += step) {
servo.write(pos);
delay(50);
}
servo.write(to);
}
Single Crossover Turnout Servo Control with 4 LED and 1 Push Button
The next challenge is to pair the servos for simulateneous turnout control. On my test track (below), I want Turnout/Servo 1 and Turnout/Servo 2 to switch to divergent/straight route simulteneously, just like on real-life, rather than having to press each button separately. When the route changed to divergent/straight, the respective LED for each turnout will lit accordingly.

Here is the sketch to enable this paired servo.
// Single Crossover Servo Turnout with 4 LED (straight/divergent) and a push button
// Created by Jimmy Low (germaN87AC.com) on 15th April 2025
// Version 1.0 (Beta Test)
#include <Bounce2.h>
#include <Servo.h>
// Turnout 1 configuration
const int buttonPin1 = 2;
const int straightLedPin1 = 12;
const int divergentLedPin1 = 13;
const int servoPin1 = 9;
const int servoAngles1[] = {0, 45};
// Turnout 2 configuration
const int buttonPin2 = 4;
const int straightLedPin2 = 5;
const int divergentLedPin2 = 6;
const int servoPin2 = 10;
const int servoAngles2[] = {0, 45};
// States for Turnout 1
int straightLed1State = HIGH;
int divergentLed1State = LOW;
int servoState1 = 0;
int servoPosition1 = servoAngles1[0];
int servoTarget1 = servoAngles1[0];
// States for Turnout 2
int straightLed2State = HIGH;
int divergentLed2State = LOW;
int servoState2 = 0;
int servoPosition2 = servoAngles2[0];
int servoTarget2 = servoAngles2[0];
// Bounce buttons and servos
Bounce2::Button button1 = Bounce2::Button();
Bounce2::Button button2 = Bounce2::Button();
Servo switch1;
Servo switch2;
void setup() {
// LED setup
pinMode(straightLedPin1, OUTPUT);
pinMode(divergentLedPin1, OUTPUT);
digitalWrite(straightLedPin1, straightLed1State);
digitalWrite(divergentLedPin1, divergentLed1State);
pinMode(straightLedPin2, OUTPUT);
pinMode(divergentLedPin2, OUTPUT);
digitalWrite(straightLedPin2, straightLed2State);
digitalWrite(divergentLedPin2, divergentLed2State);
// Button setup
button1.attach(buttonPin1, INPUT_PULLUP);
button1.interval(10);
button1.setPressedState(LOW);
button2.attach(buttonPin2, INPUT_PULLUP);
button2.interval(10);
button2.setPressedState(LOW);
// Servo setup
switch1.attach(servoPin1, 440, 2400);
switch1.write(servoPosition1);
switch2.attach(servoPin2, 440, 2400);
switch2.write(servoPosition2);
}
void loop() {
button1.update();
button2.update();
if (button1.pressed()) {
straightLed1State = !straightLed1State;
divergentLed1State = !divergentLed1State;
digitalWrite(straightLedPin1, straightLed1State);
digitalWrite(divergentLedPin1, divergentLed1State);
servoState1 = !servoState1;
servoTarget1 = servoAngles1[servoState1];
moveServoSmoothly(switch1, servoPosition1, servoTarget1);
servoPosition1 = servoTarget1;
}
if (button2.pressed()) {
straightLed2State = !straightLed2State;
divergentLed2State = !divergentLed2State;
digitalWrite(straightLedPin2, straightLed2State);
digitalWrite(divergentLedPin2, divergentLed2State);
servoState2 = !servoState2;
servoTarget2 = servoAngles2[servoState2];
moveServoSmoothly(switch2, servoPosition2, servoTarget2);
servoPosition2 = servoTarget2;
}
}
void moveServoSmoothly(Servo &servo, int from, int to) {
int step = (from < to) ? 1 : -1;
for (int pos = from; pos != to; pos += step) {
servo.write(pos);
delay(50);
}
servo.write(to);
}
Just like the single servo turnout, you can tweak the codes and pins according to your setting. Here is a short video to show how this paired servo works.
My next project is to move the servos and LED to PCA9685 module, and free up the pins on Uno. My N-scale Timesaver, simultaneously test track, has 4 turnouts and 8 LED. 1 PCA9685 module fits nicely. For my RailAdventure module, I have 8 turnouts (16 LED) on Segment 2, 4 turnouts (8 LED) on Segment 4 and 7 turnouts (14 LED) on Segment 5!

Coming soon: Adding PCA9685 Module for Servos and LED.

Hi just read through this and there is a very useful YouTube site that shows the best way of doing this his website is called digital town and it’s welll worth a look. One thing that he teaches is never use delay in your code
Hello,
Thanks for reading my sketches and referring me to Digital Town YT channel. I will go through his videos and update the sketches.
Regards Jimmy