Author Topic: Xbee Servo Control  (Read 5114 times)

0 Members and 1 Guest are viewing this topic.

Offline cyberdynewinsTopic starter

  • Jr. Member
  • **
  • Posts: 16
  • Helpful? 0
Xbee Servo Control
« on: September 23, 2010, 01:31:12 AM »
I like many others am trying to use Xbee modules for R/C control.

Towards this end: I'm trying to code up a good communications protocol minimizing latency if at all possible.

Code: [Select]
#include <PS2X_lib.h>

PS2X ps2x; // create PS2 Controller Class

//right now, the library does NOT support hot pluggable controllers, meaning
//you must always either restart your Arduino after you conect the controller,
//or call config_gamepad(pins) again after connecting the controller.
int error = 0;
int Start = 42;   //packet start
int Count = 66;  //66 is placeholder value ultimately should be a packet counter for lostsignal
int EOB = 10;     // new line character
int z = 14;         // variable home value14 is the lowest ASCII character that hasn't caused
int trimly = 129;  //weird inconsistencies
int trimlx = 136;
int trimry = 120;
int trimrx = 113;

byte vibrate = 0;

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

 
 error = ps2x.config_gamepad(13,11,10,12);   //setup GamePad(clock, command, attention, data) pins, check for error
 
 if(error == 0){
   Serial.println("Found Controller, configured successful");
   Serial.println("Try out all the buttons, X will vibrate the controller, faster as you press harder;");
  Serial.println("holding L1 or R1 will print out the analog stick values.");
  Serial.println("Go to [url=http://www.billporter.info]www.billporter.info[/url] for updates and to report bugs.");
 }
   
  else if(error == 1)
   Serial.println("No controller found, check wiring, see readme.txt to enable debug. visit [url=http://www.billporter.info]www.billporter.info[/url] for troubleshooting tips");
   
  else if(error == 2)
   Serial.println("Controller found but not accepting commands. see readme.txt to enable debug. Visit [url=http://www.billporter.info]www.billporter.info[/url] for troubleshooting tips");
   
   //Serial.print(ps2x.Analog(1), HEX);
 
 
 ps2x.enableRumble();              //enable rumble vibration motors
 ps2x.enablePressures();           //enable reading the pressure values from the buttons.
 

 
}

void loop(){

  static int Chan1 = 00;
  static int Chan2 = 00;
  static int Chan3 = 00;
  static int Chan4 = 00;
  static int Chan5 = 00;
  static int Chan6 = 00;
  static int Chan7 = 00;
  static int Chan8 = 00;
  static int Chan9 = 00;
  if(error != 0)
  return;
 
  ps2x.read_gamepad(false, vibrate);          //read controller and set large motor to spin at 'vibrate' speed
 
  {   
  Chan1 = (ps2x.Analog(PSAB_RED) + z);  //Button Channel ASCII Prep
  Chan2 = (ps2x.Analog(PSAB_PINK) + z);
  Chan3 = (ps2x.Analog(PSAB_BLUE) + z);
  Chan4 = (ps2x.Analog(PSAB_L1) + z);
  Chan5 = (ps2x.Analog(PSAB_R1) + z);
  //Joystick compensation prep
  Chan6 = (ps2x.Analog(PSS_LY) - trimly); //Left stick, Y axis. Other options: LX, RY, RX 
  Chan7 = (ps2x.Analog(PSS_LX) - trimlx);
  Chan8 = (ps2x.Analog(PSS_RY) - trimry);
  Chan9 = (ps2x.Analog(PSS_RX) - trimrx);
   
   
  Serial.print(Start, BYTE);
  Serial.print(Count, BYTE);
  Serial.print(Chan1, BYTE);
  Serial.print(Chan2, BYTE);
  Serial.print(Chan3, BYTE);
  Serial.print(Chan4, BYTE);
  Serial.print(Chan5, BYTE);
  Serial.print(Chan6, BYTE);
  Serial.print(Chan7, BYTE);
  Serial.print(Chan8, BYTE);
  Serial.print(Chan9, BYTE);
  Serial.print(EOB, BYTE); 
  }
 
 delay(0);
     
 
  Serial.println("");      // prints another carriage return
     
}

This is what I have going right now, and the PS2 controller is doing a great job as an interface device.(Thanks Madsci1016!)




At this point: I'm at the point in the code where I'm having data handling problems.


This is the output if I change:
Count, & Chan1-Chan5 to HEX
&:
Chan6-Chan9 to OCT:

Code: [Select]
Found Controller, configured successful
Try out all the buttons, X will vibrate the controller, faster as you press harder;
holding L1 or R1 will print out the analog stick values.
Go to [url=http://www.billporter.info]www.billporter.info[/url] for updates and to report bugs.
*42EEEEE237777777761037777777765

*42EEEEE237777777761037777777765

*42EEEEE237777777761037777777765

*42EEEEE237777777761037777777765

*42EEEEE237777777761037777777765

*42EEEEE237777777761037777777765

*42EEEEE2377777777613777777777237777777762

*42EEEEE2377777777613777777776437777777763

*42EEEEE2377777777613777777776337777777763

*42EEEEE2377777777613777777776137777777763

*42EEEEE2377777777613777777774737777777760

*42EEEEE2377777777613777777765137777777744

*42EEEEE2377777777613777777761037777777741

*42EEEEE2377777777613777777761037777777732

*42EEEEE2377777777613777777761037777777733

*42EEEEE2377777777613777777761037777777733

*42EEEEE2377777777613777777761037777777733

*42EEEEE2377777777613777777761037777777734

*42EEEEE2377777777613777777761037777777734

*42EEEEE2377777777613777777761037777777734

*42EEEEE2377777777613777777761037777777734

*42EEEEE2377777777613777777761037777777734

*42EEEEE2377777777613777777761037777777734

*42EEEEE2377777777613777777764637777777733

*42EEEEE2377777777612137777777762

*42EEEEE2377777777612637777777762

*42EEEEE2377777777612737777777762

*42EEEEE2377777777613037777777762

*42EEEEE2377777777613237777777763

*42EEEEE3377777777613537777777764

*42EEEEE15377777777614337777777764

*42EEEEE213777777775511137777777756

*42EEEEE433777777774016337777777740

*42EEEEE1523777777767316337777777740

*42EEEEE176377777775771037777777774

*42EEEEE176377777775741137777777774

*42EEEEE176377777775741137777777773

*42EEEEE176377777775701137777777774

*42EEEEE176377777775701137777777774

*42EEEEE141377777775701137777777774

*42EEEEE64377777775701137777777774

*42EEEEE3377777775701137777777773

*42EEEEE37777777713377777775701137777777774

*42EEEEE37777777706377777775701137777777774

*42EEEEE37777777671377777775701137777777774

*42EEEEE37777777645377777775701137777777773

*42EEEEE37777777636377777775701137777777774

*42EEEEE37777777624377777775761137777777774

*42EEEEE37777777714377777777731137777777773

*42EEEEE37777777757671137777777774

*42EEEEE377777777571011137777777774

*42EEEEE377777777571021137777777774

*42EEEEE377777777571121137777777774

*42EEEEE377777777741611137777777774

*42EEEEE721671137777777774

*42EEEEE761671137777777774

*42EEEEE761671137777777774

*42EEEEE761671137777777777

*42EEEEE76167110

*42EEEEE76167110

*42EEEEE76167100

*42EEEEE3777777777741037777777777

*42EEEEE7377777777741037777777777

*42EEEEE6377777777741037777777777


Here it is with "Count"-"Chan9" as DEC:
Code: [Select]
*661414141414162549

*661414141414162549

*661414141414162549

*661414141414162549

*661414141414162549

*661414141414162541

*66141414141416251-18

*6614141414141625-20-33

*6614141414141625-120-12

*6614141414531625-120-9

*6614141414661625-120-9

*6614141414781625-120-9

*661414141499635-120-9

*6614141414160236-120-9

*6614141414189-1536-120-9

*6614141414191-7231-1200

*6614141414190-12922-1201

*6614141414120-12916-1201

*661414141482-1294-120-1

*661414144264-129-22-113-49

*661414145063-129-24-14-98

*661414145162-129-3139-113

*661414146363-129-4029-113

*661414147363-129-554-113

*661414147563-129-633-113

*661414147966-68-49-34-113

*661414141227719-10-100-95

*661414141368925-8-120-65

*6614141414094442-120-52

*66141414149956816-120-41

*66141414154967716-120-9

*6614141415897292-12021

*6614141416097-40-26-12046

*6614142815897-107-55-12063

*6614143712794-129-83-12067

*661414476898-118-111-12080

*661414546297-99-132-12082

*661414566397-94-134-12082

*661414576099-91-136-12082

*6614145851100-86-136-12082

*6614146051104-77-136-12080

*6614146255110-76-136-12071

*6614146159113-74-13656-12

*6614146058113-66-136-38

*6614145356114-28-13646

*661414495611418-13646

*661419465511545-13646

*661431375311771-13646

*661463145216098-13646

*6614761453179118-13646

*6614871452186126-11946

*6614961451188126-9246

*66141001451196126-6346

*6614921452269126-3046

*6614791452269126846

*66146314482691264246

*66143614352691036346

*6614141423269-104446

*6614141414269-124246

*6630141414269-143646

*6641141414269-153446

*6643141414205-163246

*6643141414192-163246

*6642141414184-163246

*6639141414100-93246

*662914141467-13146

*661914141437-13146

*661414141414-13146

*661414141414-23246

*661414141414-23246

*661414141414-23246

*661414141414-23246


Looks fine to me as both DEC and BYTE, but if I try printing as OCT or HEX the output stops making sense.

I think this must have something to do with ASCII vs Decimal output and I need to add quotation marks or change variable types or something, but I'm not sure where?

Does anyone have any ideas on why the values go non-sensical as OCT or HEX?

EDIT
-------------
I probably should have put this in the comments of the code but I thought it was important to acknowledge large chunks of this code is from the example code distributed with the PS2 Controller library.(including comments referencing the homepage for that library :P)
« Last Edit: September 23, 2010, 01:45:22 AM by cyberdynewins »

Offline cyberdynewinsTopic starter

  • Jr. Member
  • **
  • Posts: 16
  • Helpful? 0
Re: Xbee Servo Control
« Reply #1 on: September 23, 2010, 01:51:13 AM »
Could it have something to do with the fact that Hex/Oct only have 255 characters and
269 is the value resulting from max pressure on the buttons Chan1-Chan5?

Maybe the negative values on the joysticks aren't doing me any favors?

Offline cyberdynewinsTopic starter

  • Jr. Member
  • **
  • Posts: 16
  • Helpful? 0
Re: Xbee Servo Control
« Reply #2 on: September 23, 2010, 01:54:03 AM »
Could it have something to do with the fact that Hex/Oct only have 255 characters and
269 is the value resulting from max pressure on the buttons Chan1-Chan5?

Maybe the negative values on the joysticks aren't doing me any favors?

Let's test that theory:
Code: [Select]
#include <PS2X_lib.h>

PS2X ps2x; // create PS2 Controller Class

//right now, the library does NOT support hot pluggable controllers, meaning
//you must always either restart your Arduino after you conect the controller,
//or call config_gamepad(pins) again after connecting the controller.
int error = 0;
int Start = 42;   //packet start
int Count = 0;
int EOB = 10;  // new line character
int z = 0;    // variable
int trimly = 129;
int trimlx = 136;
int trimry = 120;
int trimrx = 113;

byte vibrate = 0;

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

 
 error = ps2x.config_gamepad(13,11,10,12);   //setup GamePad(clock, command, attention, data) pins, check for error
 
 if(error == 0){
   Serial.println("Found Controller, configured successful");
   Serial.println("Try out all the buttons, X will vibrate the controller, faster as you press harder;");
  Serial.println("holding L1 or R1 will print out the analog stick values.");
  Serial.println("Go to [url=http://www.billporter.info]www.billporter.info[/url] for updates and to report bugs.");
 }
   
  else if(error == 1)
   Serial.println("No controller found, check wiring, see readme.txt to enable debug. visit [url=http://www.billporter.info]www.billporter.info[/url] for troubleshooting tips");
   
  else if(error == 2)
   Serial.println("Controller found but not accepting commands. see readme.txt to enable debug. Visit [url=http://www.billporter.info]www.billporter.info[/url] for troubleshooting tips");
   
   //Serial.print(ps2x.Analog(1), HEX);
 
 
 ps2x.enableRumble();              //enable rumble vibration motors
 ps2x.enablePressures();           //enable reading the pressure values from the buttons.
 

 
}

