help with my project please

Hello, I need some help,

I have an Arduino Uno and a Adafruit 16-Channel 12-bit PWM/Servo Shield - I2C interface,

so it uses the adafruit servo library and not the regular arduino servo

I tried to ask on the adafruit forums and the guy pointed me to a tutorial for regular arduino library and one to set pulse lengths with the servoshield but for some reason I cannot put 2 and 2 together

If someone could help me out with writing some code that would be great here is an example of what i need it to do

I need 5 servos to move 45 degrees down and up every 48 seconds and 5 servos to move 45 degrees down and up every 2 seconds and and 5 servos to move 45 degrees down and up every 3.5 mins and return.

This is for a phone tapper with stylus pens project.

any help would be greatly appreciated.

Thank you

Have you done any programming with the Arduino and C++

I have not, I was trying to do this based off the exapmle code that the adafruit servo shield uses, but all i can manage to do is make them sweep.

this is the default servo example

/*************************************************** 
  This is an example for our Adafruit 16-channel PWM & Servo driver
  Servo test - this will drive 16 servos, one after the other

  Pick one up today in the adafruit shop!
  ------> http://www.adafruit.com/products/815

  These displays use I2C to communicate, 2 pins are required to  
  interface. For Arduino UNOs, thats SCL -> Analog 5, SDA -> Analog 4

  Adafruit invests time and resources providing this open source code, 
  please support Adafruit and open-source hardware by purchasing 
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.  
  BSD license, all text above must be included in any redistribution
 ****************************************************/

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

// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
// you can also call it with a different address you want
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x41);

// Depending on your servo make, the pulse width min and max may vary, you 
// want these to be as small/large as possible without hitting the hard stop
// for max range. You'll have to tweak them as necessary to match the servos you
// have!
#define SERVOMIN  150 // this is the 'minimum' pulse length count (out of 4096)
#define SERVOMAX  600 // this is the 'maximum' pulse length count (out of 4096)

// our servo # counter
uint8_t servonum = 0;

void setup() {
  Serial.begin(9600);
  Serial.println("16 channel Servo test!");

#ifdef ESP8266
  Wire.pins(2, 14);   // ESP8266 can use any two pins, such as SDA to #2 and SCL to #14
#endif

  pwm.begin();
  
  pwm.setPWMFreq(60);  // Analog servos run at ~60 Hz updates

  yield();
}

// you can use this function if you'd like to set the pulse length in seconds
// e.g. setServoPulse(0, 0.001) is a ~1 millisecond pulse width. its not precise!
void setServoPulse(uint8_t n, double pulse) {
  double pulselength;
  
  pulselength = 1000000;   // 1,000,000 us per second
  pulselength /= 60;   // 60 Hz
  Serial.print(pulselength); Serial.println(" us per period"); 
  pulselength /= 4096;  // 12 bits of resolution
  Serial.print(pulselength); Serial.println(" us per bit"); 
  pulse *= 1000;
  pulse /= pulselength;
  Serial.println(pulse);
  pwm.setPWM(n, 0, pulse);
}

void loop() {
  // Drive each servo one at a time
  Serial.println(servonum);
  for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++) {
    pwm.setPWM(servonum, 0, pulselen);
  }

  delay(500);
  for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--) {
    pwm.setPWM(servonum, 0, pulselen);
  }

  delay(500);

  servonum ++;
  if (servonum > 7) servonum = 0;
}

and this is what i have to make them sweep but I need them to touch the phone screen with a stylus pen at different times

