Simultaneous but independent speed control of 2 servos

Hi
I am an absolute ignoramus to all this and have very recently started to play with an arduino and some servos.
I am trying to create a skeet shooting laser simulator(and to be honest that is the only thing i for see my self doing with the arduino,for now at least).
I have created a pcb board with all the voltage regulation for the two servos and by some extensive reading on this forum and the web in general I have managed to create a crude but effective skeet simulator.The device basically moves two servos mounted with lasers from 0 to 110 degrees at a particular speed using delays.It all works fine for my singles(one motor at a time)however on the doubles I would like to be able to have the servos start together but travel at slightly different speeds(thus giving the effect of wind against the clays).

I have the doubles working fine with both motors having the same speed,I just cant get them working with different speeds.
To be honest I am very suprised to have gotten this far(necessity being the mother of all inventions and all!!)
I have noticed that i should be using millis intead of delays and possibly a library called VarSpeedServo (just dont know how to gel it all together).
I apologise for my laboriously long post and would greatly appreciate any assistance on this please.

My problem is in the function called Double, that is where I would like to control the speed of each servo independently.

Here is the code so far:

#include <Servo.h> 
 
Servo myservoP;  // create servo object to control a servo 
Servo myservoM;  // create servo object to control a servo             
 
int posP = 0;    // variable to store the servo position Pull
int posM = 0;    // variable to store the servo position Mark  
int randNumber = 0;
int ledPinP =  12;
int ledPinM =  13;
int ledPinD = 10;
int ledPinS = 11;
int switchPin = 7;
int count = 0;
boolean lastButton = LOW;
boolean currentButton = LOW;

void setup()
{ 
  pinMode(switchPin, INPUT);
  pinMode(ledPinD, OUTPUT);
  pinMode(ledPinP, OUTPUT); 
  pinMode(ledPinM, OUTPUT);
  pinMode(ledPinS, OUTPUT);
  myservoP.attach(9);  // attaches the servo on pin 9 to the servo object 
  myservoM.attach(8);  // attaches the servo on pin 8 to the servo object
 }
   boolean debounce(boolean last) 
  {
   boolean current = digitalRead(switchPin);
  
    if (last != current)
  {
    delay(5);
    current = digitalRead(switchPin);
  }
  return current;
}
  
  
  
  
  
void Pull()            // Function for Pull
{
   myservoP.write(0); 
   beep();
  randNumber = random(200, 3000);
   delay(randNumber);      //Random start (0.2-3 sec)
   for(posP = 0; posP < 110; posP += 1)
   {
   digitalWrite(ledPinP, HIGH);
  myservoP.write(posP);              
   delay(17);               // Speed of clay at +- 1.6 sec 0-110 degrees
   digitalWrite(ledPinP, LOW);  
   }
 
} 

void Mark()              // Function for Mark
{
  myservoM.write(0); 
  beep();
  randNumber = random(200, 3000);
   delay(randNumber);      //Random start (0.2-3 sec)
   for(posM = 0; posM < 110; posM += 1) 
   {
   digitalWrite(ledPinM, HIGH);
  myservoM.write(posM);              
   delay(18);              // Speed of clay at +- 1.7 sec 0-110 degrees
   digitalWrite(ledPinM, LOW);          
   }
} 

void Double()             // Function for Doubles  
{
  myservoP.write(0);
  myservoM.write(0);
  beep();
  randNumber = random(200, 3000);
   delay(randNumber);              //Random start (0.2-3 sec)
   for(posP = 0; posP < 110; posP += 1)
   { 
  digitalWrite(ledPinP, HIGH);
  myservoP.write(posP); 
    delay(17);                 // Speed of clay at +- 1.7 sec 0-110 degrees
    for(posM = 0; posM < 110; posM += 1)
    digitalWrite(ledPinM, HIGH);
   myservoM.write(posM);  
    delay(19);                 // Speed of clay at +- 1.9 sec 0-110 degrees
    digitalWrite(ledPinP, LOW);
    digitalWrite(ledPinM, LOW);  
   }
}


void skeet()
{
  delay (55000);
 Pull();              //station 2
  delay (10000);   
 Double();
  delay (25000);
 Pull();              //station 3 
  delay (10000);
 Double();
  delay (25000);
 Pull();              //station 4
  delay (10000);
 Mark();
  delay (10000);
 Double();
  delay (10000);
 Double();
  delay (25000);
 Mark();              //station 5
  delay (10000);
 Double();
  delay (25000);
 Mark();              //station 6 
  delay (10000);
 Double();
  digitalWrite(ledPinS, LOW);
  count=1;
  while (count=1)
  {}
} 



