Friday, December 24, 2010

Arduino Xbee Wireless Full-Range Joystick FPS Servo Turret



Here we look at a full-range servo turret controlled wirelessly by Arduino via Xbee modules. The title says "fps" because if you were to have the point of view of the pencil tip, the joystick would control like an fps video game.

It works by moving the turret whenever the joystick is moved outside the neutral center position, and stays at whatever position its at when the joystick is moved back to the center position. Just like an fps video game.

You may have noticed, one joystick is superfluous in this demo. That will be used when (or if) I create an "fps bot", which would have a live wireless camera, omni wheels, and other things to allow for an fps control of a physical robot that has all of the movement freedom of a typical fps video game.

This project utilized Xbee modules with the FTDI-ready adapter that can be found at www.adafruit.com, Arduino Duemilanove, HS-311 Standard servos, and these adafruit.com joysticks.

Here's an Arduino Reference, if you need it. For the Xbees, plenty of resources online can help you configure and set it up. Good luck!

Below are two codes, one for the controller and one for the reciever. If you guys need help, comment on my corresponding Youtube video and I will try to get to it, as I am on Youtube quite often.

Controller code:

//CONTROLLER CODE
//Feel free to use this code however you like, but please credit lordvon and this site!
//'ud' and 'lr' are the pot signals for the up/down and left/right pots, respectively.

//(Joysticks and their pot values are not perfect, so some things have to be empirically
//determined. The serial read function is a great tool for collecting the values.)
//range of ud: 29-1014 (middle: around 479-540; avg:~510, center:+/-35)
//range of lr: 0-960 (middle: around 419-471; avg:445, center:+/-30)
//minimum range extreme (from avg) magnitude: 445
//maximum tolerance value: 35

//Print values as 'topic:value', where 'topic' denotes either 'ud' or 'lr',
//and 'value' is the associated pot value.
//Reciever is prompted to record by ':', and ended by ','.

//"" gives characters, '' gives characters in integer form.

//Empirically determined and/or arbitrary constants.
#define ud1 0
#define lr1 1
#define highpulse 2400
#define lowpulse 600
#define lr1avg 445
#define ud1avg 510
#define tolerance1 40
#define range1 445
#define maxadd 35

int ud1val;
int lr1val;
int lr1pulse;
int ud1pulse;
int lr1previous;
int ud1previous;
boolean update;

void setup()
{
Serial.begin(9600);
//initialize servo to its center position.
lr1previous = lr1avg;
ud1previous = ud1avg;
Serial.print(':');
Serial.print((highpulse-lowpulse)/2+lowpulse);
Serial.print(',');
Serial.print((highpulse-lowpulse)/2+lowpulse);
}

void loop()
{
ud1val = analogRead(ud1);
lr1val = analogRead(lr1);
update = (abs(lr1avg-lr1val) > tolerance1) || (abs(ud1avg-ud1val) > tolerance1);
if (update)
{
lr1val = newval(lr1val,(lr1avg-range1),(lr1avg+range1),lr1avg,lr1previous);
ud1val = newval(ud1val,(ud1avg-range1),(ud1avg+range1),ud1avg,ud1previous);
lr1previous = lr1val;
ud1previous = ud1val;
lr1pulse = map(lr1val,(lr1avg-range1),(lr1avg+range1),lowpulse,highpulse);
ud1pulse = map(ud1val,(ud1avg-range1),(ud1avg+range1),lowpulse,highpulse);
//':' is packet delimiter, ',' is UD/LR delimiter.
//Each cycle of delimiters denote a packet.
//Packet: UD value first, then LR value. (:UD,LR:)
Serial.print(':');
Serial.print(ud1pulse);
Serial.print(',');
Serial.print(lr1pulse);
delay(25);
update = false;
}
}

int newval(int val, int minval, int maxval, int avgval, int previousval)
{
int increment;
increment = val-avgval;
if (increment > 0)
{
increment = increment - tolerance1;
}
else if (increment < 0) { increment = increment + tolerance1; } increment = map(increment,0,range1-tolerance1,0,maxadd); val = increment + previousval; if (!(val <= maxval) || !(val >= minval))
{
if (val > maxval)
{
val = maxval;
}
else
{
val = minval;
}
}
return val;
}

Reciever code:

//RECIEVER CODE
//Feel free to use this code however you like, but please credit lordvon and this site!
#include
<Servo.h>

Servo elevation;
Servo azimuth;

#define highpulse 2400
#define lowpulse 600

char received;
char pulsechar[4];
int counter=0;
int pulse;
boolean delimiter;

void setup()
{
Serial.begin(9600);
while (!Serial.available()){}
received = Serial.read();
elevation.attach(12);
azimuth.attach(13);
}

void loop()
{
// if (Serial.available())
// {
// Serial.println(char(Serial.read()));
// }
delimiter = (received == ',') || (received == ':');
if (!delimiter)
{
counter = 0;
while (!delimiter)
{
while (!Serial.available()){}
pulsechar[counter] = received;
counter += 1;
received = Serial.read();
delimiter = (received == ',') || (received == ':');
}
pulsechar[counter] = '\0';
pulse = atoi(pulsechar);
switch (received)
{
case ',':
azimuth.writeMicroseconds(pulse);
break;
case ':':
elevation.writeMicroseconds(pulse);
break;
}
}
else
{
while (!Serial.available()){}
received = Serial.read();
}
}

