help controlling multiple servos via serial

here's the set up:

sending arduino with 3 pots
data trasferred via hard serial connection (TX/RX)
receiving arduino with 3 servos responding to data

eventually this will be wireless using 2 Xbees, but i want to figure this out first.

the receiving arduino seems to do okay for a little while then goes caput. i'm guessing there must be some relationship i need to tweak between baud rate, loop delay(), and buffer/memory on the receiving arduino? however, it doesn't seem like i'm sending all that much data!

i've tried various baud rates and various delays, but with no success.

thanks for any help!

here's the sender code:

// SENDER

int potpin1 = 1;
int potpin2 = 2;
int potpin3 = 3;
int val1;
int val2;
int val3;

void setup()
{
  pinMode (potpin1, INPUT);
  pinMode (potpin2, INPUT);
  pinMode (potpin3, INPUT); 
  Serial.begin(38400);
}

void loop()
{
  val1 = analogRead(potpin1);
  val1 = map(val1, 0, 1023, 0, 99);
  Serial.print(val1, BYTE);
  
  val2 = analogRead(potpin2);
  val2 = map(val2, 0, 1023, 100, 199);
  Serial.print(val2, BYTE);

delay(80);
}

here's the receiver code:

// RECIEVER

#include <Servo.h>

Servo servo1;
Servo servo2;
Servo servo3;
int incomingByte = 0;
int pos = 0;

void setup()
{
servo1.attach(9);
servo2.attach(10);
servo3.attach(11);
Serial.begin(38400);
}

void loop() 
{
if (Serial.available() > 0);
{
  incomingByte = Serial.read();
  
  if (incomingByte > 0 && incomingByte < 98);
  servo1.write(incomingByte);
  
  if (incomingByte > 100 && incomingByte < 198);
  servo2.write(incomingByte);
  
  Serial.println(incomingByte, DEC);
}
delay(80);
}

I suspect that they are getting out of synch, and you may need some way to synch the two.

Instead of sending data from sensors, set up pseudo data.
Then check that everything works well

Here is a silly example of the sensor side to show what I mean:

void loop() {
  int val = 0;
  Serial.print(val&0xff, BYTE);
  Serial.print((val>>8)&0xff, BYTE);
  val++;
  while (val) {
    int dummy = analogRead(...); // ignore it, but this sets transmission rate
    Serial.print(val&0xff, BYTE);
    Serial.print((val>>8)&0xff, BYTE);
    val++;
  }
}

On the receiver side

int i = 0;
...
void loop() {
  if (Serial.available() >= 2) {
    int val = Serial.read();
    val = Serial.read() << 8 | val;
    if (i != val) {
       ... signal error with LED
    }
    i++;
  }
}

Something like this would let you test the need for handshaking.

WARNING - code is Unverify'ed or tested.

HTH

[edit]This is not the posters problem, see my later post. I was reading the delay as a synchronisation mechanism, which it shouldn't be.

But, IMHO, the principle of testing comms with known data is fine, so I'm leaving the post.[/edit]

the receiving arduino seems to do okay for a little while then goes caput.

Can you explain what you mean by this?

You have a rather strange way of deciding which byte goes to which servo.

If pot 1 is at it's minimum position, analogRead returns 0, which will be mapped to 0, and sent. The receiver will read a 0, and not move either servo.

If pot 1 is at it's maximum position, analogRead returns 1023, which will be mapped to 99, and sent. The receiver will read a 99, and not move either servo.

If pot 2 is at it's minimum position, analogRead returns 0, which will be mapped to 100, and sent. The receiver will read a 100, and not move either servo.

If pot 2 is at it's maximum position, analogRead returns 1023, which will be mapped to 199, and sent. The receiver will read a 199, and not move either servo.

Aside from the limit condition issues, the range of values that you move servo 1 to is limited to a maximum value of 100 degrees. The range of values for servo 2 is 101 degrees to 198 degrees.

Is servo 2 capable of actually moving to 198 degrees? Why is it constrained to a minimum value of 101 degrees?

Doh!

void loop()
{
if (Serial.available() > 0)[glow];[/glow]
{
  incomingByte = Serial.read();
  
  if (incomingByte > 0 && incomingByte < 98)[glow];[/glow]
  servo1.write(incomingByte);
  
  if (incomingByte > 100 && incomingByte < 198)[glow];[/glow]
  servo2.write(incomingByte);
  
  Serial.println(incomingByte, DEC);
}
[glow]delay(80);[/glow]
}

