I am trying to run multiple servos, at the moment 2 but ultimately I would like to end up with 11.
The idea is that all of the servos will be running at the same time, and moving the exact same amount of rotation but all with a different delay.
Also when the first motor reaches the end of its rotation it needs to return to its starting position but without waiting for the other motors to finish there cycle.
The trigger for these motions is different incoming serial data. So for example if arduino receives the incoming byte of 49 rotate from 0 to 180 degrees with a delay of 20 seconds, or if it receives the incoming byte of 48 rotate from 40 to 150 degrees with a delay of 60 seconds. Then with an extra 10 second delay added for the next motor in sequence.
The problem im having is obviously arduino reads down the code and does everything in step, im sure there is away to get around this and achieve what I want but im new to arduino and dont have much experience programming so would really appreciate help with this.
#include <Servo.h>
Servo myservo;
Servo myservo2;
int relayPin = 13; // LED connected to digital pin 13
int incomingByte = 0; //declare incoming byte
// The setup() method runs once, when the sketch starts
int pos1 = 0; // variable to store servo position
int pos2 = 0;
void setup() {
// initialize the digital pin as an output:
myservo.attach(9); // servo to pin 9
myservo2.attach(10);
pinMode(relayPin, OUTPUT);
Serial.begin(19200); // set up Serial library at 19200 bps
Serial.println("Arduino is ready!");
}
// the loop() method runs over and over again,
// as long as the Arduino has power
void loop()
{
if (Serial.available() > 0) {
// read the incoming byte:
incomingByte = Serial.read();
Serial.println(incomingByte);
if (incomingByte == 49){
for(pos1 = 0; pos1 < 180; pos1 += 1) // goes from 0 degrees to 180 degrees
{ // in steps of 1 degree
myservo.write(pos1); // tell servo to go to position in variable 'pos'
delay(20); // waits 15ms for the servo to reach the position
}
for(pos1 = 180; pos1>=0; pos1-=1) // goes from 180 degrees to 0 degrees
{
myservo.write(pos1); // tell servo to go to position in variable 'pos'
delay(20); // waits 15ms for the servo to reach the position
}
} else if (incomingByte == 48) {
for(pos1 = 40; pos1 < 150; pos1 += 1) // goes from 0 degrees to 180 degrees
{ // in steps of 1 degree
myservo.write(pos1); // tell servo to go to position in variable 'pos'
delay(60); // waits 15ms for the servo to reach the position
}
for(pos1 = 150; pos1 >=40; pos1 -=1) // goes from 180 degrees to 0 degrees
{
myservo.write(pos1); // tell servo to go to position in variable 'pos'
delay(60); // waits 15ms for the servo to reach the position
}
// say what you got:
}
if (incomingByte == 49){
for(pos2 = 0; pos2 < 180; pos2 += 1) // goes from 0 degrees to 180 degrees
{ // in steps of 1 degree
myservo2.write(pos2); // tell servo to go to position in variable 'pos'
delay(30); // waits 15ms for the servo to reach the position
}
for(pos2 = 180; pos2 >=0; pos2 -=1) // goes from 180 degrees to 0 degrees
{
myservo2.write(pos2); // tell servo to go to position in variable 'pos'
delay(30); // waits 15ms for the servo to reach the position
}
} else if (incomingByte == 48) {
for(pos2 = 40; pos2 < 150; pos2 += 1) // goes from 0 degrees to 180 degrees
{ // in steps of 1 degree
myservo2.write(pos2); // tell servo to go to position in variable 'pos'
delay(60); // waits 15ms for the servo to reach the position
}
for(pos2 = 150; pos2 >=40; pos2 -=1) // goes from 180 degrees to 0 degrees
{
myservo2.write(pos2); // tell servo to go to position in variable 'pos'
delay(60); // waits 15ms for the servo to reach the position
}
// say what you got:
Serial.print("I received: ");
Serial.println(incomingByte, DEC);
}
}
}
Im sorry if this is a rather confusing description of what i am trying to achieve so feel free to ask lots of questions.
The problem is not specifically the delays, but the fact that you have used 2 separate for loops to sweep the servo. Why not replace each for loop ingles single write command, and then you can follow the blink without delay method for your 10 seconds delay, an get rid of the others (the tiny delays would not be necessary) . If, however, you want the sweep effect, than you need to restrucure your code without the for loops, so that each run of the main loop checks and increments the position of both servos.
Think of it this way: your goal is to make the main loop run as often as possible.
Sorry thats my mistake I was just experimenting with different things trying to get it to work.
#include <Servo.h>
Servo myservo;
Servo myservo2;
int relayPin = 13; // LED connected to digital pin 13
int incomingByte = 0; //declare incoming byte
// The setup() method runs once, when the sketch starts
int pos1 = 0; // variable to store servo position
int pos2 = 0;
void setup() {
// initialize the digital pin as an output:
myservo.attach(9); // servo to pin 9
myservo2.attach(10);
pinMode(relayPin, OUTPUT);
Serial.begin(19200); // set up Serial library at 19200 bps
Serial.println("Arduino is ready!");
}
// the loop() method runs over and over again,
// as long as the Arduino has power
void loop()
{
if (Serial.available() > 0) {
// read the incoming byte:
incomingByte = Serial.read();
Serial.println(incomingByte);
if (incomingByte == 49){
for(pos1 = 0; pos1 < 180; pos1 += 1) // goes from 0 degrees to 180 degrees
{ // in steps of 1 degree
myservo.write(pos1); // tell servo to go to position in variable 'pos'
myservo2.write(pos1);
delay(20);
}
for(pos1 = 180; pos1>=0; pos1-=1) // goes from 180 degrees to 0 degrees
{
myservo.write(pos1); // tell servo to go to position in variable 'pos'
myservo2.write(pos1);
delay(20);
}
} else if (incomingByte == 48) {
for(pos1 = 40; pos1 < 150; pos1 += 1) // goes from 0 degrees to 180 degrees
{ // in steps of 1 degree
myservo.write(pos1); // tell servo to go to position in variable 'pos'
myservo2.write(pos1);
delay(20);
}
for(pos1 = 150; pos1 >=40; pos1 -=1) // goes from 180 degrees to 0 degrees
{
myservo.write(pos1); // tell servo to go to position in variable 'pos'
myservo2.write(pos1);
delay(20);
}
// say what you got:
Serial.print("I received: ");
Serial.println(incomingByte, DEC);
}
}
}
The code above is in one loop land they both do move at the same time however I now need them to have individual set lengths of time to complete there tasks. I will have a look at the blink example unless anyone else has another solution thank you
The code that you just posted still has multiple for loops. Why are you not just using one servo write command to set the position?
Also, what on earth do you want to do? Please try to explain better.
I have tried removing the delay and this new approach works in a round about way, i have read various posts and basically mashed bits of code together to try and make it work so i apologise if its a bit of a mess.
My explanation of what im trying to do was terrible so ill try again. If you can image i have 11 servos but for the time being im just trying to get 2 work. I wont the servos to be able to move like a mexican wave formation. So the first servo starts to rotate from 0 - 180 degrees then back again, then just after than one starts the next one begins and so on.
As I said this code below kind of gives me that effect but i need the rotation to keep on looping until the next piece of serial data has come in, and im not sure how to do this but i guess i cant run two loops within a loop.
#include <Servo.h>
Servo myservo;
Servo myservo2;
int incomingByte = 0; //declare incoming byte
// The setup() method runs once, when the sketch starts
int pos = 0; // variable to store servo position
void setup() {
myservo.attach(9); // servo to pin 9
myservo2.attach(10); // servo to pin 10
pinMode(relayPin, OUTPUT);
Serial.begin(19200); // set up Serial library at 19200 bps
Serial.println("Arduino is ready!");
}
// the loop() method runs over and over again,
// as long as the Arduino has power
void loop()
{
if (Serial.available() > 0) {
// read the incoming byte:
incomingByte = Serial.read();
Serial.println(incomingByte);
if (incomingByte == 49){
for(pos = 0; pos < 180; pos += 180)
{
myservo.write(pos); timeGap(millis(), 350UL);
myservo2.write(pos); timeGap(millis(), 1000UL);
}
for(pos = 180; pos>=0; pos-=180)
{
myservo.write(pos); timeGap(millis(), 350UL);
myservo2.write(pos); timeGap(millis(), 1000UL);
}
} else if (incomingByte == 50) {
for(pos = 40; pos < 150; pos += 110)
{
myservo.write(pos); timeGap(millis(), 350UL);
myservo2.write(pos); timeGap(millis(), 1000UL);
}
for(pos = 150; pos >=40; pos -=110)
{
myservo.write(pos); timeGap(millis(), 350UL);
myservo2.write(pos); timeGap(millis(), 1000UL);
}
// say what you got:
Serial.print("I received: ");
Serial.println(incomingByte, DEC);
}
}
}
void timeGap(unsigned long tOld, unsigned long tGap) {
unsigned long tCurrent;
do {
tCurrent = millis();
} while ((millis() - tOld) < tGap);
return;
}
You've basically just implemented your own version of delay(), which accomplishes nothing.
If we look at the blink without delay sample and just strip out the blink code, we get:
long previousMillis = 0; // will store last time LED was updated
// the follow variables is a long because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long interval = 1000; // interval at which to blink (milliseconds)
void setup() {
}
void loop()
{
// here is where you'd put code that needs to be running all the time.
unsigned long currentMillis = millis();
if(currentMillis - previousMillis > interval) {
previousMillis = currentMillis;
//This code only gets executed every 1000 msec (interval = 1000)
//Set interval to 20, and it gets executed every 20 msec.
}
}
So use that as the basis for setting your servos to an incremental position every 20msec. As bilbo said though, don't use a for loop. Inside that if() block you can increment your position variable and write the new position to your servo. You'll have to track the position of the servo, whether or not you are sweeping the servo, and which direction you are sweeping it in (which is what the for loop was basically doing for you, but prevents you from doing anything else). You'll also have to track all these variables for every servo separately, if you want all the servos doing their thing at different times.
I'd focus on just accomplishing that with a single servo first. Once you get it working the way you want without using any looping constructs, it'll be fairly easy to expand that to any number of servos.
Yes, you will still need the check for incoming data, though that is not a loop, just a check. You would not put that inside your 'interval' block. It would go in front of that block and be completely separate from it. IE something like (just pseudo code)
void loop(){
if(serial available){
read serial data
set appropriate servo variables here
}
if(interval){
run servo code here based on servo variables
}
}
I am receiving the results i expect from the serial monitor, I should also explain that the information from this is being sent from a python script. Which sends ser.write('1') then in turn arduino receives 49.
So basically I want to be able to preform different sequences or animations with my servos depending on the serial data received. data driven sculpture test on Vimeo the video shows a frame i have made were using a servo i raise and lower a piece of wood. What i aim to do is replicate this for all 11 planks and in turn create a wave like motion.
The code being used in the video is just the basic sweep example, so I am now trying to develop form this to gain the effect i require to animate all 11 pieces of wood.
You're going to have to get rid of the for loops. Just don't use them (that includes replacing them with while loops). Increment and track the state of your position manually.
jraskell:
You're going to have to get rid of the for loops. Just don't use them (that includes replacing them with while loops). Increment and track the state of your position manually.
would it be possible for you to show me some sample code of how this is done to get me started and to make sure im looking at the right thing when I search for it.
The following code will sweep through a value from 0 to 180 back to 0 repeating. I've put the code that does this into a separate function to make it more clear what sections of code are responsible for what.
The loop() function is responsible for the looping. That's what it's there for.
The if block inside loop() is responsible for executing your actions at the desired interval (as opposed to executing them as fast as loop() executes)
the sweepVal is responsible for handling the position of your servo. Keep in mind this gets called once every interval.
This code does not include handling any serial data, or disabling/enabling the sweeping.
long previousMillis = 0;
long interval = 50;
int pos1 = 0;
int dir1 = 1;
void setup() {
Serial.begin(115200);
}
void loop()
{
unsigned long currentMillis = millis();
if(currentMillis - previousMillis > interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;
sweepVal();
}
}
void sweepVal(){
pos1 += dir1;
Serial.println(pos1);
//When position is at 180, change direction
if(pos1 == 180)
dir1 = -1;
//When position is at 0, change direction
if(pos1 == 0)
dir1 = 1;
}
Would it not be better to run the intervals with in individual voids instead of the loop? As i aim to control 11 servo all having to move at different speeds and times. Were as the loop needs to be constantly checking for an update to the serial information coming in from my python script.
I have however been able to implement the code example you supplied and im currently using 3 servos however they do all move at the same time, unless ive done it in a different way to what you had in mind.
#include <Servo.h>
long previousMillis = 0;
Servo myservo1;
Servo myservo2;
Servo myservo3;
long interval = 50;
int pos1A = 0;
int pos2A = 0;
int pos3A = 0;
int dir1A = 1;
int dir2A = 1;
int dir3A = 1;
void setup() {
myservo1.attach(9);
myservo2.attach(10);
myservo3.attach(11);
Serial.begin(19200);
}
void loop()
{
unsigned long currentMillis = millis();
if(currentMillis - previousMillis > interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;
sweepVal1A();
sweepVal2A();
sweepVal3A();
}
}
//////////////////////////////////
// Higher Squence //
// //
//////////////////////////////////
void sweepVal1A() {
pos1A += dir1A;
Serial.println(pos1A);
//When position is at 180, change direction
if(pos1A == 180)
dir1A = -1;
//When position is at 0, change direction
if(pos1A == 0)
dir1A = 1;
myservo1.write(pos1A);
}
void sweepVal2A(){
pos2A += dir2A;
Serial.println(pos2A);
//When position is at 180, change direction
if(pos2A == 180)
dir2A = -1;
//When position is at 0, change direction
if(pos2A == 0)
dir2A = 1;
myservo2.write(pos2A);
}
void sweepVal3A(){
pos3A += dir3A;
Serial.println(pos3A);
//When position is at 180, change direction
if(pos3A == 180)
dir3A = -1;
//When position is at 0, change direction
if(pos3A == 0)
dir3A = 1;
myservo3.write(pos3A);
}
I've now included the serial code to check if python is sending the right information, however the motors now only move in an increment of one each time arduino receives the value of 49, as apposed to constantly looping through and checking if the value being received has changed.
I tried to change it so it would jump in blocks of 180 but it didn't work.
Can you make any suggestions on what i need to change?