void loop(){

  static int Chan1 = 00;
  static int Chan2 = 00;
  static int Chan3 = 00;
  static int Chan4 = 00;
  static int Chan5 = 00;
  static int Chan6 = 00;
  static int Chan7 = 00;
  static int Chan8 = 00;
  static int Chan9 = 00;
  if(error != 0)
  return;
 
  ps2x.read_gamepad(false, vibrate);          //read controller and set large motor to spin at 'vibrate' speed
 
  {   
  Chan1 = (ps2x.Analog(PSAB_RED) + z);  //Button Channel ASCII Prep
  Chan2 = (ps2x.Analog(PSAB_PINK) + z);
  Chan3 = (ps2x.Analog(PSAB_BLUE) + z);
  Chan4 = (ps2x.Analog(PSAB_L1) + z);
  Chan5 = (ps2x.Analog(PSAB_R1) + z);
  //Joystick compensation prep
  Chan6 = (ps2x.Analog(PSS_LY) - trimly); //Left stick, Y axis. Other options: LX, RY, RX 
  Chan7 = (ps2x.Analog(PSS_LX) - trimlx);
  Chan8 = (ps2x.Analog(PSS_RY) - trimry);
  Chan9 = (ps2x.Analog(PSS_RX) - trimrx);
   
   
  Serial.print(Start, BYTE);
  Serial.print(Count, HEX);
  Serial.print(Chan1, HEX);
  Serial.print(Chan2, HEX);
  Serial.print(Chan3, HEX);
  Serial.print(Chan4, HEX);
  Serial.print(Chan5, HEX);
  Serial.print(Chan6, OCT);
  Serial.print(Chan7, OCT);
  Serial.print(Chan8, OCT);
  Serial.print(Chan9, OCT);
  Serial.print(EOB, BYTE); 
  }
 
 delay(50);
     
 
  Serial.println("");      // prints another carriage return

Code: [Select]

*0000000377777777533777777777437777777762

*0000000377777777533777777777437777777763

*0000000377777777533777777777437777777762

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*06800000377777777533777777777137777777763

*09B00000377777777533777777777137777777763

*0D400000377777777533777777777137777777763

*0FF00000377777777533777777777137777777763

*0FF00000377777777533777777777137777777763

*0FF00000377777777533777777777137777777763

*0FF00000377777777533777777777137777777763

*05A00000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*000E000377777777533777777777137777777763

*00022000377777777533777777777137777777763

*000A7000377777777533777777777137777777763

*000CD000377777777543777777777137777777763

*000EC000377777777533777777777137777777763

*000FF000377777777533777777777137777777763

*000FF000377777777533777777777137777777763

*000FF000377777777533777777777137777777763

*000FF000377777777533777777777137777777763

*000FF000377777777533777777777137777777763

*000FF000377777777533777777777137777777763

*000FF000377777777533777777777137777777763

*000FF000377777777533777777777137777777763

*000FF000377777777533777777777137777777763

*000FF000377777777533777777777137777777763

*00093000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*07200000377777777533777777777137777777763

*0FF00000377777777533777777777137777777763

*0FF00000377777777533777777777137777777763

*0FF00000377777777533777777777137777777763

*0FF00000377777777533777777777137777777763

*0FF00000377777777533777777777137777777763

*0FF00000377777777533777777777137777777763

*0FF00000377777777533777777777137777777763

*0FF00000377777777533777777777137777777763

*0FF00000377777777533777777777137777777763

*0FF00000377777777533777777777137777777763

*0DB00000377777777533777777777137777777763

*03F00000377777777533777777777137777777763

*0F00000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*00210000377777777533777777777137777777763

*00650000377777777533777777777137777777763

*00860000377777777533777777777137777777763

*00AA0000377777777533777777777137777777763

*00B90000377777777533777777777137777777763

*00B80000377777777533777777777137777777763

*00C00000377777777533777777777137777777763

*00C40000377777777533777777777137777777763

*00C60000377777777533777777777137777777763

*00EB0000377777777533777777777137777777763

*00FF0000377777777533777777777137777777763

*00FF0000377777777533777777777137777777763

*00C40000377777777533777777777137777777763

*005B0000377777777533777777777137777777763

*00300000377777777533777777777137777777763

*0070000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*00005900377777777533777777777137777777763

*0000B300377777777533777777777137777777763

*0000FF00377777777533777777777137777777763

*0000FF00377777777533777777777137777777763

*0000FF00377777777533777777777137777777763

*0000FF00377777777533777777777137777777763

*0000FF00377777777533777777777137777777763

*0000FF00377777777533777777777137777777763

*0000FF00377777777533777777777137777777763

*0000FF00377777777533777777777137777777763

*0000FF00377777777533777777777137777777763

*0000FF00377777777533777777777137777777763

*0000FF00377777777533777777777137777777763

*0000FF00377777777533777777777137777777763

*00001800377777777533777777777137777777763

*00000490377777777533777777777137777777763

*00000FF0377777777533777777777137777777763

*00000FF0377777777533777777777137777777763

*00000FF0377777777533777777777137777777763

*00000FF0377777777533777777777137777777763

*00000FF0377777777533777777777137777777763

*00000FF0377777777533777777777137777777763

*00000FF0377777777533777777777137777777763

*00000FF0377777777533777777777137777777763

*00000FF0377777777533777777777137777777763

*00000FF0377777777533777777777137777777763

*00000FF0377777777533777777777137777777763

*0000070377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*000B8000377777777533777777777137777777763

*000FF000377777777533777777777137777777763

*000FF000377777777533777777777137777777763

*000DC000377777777533777777777137777777763

*000D7000377777777543777777777137777777763

*000FF000377777777533777777777137777777763

*000FF000377777777533777777777137777777763

*000FF000377777777533777777777137777777763

*000FF000377777777533777777777137777777763

*00055000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*07700000377777777533777777777137777777763

*0A600000377777777533777777777137777777763

*0DC00000377777777533777777777137777777763

*0FF00000377777777533777777777137777777763

*0FF00000377777777533777777777137777777763

*0E000000377777777533777777777137777777763

*0E100000377777777533777777777137777777763

*0F100000377777777533777777777137777777763

*0EF00000377777777533777777777137777777763

*0B600000377777777533777777777137777777763

*03E00000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*00640000377777777533777777777137777777763

*00770000377777777533777777777137777777763

*007C0000377777777533777777777137777777763

*007B0000377777777533777777777137777777763

*007A0000377777777533777777777137777777763

*007F0000377777777533777777777137777777763

*008D0000377777777533777777777137777777763

*00940000377777777533777777777137777777763

*00980000377777777533777777777137777777763

*00980000377777777533777777777137777777763

*00980000377777777533777777777137777777763

*007C0000377777777533777777777137777777763

*004F0000377777777533777777777137777777763

*00390000377777777533777777777137777777763

*0090000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*00590000377777777533777777777137777777763

*00720000377777777533777777777137777777763

*00700000377777777533777777777137777777763

*003D0000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*00170000377777777533777777777137777777763

*00990000377777777533777777777137777777763

*00BD0000377777777533777777777137777777763

*00C30000377777777533777777777137777777763

*00C00000377777777533777777777137777777763

*00620000377777777533777777777137777777763

*00160000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

*0000000377777777533777777777137777777763

Offline cyberdynewinsTopic starter

  • Jr. Member
  • **
  • Posts: 16
  • Helpful? 0
Re: Xbee Servo Control
« Reply #3 on: September 23, 2010, 02:21:54 AM »
OK, solved my own problem.  ;D  ::)