The code
if (Serial.available() > 0);
if (incomingByte > 0 && incomingByte < 98);
if (incomingByte > 100 && incomingByte < 198);
is almost certainly incorrect.
The ';' terminates the if, so everything is done no matter whether there is a character available, and both servo's are being told to move and are not controlled by an if.

A good idea is to always use { ... } around the statements being controlled, even if it is only one statement. For one thing it makes stray ';' easier to see, and makes it easier to edit a program later.

I put the '{' on the same line as the ')' of the control statement, so that it is even easier to see what is happening without using a vertical line. Other people hate that :slight_smile:

Also the delay(80) is unnecessary . The program is waiting for bytes anyway, and once the ';' is fixed there is no need for a delay.

HTH
GB

I think he just did a ruff sketch.

@paulS
You are right with the min and max position and this is no big deal :) and I belive he just forgot to map the servo values back to the "normal" degrees. That would fix the sketch at these places ;)


I noticed that you can use the delay mostly in very simple sketches where you don't need to time something. I did use this sketch for my Servos. It's not so pretty because I still need to learn arrays, but it does work for me right now.

@josdavlar
I like your way of storing diffent data out of the Serial.read(). I am searching for an effective way too, to make an Arduino xbee controller for my octapod projekt. So if someone knows a simple and good way of sending diffrent serial data and save it into diffrent values please tell me :)

#include <Servo.h>

Servo servo_1;                         // create servo object to control a servo 
Servo servo_2;
Servo servo_3;
Servo servo_4;

int pos_1;                             // variable to store the servo position 
int pos_2;
int pos_3;
int pos_4;



unsigned long previousMillis = 0;
unsigned long previousMicros_1 = 0;      // the variables is a long because the time, measured in miliseconds, will quickly become a bigger number than can be stored in an int. will store last time servo was updated
unsigned long previousMicros_2 = 0;      // the variables is a long because the time, measured in miliseconds, will quickly become a bigger number than can be stored in an int. will store last time servo was updated
unsigned long previousMicros_3 = 0;      // the variables is a long because the time, measured in miliseconds, will quickly become a bigger number than can be stored in an int. will store last time servo was updated
unsigned long previousMicros_4 = 0;      // the variables is a long because the time, measured in miliseconds, will quickly become a bigger number than can be stored in an int. will store last time servo was updated


long interval = 1000; 
const int ledPin =  13;      // the number of the LED pin                    
int ledState = LOW; 



//---------------------------| SERVO_01 |-----------------------------------------------

void Servo_01(int min_1, int max_1, int speed_1)
{
  if ((micros()-previousMicros_1) >=speed_1)  {
    previousMicros_1 = micros();    
      
    if (pos_1 > servo_1.readMicroseconds())  { 
      servo_1.writeMicroseconds(servo_1.readMicroseconds() + 1);
    }     
    if (pos_1 < servo_1.readMicroseconds())  {
      servo_1.writeMicroseconds(servo_1.readMicroseconds() - 1);
    }
    if(servo_1.readMicroseconds() == max_1)  {
      pos_1 = min_1;
    }    
    if(servo_1.readMicroseconds() <= min_1)  {
      pos_1 = max_1;
    }     
  }  
}

//---------------------------| SERVO_04 |-----------------------------------------------

void Servo_02(int min_2, int max_2, int speed_2)
{
  if ((micros()-previousMicros_2) >=speed_2)  {
    previousMicros_2 = micros();    
      
    if (pos_2 > servo_2.readMicroseconds())  { 
      servo_2.writeMicroseconds(servo_2.readMicroseconds() + 1);
    }     
    if (pos_2 < servo_2.readMicroseconds())  {
      servo_2.writeMicroseconds(servo_2.readMicroseconds() - 1);
    }
    if(servo_2.readMicroseconds() == max_2)  {
      pos_2 = min_2;
    }    
    if(servo_2.readMicroseconds() <= min_2)  {
      pos_2 = max_2;
    }     
  }  
}

//---------------------------| SERVO_03 |-----------------------------------------------

