Model Train Turnout Control

Could someone pleases tell me how to slow the servos down in the attached sketch,

Thanks,

Wally.

#include <Servo.h>

Servo pointServo[6];

byte servoPin[] =     {  2,   3,   4,   5,   6,   7, }; // pin numbers for servo signals
byte switchPin[] =    { 8,  9,  10,  11,  12, 13 }; // pin numbers for switch connections
// Uno analog pins      A5   A4   A3   A2   A1   A0
byte servoLowPos[] =  { 50,  50,  50,  50,  50,  50,  }; // degrees for low servo position
byte servoHighPos[] = {150, 150, 150, 150, 150, 150,}; // degrees for high servo position

byte servoPos[6];


void setup() {
  setupServos();
  setupSwitches();
}

void loop() {
  for (byte n = 0; n < 6; n++) {
     boolean servoMove = false;
     byte sw = digitalRead(switchPin[n]);
     if (sw == HIGH) {
        if (servoPos[n] != servoHighPos[n]) { // check if the position has changed
          servoMove = true;
          servoPos[n] = servoHighPos[n];
        }
     }
     else {  // if sw == LOW
       if (servoPos[n] != servoLowPos[n]) {  // check if the position has changed
          servoMove = true;
          servoPos[n] = servoLowPos[n];
       }
     }
     if (servoMove) {  // only move the servo if the switch has changed
        pointServo[n].write(servoPos[n]);
     }
  }
}

void setupServos() {
  for (byte n = 0; n < 6; n++) {
    pointServo[n].attach(servoPin[n]); 
    servoPos[n] = servoLowPos[n]; // this is just a starting value and may be over-ridden immediately by a switch
  }
}

void setupSwitches() {
  for (byte n = 0; n < 6; n++) {
     pinMode(switchPin[n], INPUT_PULLUP);
  }
}

The classic way to do what you want would be to use a for loop to move the servo in steps with a short delay between the moves

I use the word delay with caution because it has implications depending on your requirements. For instance, if a turnout was to take 1 second to move from one extreme to the other would it be acceptable that nothing else could happen in the sketch during that 1 second ?

If so then use the delay() function. However, if the sketch must remain responsive during the 1 second then a different, non blocking, timing method is required

To slow the movement down, you need to move the servo positions by small steps, and do that in a timed way. Right now they move from 50 to 150 in a single step, so the servo moves at maximum speed. You need to move from 50 to 51 or 55, then 52 or 60 etc, and wait a short time between each step. For the waiting, ideally you should use millis() but you could try using a short delay() like delay(100). This is unlikely to miss any button presses.

There is also a library that allows you to move servos slowly GitHub - netlabtoolkit/VarSpeedServo: Arduino library for servos that extends the standard servo.h library with the ability to set speed, and wait for position to complete but it seems that it is not actively maintained

Thank you for your reply. The delay function would work fine for me. Could you please show me where to insert it in the sketch.
Wally.

in the loop in which you move the servos

However you implement it, you'll have to restructure a bit, but one way would be something like this:

if (sw == HIGH) {
    while (servoPos[n] != servoLowPos[n]) {
         servoPos[n] -= 1;
         pointServo[n].write(servoPos[n]);
         delay(10);
     }
}

if (sw == LOW) {
    while (servoPos[n] != servoHighPos[n]) {
         servoPos[n] += 1;
         pointServo[n].write(servoPos[n]);
         delay(10);
     }
}

Make sure that you don't accidentally overshoot your target point, or you'll need to add more code to deal with it.

Thank you for your help. I changed the sketch, and the servos work exactly as I wanted them to.

Wally

consider following which may be easier to maintain
(the ifdef near top is to test the code on my hardware)

#undef MyHW
#ifdef MyHW
struct Servo {
    byte pin;
    void attach (int p)   { pin = p; }
    void write  (int pos) {
        Serial.print   ("write ");
        Serial.print   (pin);
        Serial.print   (" ");
        Serial.println (pos);
    }
};

byte switchPin    [] = {  A1,  A2,  A3 };
byte servoPin     [] = {   2,   3,   4 };
byte servoLowPos  [] = {  50,  50,  50 };
byte servoHighPos [] = { 150, 150, 150 };

#else
# include <Servo.h>

byte switchPin    [] = {   8,   9,  10,  11,  12,  13 };
byte servoPin     [] = {   2,   3,   4,   5,   6,   7 };
byte servoLowPos  [] = {  50,  50,  50,  50,  50,  50 };
byte servoHighPos [] = { 150, 150, 150, 150, 150, 150 };
#endif



#define N_Servo sizeof(servoPin)

Servo pointServo  [N_Servo];
byte servoPos     [N_Servo];

void loop () {
    for (byte n = 0; n < N_Servo; n++) {
        if (HIGH == digitalRead (switchPin[n]))  {
            if (servoPos[n] != servoHighPos[n])
                pointServo[n].write (servoPos[n]++);
        }
        else {
            if (servoPos[n] != servoLowPos[n])
                pointServo[n].write (servoPos[n]--);
        }
    }
    delay (10);
}


void setup ()
{
    Serial.begin (115200);

    for (byte n = 0; n < N_Servo; n++) {
        pointServo[n].attach (servoPin[n]);
        servoPos[n] = servoLowPos[n];

        pinMode (switchPin[n], INPUT_PULLUP);
    }
}
1 Like

Thank you for the sketch. I have tested it and it is perfect. Thank you for your help.
Wally

a non blocking example can be found on my page:
https://werner.rothschopf.net/microcontroller/202207_millis_slow_servo.htm

another variant is here:

Thank you for your reply. I only understand the basic sketches. Could you please show me how to include this in my sketch.
Thanks for your help,
Wally

the code i posted should work for your pinouts.

the key difference is that instead of immediately moving the servo to the specified end position, the servo position is inc/decremented to that position until it is reached

Hah. I wish I had seen this a week ago. I spent a couple of days coming up with something very similar. Not that it was all that hard, but still....

Please send me the sketch of your system.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.