Here's the new version resolving the issue.(My "z" and "trim" variables where creating values which weren't compatible with the character set I was using. My solution to this was to neutralize the trim code, leaving it in place if for some reason I want to make my life more difficult at a later date by trying to re-enable it.


Code: [Select]
#include <PS2X_lib.h>

PS2X ps2x; // create PS2 Controller Class

//right now, the library does NOT support hot pluggable controllers, meaning
//you must always either restart your Arduino after you conect the controller,
//or call config_gamepad(pins) again after connecting the controller.
int error = 0;
int Start = 42;   //packet start
int Count = 0;
int EOB = 10;  // new line character
int z = 0;    // variable
int trimly = 127;
int trimlx = 127;
int trimry = 127;
int trimrx = 127;
int half = 127;

byte vibrate = 0;

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

 
 error = ps2x.config_gamepad(13,11,10,12);   //setup GamePad(clock, command, attention, data) pins, check for error
 
 if(error == 0){
   Serial.println("Found Controller, configured successful");
   Serial.println("Try out all the buttons, X will vibrate the controller, faster as you press harder;");
  Serial.println("holding L1 or R1 will print out the analog stick values.");
  Serial.println("Go to [url=http://www.billporter.info]www.billporter.info[/url] for updates and to report bugs.");
 }
   
  else if(error == 1)
   Serial.println("No controller found, check wiring, see readme.txt to enable debug. visit [url=http://www.billporter.info]www.billporter.info[/url] for troubleshooting tips");
   
  else if(error == 2)
   Serial.println("Controller found but not accepting commands. see readme.txt to enable debug. Visit [url=http://www.billporter.info]www.billporter.info[/url] for troubleshooting tips");
   
   //Serial.print(ps2x.Analog(1), HEX);
 
 
 ps2x.enableRumble();              //enable rumble vibration motors
 ps2x.enablePressures();           //enable reading the pressure values from the buttons.
 

 
}

void loop(){

  static int Chan1 = 00;
  static int Chan2 = 00;
  static int Chan3 = 00;
  static int Chan4 = 00;
  static int Chan5 = 00;
  static int Chan6 = 00;
  static int Chan7 = 00;
  static int Chan8 = 00;
  static int Chan9 = 00;
  if(error != 0)
  return;
 
  ps2x.read_gamepad(false, vibrate);          //read controller and set large motor to spin at 'vibrate' speed
 
  {   
  Chan1 = (ps2x.Analog(PSAB_RED) + z);  //Button Channel ASCII Prep
  Chan2 = (ps2x.Analog(PSAB_PINK) + z);
  Chan3 = (ps2x.Analog(PSAB_BLUE) + z);
  Chan4 = (ps2x.Analog(PSAB_L1) + z);
  Chan5 = (ps2x.Analog(PSAB_R1) + z);
  //Joystick compensation prep
  Chan6 = (ps2x.Analog(PSS_LY) - trimly + half); //Left stick, Y axis. Other options: LX, RY, RX 
  Chan7 = (ps2x.Analog(PSS_LX) - trimlx + half);
  Chan8 = (ps2x.Analog(PSS_RY) - trimry + half);
  Chan9 = (ps2x.Analog(PSS_RX) - trimrx + half);
   
   
  Serial.print(Start, BYTE);
  Serial.print(Count, HEX);
  Serial.print(Chan1, HEX);
  Serial.print(Chan2, HEX);
  Serial.print(Chan3, HEX);
  Serial.print(Chan4, HEX);
  Serial.print(Chan5, HEX);
  Serial.print(",");
  Serial.print(Chan6, OCT);
  Serial.print(",");
  Serial.print(Chan7, OCT);
  Serial.print(",");
  Serial.print(Chan8, OCT);
  Serial.print(",");
  Serial.print(Chan9, OCT);
  Serial.print(EOB, BYTE); 
  }
 
 delay(50);
     
 
  Serial.println("");      // prints another carriage return
     
}