void loop() {
  // Drive each servo one at a time
  Serial.println(servonum);
  for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++) {
    pwm.setPWM(1, 0, pulselen);
    pwm.setPWM(2, 0, pulselen);
    pwm.setPWM(3, 0, pulselen);
    pwm.setPWM(4, 0, pulselen);
    pwm.setPWM(0, 0, pulselen);
    pwm.setPWM(5, 0, pulselen);
    pwm.setPWM(6, 0, pulselen);
    pwm.setPWM(7, 0, pulselen);
    pwm.setPWM(8, 0, pulselen);
    pwm.setPWM(9, 0, pulselen);
    pwm.setPWM(10, 0, pulselen);
    pwm.setPWM(11, 0, pulselen);
    pwm.setPWM(12, 0, pulselen);
    pwm.setPWM(13, 0, pulselen);
    pwm.setPWM(14, 0, pulselen);
    
          
           
  }
  
  for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--) {
    pwm.setPWM(1, 0, pulselen);
    pwm.setPWM(2, 0, pulselen);
    pwm.setPWM(3, 0, pulselen);
    pwm.setPWM(4, 0, pulselen);
    pwm.setPWM(0, 0, pulselen);
    pwm.setPWM(5, 0, pulselen);
    pwm.setPWM(6, 0, pulselen);
    pwm.setPWM(7, 0, pulselen);
    pwm.setPWM(8, 0, pulselen);
    pwm.setPWM(9, 0, pulselen);
    pwm.setPWM(10, 0, pulselen);
    pwm.setPWM(11, 0, pulselen);
    pwm.setPWM(12, 0, pulselen);
    pwm.setPWM(13, 0, pulselen);
    pwm.setPWM(14, 0, pulselen);
  }

I like to think I know how to control servos but I'm pretty new to doing so with the Arduino.

I have several arrays to keep track of the "phase" of each servo. If I know the servo's phase and period, I can calculate the servo's position based on some algorithm.

Here's an example of one of these algorithms.

void driveServoLinear(uint8_t servoIndex, unsigned long period, unsigned long phase, unsigned long minPulse, unsigned long range)
{
  unsigned long halfPeriod = period >> 1;
  if (phase >= halfPeriod)
  {
    phase = period - phase;
  }
  pwm.setPWM(servoIndex, 0, (phase * range / halfPeriod) + minPulse);
  //debugServo(servoIndex, period, phase, minPulse, range);
}

I used unsigned longs since the timing in the Arduino is done with unsigned longs and I didn't want to bother worrying about overflow errors while calculating the position. If you're tight on program space, some of the variable types could probably be changed without adversely affecting the program.

I didn't install Adafuit's library so I haven't tested this program.

*** The program made the post too large. I'll post in the next reply. ***

The call to the "servoDebug" function is commented out. Using this function will likely dramatically slow down the program. I suggest only using the function if you need to debug the program.

I defined the min and max servo pulses in microseconds and then converted these values to numbers used by the shield.

// ****** Change These Values To Match Your Servos ******                          
const unsigned long SERVO_MIN_US = 610; // Based on initial values from Adafruit demo.
const unsigned long SERVO_MAX_US = 2442; 

const unsigned long DEFAULT_SERVO_MIN = SERVO_MIN_US * 4096 / REFRESH_PERIOD_US; // 610 * 4096 / 20000 = 124
const unsigned long DEFAULT_SERVO_MAX = SERVO_MAX_US * 4096 / REFRESH_PERIOD_US; // 2442 * 4096 / 20000 = 500

Adafruit used a frequency of 60Hz but I used 50Hz. If you change the FREQUENCY constant the min and max values will also change so the pulse length will stay the same.

Some digital servos will let you use frequencies of a couple hundred Hz. The higher the frequency you use, the greater the precision you'll have. If you're using analog servos, I suggest leaving the frequency at 50Hz.

I set the period of the servos to two seconds. With a two second period, the servo will take one second to to move from one side to the other. You can change the "DEFAULT_PERIOD" here.

const unsigned long DEFAULT_PERIOD = 2 * FREQUENCY;

The period is defined by the number of fresh cycles. In the above example, the period will be 100.

Each servo can have its own period. Just change the values in this array.

