Adafruit 16-Channel 12-bit PWM/Servo Driver how to control speed ?

Hello,
I am trying to learn how to work with adafruit 16 channel 12 bit PWM servo driver. I can now control the servo from a pot but what I would like to know is that is it possible to control the speed of the servo ? is it controlled by setting the frequency ?

pwm.setPWMFreq(1000)

thanks everyone in advance

Hi,
The frequency of the PWM has nothing to do with response of the servo.
The fastest the servo will move can be found by commanding a 0deg position, then when it gets there, send a 180deg command.
To slow the response down you can send incremental commands and if needed delays between them.

Say 0 to 180 in two commands will be the fastest.
But
servo.write(0)
servo.write(10)
servo.write (20)
etc will be slower.

You can write a for loop to do this more efficiently.

Tom... :slight_smile:

TomGeorge:
Hi,
The frequency of the PWM has nothing to do with response of the servo.
The fastest the servo will move can be found by commanding a 0deg position, then when it gets there, send a 180deg command.
To slow the response down you can send incremental commands and if needed delays between them.

Say 0 to 180 in two commands will be the fastest.
But
servo.write(0)
servo.write(10)
servo.write (20)
etc will be slower.

You can write a for loop to do this more efficiently.

Tom... :slight_smile:

thanks Tom for your reply, but I am using the adafruit library for this PWM driver not servo library
is it possible to use servo library ? I am controlling the servo by a pot like the below

pwm.setPWM(0, 0, potValue);

Hi,
Well potValue is your servo position value, so increment it in a for loop.

Tom... :slight_smile:

TomGeorge:
Hi,
Well potValue is your servo position value, so increment it in a for loop.

Tom... :slight_smile:

and based on what we define the i in the for loop ? is the i the speed of the servo then ?

for(int i=0; i<10;i++) {
   potValue = potValue +5;
   pwm.setPWM(0, 0, potValue);
   delay(5);
}

would this example do for an exponential speed for example:

if(potValue > 0 && potValue <=300){
   pwm.setPWM(0, 0, 300);
} else if (potValue > 300 && potValue <=800) {
   pwm.setPWM(0, 0, 800);
} ...

Servo go as fast as they can to the set point you give them, so you don't control how fast they get there. but you can control timing by taking smaller steps as explained above.

if you want to move from angle A to angle B (>A) you have B-A degrees to turn. if you want to take those steps in N seconds, then (assuming moving 1° is almost instantaneous) you can make 1° steps and pause for 1000UL* N / (B-A-1) (ms) in between steps

eg for going from 10° to 61° you have 51° to move and say you want to get there in ~10 seconds.
You take 50 steps of 1° and pause for 1000*10/50 = 200ms in between steps

Servo pulse timing varies so you need to find the minimum and maximum for your specific servo (at 0° and 180° — carefully adjust as hitting the physical limits of travel can damage your servo / the gears)

Once you have that, then to go to a specific angle in degrees between 0 and 180, you can use

pulselength = map(angle, 0, 180, SERVOMIN, SERVOMAX);

Have a look at their Servo example to see how to move things around.

J-M-L:
Servo go as fast as they can to the set point you give them, so you don't control how fast they get there. but you can control timing by taking smaller steps as explained above.

if you want to move from angle A to angle B (>A) you have B-A degrees to turn. if you want to take those steps in N seconds, then (assuming moving 1° is almost instantaneous) you can make 1° steps and pause for 1000UL* N / (B-A-1) (ms) in between steps

eg for going from 10° to 61° you have 51° to move and say you want to get there in ~10 seconds.
You take 50 steps of 1° and pause for 1000*10/50 = 200ms in between steps

Servo pulse timing varies so you need to find the minimum and maximum for your specific servo (at 0° and 180° — carefully adjust as hitting the physical limits of travel can damage your servo / the gears)

Once you have that, then to go to a specific angle in degrees between 0 and 180, you can use

pulselength = map(angle, 0, 180, SERVOMIN, SERVOMAX);

Have a look at their Servo example to see how to move things around.

thank you so much J-M-L link for the explanation it is a bit clearer now, I will create an example once I leave the office and reach home

