Serial Communication -Driving 2 Servos with 2 Pot's with Xbee's

The goal is to use 2 xbees for wireless communication with 2 Arduinos and 2 Servos and 2 potentiometers.

I’ll have my 2 pots, 1 xbee on one arduino and ill have the other xbee on the other Arduino with my two servos;

I want to be be able to control two servos with wireless communication(xbee’s),by turning the potentiometers to varying resistances. I know data is sent over serial as ASCII characters, but cant figure out a way to properly write this up using the atoi() function to convert the strings sent over.

I can send the data through serial as a string with the two values generated by my two pot’s.

//Transmitter end
const int potA = A0; //potentiometer pins
const int potB = A1;

int potApos; //used to map values to servo angle
int potBpos;

void setup(){
  Serial.begin(9600);
}

void loop()
{
  potApos = map(analogRead(potA), 0, 1023, 0, 180); //map values
  potBpos = map(analogRead(potB), 0, 1023, 0, 180);
  Serial.print("A");
  Serial.print(potApos); //send data as, example "A180"
  Serial.print("B");
  Serial.print(potBpos); //send data as, example "B180"
}

I figured im going to need a way to distinguish them from each other, thats why ive added a “A” for potA and a “B” for potB at the beginning of their respective strings.
But when I try to send the data over serial, I cant figure out how to properly distinguish which potentiometer is sending the data ,and then be able to store it into a char array and then convert using atoi() (since the atoi converts a string into an int); and then assign the proper angle to either the leftservo or rightservo.

what ive got started,but isnt anywhere near finished,im just stuck,any help would be great.

//Reciever end
#include <Servo.h>
Servo leftservo;
Servo rightservo;
char potAstring[6]; //stores incoming data, enough spaces to hold ex. "A180",used for atoi()
char potBstring[6];

int indexA = 0; //used as starting point for potAstring and potBstring index.
int indexB = 0;

int dataA;  //uses atoi() function,assigns the values to dataA,dataB.
int dataB;
                    

void setup()
{
  Serial.begin(9600);
  leftservo.attach(8);
  rightservo.attach(9);
}

void loop()
{
  if(Serial.available()) //if some data is in serial
  {
    /* I need a way to read incoming data and distingiush ifs
       it's form potA or potB and then store them into an
       array and then convert them using atoi(), and then turn
       the servos to the value received from their pot's.*\

I think it would be easier if your transmitter inserts a comma between each item so it is A,180,B,120 and then it could also put < and > around the data so that it looks like this <A,180,B,120> . The receiver will use the < and > to determine the start and end of the data. I think you will then see that all you really need to send is the numbers in the correct order <180,120> and that the A and B are superfluous.

This Arduino code in this demo shows how to receive the data and save it in an array and this demo shows how to convert the data bacck into numbers that can be used by the servo.

…R

After reading through the code you uploaded. I would use this as a pamphlet:But just to try and make sure im understanding this i posted the parts i would need to receive the data and convert it.And added quesitons with // in your code

const byte buffSize = 40;  //Size of array
char inputBuffer[buffSize];           //Name of array with buffsize as size of array
const char startMarker = '<';          
const char endMarker = '>';
byte bytesRecvd = 0;
boolean readInProgress = false;     //used to determine is read is still ongoing
boolean newDataFromPC = false;      //used to know when its done receiving

void getDataFromPC() {

    // receive data from PC and save it into inputBuffer
    
  if(Serial.available() > 0) {

    char x = Serial.read();

      // the order of these IF clauses is significant
      
    if (x == endMarker) {            
      readInProgress = false;
      newDataFromPC = true;
      inputBuffer[bytesRecvd] = 0;   //Im alittle lost as to why this = 0?
      parseData();
    }
    
    if(readInProgress) {                      //if readinProgress == true, start this block of code
      inputBuffer[bytesRecvd] = x;   //x will be assigned to inputBuffer array
      bytesRecvd ++;                               //increment the index by 1 until it recieved the full message?
      if (bytesRecvd == buffSize) {        //im guessing this is here(the -1 part) to make sure theres enough room for the NULL char?
        bytesRecvd = buffSize - 1;
      }
    }

    if (x == startMarker) {    //starts this block of code when the '<' is received
      bytesRecvd = 0;            //bytes=0 since 0 bytes have been received
      readInProgress = true;  //now turns readinProgress true,so now the program flow backsup? and starts the readinProgress                             code?
    }
  }
}

void parseData() {

    // split the data into its parts
    
  char * strtokIndx; // this is used by strtok() as an index
  
  strtokIndx = strtok(inputBuffer,",");      // get the first part - the string   //reads until a "," is recieved
  strcpy(messageFromPC, strtokIndx); // copy it to messageFromPC
  
  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  newFlashInterval = atoi(strtokIndx);     // convert this part to an integer   //Is it only one part, since ill have <180,180> ????
}

void replyToPC() {

  if (newDataFromPC) {           //if newdatafromPC is true start this block
    newDataFromPC = false;   //change back to false,until next reading
    Serial.print("<Msg ");               
    Serial.print(messageFromPC);    //now print what was received, like <180,180>

let me know if im somewhat correct or am wronge before i try to move on to using the numbers that were once stored in a array and then converted to numbers. To using these numbers to turn my servos plz :slight_smile: And thanks for the help.

You are going in the right direction. As you haven’t shown what you propose to send to the Arduino I can’t be sure of what you want to do.

In particular, if you send <180,120> I don’t understand why you have strcpy(messageFromPC, strtokIndx);

You will need two atoi()s that are nearly the same except that the first one save the value for servoA and the second one for servoB

Something like this

 char * strtokIndx;

   strtokIndx = strtok(inputBuffer,","); 
   servoAval = atoi(strtokIndx); 
  
  strtokIndx = strtok(NULL, ","); 
  servoBval = atoi(strtokIndx);

And I presume you know that the replyToPC() function is unnecessary

…R

Edit to add …
It would be a good idea just to send the data from the Serial Monitor while you figure stuff out. Then you can use lots of Serial.println()s for debugging. …R

Finite State machines!

Such a powerful concept that can be applied to various scenarios once learned.

ok,I wrote something up.But I dont really understand a part in the void loop() function under one of the if statements.When we reach our endmarker of the string why do we assign the string (buffer[bytesrecieved] = 0) ??

My code:

#include <Servo.h>
Servo leftservo;
Servo rightservo;
int leftservovalue;   //after using atoi sets values to here
int rightservovalue;

const byte MAXBYTE = 5;   //just to make sure i dont go over my index,even tohugh the max length of my index will be 3 spaces
byte bytesrecieved = 0;     //bytes used to store the char values
char buffer[bytesrecieved];  //char array used to store them
const char endmarker = '>';  //data is sent as, Example <180,120>
const char startmarker = '<';
boolean readinprogress = false;  //determines when reading char array/assigning the byte to it is done

void setup(){
  Serial.begin(9600);
  leftservo.attach(8);
  rightservo.attach(9);
}
void loop()
{
  if(Serial.available())
  {
    char x = Serial.read();  //assign data to char x as ASCII values
    if(x==endmarker)
    {
      readinginprogress = false;  //sets to false since endmarker was reached
      buffer[bytesrecieved] = 0; //I dont understand why this is assigned 0?
      parsedata();  //function below that uses atoi and strtok to read string till "," and convert the string to a int with atoi
    }
    if(readinprogress)
    {
      buffer[bytesrecieved] = x;   //assigns/increments char x to char array buffer
      bytesrecieved++
      if(bytesrecieved == MAXBYTE) //if i get close to max value it subtracts 1,room for NULL,i think.Ill check this later  tonight
      {
        bytesrecieved = bytesrecieved - 1;
      }
    }
    if(x==startmarker)
    {
      bytesrecieved = 0;   //resets bytesrecieved to 0,and sets the index to 0
      readinginprogress = true; //now that startmarker was read,it'll run through the readprogress if statement
    }
  }
  leftservo.write(leftservovalue);
  rightservo.write(rightserovalue);
}
void parsedata()
{
  char* readstring
  readstring = strtok(buffer, ",");  
  leftservovalue = atoi(readstring);
  
  readstring = strtok(NULL,",");
  rightservovalue = atoi(readstring);
}

EDIT
And thank you for the help, this is awesome,cant wait to upload htis once i get home to test and troubleshoot.I will memorize this to no end lol and learn more aobut the strtok function.

Below is some servo test code I worked on some time back for controlling a servo robotic arm. You might try it just using serrial tx/rx/gnd between the arduinos instead of the xbees to see if it works.

TX code

//zoomkat multi pot/servo test 3-23-13
//includes dead band for testing and limit servo hunting
//view output using the serial monitor

#include <Servo.h> 
Servo myservo1;  //declare servos
Servo myservo2;
Servo myservo3;
Servo myservo4;
Servo myservo5;

int potpin1 = 0;  //analog input pin A0
int potpin2 = 1;
int potpin3 = 2;
int potpin4 = 3;
int potpin5 = 4;

int newval1, oldval1;  //pot input values
int newval2, oldval2;
int newval3, oldval3;
int newval4, oldval4;
int newval5, oldval5;

void setup() 
{
  Serial.begin(9600);  
  myservo1.attach(2);  
  myservo2.attach(3);
  myservo3.attach(4);
  myservo4.attach(5);
  myservo5.attach(6);
  Serial.println("testing multi pot servo");  
}

void loop()
{ 
  newval1 = analogRead(potpin1);           
  newval1 = map(newval1, 0, 1023, 0, 179); 
  if (newval1 < (oldval1-2) || newval1 > (oldval1+2)){ //dead band 
    myservo1.write(newval1); //position the servo
    Serial.print(newval1); //print the new value for testing 
    Serial.print("a,");
    oldval1=newval1; //set the current old value
  }

  newval2 = analogRead(potpin2);
  newval2 = map(newval2, 0, 1023, 0, 179);
  if (newval2 < (oldval2-2) || newval2 > (oldval2+2)){  
    myservo2.write(newval2);
    Serial.print(newval2);
    Serial.print("b,");
    oldval2=newval2;
  }

  newval3 = analogRead(potpin3);           
  newval3 = map(newval3, 0, 1023, 0, 179); 
  if (newval1 < (oldval1-2) || newval3 > (oldval3+2)){  
    myservo1.write(newval3);
    Serial.print(newval3);
    Serial.print("c,");
    oldval3=newval3;
  }

  newval4 = analogRead(potpin4);           
  newval4 = map(newval4, 0, 1023, 0, 179); 
  if (newval1 < (oldval1-2) || newval4 > (oldval4+2)){  
    myservo1.write(newval4);
    Serial.print(newval4);
    Serial.print("d,");
    oldval4=newval4;
  }

  newval5 = analogRead(potpin5);           
  newval5 = map(newval5, 0, 1023, 0, 179); 
  if (newval1 < (oldval5-2) || newval5 > (oldval5+2)){  
    myservo1.write(newval5);
    Serial.print(newval5);
    Serial.print("e,");
    oldval5=newval5;
  } 
  delay(50);  //to slow loop for testing, adjust as needed
}

RX code

//zoomkat 11-22-12 simple delimited ',' string parse 
//from serial port input (via serial monitor)
//and print result out serial port
//multi servos added 

String readString;
#include <Servo.h> 
Servo myservoa, myservob, myservoc, myservod, myservoe;  // create servo object to control a servo 

void setup() {
  Serial.begin(9600);

  //myservoa.writeMicroseconds(1500); //set initial servo position if desired

  myservoa.attach(6);  //the pin for the servoa control
  myservob.attach(7);  //the pin for the servob control
  myservoc.attach(8);  //the pin for the servoc control
  myservod.attach(9);  //the pin for the servod control 
  myservod.attach(10);  //the pin for the servoe control 
  Serial.println("multi-servo-delimit-test-dual-input-11-22-12"); // so I can keep track of what is loaded
}

void loop() {

  //expect single strings like 700a, or 1500c, or 2000d,
  //or like 30c, or 90a, or 180d,
  //or combined like 30c,180b,70a,120d,

  if (Serial.available())  {
    char c = Serial.read();  //gets one byte from serial buffer
    if (c == ',') {
      if (readString.length() >1) {
        Serial.println(readString); //prints string to serial port out

        int n = readString.toInt();  //convert readString into a number

        // auto select appropriate value, copied from someone elses code.
        if(n >= 500)
        {
          Serial.print("writing Microseconds: ");
          Serial.println(n);
          if(readString.indexOf('a') >0) myservoa.writeMicroseconds(n);
          if(readString.indexOf('b') >0) myservob.writeMicroseconds(n);
          if(readString.indexOf('c') >0) myservoc.writeMicroseconds(n);
          if(readString.indexOf('d') >0) myservod.writeMicroseconds(n);
          if(readString.indexOf('e') >0) myservoe.writeMicroseconds(n);
        }
        else
        {   
          Serial.print("writing Angle: ");
          Serial.println(n);
          if(readString.indexOf('a') >0) myservoa.write(n);
          if(readString.indexOf('b') >0) myservob.write(n);
          if(readString.indexOf('c') >0) myservoc.write(n);
          if(readString.indexOf('d') >0) myservod.write(n);
          if(readString.indexOf('e') >0) myservoe.write(n);
        }
         readString=""; //clears variable for new input
      }
    }  
    else {     
      readString += c; //makes the string readString
    }
  }
}

When we reach our endmarker of the string why do we assign the string (buffer[bytesrecieved] = 0) ?

That is placing a NULL after the last character, changing the array of characters into a NULL terminated array of characters, otherwise known as a string. The string handling functions expect strings, not arrays of chars.

I was looking through your code and had some quesitons.

In the void loop section.Imagine I only used 3 pots and all the same code you provided up to the end of pot3 newval3=oldval3;

newval1 and oldval1 get set to equal each other at the end of the pot1 read of the loop, so wouldnt the statement //if (newval1 < (oldval1-2) for all the other readings be false,besides the beg. of the loop with pot1?

I’ll still try it out once i get home,looks interesting.Im sure i misunderstanding something

geryuu11:
I was looking through your code and had some quesitons.

I think you are referring to @Zoomkat's code. (It's useful to quote a short piece of the Post you are referring to)

He assumes that you only send data when the value changes and he sends a letter (a,b,c,d) along with the number so the receiver knows which servo to update.

My code was assuming that you would send values for all servos every time. Either approach will work, but you can't mix them.

...R

I used the code you provided R, with strtok and atoi functions and it works like a charm.For the most part. The pots when turned to least resistance, return a value of 120 when it should be 180. So later I was going to try the atol() which if I remember returns long ints. And one of my servos sometimes will turn clockwise when it should be counterclockwise,and vice versa.But im guessing this is due to having both hooked up to the Arduinos 5V line and having them both draw in bewtween 20mA-35mA I believe. Im sure once I hook them both up to their own power supply with everything grounds properly they'll work.Or I might need to recalibrate it.But thanks for the help robin.I'll try Z's code later as well and get back to you guys if I have any problems.

The pots when turned to least resistance, return a value of 120 when it should be 180.

Why do you think that?

and having them both draw in bewtween 20mA-35mA I believe. Im sure once I hook them both up to their own power supply with everything grounds properly they'll work.

Servos suck a whole more than 20 to 35 milli-amps, by more than an order of magnitude.

geryuu11:
both hooked up to the Arduinos 5V line

Don't power even one servo from the 5v pin. Give the servos their own power supply. Just make sure the servo GND is connected to the Arduino GND.

...R

Sorry for the late reply.Ive fixed the pot value problems and remapped them just for testing in the meantime.And I have the servos hooked to to their own power supply and everything GND properly. The problem now is when I transmit the data the servos have a big lag before they activate. Ive played with the vlaues and have tried to look up other methods but im alittle stuck, I would like to try and have a nice fluid response when a value of significance is received.

When a value of 10 or greater is received from my Coordinator Xbee.I maped the values of my pot’s so 10 would mean turn servos on. I would like the Servos to turn on for a period of time and turn off,if possible.They’ll begin to turn but only after a 7-10second delay. Any tips would be greatly appreciated.

#include <Servo.h>
Servo leftservo;
Servo rightservo;
int leftservovalue;   //after using atoi sets values to here
int rightservovalue;

int lfwd = 180;  //turn either left or right servo on depending on leftservovalue >10 and same for rightservovalue
int rfwd = 180;


const byte MAXBYTE = 5;   //just to make sure i dont go over my index,even tohugh the max length of my index will be 3 spaces
byte bytesrecieved = 0;     //bytes used to store the char values
char buffer[bytesrecieved];  //char array used to store them
const char endmarker = '>';  //data is sent as, Example <180,120>
const char startmarker = '<';
boolean readinprogress = false;  //determines when reading char array/assigning the byte to it is done

void setup(){
  Serial.begin(9600);
  leftservo.attach(8);
  rightservo.attach(9);
}
void loop()
{
  if(Serial.available())
  {
    char x = Serial.read();  //assign data to char x as ASCII values
    if(x==endmarker)
    {
      readinginprogress = false;  //sets to false since endmarker was reached
      buffer[bytesrecieved] = 0; //I dont understand why this is assigned 0?
      parsedata();  //function below that uses atoi and strtok to read string till "," and convert the string to a int with atoi
    }
    if(readinprogress)
    {
      buffer[bytesrecieved] = x;   //assigns/increments char x to char array buffer
      bytesrecieved++
      if(bytesrecieved == MAXBYTE) //if i get close to max value it subtracts 1,room for NULL,i think.Ill check this later  tonight
      {
        bytesrecieved = bytesrecieved - 1;
      }
    }
    if(x==startmarker)
    {
      bytesrecieved = 0;   //resets bytesrecieved to 0,and sets the index to 0
      readinginprogress = true; //now that startmarker was read,it'll run through the readprogress if statement
    }
  }
 if(leftservovalue > 10)
   {
      leftservo.write(lfwd);
      delay(200);
    }
else if (rightservovalue > 10)
   {
     rightservo.write(rfwd);
     delay(200);
   }
}
void parsedata()
{
  char* readstring
  readstring = strtok(buffer, ",");  
  leftservovalue = atoi(readstring);
  
  readstring = strtok(NULL,",");
  rightservovalue = atoi(readstring);
}

On every pass through loop, you have a potential 200 millisecond delay. Why? If the sender sends 199 then 200, why will it take your servo 200 milliseconds to move one degree. If the sender sends 10 followed by 180, you allow exactly the same length of time to get there.

Read all the serial data available on any given pass through loop, not a max of one character.

Knowing something about how the XBees are configured would be helpful, too. The delay between sending a value and the servo moving suggests that you are using broadcast mode. That is the slowest possible way to send data.

PaulS:
On every pass through loop, you have a potential 200 millisecond delay. Why? If the sender sends 199 then 200, why will it take your servo 200 milliseconds to move one degree. If the sender sends 10 followed by 180, you allow exactly the same length of time to get there.

I'm using @PaulS's useful summary for a point I want to make about the code in Reply #13

You can't use delay() in that sort of process for reading incoming data one byte at a time.

Get rid of delay() altogether - use the technique in the Blink Without Delay example to manage timing with millis()
Also, move the servo code into a separate servo function and the serial input code into a data input function so that loop() looks like this

void loop() {
getInputData();
moveServo();
}

...R

I finally had some time to sit down and try the blink w/o delay concept, and ran into some questions. All I would like to do is turn the servo on for…say 1 second when a value of 10(from my pot is receieved). Ive been playing with the millis function but cant figure out how to return the servodelay funciton’s boolean state when called upon. I pasted the sample code below with comments where i have problems/questions.

#include <Servo.h>
Servo leftservo;
Servo rightservo;
int leftservovalue;
int rightservovalue;
int lfwd = 180;
int rfwd = 180;

const byte MAXBYTE = 10;
char buffer[MAXBYTE];
byte bytesrecieved = 0;
const char endmarker = '>';
const char startmarker = '<';
boolean readinginprogress = false;

long interval = 1000; //leave servostate true for one second
long previousmillis;  //assigns to currentmillis for keeping track of time

void setup()
{
  Serial.begin(9600);
  leftservo.attach(8);
  rightservo.attach(12);
}

void loop()
{
  getinputdata(); 
  moveservo();
}


void moveservo()  //I posted this here so you guys wouldnt have to scroll to far to find this function
{
  if (leftservovalue > 10)
  {
    while(servodelay() == true)  //this is the problem,I would just like to return a boolean while its true for one second.I know this statement is wrong,but just wanted to get my idea across for help.
    {
      leftservo.write(lfwd)   //while servostate == true, turn this on for one second
    }
  }
  else if(rightservovalue > 10)
  {
    while(servodelay() == true)  //same as above two comments
    {
      rightservo.write(rfwd);
    }
  }
}
void servodelay()  //used to control how long servos are on with millis,well the idea is this
{
  unsigned long currentmillis = millis();  //start assigning time to variable
  if(currentmillis - previousmillis < interval)  //if variable is less then 1 second servostate = true
  {
    servostate = true
  }
  else   //once 1 second has passed, turn servostate false and reassign previousmillis to currentmillis
  {
    servostate = false;
    previousmillis = currentmillis;
  }
  return servostate;  //return the servostate value when function is called
}

void getinputdata()  //recieves data from serial and converts from string to int with strtok and atoi functions ex. <180,180>
{
  if(Serial.available())
  char x =Serial.read();
  if(x==endmarker)
  {
    buffer[bytesrecieved] = 0;
    readinginprogress = false;
    parsedata();
  }
  if(readinginprogress)
  {
    buffer[bytesrecieved] = x;
    bytesrecieved++;
    if(bytesrecieved == MAXBYTE)
    {
      bytesrecieved = bytesrecieved - 1;
    }
  }
  if(x==startmarker)
  {
    bytesrecieved = 0;
    readinginprogress = true;
  }
}
void parsedata()  //the converting after assigning x to the buffer[bytesrecieved] string and converts to a int
{
  char *readstring;
  readstring = strtok(buffer,",");
  leftservovalue = atoi(readstring);
  
  readstring = strtok(NULL,",");
  rightservovalue = atoi(readstring);
}

I know im probably making an easy mistake due to my incompetence with programming, please bare with me as im somewhat new. Thanks

geryuu11:
All I would like to do is turn the servo on for...say 1 second when a value of 10(from my pot is receieved).

Psuedo code:

in loop...

if I received 10
   Set received time to now
   turn servo on
end if


if now minus receieved time is greater than 1 second
  turn servo off
end if
    while(servodelay() == true)  //this is the problem,I would just like to return a boolean while its true for one second.I know this statement is wrong,but just wanted to get my idea across for help.

The function is not defined as returning a boolean. First thing to do is fix that.

    servostate = true

I'm almost certain that this statement should end with a semicolon.

  return servostate;  //return the servostate value when function is called

A return statement is allowed in a void function, but NOT with a value.

servostate is a dumb name. servoMoving or servoShouldBeMoving or something like that makes more sense.

Why does a function need to return a global variable? Or, why is the value being returned stored in a global variable? Can you imagine the difficulties if digitalRead() returned it's value in a global variable?