void Servo_03(int min_3, int max_3, int speed_3)
{
  if ((micros()-previousMicros_4) >=speed_3)  {
    previousMicros_4 = micros();    
      
    if (pos_3 > servo_3.readMicroseconds())  { 
      servo_3.writeMicroseconds(servo_3.readMicroseconds() + 1);
    }     
    if (pos_3 < servo_3.readMicroseconds())  {
      servo_3.writeMicroseconds(servo_3.readMicroseconds() - 1);
    }
    if(servo_3.readMicroseconds() == max_3)  {
      pos_3 = min_3;
    }    
    if(servo_3.readMicroseconds() <= min_3)  {
      pos_3 = max_3;
    }     
  }  
}

//---------------------------| SERVO_04 |-----------------------------------------------

void Servo_04(int min_4, int max_4, int speed_4)
{
  if ((micros()-previousMicros_4) >=speed_4)  {
    previousMicros_4 = micros();    
      
    if (pos_4 > servo_4.readMicroseconds())  { 
      servo_4.writeMicroseconds(servo_4.readMicroseconds() + 1);
    }     
    if (pos_4 < servo_4.readMicroseconds())  {
      servo_4.writeMicroseconds(servo_4.readMicroseconds() - 1);
    }
    if(servo_4.readMicroseconds() == max_4)  {
      pos_4 = min_4;
    }    
    if(servo_4.readMicroseconds() <= min_4)  {
      pos_4 = max_4;
    }     
  }  
}

//----------------------------------------------------------------------------------------------------------------------


void setup() 
{
  servo_1.attach(9);                   // attaches the servo on pin 9 to the servo object
  servo_2.attach(10);
  servo_3.attach(11);
  servo_4.attach(12);

  pinMode(ledPin, OUTPUT);  
  Serial.begin(9600);
}

void loop()
{

  Servo_01(1500,2100,1000);
  Servo_02(1500,2100,1000);
  Servo_03(800,1400,1000);
  //Servo_04(650,2400,900);
  
 
  
//-------------------------------------------------------------------------------- not important
  
 if( (millis() % 1000) == 0 )  {
   Serial.println(millis());
   Serial.println("A second has passed");
 }
 
 //-------------------------------------------------------------------------------
  
  if (millis() - previousMillis > interval)  {
    // save the last time you blinked the LED 
    previousMillis = millis();   

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW)  {
      ledState = HIGH;
    }
    else  {
      ledState = LOW;
    }
      
    // set the LED with the ledState of the variable:
    digitalWrite(ledPin, ledState);
    
    Serial.println(millis());
    Serial.println();
  }
}

josdavlar - is the posted code real?

Gonras - I would recommend you work with the Arduino examples:
File->Examples->Control-Arrays
File->Examples->Control->ForLoopIteration

The program you posted has several near-identical pieces of code, e.g.:

void Servo_01(int min_1, int max_1, int speed_1)
{
  if ((micros()-previousMicros_1) >=speed_1)  {
    previousMicros_1 = micros();    

    if (pos_1 > servo_1.readMicroseconds())  {
      servo_1.writeMicroseconds(servo_1.readMicroseconds() + 1);
    }    
    if (pos_1 < servo_1.readMicroseconds())  {
      servo_1.writeMicroseconds(servo_1.readMicroseconds() - 1);
    }
    if(servo_1.readMicroseconds() == max_1)  {
      pos_1 = min_1;
    }    
    if(servo_1.readMicroseconds() <= min_1)  {
      pos_1 = max_1;
    }    
  }  
}

and

void Servo_02(int min_2, int max_2, int speed_2)
{
  if ((micros()-previousMicros_2) >=speed_2)  {
    previousMicros_2 = micros();    

    if (pos_2 > servo_2.readMicroseconds())  {
      servo_2.writeMicroseconds(servo_2.readMicroseconds() + 1);
    }    
    if (pos_2 < servo_2.readMicroseconds())  {
      servo_2.writeMicroseconds(servo_2.readMicroseconds() - 1);
    }
    if(servo_2.readMicroseconds() == max_2)  {
      pos_2 = min_2;
    }    
    if(servo_2.readMicroseconds() <= min_2)  {
      pos_2 = max_2;
    }    
  }  
}

Arrays are ideal for solving these sorts of situations.

Once you have understood arrays, look at 'struct' which would allow you to put all the different types of information needed for a servo into one 'data block', e.g.:

struct {
Servo theServo;
int pos;
unsigned long previousMicros;
} servoArray[4];

Then a single general-purpose function can be written to replace the four, which would change
void Servo_01(int min_1, int max_1, int speed_1)
to, say
void controlServo(int servoIndex, int min_1, int max_1, int speed_1)

and servoArray[servoIndex].theServo, servoArray[servoIndex].pos
servoArray[servoIndex].previousMicros

would manipulate any of the servos

Once you've reduced the amount of code, it may get easier to see how to make improvements.

[edit]Could you explain what you are trying to communicate? I can't see anything in your posted program other than:
Serial.println(millis());
Serial.println("A second has passed");
and
Serial.println(millis());
Serial.println();
[/edit]

HTH

WARNING - code Unverify'ed & untested

For C/C++ geeks - I'd do
typedef struct {... } MyServo;
and so
void controlServo(int servoIndex, int min_1, int max_1, int speed_1)
would become either
void controlServo(MyServo* aServo, int min_1, int max_1, int speed_1)
or
void controlServo(MyServo& aServo, int min_1, int max_1, int speed_1)
but that seems like a refinement on the key idea.

thank you for your input :slight_smile:
I kow that this can be solved with array and I want to do it with them, but right now I am still strugling with them.
I will do my best to understand and then use the arrays :wink:

sadly your ideas look really really confusing for me right now ;D

Edited:
Could you explain what you are trying to communicate? I can't see anything in your posted program other than:
Serial.println(millis());
Serial.println("A second has passed");
and
Serial.println(millis());
Serial.println();

that was just for debugging and playing around. the servos stop for a really small time when the led changes states...
that's really nothing.
I had some problems with the rollover when I tried something out and the millis grew way fast and that caused problems.

The idea of an array is quite neat.
The idea is to have a single name, for many similar types of value, and choose which specific one is being used using a piece of arithmetic at run time.

The usual metaphor is houses in a street may have a common street name, and each house has a number. It is easy to process all the houses in the same way, say collect their electricity meter readings, by something like;
for (int i=1; i<=LAST_HOUSE_NUMBER; i=i+1) {
meter_reading = magic_house_meter_read(house*);
_
}_
The same process would work for a street with 1 house or a street with 10000 houses.
The helpful thing about struct, is the 'house' could be more complex than a single number. It could have an electricity meter, which is read every 3 months, and a water heater which needs to be serviced every 2-3 years (so you need to remember the last time it was serviced), ...
so the street might be
struct {
_
MeterInfo howToReadMeter;_
_
ServiceDate waterHeaterLastServiced;_
_
} houses[10000];*_
So an array makes it easy to do a very similar process to many identical pieces of data, and a struct collects together all of the related data about a thing.
HTH

good explaination :slight_smile:
I will try to reduce the code!
"sadly" I will go today for a weak on vacation. A time without programming and Arduino :cry:

but YAY sunny island!!

Gonras, maybe I'm not understanding, but you wrote

So if someone knows a simple and good way of sending diffrent serial data and save it into diffrent values please tell me

I'm asking what type of data you need to communicate, how often, how much needs to be stored, and where.

I was trying to show that the only examples of communication were those I identified. So there was no information in the program to illustrate the need.

I want to send some potentiometer, nunchuk and butten-state data over Xbee to an other arduino device (Robot) it's not too much data but it would need to be send quite often to keep the nunchuk and robot in sync.

I hope there is not to much confusion because of my lack of english... :-/

I want to send some potentiometer, nunchuk and butten-state data over Xbee to an other arduino device (Robot) it's not too much data but it would need to be send quite often to keep the nunchuk and robot in sync.

Okay, so maybe 5-10 bytes, 50-100 times/second (or less)?
There are several things to consider. They may get out of synchronisation because one sends data faster than the other can receive data, or data might get corrupted in transit.
I think Zigbee does error correction, so that isn't my dominant worry. (Please correct me if I'm mistaken)

The easy way to send data is to use ordinary printable ASCII.
This is easier to debug compared to binary because you can read it. It's feasible for you to type it, making testing even easier.
Also, because many of the possible binary values are not used, it provides a dumb sort of error checking.