J-M-L,
I would like from you please to explain if possible how to get the degree angle here as a variable ?
I read the adafruit tutorial or their example doesn't explain this part about the angles

pulselength = map(angle, 0, 180, SERVOMIN, SERVOMAX);

firashelou:
J-M-L,
I would like from you please to explain if possible how to get the degree angle here as a variable ?

your potentiometer is what gives you an angle, isn't it ?

int newAngle = map(analogRead(potPin), 0, 1024, 0, 181); // as we don't reach 1024, we won't get 181 and will stop at 180

if you keep the previous angle in a previousAngle variable then you know you have to travel from there to newAngle

if (newAngle > previousAngle) { // rotate up
  for (int angle = previousAngle; angle < newAngle; angle+=1) {
    // **move to angle**
    // **pause for a bit**
  }
} else { // rotate down
  for (int angle = previousAngle; angle > newAngle; angle-=1) {
    // **move to angle**
    // **pause for a bit**
  }
}
//**finally move to newAngle**

J-M-L:
your potentiometer is what gives you an angle, isn't it ?

int newAngle = map(analogRead(potPin), 0, 1024, 0, 181); // as we don't reach 1024, we won't get 181 and will stop at 180

if you keep the previous angle in a previousAngle variable then you know you have to travel from there to newAngle

if (newAngle > previousAngle) { // rotate up

for (int angle = previousAngle; angle < newAngle; angle+=1) {
   // move to angle
   // pause for a bit
 }
} else { // rotate down
 for (int angle = previousAngle; angle > newAngle; angle-=1) {
   // move to angle
   // pause for a bit
 }
}
//finally move to newAngle

thank you J-M-L, I was watching a tutorial on youtube by robojax about this module i understood a bit then I checked your reply and I made this sketch, the thing is that my servo seems to be a 0 to 90 degrees but the sketch is not working when i rotate the potentiometer but before that i uploaded a sketch for test and all is working well

void loop() {
  int throttlePotValue = analogRead(throttlePin);
  //int throttlePotValue = analogRead(throttlePin);
  Serial.println(throttlePotValue);
  delay(500);
  //throttlePotValue = map(throttlePotValue, 0, 1022, 200, 600);

  int newAngle = map(throttlePotValue, 0, 1024, 0, 91); // as we don't reach 1024, we won't get 181 and will stop at 180
  int previousAngle = 0;
  
  if (newAngle > previousAngle) { // rotate up
    for (int angle = previousAngle; angle < newAngle; angle+=1) {
      // **move to angle**
      // **pause for a bit**
      pwm.setPWM(0, 0, angle);
    }
  } else { // rotate down
    for (int angle = previousAngle; angle > newAngle; angle-=1) {
      // **move to angle**
      // **pause for a bit**
      pwm.setPWM(0, 0, angle);
    }
  }
}

all my servos are 180° this is weird

you can't do      pwm.setPWM(0, 0, angle);the function does not take an angle, that's where you map to a pulselength with

map(angle, 0, 90, SERVOMIN, SERVOMAX); // 90 if you have only 90°

J-M-L:
all my servos are 180° this is weird

you can't do      pwm.setPWM(0, 0, angle);the function does not take an angle, that's where you map to a pulselength with

map(angle, 0, 90, SERVOMIN, SERVOMAX); // 90 if you have only 90°

aha now i understand but I had to change to code to this and now it is working very smooth next I must integrate the timing to make it go faster or slower
when i tried the newAngle and previousAngle the transition was bad it was making like go forward then backword without me changing the pot rotation

void loop() {
  int throttlePotValue = analogRead(throttlePin);
  //int throttlePotValue = analogRead(throttlePin);
  Serial.println(throttlePotValue);
  //delay(500);
  //throttlePotValue = map(throttlePotValue, 0, 1022, 200, 600);

  int newAngle = map(throttlePotValue, 0, 1024, 0, 91); // as we don't reach 1024, we won't get 181 and will stop at 180
  int previousAngle = 0;
  int pulse = map(newAngle, 0, 90, SERVOMIN, SERVOMAX);
  pwm.setPWM(0, 0, pulse);
}

