Servos Keep Resetting On Every Upload

Hi,

48 servos + Arduino Uno and three PCA9685's.
Each PCA9685 has it's own power source and Arduino is powered through it's barrel jack with separate power source. Common ground for all.
I upload the following code which sets all servos to SERVO_MAX, then try to test just one servo on board x.
But every time I upload the code, all or some servos jump to an arbitrary position. Why ? They should not move if they are already at SERVO_MAX.


// Start > all sevos = SERVO_MAX


#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

#define SERVO_MIN 110      // Min pulse width
#define SERVO_MAX 600      // Max pulse width
#define SERVO_RANGE 270.0  // Total servo rotation

Adafruit_PWMServoDriver brd1(0x40);
Adafruit_PWMServoDriver brd2(0x41);
Adafruit_PWMServoDriver brd3(0x43);

// 1-based array: index 0 is dummy (unused)
Adafruit_PWMServoDriver* arrayBrds[] = { nullptr, &brd1, &brd2, &brd3 };


//    ******. SET UP      *************

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

  for (int b = 1; b <= 3; b++) {
    arrayBrds[b]->begin();
    arrayBrds[b]->setOscillatorFrequency(27000000);  
    arrayBrds[b]->setPWMFreq(50);
  }

Serial.println(" testing ...");


  for (int i = 0; i < 16; i++) {
    brd1.setPWM(i, 0, SERVO_MAX);
    brd2.setPWM(i, 0, SERVO_MAX);
    brd3.setPWM(i, 0, SERVO_MAX);
  }
  delay(1000);

arrayBrds[3]->setPWM(11, 0,SRVO_MIN);


void loop() {
  
}


Where in the sketch do you set all of them to SERVO_MAX ?

Bear in mind that the servos do not provide any feedback as to their position

I set them in the setup, right after

Serial.prntln("testing ....");

" Analog servos have a potentiometer that provides absolute position feedback. Digital servos have a quadrature encoder with an index channel to determine the home position."

But none of that is available for the Arduino to monitor, which is @UKHeliBob's point.

I don't use PCA9685, so the following may be of limited use, but I think what you're seeing is the delay between your code configuring the board, and your code then setting the position again; in the meantime, the PCA9685 is outputting a value which is not "SERVO_MAX", and therefore the servos are being asked to move, and the twitch you see is them moving away, then back to SERVO_MAX.

I do not know how you avoid that on the PCA9685; in Servo.h, we simply set the servo position before attaching the servo to a pin, but the '9685' is different.

For example, can you do the begin, then set position, then set frequencies?

not as I know of

Then maybe try it? I don't know the answer.

The PCA9685 output pins default to LOW during a reset or power up. The begin() call does a reset which forces all your outputs LOW until you reset them back to SERVO_MAX.

I think the best you can do is to immediately call begin() and then set the servos and then set the other parameters for the boards. It won't remove the jitter, just minimize it.

See below... Servos "home" to zero degrees at power up (try each one, individually to verify). The fact they are at arbitrary positions could be due to 48x1.5A power surge, and resetting all or some of the muxes again.

Consider what happens when you upload new code.

The PWM signal to the servos stops. And it doesn't start again until your new code is up and running.

What position does the servo controller send the servo to in the absence of a PWM signal? I'm betting it isn't the same as SERVO_MAX.

No they don't. At least not the several I have, unless you mean something other than providing 5 volts and GND.

In the absence of the control signal, servos might hiccup slightly at power up, but they do not move significantly, and do not "home".

Again, at least the cheap ones I have on hand.

Maybe at the high end. Regular r/c digital servos use a feedback potentiometer same as analog servos.

a7

My cheapo 9g servos consistently hold their position “as they were” when the pulse signal "goes away", which is ideal for servos controlling MR turnout points; similarly, they don't move(maybe a “twitch”) on power up. When any turnout is required to change position, I attach, set position, pause, and detach. This works nicely with Servo.h, which allows this. The important trick is to ensure you write a position value to Servo.h for each servo before doing the first attach(). In my case, the position I write comes from the previous write to the servo, which is stored in EEPROM. So, in theory, I write the same position to Servo.h as had been set the last time the servo was moved. In MR turnout control, it’s unlikely I’ll ever move a servo 100k times (would wear out several servos), so I don’t worry about EEPROM lifetime.

