Kinetic Sculpture
Wiring up 1 MicroServo or Servo HS-318(?)
// Working code to move 1 servo
#include "Servo.h" // include the servo library
Servo servoMotor; // creates an instance of the servo object to control a servo
int servoPin = 9; // Control pin for servo motor
// time when the servo was last updated, in ms
long lastMoveTime = 0;
void setup() {
Serial.begin(9600); // initialize serial communications
servoMotor.attach(servoPin); // attaches the servo on pin 9 to the servo object
}
void loop()
{
int analogValue = analogRead(A0); // read the analog input
Serial.println(analogValue); // print it
// if your sensor's range is less than 0 to 1023, you'll need to
// modify the map() function to use the values you discovered:
int servoAngle = map(analogValue, 0, 1023, 0, 179);
// move the servo using the angle from the sensor every 20 ms:
if (millis() - lastMoveTime > 20) {
servoMotor.write(servoAngle);
lastMoveTime = millis();
}
}
Next steps:
- Book office hours with Rios OR Rozin (â ), to put in lights, what would I need to do?
- This board can also be used with LEDs.
- Solder in a capacitor if:
- Driving more than 4â6 servos at once.
- Servos move all at the same time.
- 100”F per servo (minimum)
- So for 3 servos: ~330”F is fine (470”F is safer)
- For 16 servos: 1000â2200”F (6.3V or higher)
What value?
Servos | Suggested Cap Formula | Result |
16 | n à 100”F  (base rule) | 1600 ”F minimum |
16 | High stability scenario | 2200â4700âŻÂ”F preferred |
⥠Bonus: Power Budget for 16 SG90s
From your specs:
- Running current per servo â 400â500âŻmA
- Stall current â 1.3â1.6A
- Letâs assume real-world peak ~0.8A Ă 16 servos = 12.8A peak draw
Your 5V 10A supply is close, but you may want to:
- Be cautious with simultaneous full-speed startup
- OR eventually upgrade to 5V 15A if you hit instability
Get 360Âș servos of up to 6V. Look up attaching motors to fabric. Motor to fabric fasteners
Can I chain 2 boards using an Arduino Nano or do I need to get an Uno?
I2C scanner sketch
#include <Wire.h>
void setup() {
Wire.begin();
Serial.begin(9600);
while (!Serial); // for Nano 33 IoT
Serial.println("\nI2C Scanner");
}
void loop() {
byte error, address;
int nDevices = 0;
for (address = 1; address < 127; address++ ) {
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0) {
Serial.print("I2C device found at address 0x");
if (address < 16)
Serial.print("0");
Serial.println(address, HEX);
nDevices++;
}
}
if (nDevices == 0)
Serial.println("No I2C devices found");
delay(2000);
}
Voltage readings:
PCA9685 Side VCC & GND: has 3.3V Blue Terminal (top right on board): has 5.21V
_____ I2C is not getting recognized. Serial print at setup is not happening. Replaced PCA9685 board, which seemed to be fried && the motor.
Code working for 1 servo:
#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);
// you can also call it with a different address and I2C interface
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40, Wire);
// 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 120 // This is the 'minimum' pulse length count (out of 4096)
#define SERVOMAX 480 // This is the 'maximum' pulse length count (out of 4096)
#define USMIN 600 // This is the rounded 'minimum' microsecond length based on the minimum pulse of 150
#define USMAX 2400 // This is the rounded 'maximum' microsecond length based on the maximum pulse of 600
#define SERVO_FREQ 50 // Analog servos run at ~50 Hz updates
// our servo # counter
uint8_t servonum = 0;
void setup() {
Serial.begin(9600);
Serial.println("8 channel Servo test!");
pwm.begin();
/*
* In theory the internal oscillator (clock) is 25MHz but it really isn't
* that precise. You can 'calibrate' this by tweaking this number until
* you get the PWM update frequency you're expecting!
* The int.osc. for the PCA9685 chip is a range between about 23-27MHz and
* is used for calculating things like writeMicroseconds()
* Analog servos run at ~50 Hz updates, It is importaint to use an
* oscilloscope in setting the int.osc frequency for the I2C PCA9685 chip.
* 1) Attach the oscilloscope to one of the PWM signal pins and ground on
* the I2C PCA9685 chip you are setting the value for.
* 2) Adjust setOscillatorFrequency() until the PWM update frequency is the
* expected value (50Hz for most ESCs)
* Setting the value here is specific to each individual I2C PCA9685 chip and
* affects the calculations for the PWM update frequency.
* Failure to correctly set the int.osc value will cause unexpected PWM results
*/
pwm.setOscillatorFrequency(27000000);
pwm.setPWMFreq(SERVO_FREQ); // Analog servos run at ~50 Hz updates
delay(10);
}
// 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. It's not precise!
void setServoPulse(uint8_t n, double pulse) {
double pulselength;
pulselength = 1000000; // 1,000,000 us per second
pulselength /= SERVO_FREQ; // Analog servos run at ~60 Hz updates
Serial.print(pulselength); Serial.println(" us per period");
pulselength /= 4096; // 12 bits of resolution
Serial.print(pulselength); Serial.println(" us per bit");
pulse *= 1000000; // convert input seconds to us
pulse /= pulselength;
Serial.println(pulse);
pwm.setPWM(n, 0, pulse);
}
void loop() {
// Drive each servo one at a time using setPWM()
// Serial.println(servonum);
for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++) {
pwm.setPWM(servonum, 0, pulselen);
delay(10); // Slow it down for visual feedback
}
// delay(500);
// for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--) {
// pwm.setPWM(servonum, 0, pulselen);
// }
// delay(500);
// // Drive each servo one at a time using writeMicroseconds(), it's not precise due to calculation rounding!
// // The writeMicroseconds() function is used to mimic the Arduino Servo library writeMicroseconds() behavior.
// for (uint16_t microsec = USMIN; microsec < USMAX; microsec++) {
// pwm.writeMicroseconds(servonum, microsec);
// }
// delay(500);
// for (uint16_t microsec = USMAX; microsec > USMIN; microsec--) {
// pwm.writeMicroseconds(servonum, microsec);
// }
// delay(500);
// servonum++;
// if (servonum > 7) servonum = 0; // Testing the first 8 servo channels
}
code working to sweep through 6 servos
// #include <Wire.h>
// void setup() {
// Wire.begin();
// Serial.begin(9600);
// while (!Serial); // for Nano 33 IoT
// Serial.println("\nI2C Scanner");
// }
// void loop() {
// byte error, address;
// int nDevices = 0;
// for (address = 1; address < 127; address++ ) {
// Wire.beginTransmission(address);
// error = Wire.endTransmission();
// if (error == 0) {
// Serial.print("I2C device found at address 0x");
// if (address < 16)
// Serial.print("0");
// Serial.println(address, HEX);
// nDevices++;
// }
// }
// if (nDevices == 0)
// Serial.println("No I2C devices found");
// delay(2000);
// }
// /***************************************************
// This is an example for our Adafruit 16-channel PWM & Servo driver
// Servo test - this will drive 8 servos, one after the other on the
// first 8 pins of the PCA9685
// Pick one up today in the adafruit shop!
// ------> http://www.adafruit.com/products/815
// These drivers use I2C to communicate, 2 pins are required to
// interface.
// 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);
// you can also call it with a different address and I2C interface
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40, Wire);
// 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!
// Microservo: Min 80 and max 540 works.
#define SERVOMIN 80 // This is the 'minimum' pulse length count (out of 4096)
#define SERVOMAX 540 // This is the 'maximum' pulse length count (out of 4096)
#define USMIN 600 // This is the rounded 'minimum' microsecond length based on the minimum pulse of 150
#define USMAX 2400 // This is the rounded 'maximum' microsecond length based on the maximum pulse of 600
#define SERVO_FREQ 50 // Analog servos run at ~50 Hz updates
// our servo # counter
uint8_t servonum = 0; // Donât rely on servonum as a global iterator unless youâre sweeping/testing. Prefer direct channel numbers, descriptive names, or functions for reusable, modular code.
// â
Create helper functions like setServoAngle(channel, degrees) if you want to work in angles.
void setup() {
Serial.begin(9600);
Serial.println("8 channel Servo test!");
pwm.begin();
/*
* In theory the internal oscillator (clock) is 25MHz but it really isn't
* that precise. You can 'calibrate' this by tweaking this number until
* you get the PWM update frequency you're expecting!
* The int.osc. for the PCA9685 chip is a range between about 23-27MHz and
* is used for calculating things like writeMicroseconds()
* Analog servos run at ~50 Hz updates, It is importaint to use an
* oscilloscope in setting the int.osc frequency for the I2C PCA9685 chip.
* 1) Attach the oscilloscope to one of the PWM signal pins and ground on
* the I2C PCA9685 chip you are setting the value for.
* 2) Adjust setOscillatorFrequency() until the PWM update frequency is the
* expected value (50Hz for most ESCs)
* Setting the value here is specific to each individual I2C PCA9685 chip and
* affects the calculations for the PWM update frequency.
* Failure to correctly set the int.osc value will cause unexpected PWM results
*/
pwm.setOscillatorFrequency(27000000);
pwm.setPWMFreq(SERVO_FREQ); // Analog servos run at ~50 Hz updates
delay(10);
}
// 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. It's not precise!
void setServoPulse(uint8_t n, double pulse) {
double pulselength;
pulselength = 1000000; // 1,000,000 us per second
pulselength /= SERVO_FREQ; // Analog servos run at ~60 Hz updates
Serial.print(pulselength); Serial.println(" us per period");
pulselength /= 4096; // 12 bits of resolution
Serial.print(pulselength); Serial.println(" us per bit");
pulse *= 1000000; // convert input seconds to us
pulse /= pulselength;
Serial.println(pulse);
pwm.setPWM(n, 0, pulse);
}
void loop() {
Serial.println("Moving servos...");
for (uint8_t i = 0; i < 6; i++) {
// Sweep each servo one at a time
for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++) {
pwm.setPWM(i, 0, pulselen);
delay(10);
}
delay(250); // Optional pause between servos
}
// Drive each servo one at a time using setPWM()
// Serial.println(servonum);
// for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++) {
// pwm.setPWM(servonum, 0, pulselen);
// delay(10); // Slow it down for visual feedback
// }
// delay(500);
// for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--) {
// pwm.setPWM(servonum, 0, pulselen);
// }
// delay(500);
// // Drive each servo one at a time using writeMicroseconds(), it's not precise due to calculation rounding!
// // The writeMicroseconds() function is used to mimic the Arduino Servo library writeMicroseconds() behavior.
// for (uint16_t microsec = USMIN; microsec < USMAX; microsec++) {
// pwm.writeMicroseconds(servonum, microsec);
// }
// delay(500);
// for (uint16_t microsec = USMAX; microsec > USMIN; microsec--) {
// pwm.writeMicroseconds(servonum, microsec);
// }
// delay(500);
// servonum++;
// if (servonum > 7) servonum = 0; // Testing the first 8 servo channels
}
General tips:
- Use short, thick wires between your 5V power supply and the PCA9685âs blue terminal block.
- Make sure GND is shared between Arduino and power supply.
Issues:
- Motor 15 was overheating severely and making a noise ⊠Why? Is there something in the way I wrote the code that makes the PWM to keep getting sent there?!
- Next let's work on eliminating sudden movements of any kind from our SG90 MicroServos.
- Track the last known pulse for each servo
- Start the sweep from that last position
- Only move incrementally from where it last left off
Solution with Serial Input working and fail safe
Right now the servos are going from initial position to final angle position very fast and then back to initial position in a relatively controlled speed. I want the speed to be controlled all throughout. Changing the value of delay didn't solve the issue of very sudden movements. Maybe it's just that we're consistently moving servos from 0Âș to 180Âș and therefore they have to go back to 0 to start the loop.
â The fix:Â Store the last position and ease into new motion
Working Code
GPT: Let me know if you'd like to:
- Set angle likeÂ
angle 3 90
- Control speed likeÂ
speed 5
- Create simple motion sequences Could make a grid and then if MediaPipe is on that spot, move fabric there. Need to get a map of positions though.
Full code for 2 boards and 31 servos
Code from PCA9685 library without any changes
/***************************************************
This is an example for our Adafruit 16-channel PWM & Servo driver
Servo test - this will drive 8 servos, one after the other on the
first 8 pins of the PCA9685
Pick one up today in the adafruit shop!
------> http://www.adafruit.com/products/815
These drivers use I2C to communicate, 2 pins are required to
interface.
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);
// you can also call it with a different address and I2C interface
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40, Wire);
// 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)
#define USMIN 600 // This is the rounded 'minimum' microsecond length based on the minimum pulse of 150
#define USMAX 2400 // This is the rounded 'maximum' microsecond length based on the maximum pulse of 600
#define SERVO_FREQ 50 // Analog servos run at ~50 Hz updates
// our servo # counter
uint8_t servonum = 0;
void setup() {
Serial.begin(9600);
Serial.println("8 channel Servo test!");
pwm.begin();
/*
* In theory the internal oscillator (clock) is 25MHz but it really isn't
* that precise. You can 'calibrate' this by tweaking this number until
* you get the PWM update frequency you're expecting!
* The int.osc. for the PCA9685 chip is a range between about 23-27MHz and
* is used for calculating things like writeMicroseconds()
* Analog servos run at ~50 Hz updates, It is importaint to use an
* oscilloscope in setting the int.osc frequency for the I2C PCA9685 chip.
* 1) Attach the oscilloscope to one of the PWM signal pins and ground on
* the I2C PCA9685 chip you are setting the value for.
* 2) Adjust setOscillatorFrequency() until the PWM update frequency is the
* expected value (50Hz for most ESCs)
* Setting the value here is specific to each individual I2C PCA9685 chip and
* affects the calculations for the PWM update frequency.
* Failure to correctly set the int.osc value will cause unexpected PWM results
*/
pwm.setOscillatorFrequency(27000000);
pwm.setPWMFreq(SERVO_FREQ); // Analog servos run at ~50 Hz updates
delay(10);
}
// 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. It's not precise!
void setServoPulse(uint8_t n, double pulse) {
double pulselength;
pulselength = 1000000; // 1,000,000 us per second
pulselength /= SERVO_FREQ; // Analog servos run at ~60 Hz updates
Serial.print(pulselength); Serial.println(" us per period");
pulselength /= 4096; // 12 bits of resolution
Serial.print(pulselength); Serial.println(" us per bit");
pulse *= 1000000; // convert input seconds to us
pulse /= pulselength;
Serial.println(pulse);
pwm.setPWM(n, 0, pulse);
}
void loop() {
// Drive each servo one at a time using setPWM()
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);
// Drive each servo one at a time using writeMicroseconds(), it's not precise due to calculation rounding!
// The writeMicroseconds() function is used to mimic the Arduino Servo library writeMicroseconds() behavior.
for (uint16_t microsec = USMIN; microsec < USMAX; microsec++) {
pwm.writeMicroseconds(servonum, microsec);
}
delay(500);
for (uint16_t microsec = USMAX; microsec > USMIN; microsec--) {
pwm.writeMicroseconds(servonum, microsec);
}
delay(500);
servonum++;
if (servonum > 7) servonum = 0; // Testing the first 8 servo channels
}
SG90 9G MicroServo datasheet:
[Applications] SG90 9G servo motor, compatible with RC helicopters, micro robots, robot arm and ships etc. Support various remote control toys and Arduino project [Interface type] This SG90 servo is compatible with JR & Futaba interface [Operating speed] 0.12 second/ 60degree ( 4.8V no load), 0.1 second / 60degree ( 6.0V no load), Max rotation angle:180 degrees [Stalling torque] 17.5oz/in (1kg/cm); Dead zone width: 7 microseconds; Amplifier type: analog controller; Working voltage: 4.8Vïœ6.0V [Reminder] The starting current of the analog servo motor must be greater than 1A. SG90 Servo is an analog servo and needs to continuously provide PMW signal to work normally
Pulse Signal PWM: 50Hz/O.5-2.5ms No-load Speed: 0.09S/60 degree@4.8V Operating Voltage Range: 4.8V ~6.0V Operating Temperature Range: -25°c ~ 70°c
Mechanical Specification: âLimit angle: 200°± 1° âWeight: 9 ± 1 g âConnector wire gauge: ïŒ28 PVC âConnector wire length: 250 ± 5 mm âSpline: 21T âHorn type: Cross,star and bar-type âExcessive play: â€0.5°
Control Specification: âControl system: Change the pulse width âAmplifier type: Analog controller âOperating trave: l120°±10° (900â2100 ÎŒsec) âLeft&Right travelling Angle deviation: †6° âCentering deviation: †1° âNeutral position: 1500 ÎŒsec âDead band width: 7 ÎŒsec âRotating direction: Counter Clockwise (900â2100ÎŒsec) âPulse width range: 900â2100 ÎŒsec âMaximum travel: About 120° (900â2100 ÎŒsec)
Wire Layout: âElectrical Specification (Function of the Performance): Operating âspeed (at no load): 0.09±0.01 sec/60°(4.8V ) 0.08±0.01 sec/60°(6V) âRunning current (at no load): 400±30mA (4.8V ) 500±30mA(6V) âStall torque (at locked): 2.0±0.20kg·cm (4.8V ) 2.2±0.20kg·cm(6V) âStall current (at locked): 1300±40mA (4.8V ) 1600±50mA(6v) âIdle current (at stopped): 6±1mA (4.8V ) 6±1mA(6v) âRunning life(at no load): >350000 Turns(4.8V ) >320000 Turns(4.8V )
âIf you want, I can now show you a simple step-by-step calibration guide to figure out SERVOMIN and SERVOMAX just by uploading sketches and watching your servo move (no special equipment).â
Working code with 5V 15A
/***************************************************
This is an example for our Adafruit 16-channel PWM & Servo driver
Servo test - this will drive 8 servos, one after the other on the
first 8 pins of the PCA9685
Pick one up today in the adafruit shop!
------> http://www.adafruit.com/products/815
These drivers use I2C to communicate, 2 pins are required to
interface.
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>
// Setup for two PCA9685 boards
Adafruit_PWMServoDriver pwm1 = Adafruit_PWMServoDriver(0x40, Wire);
Adafruit_PWMServoDriver pwm2 = Adafruit_PWMServoDriver(0x41, Wire);
// you can also call it with a different address and I2C interface
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40, Wire);
// 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!
// Calibrated pulse length counts (ticks)
#define SERVOMIN 180 // This is the 'minimum' pulse length count (out of 4096)
#define SERVOMAX 440 // This is the 'maximum' pulse length count (out of 4096)
// Calibrated pulse lengths in microseconds
#define USMIN 900 // This is the rounded 'minimum' microsecond length based on the minimum pulse of updated 180
#define USMAX 2100 // This is the rounded 'maximum' microsecond length based on the maximum pulse of updated 440
#define SERVO_FREQ 50 // Analog servos run at ~50 Hz updates
// our servo # counter
// uint8_t servonum = 0; // NOT BEING USED!
unsigned long lastAliveTime = 0;
void setup() {
Serial.begin(9600);
while (!Serial) {
; // Wait for Serial to connect (needed for native USB boards)
}
Serial.println("Starting 32 servo sweep...");
/*
* In theory the internal oscillator (clock) is 25MHz but it really isn't
* that precise. You can 'calibrate' this by tweaking this number until
* you get the PWM update frequency you're expecting!
* The int.osc. for the PCA9685 chip is a range between about 23-27MHz and
* is used for calculating things like writeMicroseconds()
* Analog servos run at ~50 Hz updates, It is importaint to use an
* oscilloscope in setting the int.osc frequency for the I2C PCA9685 chip.
* 1) Attach the oscilloscope to one of the PWM signal pins and ground on
* the I2C PCA9685 chip you are setting the value for.
* 2) Adjust setOscillatorFrequency() until the PWM update frequency is the
* expected value (50Hz for most ESCs)
* Setting the value here is specific to each individual I2C PCA9685 chip and
* affects the calculations for the PWM update frequency.
* Failure to correctly set the int.osc value will cause unexpected PWM results
*/
// Initialize both boards
pwm1.begin();
pwm1.setOscillatorFrequency(27000000);
pwm1.setPWMFreq(SERVO_FREQ);
pwm2.begin();
pwm2.setOscillatorFrequency(27000000);
pwm2.setPWMFreq(SERVO_FREQ);
delay(10);
}
// 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. It's not precise!
// void setServoPulse(uint8_t n, double pulse) {
// double pulselength;
// pulselength = 1000000; // 1,000,000 us per second
// pulselength /= SERVO_FREQ; // Analog servos run at ~60 Hz updates
// Serial.print(pulselength); Serial.println(" us per period");
// pulselength /= 4096; // 12 bits of resolution
// Serial.print(pulselength); Serial.println(" us per bit");
// pulse *= 1000000; // convert input seconds to us
// pulse /= pulselength;
// Serial.println(pulse);
// pwm.setPWM(n, 0, pulse);
// }
void loop() {
// Drive each servo one at a time using setPWM()
// Check if 1 second passed
if (millis() - lastAliveTime >= 1000) {
Serial.println("I'm alive");
lastAliveTime = millis();
}
// === Sweep servos on Board 1 ===
for (uint8_t servoNum = 0; servoNum < 16; servoNum++) {
Serial.print("Board 1 - Sweeping servo ");
Serial.println(servoNum);
// Sweep up
for (uint16_t pulselen = SERVOMIN; pulselen <= SERVOMAX; pulselen++) {
pwm1.setPWM(servoNum, 0, pulselen);
delay(5); // adjust delay for speed
}
delay(500); // hold at max // MIGHT BE TOO LONG TO BE INTERACTIVE
// Sweep down
for (uint16_t pulselen = SERVOMAX; pulselen >= SERVOMIN; pulselen--) {
pwm1.setPWM(servoNum, 0, pulselen);
delay(5);
}
delay(500); // hold at min
}
// === Sweep servos on Board 2 ===
for (uint8_t servoNum = 0; servoNum < 16; servoNum++) {
Serial.print("Board 2 - Sweeping servo ");
Serial.println(servoNum);
// Sweep up
for (uint16_t pulselen = SERVOMIN; pulselen <= SERVOMAX; pulselen++) {
pwm2.setPWM(servoNum, 0, pulselen);
delay(5); // adjust delay for speed
}
delay(500); // hold at max
// Sweep down
for (uint16_t pulselen = SERVOMAX; pulselen >= SERVOMIN; pulselen--) {
pwm2.setPWM(servoNum, 0, pulselen);
delay(5);
}
delay(500); // hold at min
}
}
Sweeping through all at the same time on 5V 10A
/***************************************************
This is an example for our Adafruit 16-channel PWM & Servo driver
Servo test - this will drive 8 servos, one after the other on the
first 8 pins of the PCA9685
Pick one up today in the adafruit shop!
------> http://www.adafruit.com/products/815
These drivers use I2C to communicate, 2 pins are required to
interface.
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>
// Setup for two PCA9685 boards
Adafruit_PWMServoDriver pwm1 = Adafruit_PWMServoDriver(0x40, Wire);
Adafruit_PWMServoDriver pwm2 = Adafruit_PWMServoDriver(0x41, Wire);
// you can also call it with a different address and I2C interface
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40, Wire);
// 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!
// Calibrated pulse length counts (ticks)
#define SERVOMIN 180 // This is the 'minimum' pulse length count (out of 4096)
#define SERVOMAX 440 // This is the 'maximum' pulse length count (out of 4096)
// Calibrated pulse lengths in microseconds
#define USMIN 900 // This is the rounded 'minimum' microsecond length based on the minimum pulse of updated 180
#define USMAX 2100 // This is the rounded 'maximum' microsecond length based on the maximum pulse of updated 440
#define SERVO_FREQ 50 // Analog servos run at ~50 Hz updates
// our servo # counter
// uint8_t servonum = 0; // NOT BEING USED!
unsigned long lastAliveTime = 0;
// === Manual Servo Selection ===
// Add servo numbers you want to move in these arrays (0-15)
uint8_t board1_servos[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; // Example â change/add/remove as desired
uint8_t board2_servos[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; // Example â change/add/remove as desired
// Size of each list (automatically calculated)
const uint8_t board1_servo_count = sizeof(board1_servos) / sizeof(board1_servos[0]);
const uint8_t board2_servo_count = sizeof(board2_servos) / sizeof(board2_servos[0]);
void setup() {
Serial.begin(9600);
while (!Serial) {
; // Wait for Serial to connect (needed for native USB boards)
}
Serial.println("Starting 32 servo sweep...");
/*
* In theory the internal oscillator (clock) is 25MHz but it really isn't
* that precise. You can 'calibrate' this by tweaking this number until
* you get the PWM update frequency you're expecting!
* The int.osc. for the PCA9685 chip is a range between about 23-27MHz and
* is used for calculating things like writeMicroseconds()
* Analog servos run at ~50 Hz updates, It is importaint to use an
* oscilloscope in setting the int.osc frequency for the I2C PCA9685 chip.
* 1) Attach the oscilloscope to one of the PWM signal pins and ground on
* the I2C PCA9685 chip you are setting the value for.
* 2) Adjust setOscillatorFrequency() until the PWM update frequency is the
* expected value (50Hz for most ESCs)
* Setting the value here is specific to each individual I2C PCA9685 chip and
* affects the calculations for the PWM update frequency.
* Failure to correctly set the int.osc value will cause unexpected PWM results
*/
// Initialize both boards
pwm1.begin();
pwm1.setOscillatorFrequency(27000000);
pwm1.setPWMFreq(SERVO_FREQ);
pwm2.begin();
pwm2.setOscillatorFrequency(27000000);
pwm2.setPWMFreq(SERVO_FREQ);
delay(10);
}
// 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. It's not precise!
// void setServoPulse(uint8_t n, double pulse) {
// double pulselength;
// pulselength = 1000000; // 1,000,000 us per second
// pulselength /= SERVO_FREQ; // Analog servos run at ~60 Hz updates
// Serial.print(pulselength); Serial.println(" us per period");
// pulselength /= 4096; // 12 bits of resolution
// Serial.print(pulselength); Serial.println(" us per bit");
// pulse *= 1000000; // convert input seconds to us
// pulse /= pulselength;
// Serial.println(pulse);
// pwm.setPWM(n, 0, pulse);
// }
void loop() {
// Drive each servo one at a time using setPWM()
// Check if 1 second passed
if (millis() - lastAliveTime >= 1000) {
Serial.println("I'm alive");
lastAliveTime = millis();
}
// Sweep up
for (uint16_t pulselen = SERVOMIN; pulselen <= SERVOMAX; pulselen++) {
Serial.print("Sweeping up - Pulse: ");
Serial.println(pulselen);
// Board 1 selected servos
for (uint8_t i = 0; i < board1_servo_count; i++) {
pwm1.setPWM(board1_servos[i], 0, pulselen);
}
// Board 2 selected servos
for (uint8_t i = 0; i < board2_servo_count; i++) {
pwm2.setPWM(board2_servos[i], 0, pulselen);
}
delay(5);
}
delay(500); // hold at max
// Sweep down
for (uint16_t pulselen = SERVOMAX; pulselen >= SERVOMIN; pulselen--) {
Serial.print("Sweeping down - Pulse: ");
Serial.println(pulselen);
// Board 1 selected servos
for (uint8_t i = 0; i < board1_servo_count; i++) {
pwm1.setPWM(board1_servos[i], 0, pulselen);
}
// Board 2 selected servos
for (uint8_t i = 0; i < board2_servo_count; i++) {
pwm2.setPWM(board2_servos[i], 0, pulselen);
}
delay(5);
}
delay(500); // hold at min
// // === Sweep servos on Board 1 ===
// for (uint8_t servoNum = 0; servoNum < 16; servoNum++) {
// Serial.print("Board 1 - Sweeping servo ");
// Serial.println(servoNum);
// // Sweep up
// for (uint16_t pulselen = SERVOMIN; pulselen <= SERVOMAX; pulselen++) {
// pwm1.setPWM(servoNum, 0, pulselen);
// delay(5); // adjust delay for speed
// }
// delay(500); // hold at max // MIGHT BE TOO LONG TO BE INTERACTIVE
// // Sweep down
// for (uint16_t pulselen = SERVOMAX; pulselen >= SERVOMIN; pulselen--) {
// pwm1.setPWM(servoNum, 0, pulselen);
// delay(5);
// }
// delay(500); // hold at min
// }
// // === Sweep servos on Board 2 ===
// for (uint8_t servoNum = 0; servoNum < 16; servoNum++) {
// Serial.print("Board 2 - Sweeping servo ");
// Serial.println(servoNum);
// // Sweep up
// for (uint16_t pulselen = SERVOMIN; pulselen <= SERVOMAX; pulselen++) {
// pwm2.setPWM(servoNum, 0, pulselen);
// delay(5); // adjust delay for speed
// }
// delay(500); // hold at max
// // Sweep down
// for (uint16_t pulselen = SERVOMAX; pulselen >= SERVOMIN; pulselen--) {
// pwm2.setPWM(servoNum, 0, pulselen);
// delay(5);
// }
// delay(500); // hold at min
// }
}