unsigned long servoPeriodCycles[] = {DEFAULT_PERIOD, DEFAULT_PERIOD, DEFAULT_PERIOD, DEFAULT_PERIOD, DEFAULT_PERIOD};

You mentioned you wanted the servos to move out of phase from one another. The phase of each servo is set (again in units of refresh cycles) with this array.

unsigned long servoPhase[] = {0, DEFAULT_PERIOD / SERVOS_IN_USE, 2 * DEFAULT_PERIOD / SERVOS_IN_USE, 3 * DEFAULT_PERIOD / SERVOS_IN_USE, 4 * DEFAULT_PERIOD / SERVOS_IN_USE};

These are currently set to be one fifth of a period out of phase with each other.

I used a simple linear algorithm the code above but you can use sinusoidal algorithms like the one in this thread.

I use a similar algorithm approach to control my hexapod. In the case of the hexapod the input from the remote changes the parameters used in the control algorithm.

Here's the code:

/***************************************************
  This is an example for our Adafruit 16-channel PWM & Servo driver
  Servo test - this will drive 16 servos, one after the other

  Pick one up today in the adafruit shop!
  ------> http://www.adafruit.com/products/815

  These displays use I2C to communicate, 2 pins are required to
  interface. For Arduino UNOs, thats SCL -> Analog 5, SDA -> Analog 4

  Adafruit invests time and resources providing this open source code,
  please support Adafruit and open-source hardware by purchasing
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.
  BSD license, all text above must be included in any redistribution
 ****************************************************/

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

// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
// you can also call it with a different address you want
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x41);

// Depending on your servo make, the pulse width min and max may vary, you
// want these to be as small/large as possible without hitting the hard stop
// for max range. You'll have to tweak them as necessary to match the servos you
// have!

//#define SERVOMIN  150 // this is the 'minimum' pulse length count (out of 4096)
//#define SERVOMAX  600 // this is the 'maximum' pulse length count (out of 4096)

const unsigned long FREQUENCY = 50; // Adafruit used 60Hz. JR RC radios use about 45Hz. 50Hz is the most common (I think).
                          // The larger the frequecy value, the more precise the servo control.
const unsigned long MICROSECONDS_PER_SECOND = 1000000;
const unsigned long REFRESH_PERIOD_US = MICROSECONDS_PER_SECOND / FREQUENCY; //20,000 @ 50Hz

// ****** Change These Values To Match Your Servos ******                          
const unsigned long SERVO_MIN_US = 610; // Based on initial values from Adafruit demo.
const unsigned long SERVO_MAX_US = 2442; 

const unsigned long DEFAULT_SERVO_MIN = SERVO_MIN_US * 4096 / REFRESH_PERIOD_US; // 610 * 4096 / 20000 = 124
const unsigned long DEFAULT_SERVO_MAX = SERVO_MAX_US * 4096 / REFRESH_PERIOD_US; // 2442 * 4096 / 20000 = 500

const int SERVOS_IN_USE = 5;
const int MAX_SERVO_INDEX = SERVOS_IN_USE - 1;

const unsigned long DEFAULT_PERIOD = 2 * FREQUENCY;