Not sure what our OP can do with those PCA9685 boards.

I wish mine did! There's always a twitch on power up before the PWM signal starts up, even with writing a position before attaching. I've resorted to controlling the power to the servos with a P-MOSFET controlled by an output pin. I start the PWM signal up, wait 100mS, then turn the MOSFET on. I'm not crazy about the idea of the servo being unpowered for a bit while getting a signal, but it's the only thing I've found that works consistently. And cheap hobby servos are, well, cheap to replace if it ever comes to that. :slight_smile:

And this works with the PCA9685 boards as well.

Yes. I now see. One of mine just twitched, one moved, another did nothing. I assumed that was "zero" but could be just the descrete components getting to the quiescent state... or my imagination.

Show me your code - offline if you prefer, I’m easy. I’m not sure what we’re doing differently. The residual twitch I sometimes see(rarely more than a few degrees) I always suspected I could get rid of if I were to suppress the bootloader in my ‘final’ version, should such a chimera appear on the horizon some day. That would avoid the thumb-twiddling delay during restart, which I suspect contributes to the no-PWM movement.

One thing - my servos are adjusted so that the angle set for “home” and “thrown” is literally the degree at which the point rail is just held against the stock rail, nothing more. No overtension, pressure against the stock rail, etc., because that certainly would contribute to a mispositioning with power cycling.

I wonder if we are discussing about boot behavior or code upload one.

In my particular case, it seemed to be due to controlling the servo with a 3.3V signal. If I pulled the servo's signal pin up to 6V (which was what I was running the servo at), no twitch. If I didn't pull it up, twitch. If I pulled it up to 3.3V, twitch. I just decided it was fewer parts to control the servo voltage than to add what was necessary to be able to bring the signal level up to 6V so the signal pin could be pulled up to 6V.

So the snippet that works is bog simple; don't do anything funny with the Servo object, attach and write as normal, then 100 ms later, give power to the servo.

   servo.attach(servoPin, servoMin, servoMax);
   servo.write(servoClosed);

   delay(100);
   pinMode(servoPowerPin, OUTPUT);
   digitalWrite(servoPowerPin, HIGH);


Although I don't have any of the code around anymore that twitched, I tried doing the servo.write both before and after the attach and it didn't make a bit of difference. That's what eventually led me to try pulling up the signal line and discovering that 6V through 10K (I may be wrong on that, it's been quite a while) worked, but it was fewer parts to simply control the power. And as it was a one off personal project with a limited lifespan anyhow, it was worth the risk.

Edit: back when I was trying to puzzle this out, the one servo controller datasheet I was able to find indicated that the minimum voltage on the servo signal was 3V. I couldn't tell you what controller that was now to save my life.

Interesting. I’ve never seen a reason to delay Servo power on like that. I’m sure the approach works for you, but I do wonder what’s different. My Arduinos are 5V clone Nanos, so there’s that.

In setup I simply cycle through an array of turnout structs, from which I get initial position and pin number. I write position, attach pin, delay a quarter second or so (due to my install arrangement, the maximum movement would be about 30 degrees), and then detach. Quite simple.

All subsequent movements consist of attach, write, pause, detach, where the pause is a delay(HORRORS!) that is scaled by the length of rotation. I.e. if we’re moving 10 degrees, the pause is much shorter than if 20 degrees.

My needs are simple; the operator moves one turnout at a time, so I don’t mind that the code does the same. This approach will not work for the vast majority of servo applications we see here.

I vaguely remember running across it as a suggestion (along with pulling the signal line up or down, neither of which worked in my case). It might have even been somewhere on this forum that I read it but it was long enough ago that I could be wrong. I was just happy that something worked and I could move on to the next task. :slight_smile:

I do have something coming up where I'll be using 5V to control a pair of servos (crossing signal arms) so it'll be interesting to see if running everything at 5V also solves the problem.