void doubles()
{
  delay (25000); 
 Double();
  delay (10000);
 Double();
  delay (10000);       //station 2(3 x double)
 Double();
  delay (10000); 
 Double();          //station 3(3 x double)  
  delay (10000); 
 Double();
  delay (10000);
 Double();
  delay (10000); 
 Double();          //station 4(6 x double)
  delay (10000);  
 Double();
  delay (10000);
 Double();
  delay (10000); 
 Double();
  delay (10000);  
 Double();
  delay (10000);
 Double();
  delay (10000); 
 Double();          //station 5(3 x double)
  delay (10000);  
 Double();
  delay (10000);
 Double();
  delay (10000); 
 Double();         //station 6(3 x double)
  delay (10000);   
 Double();
  delay (10000);
 Double();
  digitalWrite(ledPinD, LOW);
 count=1;
  while (count=1)
  {}
}

void beep()
{
 tone(6, 10,0);
   delay(1000);
 noTone(6);
   delay(1000);
 tone(6, 10,0);
   delay(1000);
 noTone(6);
   delay(1000);
 tone(6, 10,0);
   delay(1000);
 noTone(6);  
}

void loop() 

{
  
     delay(500);
  currentButton = debounce(lastButton);
  if (lastButton == LOW && currentButton == HIGH)
  {
    digitalWrite(ledPinD, HIGH);
    doubles();
  }
      else 
    {
      digitalWrite(ledPinS, HIGH); 
       skeet(); 
    }

  lastButton = currentButton;
   
}

Take a look at the BlinkWithoutDelay example. It shows you how to achieve that.

