Newbie question - best way to approach a problem Servos and i2c

First an apology to the proficient amongst you - please bear with my questions, I am trying my best to learn and don't want to reinvent the wheel, (or learn poor approaches or techniques)

The project outline is very simple. 12 servos, with their position each being defined by a simple toggle switch. Switch position 1 = servo position 1, switch position 0 = servo position 0. The actual positions in either state to be adjusted by values in the code.

So we have 12 switches, 12 servos.

I can see several different ways of approaching the problem but are in doubt as to which way would be the most efficient way to code / apply a Mega 2560 and perhaps a AdaFruit 16 channel 12 bit i2c servo driver.

I would also like the option of using some of the free inputs / outputs on the Mega for other elements of the project.

Any help in respect to the most efficient ways to answer this problem would be gratefully listened to. Its very easy to go off on a tangent and learn bad / inefficient techniques from day one, personally I would prefer to see how a much more experienced user would approach the problem - and learn from that expertise and approach

Thanks in anticipation of your help

The first thing you need to do is to sort out the power supply that will drive the servos. You will probably need at least an amp per servo. Are they being moved all at once or one at a time. You need good supply decoupling on all the servos as well.

Once that is sorted out then it looks perfectly straightforward, the Mega ( and indeed the Uno ) will drive 12 servos directly with the servo libiary. You have plenty of I/O pins so you can give one to each switch. Wire the switches between the input and ground and enable the internal pull up resistors. Then a simple loop using an array for the input pin number, another tow for the servo position and you code should come out at about ten lines.

Thanks Mike

Power supply sorted - I have decided to use a PC 500w supply, so the 5v rail has 30amps possible. I will use that for the servo power supply

No problem with the inputs - that's quite simple. The question regarding that is one of is it best to use one switch per input line or to perhaps consider a matrix type arrangement?

I appreciate the Mega has 14 PWM outputs possible, but did like the Adafruit board, that seems quite an elegant way to drive the servos, especially in the context of the ease to input the 5v supply to the servos.

I anticipate each servo moving to and remaining at a new position individually.

Any suggestions / examples on code that is suited?

Regards

The question regarding that is one of is it best to use one switch per input line or to perhaps consider a matrix type arrangement?

Well you could use a Charliplexed keypad:-
http://forum.arduino.cc/index.php?topic=141978.0;topicseen

But if you have the pins then I would go for one pin per switch for simplicity.

Hi Mike,

I like the idea of the one switch per input for simplicity.

So, how is best to assign pin 1 (high) to a corresponding servo and position on the Adafruit using the i2c connection?

Do you have an example of code to point me in the right direction?

I've never used the adafruit board, but if you just use the Arduino pins for the servo control and its corresponding switch, you have something like this in loop():

if (servo1Switch == HIGH) //unpressed if using pullup
{
servo1.write(position1); // define position1 somewhere else
}
else //pressed
{
servo1.write(position2); // define position2 somewhere else
}

I appreciate the Mega has 14 PWM outputs

Servos don't need PWM pins.

Hi MR - I am confused, I thought that servo control was via PWM with the servo reading the frequency in ms as the desired position? It seems that around 1.5ms being center position.

If the servo does not use PWM how does it get controlled by the mega?

For a start, ms isn't a measure of frequency, it's a measure of actual time.

So yes, you're right that 1.5ms is the centre point, but not that it's a frequency, it's a duration.

The servo library does all the necessary voodoo to send a pulse of whataver length you require. You can even use degrees, and it will convert your (say) 90 degrees to a pulse of 1.5ms.

The library also takes care of sending the repeat pulses, as long as you keep the servo attached.

Have a look at the servo examples in the IDE at File > Examples > Servo.

Thanks for that MR.

I have previously used the mega to drive single servos. However this time I would like to use the ADAfruit - in order to keep the overhead to a minimum on the Mega - any suggestions for code to do that from the Mega, using the variable states on the 12 selected inputs to the mega?

How do I select / assign each servo connected to the Adafruit and link that to the outputs on the Mega?

I've never used the adafruit servo driver, but it must have "some" way of identifying each servo- you'll need to check adafruit's website for that. They normally have excellent tutorials. Then my code snippet above would just have the appropriate adafruit stuff where I did a servo.write:

if (servo1Switch == HIGH) //unpressed if using pullup
{
  //do whatever adafruit says you must do to move servo1 to pos 1
}
else //pressed
{
  //do whatever adafruit says you must do to move servo2 to pos 2
}

It wouldn't surprise me in the slightest if the adadfruit library code to move a servo is very similar to the servo library's, but you'll need to check their site for an example.

I have looked at the Adafruit library - no problem getting the servos to work, just a little unsure how to update the readings between the Mega and the Adafruit,

Why are you using a hardware board for driving the servo's. The software servo's library is more than enough for what you need. Why complicate things.
Using that board you need to send it values to switch the output on and off during an internal 16 bit count. Using the library you just have to specify either a delay or an angle.

You use arrays to hold the PIN number, servo number, first servo position and second servo position. Do you know what an array is?

The hardware board is easier to locate in the project I have in mind, also the power supply is easier to arrange for the servos - hence why

An explanation of arrays would be appreciated - as I said New to this

Thanks

Using an external shield makes the wiring easier...sometimes a LOT easier.

That's what I thought - the code might be a little more complex but overall the project will be easier

wg0z:
Using an external shield makes the wiring easier...sometimes a LOT easier.

.well I thing that is rubbish but play it that way if you want.

Read about arrays here
http://www.thebox.myzen.co.uk/Tutorial/Arrays.html

Thanks Mike - part of the exercise is about me developing skills with the code and hardware (learning)

I appreciate the article about arrays.

I have tried the example code for the Adafruit board and understand how it is working (in that example)

#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!");

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;
}

I can see how the above code sequentially moves each servo channel from one end of its movement to the other.

So moving on from that - how would you suggest as the best way to take the position of some toggle switches connected as inputs to the Mega (either high / low value) and output that as a value (position) to the corresponding servo connected to the Adafruit (via the i2c line)

Thanks again for your help

Please read this:-
How to use this forum
Because your post is breaking the rules about posting code.

how would you suggest as the best way to take the position of some toggle switches

Replace the loop function with:-

void loop() {
for(int servonum=0;servonum<numberOfServos; servonum++){
if(digitalRead(switchPin[servonum] == LOW {
  pwm.setPWM(servonum, 0, servoPosLeft[servonum]);
 }
else {
 pwm.setPWM(servonum, 0, servoRight[servonum]);
 }
}

Note that the variables used here will have to be set up. Variables that look like servoPosLeft[servonum] are arrays and they should be set up with the left position of each servo. Likewise for servoRight[servonum].

The array switchPin[servonum] contains the pin numbers for each switch corresponding to each servo.

Note:- not sure what the 0 parameter is in the pwm.setPWM call.