Serial char and int confusion

hey

I am working with this very sweet Animatics servo motor… it is sort of a Cadillac as far as servo’s go. You tell it over serial to go a 32 bit position like “97843288413”, or wherever, and it goes there exactly. It’s an all-in-one motor with servo, processor and encoder and amplifier built in. You just add power and serial commands and you get precise motions.

Anyway. One of the things I need to do is to query it and get its current position. The process is like this:

  • send ASCII “RP” to the motor over RS232 to get the current position
  • the motor spits back the position as a string of numbers.

The problem I am having is with dealing with what comes back. Good and accurate position numbers come back, but I am such a poor coder that i don;t know how to deal with them properly! The good positions here are highlighted, the rest is garbage:

motor position is ¢fÊ?j?394699S
motor position is 6?ª?j?493359S
motor position is É???Öü592023S
motor position is Â?º?Öü690677S
motor position is Ê??¢Öü789364S

The serial functions are working perfectly- as in they are set up properly and don’t drop any data. The problem is that I don’t know how to read back a plain old number ( int or long?) from this char array full of garbage. The “good positions” might be one digit long, as in “0”, or 12 digits long, as in “123456712345”.

I am not much of a coder, so any suggestions on how to deal with this are much appreciated! Relevant parts of the code are below.

Daniel


#include <stdlib.h> // needed for atoi

// variables
char currentPos[12]=“ABCDEFGHIJK”;
int byteREADmotor; // variable for storing bytes just read into SWS
int bytesread = 0; // pointer for keeping track of motor read

// I’ve left out the void setup but it is fine

void loop(){

gotoPosition(991010, 40, 100); // goto position # 991010 at 40% acceleration and 100% speed
delay(4000);
decelMotor(); // decelerate to stop
delay(4000);

getMotorPosition();
//int actualPosition = atoi(currentPos); // convert char position array to single number ( never realluy got this working right in this case)

Serial.print("motor position is ");
Serial.println(currentPos);

}

// function to get motor position and store in currentPos array

void getMotorPosition(){

motorSerial.println(“RP”); // ask motor to report its position “RP” ( works OK)

bytesread = 0;

byteREADmotor = motorSerial.read(); // read individual byte

while (bytesread != 13) { // while it’s not a carriage return

byteREADmotor = motorSerial.read(); // read individual byte

if (byteREADmotor == 13 || byteREADmotor== 10){ break; } // if ASCII “13” or '10" is received before the 10th digit reading, stop reading data

currentPos[bytesread] = byteREADmotor; // store the byte just read in the “currentPos” array
bytesread++; // ready to read next digit of oposition, increment array pointer

}

}


Daniel, I would expect that code to print the garbage characters at end of the string rather than before because currentPos character string is not null terminated. So although I am not exactly sure what is going on, I hope the following helps.

The function getMotorPosition throws the first received byte away. This will give invalid results on the first call. It may work for subsequent calls because you break before receiving the terminating cr. I suggest you delete the call to motorSerial.Read before the while statement and terminate the string with a 0 as per the following fragment.

#define MAX_CHARS  12  // maximum number of chars from the servo
char currentPos[ MAX_CHARS]="ABCDEFGHIJK";  // only digits are stored (not cr/lf) so this will hold up to 11 chars including terminating null 

void  getMotorPosition(){ 
 
  motorSerial.println("RP");                  // ask motor to report its position "RP" ( works OK)  
  bytesread = 0; 
  currentPos[bytesread] = 0; // start out with a 0 length string so the app can tell if no valid digits are received   
 
  while ( bytesread < MAX_CHARS) {           // wait for digits from servo   
    byteREADmotor = motorSerial.read();       //  read individual byte  
    if ( byteREADmotor== 10){   // this will be the first terminating charactor
         currentPos[bytesread] = 0   // null terminate the string if ASCII '10"  is received                            
     }    
     else if ( byteREADmotor== 13){   
       break;    // exit the while loop when ASCII 13 is received
     }     
     else {
         currentPos[bytesread++] = byteREADmotor;             
     }
  }     
}

I hope that helps.

Parse the number out something like this
long curPos;

curPos = 0;

while (currentPos > 47 && currentPos < 58) { // check for numeral character (0-9)
curPos = (curPos * 10) + (currentPos - 48) // subtract 48 to convert from ASCII
i++;
}
Hope that helps ( A new acne creme for geeks)
pb

You can also use characters instead of their ASCII values, for example:

curPos = 0;
while (currentPos >= ‘0’ && currentPos <= ‘9’) {
_ curPos = (curPos * 10) + (currentPos - ‘0’);
* i++;
}*_

thanks folks! I will try these code snippets later today.

Reading through them it occurs to me that I need a variable to keep track of how many characters the reported position is, and then I need to use that variable when reconstructing the char array as as a long variable.

BTW if you need a servo, these are the best ones I've ever used... the precision and reliability is unbelievable!

D

Reading through them it occurs to me that I need a variable to keep track of how many characters the reported position is, and then I need to use that variable when reconstructing the char array as as a long variable.

if you null terminate the array as per the fragment posted you can get the number of digits as follows: int NbrDigits = strlen(currentPos); // strlen returns the number of chars

thanks everyone....

as it turns out, the numbers above weren't coming as a result of a request. I had programmed the motor own internal processor to spit them out. Then the software serial would catch them in an odd order and geenrate all that junk. That was problem one.

Problem 2 was that instead of sending "ZS RP" (reset errors, report position), I was sending (of course) "ZSRP" and the motor wasnt recognizing the report position command.

Now I get a nice stream of position reports! Super accurate: here's 1 RPM rotation over a couple of seconds: 526448 532466 538451 544449

One handy thing I did discover is that you can put a KeySpan USA19 serial adapter on the spare USB port of your computer, run ZTERM and use the Keyspan's RS232 receive pin as a probe to watch what's being sent!

thanks again

D

here’s what I ended up with. This sends motor commands via Software serial, and receives the response over hardware serial.

Calling getPosition() returns a long with the exact motor position:

long position = 0; 

long  getMotorPosition(){
  blinkStatusLed(2, 40);                      // blink status led
  Serial.flush();                                  // clear junk from hardware serial port
  bytesread = 0;                                // zero the variables for the serial read 
  motorSerial.println("ZS");                // tell motor to ignore internal errors
  delay(5);
  motorSerial.println("RP");                   // ask motor to report its position "RP"
 delay (10);                                                // wait for the 128 byte hardware serial buffer to fill up a bit

  while (Serial.available() > 0 )  {                 // while there is data in the hardware serial buffer
    currentPos[bytesread] = Serial.read();     //  read a byte

if (currentPos[bytesread]==13){                  // escape if end of data CR
    bytesread= bytesread - 1;                       // decrement array if last number is CR (13)
    break;
}

    bytesread= bytesread +1;                               // ready to read next digit of position, increment array pointer 
 }    
  
   
position = 0;
    for (int pointer= 0; pointer<= bytesread; pointer++){   // reconstruct the position as long
  position = (position * 10) + (currentPos[pointer] - 48 );
 }

return position;

}