Thanks for your quick reply. However like I explained I am not a programmer nor have I studied electronics, my entire code is regurgitated and modified from examples on the net. Everything I got working is by trial and error(i dont fully comprehend how to modify the "blink without delay" code to suit my purpose. I tried using a for loop within a for loop, only to get the same problems everyone else has experienced. The code I have as crude as it is, it actually does everything I need. I need some kind of code example to use in my Double function so that I may change the speeds of the servos(manually via the arduino code). I am sure this seems all too simple to you guys,but it has me baffled. Thanks anyway.

The thing you have to remember is that everywhere you have a call to "delay", the processor does nothing until that delay time is up. If all you're ever controlling is a single item, then 9 times out of 10, using "delay" will be OK. Add just one other thing to be controlled at a different rate, you cannot use "delay". The Blink Without Delay example has about four significant lines of code, but the implications are huge. Suggest you persevere.

Read this:

http://www.gammon.com.au/blink

Thank you very much for your reply. The link you showed me certainly explained the use of millis better. However although I now understand how to use millis to start servos or light up leds at different times in a program, I still dont understand how I would use millis in my for loop in order to slow down the speed with wich the servo will go from 0-110 degrees. I am sorry I have tried it and all I get is the servo going as fast as it can from 0-110 degrees. Thanks anyway.

I still dont understand how I would use millis in my for loop in order to slow down the speed

That's because you can't use a for loop like this - you have to lose the for loop, and use the looping of the "loop" function and a series of time-outs to drive your servo. There is also somewhere, a variable speed servo library.

Post your code again, and we'll try to help.

Thank you for your reply again.
This really is not my field and trust me when I say its not from lack of trying that I cant get it right.
I am posting a shorter version of my code which includes the function that I am having trouble with.
I am happy to keep the delay for the single events(although it is frowned uppon in your programming circles).
So its just the doubles that I need help with starting the servos simultaneously and covering the same range of motion(0-110 degrees), but at different speeds.
Thanks again.
P.S.
I tried to use the VarSpeed library with no success either.

#include <Servo.h> 
 
Servo myservoP;  // create servo object to control a servo 
Servo myservoM;  // create servo object to control a servo             
 
const unsigned long SinglePullSpeed = 1550;
const unsigned long SingleMarkSpeed = 1650;
const unsigned long DoublePullSpeed = 1550;
const unsigned long DoubleMarkSpeed = 1650;


// Variable holding the timer value so far. One for each "Timer"
unsigned long SinglePullTimer;
unsigned long SingleMarkTimer;
unsigned long DoublePullTimer;
unsigned long DoubleMarkTimer;
 
int posP = 0;    // variable to store the servo position Pull
int posM = 0;    // variable to store the servo position Mark  
int randNumber = 0;
int ledPinP =  12;
int ledPinM =  13;
int ledPinD = 10;
int ledPinS = 11;
int switchPin = 7;
int count = 0;
boolean lastButton = LOW;
boolean currentButton = LOW;

void setup()
{ 
  SinglePullTimer = millis ();
  SingleMarkTimer = millis ();
  DoublePullTimer = millis ();
  DoubleMarkTimer = millis ();
  pinMode(switchPin, INPUT);
  pinMode(ledPinD, OUTPUT);
  pinMode(ledPinP, OUTPUT); 
  pinMode(ledPinM, OUTPUT);
  pinMode(ledPinS, OUTPUT);
  myservoP.attach(9);  // attaches the servo on pin 9 to the servo object 
  myservoM.attach(8);  // attaches the servo on pin 8 to the servo object
 }
   boolean debounce(boolean last) 
  {
   boolean current = digitalRead(switchPin);
  
    if (last != current)
  {
    delay(5);
    current = digitalRead(switchPin);
  }
  return current;
}
  
void Double()             // Function for Doubles  
{
  myservoP.write(0);
  myservoM.write(0);
  beep();
  randNumber = random(200, 3000);
   delay(randNumber);              //Random start (0.2-3 sec)
   for(posP = 0; posP < 110; posP += 1)
   {
    digitalWrite(ledPinP, HIGH);
  myservoP.write(posP); 
   //  delay(8.5);                 // Speed of clay at +- 1.7 sec 0-110 degrees
     digitalWrite(ledPinM, HIGH);
   myservoM.write(posP);  
  //  delay(8.5);                 // Speed of clay at +- 1.7 sec 0-110 degrees
    digitalWrite(ledPinP, LOW);
    digitalWrite(ledPinM, LOW);
  DoublePullTimer = millis ();  
  DoubleMarkTimer = millis (); 
   }
}
void loop() 

{
  
  if ( (millis () - SinglePullTimer) >= SinglePullSpeed)
     Double ();
}
  myservoP.attach(9);  // attaches the servo on pin 9 to the servo object 
  myservoM.attach(8);  // attaches the servo on pin 8 to the servo object
 }
   boolean debounce(boolean last) 
  {
   boolean current = digitalRead(switchPin);
  
    if (last != current)
  {
    delay(5);

A little white space between functions is useful. Noticethatweusewhitespaceeverydaytofacilitatereadingcomprehension.

Using the Tools + Auto Format menu item to get consistent indenting would be appreciated, too. There are a number of styles of coding that dictate where the surly braces go, with respect the the function/statement that they follow, but NONE of them outdents the braces.

The digitalRead function does not return a boolean. It is easier to read and understand your code if the variables match the return type of the function that is providing the value for the variable.

  myservoP.write(0);
  myservoM.write(0);

The instance name does not need to contain my. It doesn't need to contain servo, either. Use some meaningful names. pull and mark would work for me.

void Double()             // Function for Doubles

Really? Who'd have guessed? If you are going to have comments, and I recommend that you do, have some that explain the purpose of the function, not ones that state the obvious.

Where does Double() determine if it is time to move one or the other servos? It doesn't. Just recording when the end of the loop is reached is not useful.

Ok since we are pointing out the obvious,I did mention that I am completly ignorant of programming and have only very recently dove into the deep end of this Arduino world , out of necessity. I am very sorry if the format of my code ofended you in any way, once again I copied 70% of it from other programs and modified what little I could. All I need is a way to run both servos simultaneously from (0-110 degrees) with variable speed. Thanks for the format tips but since I am not planning to become a programmer in the future, I would much rather prefer some assistance on my specific problem.

Thanks for the format tips but since I am not planning to become a programmer in the future, I would much rather prefer some assistance on my specific problem.

Then you should be willing to take 5 minutes to make the code readable. If not, oh well.

akiskalo: Thanks for the format tips but since I am not planning to become a programmer in the future, I would much rather prefer some assistance on my specific problem.

You are seeking assistance, but reject the advice you are given. Proper formatting is like punctuation. It makes what you are writing easier to read. For others, and yourself.

This is the code I originaly had to the best of my abilities.
I hope its clearer to read this time around.
Thanks again

//Olympic Skeet Simulator


#include <Servo.h> 

Servo myservoP;  // create servo object to control a servo 
Servo myservoM;  // create servo object to control a servo             


int pos = 0;    // variable to store the servo position 
int randNumber = 0;
int ledPinP =  12;
int ledPinM =  13;
int ledPinD = 10;
int ledPinS = 11;
int switchPin = 7;
int count = 0;
boolean lastButton = LOW;
boolean currentButton = LOW;




void setup()


{ 
  pinMode(switchPin, INPUT);
  pinMode(ledPinD, OUTPUT);
  pinMode(ledPinP, OUTPUT); 
  pinMode(ledPinM, OUTPUT);
  pinMode(ledPinS, OUTPUT);
  myservoP.attach(9);  // attaches the servo on pin 9 to the servo object 
  myservoM.attach(8);  // attaches the servo on pin 8 to the servo object
}
boolean debounce(boolean last) 
{
  boolean current = digitalRead(switchPin);

  if (last != current)
  {
    delay(5);
    current = digitalRead(switchPin);
  }
  return current;
}





void Pull()            // At a random time(between 0.2-3 sec. sweep from 0-110 degrees at a particular speed


{


  myservoP.write(0); 
  beep();
  randNumber = random(200, 3000);
  delay(randNumber);      //Random start (0.2-3 sec)
  for(pos = 0; pos < 110; pos += 1)
  {
    digitalWrite(ledPinP, HIGH);
    myservoP.write(posP);
    delay(17);  
    digitalWrite(ledPinP, LOW);  


  } 
}



void Mark()              // At a random time(between 0.2-3 sec. sweep from 0-110 degrees at a particular speed


{
  myservoM.write(0); 
  beep();
  randNumber = random(200, 3000);
  delay(randNumber);      //Random start (0.2-3 sec)
  for(pos = 0; pos < 110; pos += 1)
  {
    digitalWrite(ledPin, HIGH);
    myservoM.write(pos);              
    delay(18);              // Speed of clay at +- 1.7 sec 0-110 degrees
    digitalWrite(ledPinM, LOW);          
  }
} 



void Double()             // At a random time(between 0.2-3 sec. sweep both servos from 0-110 degrees at a different speeds.



{
  myservoP.write(0);
  myservoM.write(0);
  beep();
  randNumber = random(200, 3000);
  delay(randNumber);              //Random start (0.2-3 sec)
  for(pos = 0; pos < 110; pos += 1)
  {
    digitalWrite(ledPinP, HIGH);
    myservoP.write(pos); 
    delay(8.5);                 // Speed of clay at +- 1.7 sec 0-110 degrees
    digitalWrite(ledPinM, HIGH);
    myservoM.write(pos);  
    delay(8.5);                 // Speed of clay at +- 1.7 sec 0-110 degrees
    digitalWrite(ledPinP, LOW);
    digitalWrite(ledPinM, LOW);  
  }
}




void skeet()


{
  delay (55000);
  Pull();              //station 2
  delay (10000);   
  Double();
  delay (25000);
  Pull();              //station 3 
  delay (10000);
  Double();
  delay (25000);
  Pull();              //station 4
  delay (10000);
  Mark();
  delay (10000);
  Double();
  delay (10000);
  Double();
  delay (25000);
  Mark();              //station 5
  delay (10000);
  Double();
  delay (25000);
  Mark();              //station 6 
  delay (10000);
  Double();
  digitalWrite(ledPinS, LOW);
  count=1;
  while (count=1)
  {
  }
} 





void doubles()



{
  delay (25000); 
  Double();
  delay (10000);
  Double();
  delay (10000);       //station 2(3 x double)
  Double();
  delay (10000); 
  Double();          //station 3(3 x double)  
  delay (10000); 
  Double();
  delay (10000);
  Double();
  delay (10000); 
  Double();          //station 4(6 x double)
  delay (10000);  
  Double();
  delay (10000);
  Double();
  delay (10000); 
  Double();
  delay (10000);  
  Double();
  delay (10000);
  Double();
  delay (10000); 
  Double();          //station 5(3 x double)
  delay (10000);  
  Double();
  delay (10000);
  Double();
  delay (10000); 
  Double();         //station 6(3 x double)
  delay (10000);   
  Double();
  delay (10000);
  Double();
  digitalWrite(ledPinD, LOW);
  count=1;
  while (count=1)
  {
  }
}


void beep()


{
  tone(6, 10,0);
  delay(1000);
  noTone(6);
  delay(1000);
  tone(6, 10,0);
  delay(1000);
  noTone(6);
  delay(1000);
  tone(6, 10,0);
  delay(1000);
  noTone(6);  
}



void loop() 



{



  delay(500);
  currentButton = debounce(lastButton);
  if (lastButton == LOW && currentButton == HIGH)
  {
    digitalWrite(ledPinD, HIGH);
    doubles();
  }
  else 
  {
    digitalWrite(ledPinS, HIGH); 
    skeet(); 
  }

  lastButton = currentButton;

}
 count=1;
  while (count=1)
  {
  }

It will never leave this loop for two reasons:

  • Count starts off at 1, and you don't change it (however see below).
  • In the loop, you keep resetting count to 1.

The reason I have that at the end of the function is because it was the only way I figured out to stop the program. I understand that it sits there and waits for ever, I dont have a problem with that i just reset the arduino. The program I need is fairly simple for you guys, unfortunatly for me my mind is also simple when it comes to programming. I understand what you are trying to say about punctuation and correct format and in no way did I mean to offend anyone. Anyone's help is appreciated greatly, its just because this is a once off for me I did not want to waist anyones time with the other problems in my code other then the one that is preventing me from finishing this crude but effective training aid which I have build. Thanks

We need to concentrate on one function. The Double() function seems to be the only one that is trying to move two servos at the same time, so we’ll concentrate on that one. No need, then, to keep posting the rest of the code.

Lets start with you describing how YOU (not the Arduino) would move the servos the way you want. You have a watch, two servos, a pad of paper, and a pencil. The watch will tell you, obviously, what time it is. The pencil and paper are so you can record the last time you moved each servo.

Lets imagine that the two…

Now, you tell us how you would move the two servos.

I’m in the middle of writing this story to get you to describe your problem, and looking at the code, and I suddenly realized that you have made so many changes to the Double() function that I no longer know what it is you are trying to accomplish.

He he he. I have tried various wrong ways to achieve it , all to no avail. I would like the two servos to start a sweep together from 0 to 110 degrees but the one will sweep at a lower speed than the other. I would need to have some kind of field where I can input the amount of time it should take each servo to complete the one directional sweep. So basically they start together but finish separately. I hope this is clearer and sorry for all the confusion.

I would need to have some kind of field where I can input the amount of time it should take each servo to complete the one directional sweep. So basically they start together but finish separately.

OK, that's what I thought I remembered.

So, to continue my story, lets say that you want servo P to sweep 110 degrees in 110 minutes, and that you want servo M to sweep 110 degrees in 88 minutes.

Write down the steps that you would perform to accomplish this. When you can do that, and it isn't that hard, then we can show you how to convert those steps to Arduino-ese.

Variables will take the place of the pen and paper, and millis() will take the place of the watch.

To get you started:

Calculate how long it will take servo P to sweep the whole range. Calculate how long it will take servo M to sweep the whole range. Write down the time that the servos are to start moving. Move servo P the required distance. Move servo M the required distance.

The next step(s) involving checking the watch periodically, and moving a servo when it is time to move it.

I see what you are trying to do and I am a big fan of teach a man to fish rather than give him a fish to eat, however you are assuming that I have some level of basic programming foundations. The problem is I dont and therfore will end up confusing you and myself in the process. I am afraid that you think I am smarter then what i seem to be, he he. Here goes nothing I will try and put down my thoughts in laymans terms. Lets say that at 13H00 we want the servos to start moving. we check the watch to see what time it is. If it is 13H00 the servos will be told to start moving. we need to stop servo P 110 min. and servo M 88 min later respectively. So we check the watch in each instance to see if those time parameters have been met. If their respective time frames have been reached we stop each servo accordingly. I am sorry if I did not quite understand if this is what you were asking of me.

Not quite - remember, once told where to go, the servo will go there at its own pace, so you can only slow it it down (and you cannot speed it up) by telling it to move smaller amounts or longer periods of time. There are at least two ways of looking at the problem: 1)Break the move into, say 1 "degree" steps, and work out how long (in milliseconds) each step lasts 2)Break the move into, say, 1/10th second steps, and figure out how big (in fractional "degrees") each step is.