unsigned long servoPeriodCycles[] = {DEFAULT_PERIOD, DEFAULT_PERIOD, DEFAULT_PERIOD, DEFAULT_PERIOD, DEFAULT_PERIOD};
unsigned long servoPhase[] = {0, DEFAULT_PERIOD / SERVOS_IN_USE, 2 * DEFAULT_PERIOD / SERVOS_IN_USE, 3 * DEFAULT_PERIOD / SERVOS_IN_USE, 4 * DEFAULT_PERIOD / SERVOS_IN_USE};
unsigned long servoPulseMin[] = {DEFAULT_SERVO_MIN, DEFAULT_SERVO_MIN, DEFAULT_SERVO_MIN, DEFAULT_SERVO_MIN, DEFAULT_SERVO_MIN};
unsigned long servoPulseMax[] = {DEFAULT_SERVO_MAX, DEFAULT_SERVO_MAX, DEFAULT_SERVO_MAX, DEFAULT_SERVO_MAX, DEFAULT_SERVO_MAX};
unsigned long servoPulseRange[SERVOS_IN_USE];
unsigned long lastRefreshTime;

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

  Serial.print(SERVOS_IN_USE);
  Serial.println(" Channel Servo Test");
  Serial.println("Servo Ranges:");
  for (byte i = 0; i < SERVOS_IN_USE; i++)
  {
    servoPulseRange[i] = servoPulseMax[i] - servoPulseMin[i];
    Serial.print("channel # ");
    Serial.print(i);
    Serial.print(", min = ");
    Serial.print(servoPulseMin[i]);
    Serial.print(", max ");
    Serial.print(servoPulseMax[i]);
    Serial.print(", range = ");
    Serial.println(servoPulseRange[i]);
  }

  pwm.begin();

  pwm.setPWMFreq(FREQUENCY);  // Analog servos run at ~50 Hz updates

  yield();
  lastRefreshTime = micros();
}

void loop()
{
  checkServoTime();
}

void checkServoTime()
{
  if (micros() - lastRefreshTime >= REFRESH_PERIOD_US)
  {
    lastRefreshTime += REFRESH_PERIOD_US;
    driveServosLinear(0, MAX_SERVO_INDEX);
  }
}

void driveServosLinear(uint8_t firstServo, uint8_t lastServo)
{
  for (uint8_t servoIndex = firstServo; servoIndex <= lastServo; servoIndex++)
  {
    if (++servoPhase[servoIndex] >= servoPeriodCycles[servoIndex])
    {
      servoPhase[servoIndex] = 0;
      //Serial.print("zero phase of channel # ");
      //Serial.println(servoIndex);
    }
    driveServoLinear(servoIndex, servoPeriodCycles[servoIndex], servoPhase[servoIndex], servoPulseMin[servoIndex], servoPulseRange[servoIndex]);
  }
}

void driveServoLinear(uint8_t servoIndex, unsigned long period, unsigned long phase, unsigned long minPulse, unsigned long range)
{
  unsigned long halfPeriod = period >> 1;
  if (phase >= halfPeriod)
  {
    phase = period - phase;
  }
  pwm.setPWM(servoIndex, 0, (phase * range / halfPeriod) + minPulse);
  //debugServo(servoIndex, period, phase, minPulse, range);
}

void debugServo(uint8_t servoIndex, unsigned long period, unsigned long pseudoPhase, unsigned long minPulse, unsigned long range)
{
  Serial.print("channel # ");
  Serial.print(servoIndex);
  Serial.print(", min = ");
  Serial.print(minPulse);
  Serial.print(", max ");
  Serial.print(range + minPulse);
  Serial.print(", range = ");
  Serial.print(range);
  Serial.print(", pseudoPhase/period = ");
  Serial.print(pseudoPhase);  // It's a "pseudo" because it has been altered by "driveServoLinear" if the phase was greater than or equal to half the period.
  Serial.print(" / ");
  Serial.print(period);
  Serial.print(", active pulse = ");
  Serial.println((pseudoPhase * range / (period >> 1)) + minPulse);
}

You Saved me, This works perfect for what I Need
this is how I modified it to do all 15 servos, could you let me know if I did it correctly?

/***************************************************
  This is an example for our Adafruit 16-channel PWM & Servo driver
  Servo test - this will drive 16 servos, one after the other

  Pick one up today in the adafruit shop!
  ------> http://www.adafruit.com/products/815

  These displays use I2C to communicate, 2 pins are required to
  interface. For Arduino UNOs, thats SCL -> Analog 5, SDA -> Analog 4

  Adafruit invests time and resources providing this open source code,
  please support Adafruit and open-source hardware by purchasing
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.
  BSD license, all text above must be included in any redistribution
 ****************************************************/

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

// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
// you can also call it with a different address you want
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x41);

// Depending on your servo make, the pulse width min and max may vary, you
// want these to be as small/large as possible without hitting the hard stop
// for max range. You'll have to tweak them as necessary to match the servos you
// have!

//#define SERVOMIN  150 // this is the 'minimum' pulse length count (out of 4096)
//#define SERVOMAX  400 // this is the 'maximum' pulse length count (out of 4096)

const unsigned long FREQUENCY = 50; // Adafruit used 60Hz. JR RC radios use about 45Hz. 50Hz is the most common (I think).
                          // The larger the frequecy value, the more precise the servo control.
const unsigned long MICROSECONDS_PER_SECOND = 1000000;
const unsigned long REFRESH_PERIOD_US = MICROSECONDS_PER_SECOND / FREQUENCY; //20,000 @ 50Hz

// ****** Change These Values To Match Your Servos ******                          
const unsigned long SERVO_MIN_US = 610; // Based on initial values from Adafruit demo.
const unsigned long SERVO_MAX_US = 1500; 

const unsigned long DEFAULT_SERVO_MIN = SERVO_MIN_US * 4096 / REFRESH_PERIOD_US; // 610 * 4096 / 20000 = 124
const unsigned long DEFAULT_SERVO_MAX = SERVO_MAX_US * 4096 / REFRESH_PERIOD_US; // 2442 * 4096 / 20000 = 500

const int SERVOS_IN_USE = 15;
const int MAX_SERVO_INDEX = SERVOS_IN_USE - 1;

const unsigned long DEFAULT_PERIOD = 2 * FREQUENCY;
const unsigned long FOURTY_PERIOD = 47 * FREQUENCY;
const unsigned long THREEMIN_PERIOD = 190 * FREQUENCY;

unsigned long servoPeriodCycles[] = {DEFAULT_PERIOD, DEFAULT_PERIOD, DEFAULT_PERIOD, DEFAULT_PERIOD, DEFAULT_PERIOD, FOURTY_PERIOD, FOURTY_PERIOD, FOURTY_PERIOD, FOURTY_PERIOD, FOURTY_PERIOD, THREEMIN_PERIOD, THREEMIN_PERIOD, THREEMIN_PERIOD, THREEMIN_PERIOD, THREEMIN_PERIOD};
unsigned long servoPhase[] = {0, DEFAULT_PERIOD / SERVOS_IN_USE, 2 * DEFAULT_PERIOD / SERVOS_IN_USE, 3 * DEFAULT_PERIOD / SERVOS_IN_USE, 4 * DEFAULT_PERIOD / SERVOS_IN_USE, 5 * FOURTY_PERIOD / SERVOS_IN_USE, 6 * FOURTY_PERIOD / SERVOS_IN_USE, 7 * FOURTY_PERIOD / SERVOS_IN_USE, 8 * FOURTY_PERIOD / SERVOS_IN_USE, 9 * FOURTY_PERIOD / SERVOS_IN_USE, 10 * THREEMIN_PERIOD / SERVOS_IN_USE, 11 * THREEMIN_PERIOD / SERVOS_IN_USE, 12 * THREEMIN_PERIOD / SERVOS_IN_USE, 13 * THREEMIN_PERIOD / SERVOS_IN_USE, 14 * THREEMIN_PERIOD / SERVOS_IN_USE};
unsigned long servoPulseMin[] = {DEFAULT_SERVO_MIN, DEFAULT_SERVO_MIN, DEFAULT_SERVO_MIN, DEFAULT_SERVO_MIN, DEFAULT_SERVO_MIN, DEFAULT_SERVO_MIN, DEFAULT_SERVO_MIN, DEFAULT_SERVO_MIN, DEFAULT_SERVO_MIN, DEFAULT_SERVO_MIN, DEFAULT_SERVO_MIN, DEFAULT_SERVO_MIN, DEFAULT_SERVO_MIN, DEFAULT_SERVO_MIN, DEFAULT_SERVO_MIN};
unsigned long servoPulseMax[] = {DEFAULT_SERVO_MAX, DEFAULT_SERVO_MAX, DEFAULT_SERVO_MAX, DEFAULT_SERVO_MAX, DEFAULT_SERVO_MAX, DEFAULT_SERVO_MAX, DEFAULT_SERVO_MAX, DEFAULT_SERVO_MAX, DEFAULT_SERVO_MAX, DEFAULT_SERVO_MAX, DEFAULT_SERVO_MAX, DEFAULT_SERVO_MAX, DEFAULT_SERVO_MAX, DEFAULT_SERVO_MAX, DEFAULT_SERVO_MAX};
unsigned long servoPulseRange[SERVOS_IN_USE];
unsigned long lastRefreshTime;

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

  Serial.print(SERVOS_IN_USE);
  Serial.println(" Channel Servo Test");
  Serial.println("Servo Ranges:");
  for (byte i = 0; i < SERVOS_IN_USE; i++)
  {
    servoPulseRange[i] = servoPulseMax[i] - servoPulseMin[i];
    Serial.print("channel # ");
    Serial.print(i);
    Serial.print(", min = ");
    Serial.print(servoPulseMin[i]);
    Serial.print(", max ");
    Serial.print(servoPulseMax[i]);
    Serial.print(", range = ");
    Serial.println(servoPulseRange[i]);
  }

  pwm.begin();

  pwm.setPWMFreq(FREQUENCY);  // Analog servos run at ~50 Hz updates

  yield();
  lastRefreshTime = micros();
}