Okay, so the next question is whether the communications is two way. It is more complex to get two ends to agree and handshake than simply pump data in one direction. For real-time data, the sort of need you outline, I'd be very tempted to try one-way comms. I'm assuming that if the receiver detects data loss, it will just ignore some data, and pick up when it can. With real-time communications it is often more important to have the most up to date data than try to correct for an error and get old, stale, data correctly.

Normal techniques for comms (i.e. not security focused), have to solve a few problems.
When sending structured data (i.e. not just a sequence of dumb bytes), it should try to make it easy for the receiver to separate the stream of bytes into separate data values, and to detect which bytes have which meaning (i.e. which value they belong to).
It is very easy to overcomplicate this.
(Some folks went nuts with XML, and IMHO, pushed a pile of extra stuff into communication streams which was never needed, but some folks disagree)

It is helpful, if the length of each data can vary. Fixed length fields are great if the comms is very reliable, i.e. from hard disk to memory, but less useful for unreliable communication because there still needs to be a way to detect lost bytes.

I would suggest a simple path is to send the data values with a single byte maker between each one to say what the data is:
e.g. P375A6B0C678;
the letters say what a number means, maybe P for potentiometer, etc. The ';' is the end of the set of data items, or 'packet'.
I'd likely send button open and close as '0' and '1' because that translates directly to what the Arduino uses, and you only need one way to get at data, it's all numbers.
[edit]If all the data is numbers, and numbers only, it is even easier when the 'tag' for the name of the data comes after the number, because the number (the data) is collected in a single way, then immediately used when the 'tag' is received.

If there is more than one type of data, the 'tag' is usually enough to decide what type it is, so put the tag in front of the value (data).

These are examples of a classic comms technique called 'Name Value pairs'[/edit]

The single letters are easy to decode using a switch or if tests.
The numbers are always numbers, so that is straightforward, and they can be different lengths (up to 31 bits of data) with only one piece of code to decode it.
The letters and ';' make it easy to detect data loss and resynchronise if data gets lots.

I didn't use any control characters, like end of line, because they are hard to create from the serial monitor, so control characters make communication harder to test by hand.

[edit]Unless testing by hand is an issue, I would use '\n' as a record or packet separator.
That is for a bunch of reasons:
a. I am a *nix person, so I have lots of programming idioms in my fingers
b. most people recognise what it means
c. A file of test data is easy to read
d. A very dumb program can act as a receiver, store data in EEPROM, and play it back to a serial monitor
e. dumb comparison programs like cmp or diff work well to compare intended output with actual output for automated testing.

I'd probably make the packet terminator a variable in the program so that I could change my mind at a later date.

Maybe this is an argument for a fancier serial monitor :slight_smile: [/edit]

HTH

I hope there is not to much confusion because of my lack of english...

It is very kind of you to accept and work around my lack of your language. Thank you.

The "P375A6B0C678" solution sounds quite interresting.
I would love to hear more about it.
But I need to go now....
be back in a week. :stuck_out_tongue:

you are really helpfull! :slight_smile:

Have a good vacation.

Post when you get back, if you would like some more help.

my post has been hijacked!

i'll do my best to explain my code. i'm going to say right now that i'm not an experienced programmer. i'm still trying to understand!

PaulS asked:

"If pot 1 is at it's minimum position, analogRead returns 0, which will be mapped to 0, and sent. The receiver will read a 0, and not move either servo.

If pot 1 is at it's maximum position, analogRead returns 1023, which will be mapped to 99, and sent. The receiver will read a 99, and not move either servo."

regarding this code:

  val1 = analogRead(potpin1);
  val1 = map(val1, 0, 1023, 0, 99);
  Serial.print(val1, BYTE);

first off, potpin1 controls servo1, and potpin2 controls servo2. why did i map in increments of ~100? simplicity. i assumed, incorrectly i guess, that it didn't matter what you remapped to, the servo would still move 180 degrees. for example if my remapped val were 50 the servo would move to a 90 degree position. but i guess i'm wrong. i'll modify accordingly.

even so, with the limited range of motion, why doesn't the servo respond? it must be my stray semicolons.

so somehow sync'ing the serial communication isn't critical?

i might need to wait until this evening to work on this, but thanks for the help.

my post has been hijacked!

Sorry, but the thread title seemed appropriate.

I think if you remove the extraneous ';' that I posted on, it will behave very differently.

Reclaim your thread when you get time :slight_smile: