Wireless Servo motor control via XBee

Hey guys,

I'm trying to control a servo motor with a potentiometer wirelessly using 2 arduino uno and 2 XBee series 1 modules.

I configured my XBees using X-CTU and they've been working fine. I also followed Jeremy's tutorial:

In his application, the servo motor moves in 9 independent position. For me, I want higher resolution, possibly a 0 to 180 mapping.

Can you guys point me in the right direction here please?

I'm using this simple code:

void loop()

{
while(Serial.available() == 0);

int data = Serial.read()-'0';
int pos = map(data, 0, 9, 0, 180);
pos = constrain(pos, 0, 180);

delay(20);

while(Serial.available()>0) Serial.read();
Serial.println(data);
Serial.println(pos);
servo.write(pos);

}
int pos = map(data, 0, 9, 0, 180);

This wont's allow you to reach more than 10 positions. They will just have different values.

pos = constrain(pos, 0, 180);

Please explain why you are doing this. If you properly check that the serial data was a letter that represented a number, you wouldn't need this.

Can you guys point me in the right direction here please?

How to send data over the serial port is an almost daily topic. I can't believe you couldn't find anything.

int pos = map(data, 0, 9, 0, 180);

This wont's allow you to reach more than 10 positions. They will just have different values.

I did try changing the mapping to something like map(data, 0, 180, 0, 180), but the servo didn't move.

pos = constrain(pos, 0, 180);

Please explain why you are doing this. If you properly check that the serial data was a letter that represented a number, you wouldn't need this.

I was just following Jeremy on that one. He mentioned something like preventing any erroneous values from coming in which might make the servo behaving erratically.

Can you guys point me in the right direction here please?

How to send data over the serial port is an almost daily topic. I can't believe you couldn't find anything.

I do have a slight idea how to send data over the serial port. But I just wanted to know why the servo doesn't work when I do the the mapping like map(data, 0, 180, 0, 180) with the code I'm using.

Have you gotten your code working just using the serial monitor without the xbee in the mix?

You are reading one character from the serial port. How many discrete values can you read, when each needs to be a numeric digit?

deathrow:
I did try changing the mapping to something like map(data, 0, 180, 0, 180), but the servo didn't move.

Of course not. The problem has nothing to do with the map() function and everything to do with how you receive the serial data. Assuming you are limiting yourself to sending the ASCII characters '0', '1', ..., '9' then you only have 10 possible values for data. If I handed you 10 flags and told you to mark 180 spots on the ground with those flags, you would probably you at me funny and say "...well I need more flags!"

To "get more flags" you need to use something like Serial.parseInt() to receive a multiple character value from the Serial port. You could then use the constrain() function to guard against someone entering something like "200".

zoomkat:
Have you gotten your code working just using the serial monitor without the xbee in the mix?

It works with the XBee but I wanted a higher resolution for the servo.

Using the serial monitor, I can change the angle of the servo by pressing different characters on the keyboard. Is this what you meant?

Is this what you meant?

No. If you read up on how to read strings, not just single characters, from the serial port, you can send values like 17 or 120, not just one digit numbers, to the Arduino. You need some kind of delimiter (the Serial Monitor can add one for you), and you need to read and process serial data until the delimiter arrives. Then, you use the value.

It works with the XBee but I wanted a higher resolution for the servo.

Below is some servo test code that you can use with the serial monitor to find the mechanical limits of your servo using the us values (~500-2500). Once you find the min/max values, you can map these to the pot input. Should provide some better resolution than using degrees.

// zoomkat 10-22-11 serial servo test
// type servo position 0 to 180 in serial monitor
// or for writeMicroseconds, use a value like 1500
// for IDE 0022 and later
// Powering a servo from the arduino usually *DOES NOT WORK*.

String readString;
#include <Servo.h> 
Servo myservo;  // create servo object to control a servo 

void setup() {
  Serial.begin(9600);
  myservo.writeMicroseconds(1500); //set initial servo position if desired
  myservo.attach(7);  //the pin for the servo control 
  Serial.println("servo-test-22-dual-input"); // so I can keep track of what is loaded
}

void loop() {
  while (Serial.available()) {
    char c = Serial.read();  //gets one byte from serial buffer
    readString += c; //makes the string readString
    delay(2);  //slow looping to allow buffer to fill with next character
  }

  if (readString.length() >0) {
    Serial.println(readString);  //so you can see the captured string 
    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);
      myservo.writeMicroseconds(n);
    }
    else
    {   
      Serial.print("writing Angle: ");
      Serial.println(n);
      myservo.write(n);
    }

    readString=""; //empty for next input
  } 
}

Parsing a number out of the input stream as mentioned above will let you get to any resolution you want. For a little more resolution with a couple of trivial changes in the code though, use keys 'a' to 'z' instead of '0' to '9'.

I modified the code using the Serial.parseInt ()

void loop()

{
while(Serial.available() == 0);

int data = Serial.parseInt();
int pos = map(data, 0, 180, 0, 180);
pos = constrain(pos, 0, 180);

delay(20);

while(Serial.available()>0) Serial.read();
Serial.println(data);
Serial.println(pos);
servo.write(pos);

}

I have more resolution now but it's not too smooth and the servo keeps making a lot of noise internally.

int pos = map(data, 0, 180, 0, 180);

What exactly is the point of this?

delay(20);

Why?

while(Serial.available()>0) Serial.read();

Why are you throwing away random amounts of unread data?

Serial.println(data);
Serial.println(pos);

So you've got some debug output, but you're not going to tell us what this tells you?

int pos = map(data, 0, 180, 0, 180);

What exactly is the point of this?

This is the data coming from the transmitter side (potentiometer) which has been mapped from 0, 1023, 0, 180. It's redundant though, so ma bad.

delay(20);

Why?

You think this maybe causing the sluggishness?

while(Serial.available()>0) Serial.read();

Why are you throwing away random amounts of unread data?

To be honest, I'm not exactly sure how this works. But I saw that they use that instead of serial.flush (). You think that this shouldn't be in the code?

Serial.println(data);

Serial.println(pos);




So you've got some debug output, but you're not going to tell us what this tells you?

It's just printing the data and pos values. And they're both equal from the mapping.

I modified the code to something like this:

void loop()

{
while(Serial.available() == 0);

int data = Serial.parseInt();
int pos = data;
pos = constrain(pos, 0, 180);

servo.write(pos);

}

It works well now.

int data = Serial.parseInt();
int pos = data;
pos = constrain(pos, 0, 180);

servo.write(pos);

What, exactly, does having two variables containing the same data contribute?

What, exactly, does having two variables containing the same data contribute?

I removed that part after I posted it.

hi friends..the above code with more resolution works properly the issue is that the servo motor takes some time to respond (delay) and is not suitable for I’m hoping to do..i found this comment says(Yes, use this to read until a newline (‘\n’): Serial.readBytesUntil() - Arduino Reference ,Then use the atoi() function to convert it to an integer.)..can someone help me control servo motor without any slight delay.??

So, you've modified the last code you've posted, and you are still having issues, and you'd like us to guess where and how to fix them. OK, I'll guess that the problem is on line 38. You'll need to totally rewrite that line.

hi PaulS!..thanks for responding ..i understand the code just a little bit.. this is the code am having so far ..but it has some delay .. am building a project on remote control plane and would like to use arduino and xbee for control system ...can you help help me expand the code to send four readings from my arduino analog joystick over serial (xbee) and control four servos independently with more resolution on the other side... :slight_smile:

//Include Servo Library
#include <Servo.h>

//Define Pins
int servoPin = 9;

//Create Servo Object
Servo servo;

void setup()
{
 //Start Serial
 Serial.begin(9600);
  
  //Attaches the Servo to our object
  servo.attach(servoPin);
  
}

void loop()

{
while(Serial.available() == 0);

int data = Serial.parseInt();
int pos = data;
pos = constrain(pos, 0, 180);

servo.write(pos);

}

but it has some delay

Where? I'm going to guess here:

int data = Serial.parseInt();

That code reads all the serial data, until it encounters a non-digit or until a period of time has elapsed during which no serial data arrives. To get rid of the delay, you must make sure that a non-digit follows the numeric data being sent.

The process of converting a numeric value to a string, sending the string one character at a time, reading those characters, and converting them back to a numeric value is not a fast process. Since the value is in the range of a byte, sending the byte as binary data would be much faster, since 1) no conversion is required, 2) fewer bytes are sent, 3) no conversion is required.

Learn how to do that, for one value (hint: Serial.write() instead of Serial.print() and Serial.read() instead of Serial.parseInt()), and then we can talk about the issues of expanding the code to deal with multiple servos.

One issue is that RC equipment is far better designed for joystick control of servos than the Arduino is. Cheaper, too, generally.