Unexpected results with servo

Hello,
My first post and something of a catch up from school days and feel like I'm climbing Mount Snowdon, so please bear with me as I'm trying to relearn.

OK, so my project is a semaphore railway signal control - its to replace the RTR base of a Dapol OO signal's original vertical motor . I have gone through the early projects of servo control using a potentiometer etc and then came across @gingerangles project on his semaphore signalling and thought rather that try and re-invent the wheel myself, use a sketch someone has already done. ( Programming for realistic semaphore control using PCA 9685

Now, just to make clear, I have had the servo working fine up until I changed to the below sketch, which just boots up and starts the servo motor spinning round and around as if its pulsing. i cant get it to stop or reverse.

its wired as above and has the triggers to pins 2,3, & 4. Sadly I think jumping in to this level its gone a bit over my head which I will admit may be wrong to have done, But if anyone can help out as to why my servo is spinning and not moving as prescribed would be great

Andy

//move_signals_17
//https://forum.arduino.cc/t/programming-for-realistic-semaphore-control-using-pca-9685/1283543

//NOTE : dangerPWM must always be less than clearPWM
//https://forum.arduino.cc/t/programming-for-realistic-semaphore-control-using-pca-9685/1283543/54

#include <Adafruit_PWMServoDriver.h>
Adafruit_PWMServoDriver signalBoard01 = Adafruit_PWMServoDriver(0x40);

enum states
{
    AT_DANGER,  //each signal can be in any one of the following states
    AT_CLEAR,
    MOVING_TO_DANGER,
    MOVING_TO_CLEAR,
    BOUNCING_AT_DANGER,
    BOUNCING_AT_CLEAR
};

struct signalData  //struct for signal variables
{
    int currentPWM;             //current PWM value
    unsigned long lastMoveTime;  //time of last move
    states currentState;         //current state of signal
    boolean moveDone;            //true if the move to a new state is finished
    boolean bounceDone;          //flag to prevent bouncing being repeated
    int bounceCount;             //counter for bounces
    boolean bounceAway;          //true if bouncing away from end position
    int  bounceDistance;         //distance to bounce
};

struct signalDefs  //signal definitions
{
    const byte swPinNum;                      //switch pin number
    const int clearPWM;                      //PWM value for clear signal
    const int dangerPWM;                     //PWM value for danger signal
    const byte pwmChannel;                    //servo channel number
    const byte dangerInterval;                //interval between steps to danger.  Lower values are faster
    const byte clearInterval;                 //unterval between steps to clear
    const unsigned int dangerBounceInterval;  //interval between danger bounces
    const byte dangerNumBounces;              //number of bounces to perform at danger
    const byte dangerBouncePercent;           //percentage of total movement to bounce when reaching danger
    const unsigned int clearBounceInterval;   //interval between danger bounces
    const byte clearNumBounces;               //number of bounces to perform at danger
    const byte clearBouncePercent;            //percentage of total movement to bounce when reaching danger
};

signalDefs signals[] = {
  //pin no, clear PWM, danager PWM, PWM Channel, danger Interval, clear Interval, danger: Bounce interval; Bounce No; Bounce %, clear: Bounce interval; Bounce No; Bounce %
    { 3, 150, 115, 0, 15, 10, 100, 5, 40, 200, 0, 50 },
    { 4, 200, 130, 1, 15, 10, 100, 5, 40, 200, 0, 50 },
    { 5, 360, 200, 2, 2, 2, 130, 4, 50, 100, 2, 20 },
};

const byte signalCount = sizeof(signals) / sizeof(signals[0]);
signalData sigVars[signalCount];

unsigned long currentTime;

void setup()
{
    Serial.begin(115200);
    Serial.println("GingerAngles Signal Control!");
    signalBoard01.begin();
    signalBoard01.setPWMFreq(50);  // Analog servos run at ~60 Hz updates
    initialse();
}

void loop()
{
    for (int s = 0; s < signalCount; s++)
    {
        currentTime = millis();
        byte currentSwitchState = digitalRead(signals[s].swPinNum);
        switch (sigVars[s].currentState)
        {
            case AT_DANGER:
                if (signals[s].dangerNumBounces > 0 && sigVars[s].bounceDone == false)
                {
                    sigVars[s].currentState = BOUNCING_AT_DANGER;
                    Serial.print(signals[s].pwmChannel);
                    Serial.println(" bouncing at danger");
                    //                    sigVars[s].bounceDone = false;
                    sigVars[s].lastMoveTime = currentTime + signals[s].dangerBounceInterval;    //force immediate bounce  BBB
                    sigVars[s].currentPWM = signals[s].dangerPWM;
                    sigVars[s].bounceCount = 0;
                    sigVars[s].bounceAway = true;
                    sigVars[s].bounceDistance = (signals[s].clearPWM - signals[s].dangerPWM) / (100 / signals[s].dangerBouncePercent);  //initial bounce back distance
                }
                else if (currentSwitchState == LOW)  //the switch is closed
                {
                    sigVars[s].currentState = MOVING_TO_CLEAR;
                    Serial.print(signals[s].pwmChannel);
                    Serial.println(" moving to clear");
                    sigVars[s].moveDone = false;
                    sigVars[s].lastMoveTime = currentTime;
                }
                break;
            case MOVING_TO_CLEAR:
                moveTowardsClear(s);
                if (sigVars[s].moveDone == true)
                {
                    sigVars[s].currentState = AT_CLEAR;
                    Serial.print(signals[s].pwmChannel);
                    Serial.println(" arrived at clear");
                    sigVars[s].moveDone = false;
                    sigVars[s].bounceDone = false;
                }
                break;
            case AT_CLEAR:
                if (signals[s].clearNumBounces > 0 && sigVars[s].bounceDone == false)
                {
                    sigVars[s].currentState = BOUNCING_AT_CLEAR;
                    Serial.print(signals[s].pwmChannel);
                    Serial.println(" bouncing at clear");
                    sigVars[s].lastMoveTime = currentTime +signals[s].clearBounceInterval;  //force immediate bounce
                    sigVars[s].currentPWM = signals[s].clearPWM;
                    sigVars[s].bounceCount = 0;
                    sigVars[s].bounceAway = true;
                    sigVars[s].bounceDistance = (signals[s].clearPWM - signals[s].dangerPWM) / (100 / signals[s].clearBouncePercent);  //initial bounce back distance
                }
                else if (currentSwitchState == HIGH)  //the switch is open
                {
                    sigVars[s].currentState = MOVING_TO_DANGER;
                    Serial.print(signals[s].pwmChannel);
                    Serial.println(" moving to danger");
                    sigVars[s].moveDone = false;
                    sigVars[s].lastMoveTime = currentTime;
                }
                break;
            case MOVING_TO_DANGER:
                moveTowardsDanger(s);
                if (sigVars[s].moveDone == true)
                {
                    sigVars[s].currentState = AT_DANGER;
                    Serial.print(signals[s].pwmChannel);
                    Serial.println(" arrived at danger");
                    sigVars[s].moveDone = false;
                    sigVars[s].bounceDone = false;
                }
                break;
            case BOUNCING_AT_CLEAR:
                if (currentTime - sigVars[s].lastMoveTime >= signals[s].clearBounceInterval)  //time to do another bounce
                {
                    if (sigVars[s].bounceAway == true)  //bounce away from clear - PWM is smaller when bounced away from clear
                    {
                        // Serial.print("bounceDistance ");
                        // Serial.println(sigVars[s].bounceDistance);
                        // Serial.print("clear PWM value ");
                        // Serial.println(signals[s].clearPWM - sigVars[s].bounceDistance);
                        // Serial.print("bounce # ");
                        // Serial.println(sigVars[s].bounceCount);

                        signalBoard01.setPWM(signals[s].pwmChannel, 0, signals[s].clearPWM - sigVars[s].bounceDistance);
                        sigVars[s].bounceAway = false;  //next movement is back to clear
                        sigVars[s].lastMoveTime = currentTime;
                        sigVars[s].bounceCount++;
                        //sigVars[s].bounceDistance -= sigVars[s].bounceDistance / signals[s].clearNumBounces;  //adjust bounce distance
                        sigVars[s].bounceDistance -= adjustBounceDistance(sigVars[s].bounceDistance, signals[s].clearNumBounces);
                        if (sigVars[s].bounceCount >= signals[s].clearNumBounces)  //done required bounces ?
                        {
                            sigVars[s].currentState = AT_CLEAR;
                            sigVars[s].bounceDone = true;
                            signalBoard01.setPWM(signals[s].pwmChannel, 0, signals[s].clearPWM);  //bounce back to clearPWM
                            Serial.print(signals[s].pwmChannel);
                            Serial.println(" stoppped bouncing at clear");
                        }
                    }
                    else  //back to danger
                    {
                        signalBoard01.setPWM(signals[s].pwmChannel, 0, signals[s].clearPWM);  //bounce back to clear
                        sigVars[s].lastMoveTime = currentTime;
                        sigVars[s].bounceAway = true;
                    }
                }  //end time to bounce
                break;

            case BOUNCING_AT_DANGER:
                if (currentTime - sigVars[s].lastMoveTime >= signals[s].dangerBounceInterval)  //time to do another bounce
                {
                    if (sigVars[s].bounceAway == true)  //bounce away from danger - PWM is smaller when bounced away from danger
                    {
                        // Serial.print("bounceDistance ");
                        // Serial.println(sigVars[s].bounceDistance);
                        // Serial.print("danger PWM value ");
                        // Serial.println(signals[s].dangerPWM + sigVars[s].bounceDistance);
                        // Serial.print("bounce # ");
                        // Serial.println(sigVars[s].bounceCount);

                        signalBoard01.setPWM(signals[s].pwmChannel, 0, signals[s].dangerPWM + sigVars[s].bounceDistance);
                        sigVars[s].bounceAway = false;  //next movement is back to danger
                        sigVars[s].lastMoveTime = currentTime;
                        sigVars[s].bounceCount++;
                        //sigVars[s].bounceDistance -= sigVars[s].bounceDistance / signals[s].dangerNumBounces;  //adjust bounce distance
                        sigVars[s].bounceDistance -= adjustBounceDistance(sigVars[s].bounceDistance, signals[s].dangerNumBounces);

                        if (sigVars[s].bounceCount >= signals[s].dangerNumBounces)  //done required bounces ?
                        {
                            sigVars[s].currentState = AT_DANGER;
                            sigVars[s].bounceDone = true;
                            signalBoard01.setPWM(signals[s].pwmChannel, 0, signals[s].dangerPWM);  //bounce back to dangerPWM
                            Serial.print(signals[s].pwmChannel);
                            Serial.println(" stoppped bouncing at danger");
                        }
                    }
                    else  //back to danger
                    {
                        signalBoard01.setPWM(signals[s].pwmChannel, 0, signals[s].dangerPWM);  //bounce back to danger
                        sigVars[s].lastMoveTime = currentTime;
                        sigVars[s].bounceAway = true;
                    }
                }  //end time to bounce
                break;

        }  //end switch case
    }      //end for loop
}

byte adjustBounceDistance(byte distance, byte numBounces)
{
    return distance / numBounces;
}

void moveTowardsClear(byte target)  //consider adding code to move in the reverse direction based on an entry in the struct
{
    if (currentTime - sigVars[target].lastMoveTime > signals[target].clearInterval)  //time to move ?
    {
        signalBoard01.setPWM(signals[target].pwmChannel, 0, sigVars[target].currentPWM);
        sigVars[target].lastMoveTime = currentTime;  //save time move was made
        sigVars[target].currentPWM++;
        if (sigVars[target].currentPWM >= signals[target].clearPWM)
        {
            sigVars[target].moveDone = true;
        }
    }
}

void moveTowardsDanger(byte target)  //consider adding code to move in the reverse direction based on an entry in the struct
{
    if (currentTime - sigVars[target].lastMoveTime > signals[target].dangerInterval)  //time to move ?
    {
        signalBoard01.setPWM(signals[target].pwmChannel, 0, sigVars[target].currentPWM);
        sigVars[target].lastMoveTime = currentTime;  //save time move was made
        sigVars[target].currentPWM--;
        if (sigVars[target].currentPWM <= signals[target].dangerPWM)
        {
            sigVars[target].moveDone = true;
        }
    }
}

void initialse()
{
    boolean error = false;
    for (int s = 0; s < signalCount; s++)
    {
        if (signals[s].dangerPWM > signals[s].clearPWM)
        {
            Serial.print("check clearPWM and dangerPWM values for signal ");
            Serial.println(signals[s].pwmChannel);
            error = true;
        }
        if (!error)
        {
            pinMode(signals[s].swPinNum, INPUT_PULLUP);
            sigVars[s].currentState = AT_DANGER;  //set all signals to danger
            sigVars[s].currentPWM = signals[s].dangerPWM;
            signalBoard01.setPWM(signals[s].pwmChannel, 0, sigVars[s].currentPWM);
            sigVars[s].moveDone = true;
            sigVars[s].bounceDone = true;
        }
    }
    while (error)
    {
    }
}

Which motor are you referring to ? If it is the servo then is it turning through more than 180 degrees or maybe even turning through more than 360 degrees ?

Hi @UKHelibob,

I know you were working with @gingerangles on his original project, so thanks for replying.

Its the servo motor that just spins... give me two mins and I will try and get a video.

andy

If the servo just spins then either it has become faulty or it is a so called "continuous rotation servo"

Have you used this exact servo before ?

1 Like

It’s a brand new servo.. and the same one used in the earlier sketch tests

And I can’t upload the video.

Ah, the one thing I must add, I have added the V+ connection on the PCA9685 to one of the V5+ connections on the Uno R3. Otherwise nothing works. I am yet to get an external power supply for the PCA9685. is that an issue with just one servo?

Put the PCA9685 board aside for now and try the Sweep example from the normal Servo library connected directly to the Uno. It should be OK to run the servo on the 5V supply from the Uno for a short time but do not put any load on it

Report back what happens when you run the Sweep example

2 Likes

Ok, so managed to create a video

Servo video

First part connecting to the potentiometer using the uno to control the servo sweep.

Secondly moving on to automated control using an example uno sketch. Sweep…The servo goes back and forth.

Thirdly using the adopted sketch, where it seems to fail and just spins with no control.

So it has worked (and don’t worry about the other bits, they are just micro switches on another battery operated circuit.

Having watched the video, I can only repeat what I said previously

When running Sweep, it is not possible to see what is going on
When the servo is used to operate the microswitches is it stopping at the ends of travel or is it stalled and still trying to move ?

No servo should rotate continuously

3 Likes

Hi @UKHeliBob

The first part was stopped on the pot when I manually rotated it. I had manually set the limits on the full throw of the pot.

I’ll step back and look at the sweep then.

Andy

Do not use the Arduino 5V to power servos or motors, as you can damage the Arduino doing so. Use a separate power supply capable of 1 Ampere per servo for small ones (SG90) and 2.5 Amperes/servo for larger ones like the MG996R. Don't forget to connect the grounds.

@jremington, on your servo connection picture:
How about a green signal wire in place of the near invisible (to me) yellow?
TNX, JCA. :smiley:

I think they are the stock images off the web @JCA34F

I have had a gent PM me with details suggesting I may have the wrong servo. Some of these blue micro servos are sold as either 180 degree or 360 degree servos, but there is nothing externally to suggest which is which (as they have found themselves when checking) and appears down to a pot inside the servo.

With this in mind, I have ordered a replacement servo that is marked up for sale as a 180 degree servo. So will have to shelve it till this comes

Also @jremington suggested separate power, I will look at a power feed for the board separate - but I can’t seem to find what the max voltage is for the PCA9685 board? I have a spare variable DC power plug I can use, but it’s lowest voltage is 7.10V DC

Andy

Oh well, guess I'll wear yellow sunglasses. :sunglasses:

What is it doing in the video?
I would say it is going back and forth, BUT your code is not responding to the two limit switches.

Check that they are working.
Write a simple code that JUST operates the servo in sweep, physically remove the limits.
In your code use the serial monitor to show sweep values and the limit switch values when you MANUALLY operate them.

In other words write a diagnostic/troubleshooting code to check your output and input signals.

Can you please post a copy of your COMPLETE circuit, a picture of a hand drawn circuit in jpg, png?
Hand drawn and photographed is perfectly acceptable.
Please include ALL hardware, power supplies, component names and pin labels.

Tom.. :smiley: :+1: :coffee: :australia:

@TomGeorge ignore the contact switches mounted along side the servo, they are for something different (acting as a SPDT circuit for the signal repeater and not connected to the servo or the uno in any way.

Tom, it’s as per Ginger’s original drawing (in the linked post mentioned in post 1 of this thread)

But the PCA9824 board is not shown, but we know how a decoder links to that as it’s shown in my other diagram.

1 Like

Hi,
Well still write some test code, and control the amount of sweep.
Remove the limit switches so you can see how much of a sweep you have.

Back to basics. Use one of the library examples.

Tom.. :smiley: :+1: :coffee: :australia:

1 Like

You have been told that in this topic but seem to have taken no notice.

Note that a "360 degree servo" is probably not a servo at all in the sense that you cannot control its angle, only its speed and direction so it is not a servo at all

1 Like

@UKHeliBob with apologies, I had misinterpreted what you had put and it wasn’t till I had the PM and re-read what you said about the servo I understood - I had never heard of the difference of a 180 and 360 servo before. Last time I used RC servos was pre digital and this one was simply ordered when I had the board as was for a different project that stalled. The gent who PM’d me however went in to a little more detail to explain to me re the internals and what to look for.

The new servo arrived about 4pm GMT, it’s looks identical to the one I had before with no visible marking and only the bag it came in saying 180. It has now been connected, sketch re-uploaded to the uno Mini and nothing…

So I triggered the switches in the drawing and lo and behold we had movement as expected and released the switch and it went back.!! Hurrah!

Next is to fit it in to my servo mount and adjust my micro switches to trigger at the current set limits.

Then the none electric part of working out just where I need to connect my wire on the servo arm so it has the appropriate amount of swing to move the signal arm.

All good and thanks for the help.

Andy