Code: [Select]
Found Controller, configured successful
Try out all the buttons, X will vibrate the controller, faster as you press harder;
holding L1 or R1 will print out the analog stick values.
Go to [url=http://www.billporter.info]www.billporter.info[/url] for updates and to report bugs.
*000000,206,203,160,156

*000000,206,203,160,156

*000000,206,203,160,156

*000000,206,203,160,156

*000000,206,203,160,156

*000000,206,203,160,156

*000000,206,203,160,156

*000000,206,203,160,156

*000000,206,203,160,156

*000000,206,203,160,156

*000000,206,203,160,156

*000000,206,203,160,156

*000000,206,203,160,156

*000000,210,177,160,156

*0003600,111,203,160,156

*0005500,0,203,160,156

*0008200,0,203,160,156

*0008300,0,163,160,156

*0008A00,0,34,160,156

*0009200,47,0,160,156

*0009300,222,0,160,156

*0009300,376,0,160,156

*0008E00,377,51,160,156

*0004000,377,257,160,156

*0001B00,354,377,160,156

*000000,143,377,160,156

*000000,33,373,161,146

*000000,0,340,161,146

*000000,0,306,161,146

*000000,0,270,161,146

*003B000,161,214,161,146

*008B000,175,213,161,146

*0091000,175,213,161,146

*0094000,175,213,161,146

*0091000,175,213,161,146

*007C000,175,213,161,146

*0020000,175,213,161,146

*000000,175,213,161,146

*0140000,175,213,161,146

*06F0000,175,213,161,146

*0AF0000,175,213,161,146

*0D20000,175,213,161,146

*0CC0000,175,213,161,146

*0790000,175,213,161,146

*000000,175,213,161,146

*000000,175,213,161,146

*000000,164,221,161,146

*000000,15,250,76,53

*000000,0,227,0,32

*000000,0,132,0,113

*000000,1,23,0,326

*000000,116,0,111,377

*000000,223,0,372,377

*000000,377,103,377,30

*000000,360,360,344,0

*000000,166,377,162,0

*000000,16,362,0,126

*000000,0,251,0,257

*000000,0,101,35,376

*000000,13,13,263,377

*000000,61,0,354,351

*000000,221,214,166,156

*000000,201,206,166,156

Because the new outputs in both HEX and OCT are printing "0" as "0" instead of "00" and "000" they fail to provide the solution I was originally looking for in them.(consistent message character length) ASCII on the other hand seems to be holding a consistent length so I think I'm going to stick to my original plan of using ASCII.

Offline KurtEck

  • Robot Overlord
  • ****
  • Posts: 217
  • Helpful? 12
Re: Xbee Servo Control
« Reply #4 on: September 23, 2010, 07:39:33 AM »
Sounds like you are having fun.  I use XBees to talk from either a DIY remote control that is talked about on the Lynxmotion site (2 RC gimbals, 2 sliders, keypad...), or from a VB app, to my different robots. I set up a binary communication format, with handshakes.  My more recent versions of the software converted the XBEE code from serial pass through mode, to packet mode.  I know in the thread I mentioned I have the code for the Basic Micro processors (remote control, Phoenix hex running on Basic Atom Pro 28 with a SSC-32 servo controller and on the Arc32 without SSC-32, as well as the VB application).  I also have the code/protocols running on my my Axon2 based biped brat, as well as the phoenix code converted to C and then converted to run on an Arduino Mega processors, which I talked about (and posted code) on the Lynxmotion thread: http://www.lynxmotion.net/viewtopic.php?f=20&p=66245#p66245

Kurt

Offline cyberdynewinsTopic starter

  • Jr. Member
  • **
  • Posts: 16
  • Helpful? 0
Re: Xbee Servo Control
« Reply #5 on: September 23, 2010, 01:09:21 PM »
That's some hardcore coding. I wouldn't even think it was possible to do inverse kinematics on an arduino. This is code for a 6-8 legged robot?

It looked to me like you were outputting your Xbee communications to Digital I/O?
Code: [Select]

//=============================================================================
// Loop: the main arduino main Loop function
//=============================================================================


void loop(void)
{
    //Start time
    lTimerStart = millis();
    digitalWrite(2, HIGH);
    //Read input
    ControlInput();

#ifdef DEBUG_GAITS 
  // If we are in this mode, lets normalize the inputs as to make it easier to compare later...
  DebugLimitJoysticks(&TravelLengthX);
  DebugLimitJoysticks(&TravelLengthZ);
  DebugLimitJoysticks(&TravelRotationY);
  DebugLimitJoysticks(&BodyPosX);
  DebugLimitJoysticks(&BodyPosZ);
  DebugLimitJoysticks(&BodyRotY1);
  DebugLimitJoysticks(&BodyRotY1);
  DebugLimitJoysticks(&BodyRotX1);
  DebugLimitJoysticks(&BodyRotY1);
  DebugLimitJoysticks(&BodyRotZ1);
 
  // Try to also normalize BodyposY
  BodyPosY &= 0xFFF8;
#endif ;DEBUG_GAITS

   
    //GOSUB ReadButtons    //I/O used by the remote
    WriteOutputs();        //Write Outputs
           
    // Some debug stuff...
    #ifdef DEBUG_GAITS
    if (g_fHexOn && (TravelLengthX || TravelLengthZ))
    {
        DBGPrintf("Gait: %d %d BP:(%d, %d, %d) TL: (%d, %d), TRY: %d\n\r",
            GaitType, GaitStep,
            (short)BodyPosX,(short)BodyPosY, (short)BodyPosZ,
            (short)TravelLengthX, (short)TravelLengthZ, (short)TravelRotationY);
    }
    #endif       
    //GP Player
    if (GPEnable)
        GPPlayer();
           
    //Single leg control
    SingleLegControl ();
           
    //Gait
    GaitSeq();
             
    //Balance calculations
    TotalTransX = 0;     //reset values used for calculation of balance
    TotalTransZ = 0;
    TotalTransY = 0;
    TotalXBal1 = 0;
    TotalYBal1 = 0;
    TotalZBal1 = 0;
    if (BalanceMode) {
        for (LegIndex = 0; LegIndex <= 2; LegIndex++) {    // balance calculations for all Right legs
#ifdef DEBUG_GAITS
            if (g_fHexOn && (TravelLengthX || TravelLengthZ))   {
                DBGPrintf("GP? %d %d %d-", (short)GaitPosX[LegIndex], (short)GaitPosY[LegIndex], (short)GaitPosZ[LegIndex]);
            }
#endif           

            BalCalcOneLeg (-LegPosX[LegIndex]+GaitPosX[LegIndex],
                        LegPosZ[LegIndex]+GaitPosZ[LegIndex],
                        (LegPosY[LegIndex]-cInitPosY[LegIndex])+GaitPosY[LegIndex], LegIndex);
        }

        for (LegIndex = 3; LegIndex <= 5; LegIndex++) {    // balance calculations for all Right legs
#ifdef DEBUG_GAITS
            if (g_fHexOn && (TravelLengthX || TravelLengthZ))   {
                DBGPrintf("GP? %d %d %d-", (short)GaitPosX[LegIndex], (short)GaitPosY[LegIndex], (short)GaitPosZ[LegIndex]);
            }
#endif           
            BalCalcOneLeg(LegPosX[LegIndex]+GaitPosX[LegIndex],
                        LegPosZ[LegIndex]+GaitPosZ[LegIndex],
                        (LegPosY[LegIndex]-cInitPosY[LegIndex])+GaitPosY[LegIndex], LegIndex);
        }
        BalanceBody();
    }
         
 //Reset IKsolution indicators
     IKSolution = 0 ;
     IKSolutionWarning = 0;
     IKSolutionError = 0 ;
           
     //Do IK for all Right legs
     for (LegIndex = 0; LegIndex <=2; LegIndex++) {   
        BodyIK(-LegPosX[LegIndex]+BodyPosX+GaitPosX[LegIndex] - TotalTransX,
                LegPosZ[LegIndex]+BodyPosZ+GaitPosZ[LegIndex] - TotalTransZ,
                LegPosY[LegIndex]+BodyPosY+GaitPosY[LegIndex] - TotalTransY,
                GaitRotY[LegIndex], LegIndex);
                               
        LegIK (LegPosX[LegIndex]-BodyPosX+BodyIKPosX-(GaitPosX[LegIndex] - TotalTransX),
        LegPosY[LegIndex]+BodyPosY-BodyIKPosY+GaitPosY[LegIndex] - TotalTransY,
        LegPosZ[LegIndex]+BodyPosZ-BodyIKPosZ+GaitPosZ[LegIndex] - TotalTransZ, LegIndex);
    }
         
    //Do IK for all Left legs 
    for (LegIndex = 3; LegIndex <=5; LegIndex++) {
        BodyIK(LegPosX[LegIndex]-BodyPosX+GaitPosX[LegIndex] - TotalTransX,
                LegPosZ[LegIndex]+BodyPosZ+GaitPosZ[LegIndex] - TotalTransZ,
                LegPosY[LegIndex]+BodyPosY+GaitPosY[LegIndex] - TotalTransY,
                GaitRotY[LegIndex], LegIndex);
        LegIK (LegPosX[LegIndex]+BodyPosX-BodyIKPosX+GaitPosX[LegIndex] - TotalTransX,
                LegPosY[LegIndex]+BodyPosY-BodyIKPosY+GaitPosY[LegIndex] - TotalTransY,
                LegPosZ[LegIndex]+BodyPosZ-BodyIKPosZ+GaitPosZ[LegIndex] - TotalTransZ, LegIndex);
    }

    //Check mechanical limits
    CheckAngles();
               
    //Write IK errors to leds
    LedC = IKSolutionWarning;
    LedA = IKSolutionError;
           
    //Drive Servos
    if (g_fHexOn) {
        if (g_fHexOn && !g_fPrev_HexOn) {
            MSound(SOUND_PIN, 3, 60, 2000, 80, 2250, 100, 2500);
            XBeePlaySounds(3, 60, 2000, 80, 2250, 100, 2500);
            Eyes = 1;
        }
       
        //Set SSC time
        if ((abs(TravelLengthX)>cTravelDeadZone) || (abs(TravelLengthZ)>cTravelDeadZone) ||
                (abs(TravelRotationY*2)>cTravelDeadZone)) {         
            SSCTime = NomGaitSpeed + (InputTimeDelay*2) + SpeedControl;
               
            //Add aditional delay when Balance mode is on
            if (BalanceMode)
                SSCTime = SSCTime + 100;
        } else //Movement speed excl. Walking
            SSCTime = 200 + SpeedControl;
       
        // note we broke up the servo driver into start/commit that way we can output all of the servo information
        // before we wait and only have the termination information to output after the wait.  That way we hopefully
        // be more accurate with our timings...
        ServoDriverStart();
       
        // Sync BAP with SSC while walking to ensure the prev is completed before sending the next one
               
        fContinueWalking = false;
           
        // Finding any incident of GaitPos/Rot <>0:
        for (LegIndex = 0; LegIndex <= 5; LegIndex++) {
            if ( (GaitPosX[LegIndex] > 2) || (GaitPosX[LegIndex] < -2)
                    || (GaitPosY[LegIndex] > 2) || (GaitPosY[LegIndex] < -2)
                    || (GaitPosZ[LegIndex] > 2) || (GaitPosZ[LegIndex] < -2)
                    || (GaitRotY[LegIndex] > 2) || (GaitRotY[LegIndex] < -2) )    {
                fContinueWalking = true;
                break;
            }
        }
        if (fWalking || fContinueWalking) {
            word  wDelayTime;
            fWalking = fContinueWalking;
                 
            //Get endtime and calculate wait time
            lTimerEnd = millis();
            if (lTimerEnd > lTimerStart)
                CycleTime = lTimerEnd-lTimerStart;
            else
                CycleTime = 0xffffffffL - lTimerEnd + lTimerStart + 1;
           
            // if it is less, use the last cycle time...
            //Wait for previous commands to be completed while walking
            wDelayTime = (min(max ((PrevSSCTime - CycleTime), 1), NomGaitSpeed));
//            DBGPrintf("Delay: %d %d %d %d\n\r", (word)NomGaitSpeed, (word)CycleTime, (word)PrevSSCTime, (word)wDelayTime);
            digitalWrite(3, HIGH);
            delay (wDelayTime);
            digitalWrite(3, LOW);
        }
       
        ServoDriverCommit();
        digitalWrite(2, LOW);           

    } else {
        //Turn the bot off
        if (g_fPrev_HexOn || (AllDown= 0)) {
            SSCTime = 600;
            ServoDriverStart();
            ServoDriverCommit();
            MSound(SOUND_PIN, 3, 100, 2500, 80, 2250, 60, 2000);
            XBeePlaySounds(3, 100, 2500, 80, 2250, 60, 2000);
            delay(600);
        } else {
            FreeServos();
            Eyes = 0;
        }
    }
                 
    //Store previous g_fHexOn State
    if (g_fHexOn)
        g_fPrev_HexOn = 1;
    else
        g_fPrev_HexOn = 0;
}


//===================================================================
#ifdef DEBUG_GAITS
void DebugLimitJoysticks(long int *pDLJVal)
{
    // If we are in this mode, lets normalize the inputs as to make it easier to compare later...
    if (*pDLJVal < -64)
        *pDLJVal = -127;
    else if( *pDLJVal > 64)
        *pDLJVal = 127;
    else
        *pDLJVal = 0;
}
#endif


//--------------------------------------------------------------------
//[ReadButtons] Reading input buttons from the ABB
//--------------------------------------------------------------------
void ReadButtons(void)
{
}
//--------------------------------------------------------------------
//[WriteOutputs] Updates the state of the leds
//--------------------------------------------------------------------
void WriteOutputs(void)
{
 #if 0
    if (Eyes == 0)
        digitalWrite(cEyesPin, LOW);
    else
        digitalWrite (cEyesPin, HIGH);
#endif       
}

//--------------------------------------------------------------------
//[GP PLAYER]
//--------------------------------------------------------------------
boolean FIsSSCGPEnabled(void)
{
    char abVer[40];        // give a nice large buffer.
    byte cbRead;
    SSCSerial.print("ver\r");
   
    cbRead = SSCRead((byte*)abVer, sizeof(abVer), 10000, 13);

    DBGPrintf("Check GP Enable - cb %d\r", cbRead);
    if (cbRead > 0) {
        byte iT;
        for (iT = 0; iT < cbRead; iT++)
            DBGPrintf("%2x ", abVer[iT]);
        DBGSerial.write((byte*)abVer, cbRead);
    }
       
    if ((cbRead > 3) && (abVer[cbRead-3]=='G') && (abVer[cbRead-2]=='P') && (abVer[cbRead-1]==13))
        return true;

    MSound (SOUND_PIN, 2, 40, 2500, 40, 2500);
        XBeePlaySounds(2, 40, 2500, 40, 2500);
   
    return false;
}



Here's another chunk of code I've modified for my purposes.


Code: [Select]
// this program decodes a clock string sent via XBee radio
// the end result is six integer variables that hold the current year, month, day, hour, minute and second
//
// the time string is in the following format:
// *20061003143227
// where the year is 2006, month is October, day is the 3rd, hour is 14 (2 p.m.), minute is 32 and second is 27
//
// by Rob Faludi
// http://www.faludi.com

////// DEBUG OUTPUT IS HANDY, BUT NOT NECESSARY FOR CLOCK DECODING ///////
#include <Servo.h>

int Count;
byte Chan1, Chan2, Chan3, Chan4, Chan5, Chan6, Chan7, Chan8, Chan9;

void setup() {
  Serial.begin(9600); // start hardware serial process
}


void loop() {
  Serial.flush(); // clear the serial buffer before reading new data
  char packetString[12]; // create a string to hold the time value when it's read
  memset(packetString,'\0',12); // initialize that string to all NULL characters
  boolean packetStringValid = false; // declare and initialize a variable to track whether the string has all valid characters
  Serial.print("GET");
  byte inByte = '\0'; // declare and initialize a byte to read in serial data
  long startTime = millis();//makes the start time = to now
  int timeout = 1000; // timeout after one second
  while(millis() - startTime < timeout && inByte != '*') {
    inByte = Serial.read(); // read data and wait for an asterisk character
  }

  if (inByte == '*') { // if we got the correct start character (instead of a timeout)
    packetStringValid = true; // declare and initialize a variable to track whether the string has all valid characters
    long startTime = millis();//makes the start time = to now
    int timeout = 1000; // timeout after one second
    while(millis() - startTime < timeout && Serial.available() < 14) {
      ; //wait for enough data to be available (14 characters of time string), while doing nothing else
    }
    for (int i=0; i < 11; i++) {
      packetString[i] = Serial.read(); // reach each time string character into a character array
      if(packetString[i] < '0') {
        packetStringValid = false;  // if any character is bad then the whole string is bad
      }
    }
//    softSerial.print("Time: ");
//    softSerial.println(packetString);
  }

  if (packetStringValid == true) {
    char CountString[2];  // create a string to hold the year part of the string
    memset(CountString,'\0',2); // initialize that string to all NULL characters
    strncpy( CountString, packetString, 1); // copy the first two characters of packetString into the year string
    Count = atoi(CountString); // convert ASCII year string to integer and store in the year integer variable
//    softSerial.print("Count: ");
//    softSerial.println(count, DEC);

    char chan1String[2]; // create a string to hold the month part of the string
    memset(chan1String,'\0',2); // initialize that string to all NULL characters
    strncpy( chan1String, packetString+1, 1); // skip two character, then copy the next two of packetString into the month string
    Chan1 = atoi(chan1String);  // convert ASCII month string to integer and store in the month integer variable
//    softSerial.print("Month: ");
//    softSerial.println(month, DEC);

    char chan2String[2];
    memset(chan2String,'\0',2);
    strncpy( chan2String, packetString+2, 1);
    Chan2 = atoi(chan2String);
//    softSerial.print("Day: ");
//    softSerial.println(day, DEC);

    char chan3String[2];
    memset(chan3String,'\0',2);
    strncpy( chan3String, packetString+3, 1);
    Chan3 = atoi(chan3String);
//    softSerial.print("Hour: ");
//    softSerial.println(hour, DEC);

    char chan4String[2];
    memset(chan4String,'\0',2);
    strncpy( chan4String, packetString+4, 1);
    Chan4 = atoi(chan4String);
//    softSerial.print("Minute: ");
//    softSerial.println(minute, DEC);

    char chan5String[2];
    memset(chan5String,'\0',2);
    strncpy( chan5String, packetString+5, 1);
    Chan5 = atoi(chan5String);
//    softSerial.print("Second: ");
//    softSerial.println(second, DEC);

    char chan6String[2];
    memset(chan6String,'\0',2);
    strncpy( chan6String, packetString+6, 1);
    Chan6 = atoi(chan6String);
//    softSerial.print("Day: ");
//    softSerial.println(day, DEC);

    char chan7String[2];
    memset(chan7String,'\0',2);
    strncpy( chan7String, packetString+7, 1);
    Chan7 = atoi(chan7String);
//    softSerial.print("Hour: ");
//    softSerial.println(hour, DEC);

    char chan8String[2];
    memset(chan8String,'\0',2);
    strncpy( chan8String, packetString+8, 1);
    Chan8 = atoi(chan8String);
//    softSerial.print("Minute: ");
//    softSerial.println(minute, DEC);

    char chan9String[2];
    memset(chan9String,'\0',2);
    strncpy( chan9String, packetString+9, 1);
    Chan9 = atoi(chan9String);
//    softSerial.print("Second: ");
//    softSerial.println(second, DEC);

  Serial.print(Count, DEC);
  Serial.print(",");
  Serial.print(Chan1, DEC);
  Serial.print(",");
  Serial.print(Chan2, DEC);
  Serial.print(Chan3, DEC);
  Serial.print(Chan4, DEC);
  Serial.print(Chan5, DEC);
  Serial.print(Chan6, DEC);
  Serial.print(Chan7, DEC);
  Serial.print(Chan8, DEC);
  Serial.print(Chan9, DEC);

  }

  delay(1000); // wait for a moment so that output is readable
}

I made the mistake of removing all references to Software serial which is making it difficult to debug. I need to write some processing code which will loop through all ASCII values printing them to the "*a123456789 " format.

For testing, it's tempting to substitute a different character for the end of line instead of carriage return cause I can't think of a way to type that in to the serial console.

Another thing I want to do is error check the code by making sure that there is the correct number of characters between the * and the end of line character, as well as turn the "a" from above in to an actual counter.

Right now: my output is looking something like:
Code: [Select]
GETGETGETGETGETGETGETGETGET0,0,00000000GETGET
I'll feed it a line like "*a123456789a" and that's all I'll get.

It seems to be chopping SOMETHING up in to the individual chan1-9 variables, but what that something is: I'm not entirely sure. I doesn't seem to be manipulating the data properly.

Offline cyberdynewinsTopic starter

  • Jr. Member
  • **
  • Posts: 16
  • Helpful? 0
Re: Xbee Servo Control
« Reply #6 on: September 23, 2010, 01:18:33 PM »

Code: [Select]
// this program decodes a clock string sent via XBee radio
// the end result is six integer variables that hold the current year, month, day, hour, minute and second
//
// the time string is in the following format:
// *20061003143227
// where the year is 2006, month is October, day is the 3rd, hour is 14 (2 p.m.), minute is 32 and second is 27
//
// by Rob Faludi
// http://www.faludi.com

////// DEBUG OUTPUT IS HANDY, BUT NOT NECESSARY FOR CLOCK DECODING ///////
#include <Servo.h>

int Count;
byte Chan1, Chan2, Chan3, Chan4, Chan5, Chan6, Chan7, Chan8, Chan9;

void setup() {
  Serial.begin(9600); // start hardware serial process
}


void loop() {
  Serial.flush(); // clear the serial buffer before reading new data
  char packetString[12]; // create a string to hold the time value when it's read
  memset(packetString,'\0',12); // initialize that string to all NULL characters
  boolean packetStringValid = false; // declare and initialize a variable to track whether the string has all valid characters
  Serial.print("GET");
  byte inByte = '\0'; // declare and initialize a byte to read in serial data
  long startTime = millis();//makes the start time = to now
  int timeout = 1000; // timeout after one second
  while(millis() - startTime < timeout && inByte != '*') {
    inByte = Serial.read(); // read data and wait for an asterisk character
  }

  if (inByte == '*') { // if we got the correct start character (instead of a timeout)
    packetStringValid = true; // declare and initialize a variable to track whether the string has all valid characters
    long startTime = millis();//makes the start time = to now
    int timeout = 1000; // timeout after one second
    while(millis() - startTime < timeout && Serial.available() < 14) {
      ; //wait for enough data to be available (14 characters of time string), while doing nothing else
    }
    for (int i=0; i < 11; i++) {
      packetString[i] = Serial.read(); // reach each time string character into a character array
      if(packetString[i] < '0') {
        packetStringValid = false;  // if any character is bad then the whole string is bad
      }
    }
//    softSerial.print("Time: ");
//    softSerial.println(packetString);
  }

  if (packetStringValid == true) {
    char CountString[2];  // create a string to hold the year part of the string
    memset(CountString,'\0',2); // initialize that string to all NULL characters
    strncpy( CountString, packetString, 1); // copy the first two characters of packetString into the year string
    Count = atoi(CountString); // convert ASCII year string to integer and store in the year integer variable
//    softSerial.print("Count: ");
//    softSerial.println(count, DEC);

    char chan1String[2]; // create a string to hold the month part of the string
    memset(chan1String,'\0',2); // initialize that string to all NULL characters
    strncpy( chan1String, packetString+1, 1); // skip two character, then copy the next two of packetString into the month string
    Chan1 = atoi(chan1String);  // convert ASCII month string to integer and store in the month integer variable
//    softSerial.print("Month: ");
//    softSerial.println(month, DEC);

    char chan2String[2];
    memset(chan2String,'\0',2);
    strncpy( chan2String, packetString+2, 1);
    Chan2 = atoi(chan2String);
//    softSerial.print("Day: ");
//    softSerial.println(day, DEC);

    char chan3String[2];
    memset(chan3String,'\0',2);
    strncpy( chan3String, packetString+3, 1);
    Chan3 = atoi(chan3String);
//    softSerial.print("Hour: ");
//    softSerial.println(hour, DEC);

    char chan4String[2];
    memset(chan4String,'\0',2);
    strncpy( chan4String, packetString+4, 1);
    Chan4 = atoi(chan4String);
//    softSerial.print("Minute: ");
//    softSerial.println(minute, DEC);

    char chan5String[2];
    memset(chan5String,'\0',2);
    strncpy( chan5String, packetString+5, 1);
    Chan5 = atoi(chan5String);
//    softSerial.print("Second: ");
//    softSerial.println(second, DEC);

    char chan6String[2];
    memset(chan6String,'\0',2);
    strncpy( chan6String, packetString+6, 1);
    Chan6 = atoi(chan6String);
//    softSerial.print("Day: ");
//    softSerial.println(day, DEC);

    char chan7String[2];
    memset(chan7String,'\0',2);
    strncpy( chan7String, packetString+7, 1);
    Chan7 = atoi(chan7String);
//    softSerial.print("Hour: ");
//    softSerial.println(hour, DEC);

    char chan8String[2];
    memset(chan8String,'\0',2);
    strncpy( chan8String, packetString+8, 1);
    Chan8 = atoi(chan8String);
//    softSerial.print("Minute: ");
//    softSerial.println(minute, DEC);

    char chan9String[2];
    memset(chan9String,'\0',2);
    strncpy( chan9String, packetString+9, 1);
    Chan9 = atoi(chan9String);
//    softSerial.print("Second: ");
//    softSerial.println(second, DEC);

  Serial.print(Count, DEC);
  Serial.print(",");
  Serial.print(Chan1, DEC);
  Serial.print(",");
  Serial.print(Chan2, DEC);
  Serial.print(Chan3, DEC);
  Serial.print(Chan4, DEC);
  Serial.print(Chan5, DEC);
  Serial.print(Chan6, DEC);
  Serial.print(Chan7, DEC);
  Serial.print(Chan8, DEC);
  Serial.print(Chan9, DEC);

  }

  delay(1000); // wait for a moment so that output is readable
}

I made the mistake of removing all references to Software serial which is making it difficult to debug. I need to write some processing code which will loop through all ASCII values printing them to the "*a123456789 " format.

For testing, it's tempting to substitute a different character for the end of line instead of carriage return cause I can't think of a way to type that in to the serial console.

Another thing I want to do is error check the code by making sure that there is the correct number of characters between the * and the end of line character, as well as turn the "a" from above in to an actual counter.

Right now: my output is looking something like:
Code: [Select]
GETGETGETGETGETGETGETGETGET0,0,00000000GETGET
I'll feed it a line like "*a123456789a" and that's all I'll get.

It seems to be chopping SOMETHING up in to the individual chan1-9 variables, but what that something is: I'm not entirely sure. I doesn't seem to be manipulating the data properly.

Some inputs and outputs:
Code: [Select]
*m123456789s->0,1,23456789
*m123456789s->Nothing
*m123456789ss->0,1,23456789
*9123456789s->9,1,23456789
*9123456789s->9,1,23456789
*9123456789s->Nothing
*9123456789ss->9,1,23456789
*9123456789@->Nothing

It will take a-z & A-Z and turn them in to zeros.
It will take integers 1-9 and output them back out as 1-9.
It will refuse all Shift+(1-9) special characters

It's inconsistent about rejecting packets. The above responses aren't actual outputs, so the distinction between s & ss outputs isn't accurate for the moment.

Edit:
It allows some special characters:ASCII 48-126 as far as I can tell are OK.

I have a "If <"0" ==false" in the code so it makes a lot of sense, although I'm not sure why it's rejecting 128-255?

I'm not sure why the value it outputs is always zero for a non 1-9 character?

Newer Edit:
I changed the "0" to a "!" and now the code accepts
 
*!123456789ss

So I guess I've expanded to !-127

Newer Edit:
My gut tells me the answer I'm looking for is at this URL:
http://www.arduino.cc/en/Reference/String

Am I printing the null characters?
« Last Edit: September 23, 2010, 01:51:02 PM by cyberdynewins »

Offline cyberdynewinsTopic starter

  • Jr. Member
  • **
  • Posts: 16
  • Helpful? 0
Re: Xbee Servo Control
« Reply #7 on: September 24, 2010, 02:37:37 AM »

Some inputs and outputs:
Code: [Select]
*m123456789s->0,1,23456789
*m123456789s->Nothing
*m123456789ss->0,1,23456789
*9123456789s->9,1,23456789
*9123456789s->9,1,23456789
*9123456789s->Nothing
*9123456789ss->9,1,23456789
*9123456789@->Nothing

It will take a-z & A-Z and turn them in to zeros.
It will take integers 1-9 and output them back out as 1-9.
It will refuse all Shift+(1-9) special characters

It's inconsistent about rejecting packets. The above responses aren't actual outputs, so the distinction between s & ss outputs isn't accurate for the moment.

Edit:
It allows some special characters:ASCII 48-126 as far as I can tell are OK.

I have a "If <"0" ==false" in the code so it makes a lot of sense, although I'm not sure why it's rejecting 128-255?

I'm not sure why the value it outputs is always zero for a non 1-9 character?

Newer Edit:
I changed the "0" to a "!" and now the code accepts
 
*!123456789ss

So I guess I've expanded to !-127

Newer Edit:
My gut tells me the answer I'm looking for is at this URL:
http://www.arduino.cc/en/Reference/String

Am I printing the null characters?


Quote
The char datatype is a signed type, meaning that it encodes numbers from -128 to 127. For an unsigned, one-byte (8 bit) data type, use the byte data type.

http://arduino.cc/en/Reference/Char

If I wasn't so tired I would be really excited right now.

Offline cyberdynewinsTopic starter

  • Jr. Member
  • **
  • Posts: 16
  • Helpful? 0
Re: Xbee Servo Control
« Reply #8 on: September 24, 2010, 12:18:11 PM »
I have no idea how to fix this? I think I'm stumped.  ???

I want to convert these from char to byte, but then the strncpy stops working.

It would be nice to be able to properly handle the msgs as is(ASCII 0-255)?

I guess one thing I could do is try to feed the program proper -127 through 127 values. I could reformat my msg before it's sent(adding polarity characters?) or I could cut my sensitivity in half and have a servo position range of ~170degrees ((127-42)*2) in 2 degree increments.

Offline madsci1016

  • Contest Winner
  • Supreme Robot
  • ****
  • Posts: 1,450
  • Helpful? 43
    • Personal Website
Re: Xbee Servo Control
« Reply #9 on: September 24, 2010, 12:32:19 PM »
cyber, can you sum up the problem for me? All these posts would take an hour to go through, and i haven't been keeping up. 

strncpy will return early if it get's a non-standard ASCII character, or line return type null character. Your better off using a memcpy if this is happening.

If your parsing out ascii sentences, I like using strtod() instead of atoi(). If your ASCII sentences are delimited (commas between values for example), you don't need to setup all these temp strings and such, just keep calling strtod() with an advancing pointer. Here's how i do it with my SAGAR robot:

Code: [Select]
robodata.req_speed_l = strtod(&incoming[decode_index], NULL);
decode_index = next_token(istr, decode_index);
robodata.req_speed_r = strtod(&incoming[decode_index], NULL);
decode_index = next_token(istr, decode_index);
robodata.wheelspeed_l = strtod(&incoming[decode_index], NULL);
decode_index = next_token(istr, decode_index);

where
Code: [Select]
char* istr = (char*)incoming;


is a pointer to my incoming buffer, and

Code: [Select]
int next_token(char* str,int inx){
while(str[inx++] != ',');
return inx;
}

just to make sure I am advancing my pointer to the next token in the string.



Offline cyberdynewinsTopic starter

  • Jr. Member
  • **
  • Posts: 16
  • Helpful? 0
Re: Xbee Servo Control
« Reply #10 on: September 24, 2010, 01:36:47 PM »
cyber, can you sum up the problem for me? All these posts would take an hour to go through, and i haven't been keeping up. 

strncpy will return early if it get's a non-standard ASCII character, or line return type null character. Your better off using a memcpy if this is happening.

If your parsing out ascii sentences, I like using strtod() instead of atoi(). If your ASCII sentences are delimited (commas between values for example), you don't need to setup all these temp strings and such, just keep calling strtod() with an advancing pointer. Here's how i do it with my SAGAR robot:

Code: [Select]
robodata.req_speed_l = strtod(&incoming[decode_index], NULL);
decode_index = next_token(istr, decode_index);
robodata.req_speed_r = strtod(&incoming[decode_index], NULL);
decode_index = next_token(istr, decode_index);
robodata.wheelspeed_l = strtod(&incoming[decode_index], NULL);
decode_index = next_token(istr, decode_index);

where
Code: [Select]
char* istr = (char*)incoming;


is a pointer to my incoming buffer, and

Code: [Select]
int next_token(char* str,int inx){
while(str[inx++] != ',');
return inx;
}

just to make sure I am advancing my pointer to the next token in the string.




Hehe. Thread is starting to get a little bit cluttered with all these
Code: [Select]
boxes.

Alot of issues I eventually figured out.

Newest problem is the code can't handle characters with a value larger than 127, and any characters other than "0-9" get output as "0".


One other problem is I tried using the output variables(chan1-chan9) to drive a servo(using values between "0-9") and they print fine but they don't seem to be useful for servo positions?

Code: [Select]
// this program decodes a clock string sent via XBee radio
// the end result is six integer variables that hold the current year, month, day, hour, minute and second
//
// the time string is in the following format:
// *20061003143227
// where the year is 2006, month is October, day is the 3rd, hour is 14 (2 p.m.), minute is 32 and second is 27
//
// by Rob Faludi
// http://www.faludi.com

////// DEBUG OUTPUT IS HANDY, BUT NOT NECESSARY FOR CLOCK DECODING ///////
#include <Servo.h>
#define SERVO_1_PIN 2

int Count, ch, pos;
char Chan1, Chan2, Chan3, Chan4, Chan5, Chan6, Chan7, Chan8, Chan9;

[color=red]Servo g_servo1;[/color]

void setup()
{[color=red]
 pinMode(SERVO_1_PIN, OUTPUT);
 g_servo1.attach(SERVO_1_PIN);[/color]
 Serial.begin(9600); // start hardware serial process

}



void loop()

{
  static int val = 0;
 
  Serial.flush(); // clear the serial buffer before reading new data
  byte packetString[12]; // create a string to hold the time value when it's read
  memset(packetString,'\0',12); // initialize that string to all NULL characters
  boolean packetStringValid = false; // declare and initialize a variable to track whether the string has all valid characters
  //Serial.print("GET");
  byte inByte = '\0'; // declare and initialize a byte to read in serial data
  long startTime = millis();//makes the start time = to now
  int timeout = 1000; // timeout after one second
  while(millis() - startTime < timeout && inByte != '*') {
    inByte = Serial.read(); // read data and wait for an asterisk character
  }

  if (inByte == '*') { // if we got the correct start character (instead of a timeout)
    packetStringValid = true; // declare and initialize a variable to track whether the string has all valid characters
    long startTime = millis();//makes the start time = to now
    int timeout = 1000; // timeout after one second
    while(millis() - startTime < timeout && Serial.available() < 14) {
      ; //wait for enough data to be available (14 characters of time string), while doing nothing else
    }
    for (int i=0; i < 11; i++) {
      packetString[i] = Serial.read(); // reach each time string character into a character array
      if(packetString[i] < '!') {
        packetStringValid = false;  // if any character is bad then the whole string is bad
      }
    }
//    softSerial.print("Time: ");
//    softSerial.println(packetString);
  }

  if (packetStringValid == true) {
    char CountString[2];  // create a string to hold the year part of the string
    memset(CountString,'\0',2); // initialize that string to all NULL characters
    strncpy( CountString, packetString, 1); // copy the first two characters of packetString into the year string
    Count = atoi(CountString); // convert ASCII year string to integer and store in the year integer variable
//    softSerial.print("Count: ");
//    softSerial.println(count, DEC);

    char chan1String[2]; // create a string to hold the month part of the string
    memset(chan1String,'\0',2); // initialize that string to all NULL characters
    strncpy( chan1String, packetString+1, 1); // skip two character, then copy the next two of packetString into the month string
    Chan1 = atoi(chan1String);  // convert ASCII month string to integer and store in the month integer variable
//    softSerial.print("Month: ");
//    softSerial.println(month, DEC);

    char chan2String[2];
    memset(chan2String,'\0',2);
    strncpy( chan2String, packetString+2, 1);
    Chan2 = atoi(chan2String);
//    softSerial.print("Day: ");
//    softSerial.println(day, DEC);

    char chan3String[2];
    memset(chan3String,'\0',2);
    strncpy( chan3String, packetString+3, 1);
    Chan3 = atoi(chan3String);
//    softSerial.print("Hour: ");
//    softSerial.println(hour, DEC);

    char chan4String[2];
    memset(chan4String,'\0',2);
    strncpy( chan4String, packetString+4, 1);
    Chan4 = atoi(chan4String);
//    softSerial.print("Minute: ");
//    softSerial.println(minute, DEC);

    char chan5String[2];
    memset(chan5String,'\0',2);
    strncpy( chan5String, packetString+5, 1);
    Chan5 = atoi(chan5String);
//    softSerial.print("Second: ");
//    softSerial.println(second, DEC);

    char chan6String[2];
    memset(chan6String,'\0',2);
    strncpy( chan6String, packetString+6, 1);
    Chan6 = atoi(chan6String);
//    softSerial.print("Day: ");
//    softSerial.println(day, DEC);

    char chan7String[2];
    memset(chan7String,'\0',2);
    strncpy( chan7String, packetString+7, 1);
    Chan7 = atoi(chan7String);
//    softSerial.print("Hour: ");
//    softSerial.println(hour, DEC);

    char chan8String[2];
    memset(chan8String,'\0',2);
    strncpy( chan8String, packetString+8, 1);
    Chan8 = atoi(chan8String);
//    softSerial.print("Minute: ");
//    softSerial.println(minute, DEC);

    char chan9String[2];
    memset(chan9String,'\0',2);
    strncpy( chan9String, packetString+9, 1);
    Chan9 = atoi(chan9String);
//    softSerial.print("Second: ");
//    softSerial.println(second, DEC);
[color=red]

 ch = Chan1;     
 pos = ch * 10 + ch - '0';[/color]
   
  Serial.print(Count, DEC);
  Serial.print(ch, DEC);
  Serial.print(Chan2, DEC);
  Serial.print(Chan3, DEC);
  Serial.print(Chan4, DEC);
  Serial.print(Chan5, DEC);
  Serial.print(Chan6, DEC);
  Serial.print(Chan7, DEC);
  Serial.print(Chan8, DEC);
  Serial.print(Chan9, DEC);
[color=red]  g_servo1.write(ch);[/color]
  delay(1000); // wait for a moment so that output is readable
}
}

Normally I like to work on only one problem at a time, so I only bring it up because perhaps the behavior helps cast light on my primary and current problem.

I'm gonna spend the afternoon trying to fix things having the benefit of your last post. Thank you. I haven't quite settled in to head scratching mode, but once I do: I imagine your post will be very helpful.

Thanks again,
-Cyber

Offline madsci1016

  • Contest Winner
  • Supreme Robot
  • ****
  • Posts: 1,450
  • Helpful? 43
    • Personal Website
Re: Xbee Servo Control
« Reply #11 on: September 24, 2010, 01:46:29 PM »
Code: [Select]
char chan2String[2];
    memset(chan2String,'\0',2);
    strncpy( chan2String, packetString+2, 1);
    Chan2 = atoi(chan2String);

You are creating a temp string that is two characters long. Then you are only copying one character from packetString into the temp string. then you are running the atoi() function on the temp string.

Well, first why is the temp string 2 characters if you are only copying one from the packetString?

Next, atoi() only handles numbers, 0-9. All characters will be returned as 0, as they are not numbers.

So what exactly are you trying to do?

Offline cyberdynewinsTopic starter

  • Jr. Member
  • **
  • Posts: 16
  • Helpful? 0
Re: Xbee Servo Control
« Reply #12 on: September 24, 2010, 02:06:51 PM »
That definately explains it.

I didn't entirely understand it. My understanding was that the +1 was for the sake of making room for some sort of formatting null characters?(the " '\0' " from "  memset(chan2String,'\0',2);")

I pretty much just blindly modified the original "clock" code by shifting all the values by the amount of difference between his msg formats and mine.


trying to do:
Quote
I'm trying to transmit servo positions as values from 0 to 255 via Xbee(while minimizing latency) to a second arduino which will use those servo positions to drive up to 9 servos.

I was hoping by minimizing packet size via transmitting the bare minimum number of characters: that would reduce latency?

I'm beginning to wonder if processing power will be latency bottleneck, making the goal of minimizing packet size misguided.

I was going to try and optimize the code later on with latency in mind based on the results of testing.

Looking over the code you posted as we speak.
« Last Edit: September 24, 2010, 02:11:14 PM by cyberdynewins »

Offline madsci1016

  • Contest Winner
  • Supreme Robot
  • ****
  • Posts: 1,450
  • Helpful? 43
    • Personal Website
Re: Xbee Servo Control
« Reply #13 on: September 24, 2010, 02:10:27 PM »
Raw binary would be the fastest, one byte per servo.

ASCII would be something like

255,255,255,255,...

then the code i posted would work perfectly for you. But now it takes 4 bytes per servo.

Offline cyberdynewinsTopic starter

  • Jr. Member
  • **
  • Posts: 16
  • Helpful? 0
Re: Xbee Servo Control
« Reply #14 on: September 24, 2010, 02:19:10 PM »
Raw binary would be the fastest, one byte per servo.

ASCII would be something like

255,255,255,255,...

then the code i posted would work perfectly for you. But now it takes 4 bytes per servo.

I like the looks of it.

I originally snubbed binary because of misconceptions about the possibility of Serial.Print treating each 0 or 1 as an ASCII character, but having the benefit of more experience/understanding I now see that was misguided.

Can I mix binary and ASCII by seperating values by an ASCII comma or is that absurd? Should I just take the entire string "42,255,255,255,255,255,255,255,255,255,255," and print it to serial as binary?

Offline madsci1016

  • Contest Winner
  • Supreme Robot
  • ****
  • Posts: 1,450
  • Helpful? 43
    • Personal Website
Re: Xbee Servo Control
« Reply #15 on: September 24, 2010, 02:24:22 PM »
Can I mix binary and ASCII by seperating values by an ASCII comma or is that absurd? Should I just take the entire string "42,255,255,255,255,255,255,255,255,255,255," and print it to serial as binary?

Not really. remember, everything is a number to a computer, even a comma. So when your servo value is the same number as a comma, you could have problems.

Everything out of a serial port is binary. Serial.print() just prints the binary ASCII value of each character in a string representing a number. Serial.write() would print out a binary number as is.

Offline cyberdynewinsTopic starter

  • Jr. Member
  • **
  • Posts: 16
  • Helpful? 0
Re: Xbee Servo Control
« Reply #16 on: September 24, 2010, 02:46:38 PM »
Can I mix binary and ASCII by seperating values by an ASCII comma or is that absurd? Should I just take the entire string "42,255,255,255,255,255,255,255,255,255,255," and print it to serial as binary?

Not really. remember, everything is a number to a computer, even a comma. So when your servo value is the same number as a comma, you could have problems.

Everything out of a serial port is binary. Serial.print() just prints the binary ASCII value of each character in a string representing a number. Serial.write() would print out a binary number as is.

Ah, that's handy.

I guess from here: I'll spend a few more minutes trying to salvage what code I've got, then shelve it as a learning experience while I see if I can't use this new info to my advantage.

Offline KurtEck

  • Robot Overlord
  • ****
  • Posts: 217
  • Helpful? 12
Re: Xbee Servo Control
« Reply #17 on: September 24, 2010, 07:38:50 PM »
I have been following along here and I mentioned I have done some similar stuff, with XBees. My guess is what you are planning to do is to have a remote control maybe based on a PS2, with an Arduino and XBee talk to another Xbee and probably another Arduino that then tells the servos to do something.   I have had a lot of fun doing similar stuff and I have done a couple of different versions of it.  My first one used the XBees in simple terminal replacement mode, that is you write something out on one end and assuming the My's and DL's are set up properly it comes out the other side on the other XBee.  This was definitely the simpler way to get started.  My current version uses the XBees in packet mode, for a few different reasons.  One is my text replacement mode duplicated a lot of packet stuff, I had my own byte count for a packet, plus a checksum... Also in terminal replacement mode, if you are sending binary data, it may be difficult to align your input properly as to know when one logical packet starts and the previous one ended. In packet mode, there is a predefined format for packets and the XBees verify the format and checksums...

For my implementations I defined a set of messages, which within the XBee packet was the first data byte.  I also went through a couple of generations of this.  I started out having the remote blindly send out data as fast as it could, which can work fine, but I decided why have the sender/receiver waste all of their time wasting time reading in the same data if nothing has changed, which may be a large percentage of the time.  So I now have the remote send a message saying the data has changed and it waits for the other end to send back a packet saying give it to me at which time it sends the data... I have other packets defined that allows the robot to have the remote display a message on it's LCD... but that is secondary.

I am not sure if it helps you or not, but for example I have a simple helper function:

Code: [Select]
inline void WriteToXBee(byte *pb, byte cb) __attribute__((always_inline));
void WriteToXBee(byte *pb, byte cb)
{
    XBeeSerial.write((byte*)pb, cb);
}

void SendXBeePacket(byte bPHType, byte cbExtra, byte *pbExtra)
{
    // Tell system to now output to the xbee
    byte abPH[9];
    byte *pbT;
    byte bChkSum;
    int i;

    // We need to setup the xbee Packet
    abPH[0]=0x7e;                        // Command prefix
    abPH[1]=0;                            // msb of size
    abPH[2]=cbExtra+XBEE_API_PH_SIZE + 5;    // size LSB
    abPH[3]=1;                             // Send to 16 bit address.

    g_diystate.bPacketNum = g_diystate.bPacketNum + 1;
    if (g_diystate.bPacketNum == 0)
        g_diystate.bPacketNum = 1;        // Don't pass 1 as this says no ack
    abPH[4]=g_diystate.bPacketNum;        // frame number
    abPH[5]=g_diystate.wAPIDL >> 8;        // Our current destination MSB/LSB
    abPH[6]=g_diystate.wAPIDL & 0xff;
    abPH[7]=0;                            // No Options

    abPH[8]=bPHType;

    // Now compute the initial part of the checksum
    bChkSum = 0;
    for (i=3;i <= 8; i++)
        bChkSum += abPH[i];

    // loop through the extra bytes in the exta to build the checksum;
    pbT = pbExtra;
    for (i=0; i < cbExtra; i++)
        bChkSum += *pbT++;                // add each byte to the checksum

    // Ok lets output the fixed part
    WriteToXBee(abPH,9);

    // Ok lets write the extra bytes if any to the xbee
    if (cbExtra)
        WriteToXBee(pbExtra, cbExtra);

    // Last write out the checksum
    bChkSum = 0xff - bChkSum;
    WriteToXBee(&bChkSum, 1);
}
I keep a running sequence number, which for the most part is not used.  also the g_diystate.wAPIDL holds the 16 bit XBee address of my current destination.

So with this, to send an array of desired servo positions, you could simply define a packet type for this, pass in a pointer to the array and a count of bytes and this would send it...  The Xbees themselves take care of verifying that the checksums are correct, so at the other end you simply have to look at the first user byte, see it is a message is your servo data, and extract the data. 

Secondary note on sending binary data.  If you are sending data to a different processor and if your values are not bytes but instead something like words or longs, you may have to be careful as not all processors store their binary data in the same format.  That is some processors store the binary date with the Most significant byte first where others store it with the Least significant one first.

Again I am not sure if this is the type of information you are looking for or not.  If you have any questions on this let me know.

Kurt

 


Get Your Ad Here

data_list