17 comments:

  1. dude this stuff is awesome, why is it so difficult to split the serial read data in arduino? I'm trying to send bytes seperated by a tab to servo motors. I think the answer is in this code above! the use of delimiter? Thanks for sharing code.
    Richard

    ReplyDelete
  2. Yup! The sent and received serial data are delimited by colons and commas, to differentiate between up-and-down and left-to-right on the joystick. Search for 'delimiter' in the above code to find all of the relevant code snippets. Thanks for reading!

    ReplyDelete
  3. cool stuff dude.......im trying to do the same, except with a 5-axis usb joystick........how will i get that to work?....any idea's?

    ReplyDelete
  4. This is the only working code I could find online that allows a Joystick to control 2 servos. Thank you for sharing it. However I do have a few questions. I previously built a Sentry Turret based off of Project Sentry Gun's code. My tilt can't go the full 180 degrees of movement. How can I adjust this. Also with this code the servos don't hold their position. When the joystick is centered it keeps moving in one direction until the servos can't move anymore. How can I tune the code so that this stops? I am guessing that my joystick might be the problem since the center value might not be perfect.

    THanks!

    ReplyDelete
    Replies
    1. Can you specify how the servos cannot move full 180, yet also move in one direction until they cannot move anymore?

      For the latter problem, it seems like the commanded pulse length might be beyond the range of accepted values. Be sure the duty cycle your servo takes and the duty cycle commanded by the Arduino are compatible. Are you using the Arduino servo library? If so, this should be very easy to figure out.

      If you think your joystick readings are wrong, try measuring the resistance of each axis directly with your Arduino or a Digital Multimeter, as you move the joystick. I do not think this would be a problem, unless your joystick is just plain broken.

      Good luck and thanks for reading.

      Delete
  5. Thanks for the quick reply! I figured out the center readings on my joystick and changed the lr1avg and ud1 avg to 514 in the code. Now the Pan and Tilt servos work perfectly for me. Since my joystick readings while centered didn't match your code it caused them to keep moving in one direction until the servos couldn't move anymore. Now it works fine. It works better than any code I would ever think of. This is an awesome code for this application.

    My servos do move 180 degrees. It is just that my airsoft gun is too large and it isn't high enough off the base for it to allow the servo to move 180 degrees. It's tilt range is maybe 140 degrees. Not too concerned about that right now though. My Pan servo is unobstructed and can move the full 180 degrees. I might add a second servo on top my current one eventually like you did in the video so I can move it 360 degrees.

    Now I am currently trying to figure out how to add 2 more inputs to the controller and 2 more outputs to the receiver. My airsoft gun turret has a laser sight on it that I want to be able to activate remotely. Not only that I want to be able to trigger the gun remotely. I have been playing with your code to try and add to the delimiters to allow 2 more signals to be sent across but I am having trouble getting the code to do what I want it to.

    Basically I want to add 2 more digital inputs to the controller using the existing code you created. One input to fire the gun and one to activate the laser sight.

    Programming is a new experience for me. I am somewhat familiar with the language to program arduino, but your code has a lot of new terms I do not yet fully understand. My experience is mainly with wiring and circuits.

    If you can help me add 2 inputs on the controller and 2 outputs on the receiver you will be my hero :)

    I would love to share the video of my turret once I get it working!

    Thanks again!

    ReplyDelete
  6. I have spent many hours over the past month trying to figure out how to add to this code to allow me to trigger my airsoft gun and trigger my laser sight. The servo movement from this code works flawlessly from this code, so I would love to be able to keep that same functionality.

    I would like to offer the first person to solve my problem $20.00 via PayPal. Again I just need the code above modified to allow 2 digital inputs on the controller to trigger 2 digital outputs on the receiver.

    Please help me!!! Thanks :)

    ReplyDelete
    Replies
    1. Hello Justin,

      Sorry for the frustration of spending many hours on a project without apparent progress, but it is part of the hands-on learning process! I definitely know that feel.

      I will take you up on your offer. I will be contacting you back shortly!

      Delete
    2. Hello Justin,

      I have completed the code that you requested. You can give me your email address, or I could just post it here.

      Delete
  7. Please email it to jbeldredge14@gmail.com so no one else tries to take credit for your work. I should have said that earlier. I'm excited to test it out! I will be in contact with you via email once it is received.

    Thanks!

    ReplyDelete
  8. Lordvon, im making a simple project on servo, controlled by arduino mega(transmitter side) arduino uno(receiver side) and xbee s1 in wireless communication.. can you help me regarding wtih the codes?

    ReplyDelete
    Replies
    1. Sure! If it is simple, we can discuss it in the comments here. But if looking over large code is required, then can you post your email here? I will send you a message and we can get started.

      Delete
  9. eceluyanthony@yahoo.com
    i have already made my codes on 4buttons, the requirement of my project is to convert that 2buttons into 2servos that will make it 2button and 2 servo..

    ReplyDelete
  10. RECEIVER SIDE(SERVO)
    #include
    #include

    int servoPin = 9;

    Servo myServo;

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

    myServo.attach(servoPin);

    }

    void loop()
    {

    if ( Serial.available() > 0){
    int data = Serial.read();

    Serial.println(data);
    myServo.write(data);
    delay(20);

    }
    }

    TRANSMITTER SIDE (POTENTIOMETER)
    #include

    int potPin = 0;

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

    void loop()
    {
    int val = analogRead (potPin);
    val = map(val, 0, 1023, 0, 180);
    Serial.write(val);
    delay(50);

    }

    ReplyDelete
  11. Hey Robert,
    Can you email me agian? Sorry, i accidentally erase the conversation that we had. Thank you

    ReplyDelete