void loop()
{
  checkServoTime();
}

void checkServoTime()
{
  if (micros() - lastRefreshTime >= REFRESH_PERIOD_US)
  {
    lastRefreshTime += REFRESH_PERIOD_US;
    driveServosLinear(0, MAX_SERVO_INDEX);
  }
}

void driveServosLinear(uint8_t firstServo, uint8_t lastServo)
{
  for (uint8_t servoIndex = firstServo; servoIndex <= lastServo; servoIndex++)
  {
    if (++servoPhase[servoIndex] >= servoPeriodCycles[servoIndex])
    {
      servoPhase[servoIndex] = 0;
      //Serial.print("zero phase of channel # ");
      //Serial.println(servoIndex);
    }
    driveServoLinear(servoIndex, servoPeriodCycles[servoIndex], servoPhase[servoIndex], servoPulseMin[servoIndex], servoPulseRange[servoIndex]);
  }
}

void driveServoLinear(uint8_t servoIndex, unsigned long period, unsigned long phase, unsigned long minPulse, unsigned long range)
{
  unsigned long halfPeriod = period >> 1;
  if (phase >= halfPeriod)
  {
    phase = period - phase;
  }
  pwm.setPWM(servoIndex, 0, (phase * range / halfPeriod) + minPulse);
  //debugServo(servoIndex, period, phase, minPulse, range);
}

void debugServo(uint8_t servoIndex, unsigned long period, unsigned long pseudoPhase, unsigned long minPulse, unsigned long range)
{
  Serial.print("channel # ");
  Serial.print(servoIndex);
  Serial.print(", min = ");
  Serial.print(minPulse);
  Serial.print(", max ");
  Serial.print(range + minPulse);
  Serial.print(", range = ");
  Serial.print(range);
  Serial.print(", pseudoPhase/period = ");
  Serial.print(pseudoPhase);  // It's a "pseudo" because it has been altered by "driveServoLinear" if the phase was greater than or equal to half the period.
  Serial.print(" / ");
  Serial.print(period);
  Serial.print(", active pulse = ");
  Serial.println((pseudoPhase * range / (period >> 1)) + minPulse);
}

delwig:
could you let me know if I did it correctly?

That looks right to me.

Let us know if something doesn't work as expected.