I am trying to integrate the time to make the servo go fast or slow but i don't want that to be dependent on how fast I rotate the potentiometer but it is increasing the response time from where I turn the pot to servo angle rotating

int newAngle = map(throttlePotValue, 0, 1024, 0, 91); // as we don't reach 1024, we won't get 181 and will stop at 180
  int previousAngle = 0;
  int pulse = map(newAngle, 0, 90, SERVOMIN, SERVOMAX);

  for (int i=0; i<91; i++) {
    int timeDelay = 1000*1/89;
    pwm.setPWM(0, 0, pulse);
    delay(timeDelay);
  }

yes to go fast or slow you need to change how long the delay is but you won't be able to go faster that what the Servo can do. --> can it turn 90° in 1 second ?

J-M-L:
yes to go fast or slow you need to change how long the delay is but you won't be able to go faster that what the Servo can do. --> can it turn 90° in 1 second ?

I think yes it can but that depends on how fast I tun the potentiometer

I mean if you set the angle to 0° and then send the command to move to 90° directly, will that take less than 1s to be done ?

if not, there is limited chance that

for (int i=0; i<90; i++) {
    unsigned long timeDelay = 1000UL*1/89;
    pwm.setPWM(0, 0, pulse);
    delay(timeDelay);
  }

will work at that speed

J-M-L:
I mean if you set the angle to 0° and then send the command to move to 90° directly, will that take less than 1s to be done ?

if not, there is limited chance that

for (int i=0; i<90; i++) {

unsigned long timeDelay = 1000UL*1/89;
    pwm.setPWM(0, 0, pulse);
    delay(timeDelay);
  }


will work at that speed

according to the specs of the servo 0.16sec/60deg (6.0V)

OK - could work then (although there is always some inertia to work against if you do small steps)

J-M-L:
OK - could work then (although there is always some inertia to work against if you do small steps)

J-M-L:
OK - could work then (although there is always some inertia to work against if you do small steps)

ok what I am trying to do here is having many maps for the servo for example if I turn the potentiometer a little bit, I want the servo to turn more than the pot position so I tried this but it is not giving the desired result

int throttlePotValue = analogRead(throttlePin);
  //int throttlePotValue = analogRead(throttlePin);
  //Serial.println(throttlePotValue);
  //delay(500);
  //throttlePotValue = map(throttlePotValue, 0, 1022, 200, 600);

  int newAngle = map(throttlePotValue, 0, 1024, 0, 91); // as we don't reach 1024, we won't get 181 and will stop at 180
  int previousAngle = 0;
  int pulse = map(newAngle, 0, 90, SERVOMIN, SERVOMAX);
  int previousPulse;
  int currentPulse;
  Serial.println(pulse);
  if(currentPulse == 600 && previousPulse == 200) {
    currentPulse = 200;
    pulse = 600;
    if(pulse > 600) {
      pulse = 600;
    }
    previousPulse = 600;
  } else if (currentPulse == 200 && previousPulse == 600) {
    currentPulse = 600;
    pulse = 200;
    previousPulse = 200;
  }
  Serial.println(pulse);
  //delay(500);
  /*if(pulse > 200 && pulse < 601) {
    pulse = pulse + 200;
    if(pulse > 600) {
      pulse = 600;
    }
  } else if (pulse > 601) {
    pulse = pulse - 200;
  }
  Serial.println(pulse);
  //delay(500);*/
  pwm.setPWM(0, 0, pulse);

ok what I am trying to do here is having many maps for the servo for example if I turn the potentiometer a little bit, I want the servo to turn more than the pot position so I tried this but it is not giving the desired result

you define your transfer function from a value between 0 and 1023 to your angle (or SERVOMIN, SERVOMAX directly). with map() you have an affine function "y = ax + b" passing by the 2 points (well and converted to integers, so you get steps, not a line).

what you are saying now is that if you move "just a bit' (meaning the difference between newAngle and oldAngle is small) you want to actually go further ? in that case you are loosing the mapping and need to think exactly what that mean when the pot value increases... --> you might be better off using a rotary encoder