Serial Comm with XSens Motion Tracker

I have an XSens MTi-G motion tracker and an Arduino Uno. The message structure over serial is defined as follows:

PRE = 0xFA
BID = 0xFF
MID = varies
LEN = varies
DATA = LEN bytes
CS = checksum

The baud rate is 115200 on the device and it's programmed to send a data message (MID=0x32) at a 10 Hz update rate. Using the Serial monitor sample code, I never receive the 0xFA-0xFF byte sequence. Attached to a PC, the XSens sends this sequence, but reading the serial output with the Arduino only randomly (coincidentally) receives it. Previously, a PIC-based uC was used to read the serial output and it worked fine - is there any reason the Arduino is having trouble? Here's the code I'm using:

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

int incomingByte = 0;
void loop() {
if (Serial.available() > 0) {
incomingByte = Serial.read();
Serial.print("S: ");
Serial.println(incomingByte, HEX);
}
}

If you're interested in the raw output it can be downloaded at http://cl.ly/3Q0z33353w0s0z1w0J0G for analysis.

Two questions, what clock source are you using and have you tried to lower the baud rate to see if it makes a difference?

I tried lowering the baud rate to 9600 in the XSens software but Windows crashed in the middle of the update, and upon restart Windows no longer sees the XSens as a serial device, but as a mouse. Plugging it in causes the mouse pointer to start flying all over the place and jumping around. I tried uninstalling and reinstalling the drivers, rebooting after every step, and now Windows can't install the drivers at all.

Anyhow, back on the Mac, I set the baud to 9600 in the Arduino monitor and in the Arduino sketch. I receive precisely the same output, just marginally slower… so it's not the baud rate.

I'm using the clock on board the Arduino, what would you suggest otherwise?

EDIT: Just to confirm, I've tried the other XSens supported bauds with no success...

The next thing I would do is to put a monitor on the comms line. I use the BusPirate from Sparkfun to sniff what is being sent and received. I use a terminal called Tera Term to see the raw data. This way you will at least know that the data comes from the Adruino in the form that you expect.

This is an old topic but I need this again and the same issue has come up.

The baud rate everywhere is 57600. Using a raw serial monitor the bytes come back with the repeating 0xFA 0xFF sequence that I need. However, using the Arduino code listed below I get completely different results than the serial monitor (set to 8-N-1, the same as the Arduino) gives. Very frustrating. I've even tried inverting the bit order on the input and actually inverting the bits themselves (and both) to no avail.

WHY is the serial input on the Arduino different than the serial input on a serial monitor???

Also, I've used multiple Arduinos (two Unos and a Mega) with identical results, so it's not the board itself.

Code used, verbatim:

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

void loop() {
while (Serial.available() > 0) {
incomingByte = Serial.read();
if (incomingByte == 0xFA) {
Serial.println(""); // line break on that key byte
}

Serial.print(incomingByte, HEX);
Serial.print(" ");
}
}

Hi jfm429, and hi everybody. I've the same sensor and exaclty the same problem described in this topic. Have you found a solution? Has anyone else the solution to fix this problem?

Thank you fo helping me.

i have had these problems, too, but found the reasons of this behaviour: the xsens communicates over uart with the standard rs232 voltage level. this means the input from arduino to MTi-G has to be

  1. 2.0V to 25.0V ('1')
  2. -25.0V to 0.6V ('0')
    the voltage between 0.6V and to 2V is not defined. and this is because the mti-g never received his GoToMeasurement or other messages from the arduino to start. the other direction is not as critical as this. i used a level shifter (Maxim Max232AEPE) between and it works.

another thing is the order of data. in the "mt low-level communication protocol documentation", page 27... the order of data (MTData frame) is described with 1. GPS PVT data (44byte) 2. Temp (4bytes float) 3. ...
but this is not correct. the order has to be 1. Temp 2. GPS PVT data ...

hope i could be of help,
susan

Thanks a lot!

For precision I've the MTi ; not the MTi-G.

I've to say that i dont' need to decode the input message because I do it with Matlab/Simulink. It's not a problem for me.

Actually my problem is just that when I connect the sensor to the board i receive wrong values compared to what I'm expecting. Could it be linked to the electrical connection?

The voltage I provide to the sensor is 5 V and also the TX pin in output has a level of 5 V. So, is it necessary a MAX232 even in this case? I've no voltage to bring down!

Have anyone the solution?

Thank you!!!

i have the same sensor and the same problem... is there anybody success to get MTi Xsens Data????!!

Are you Ready for this? I HAVE AN ANSWER! It took me about 2 weeks of constant work to get this thing to talk to the arduino correctly. So I'm not professional programmer. I'm working mostly off of understandings I learned in VB 10 years ago in high school so there may be a more elegant way to structure this than what I've done. I'm more than open to ideas if someone finds an easier way but the internet is pretty dry on Xsense - Arduino communications. So I'll give you my hardware setup, post my code, and answer questions if need be.

FIRST: You have to match the levels. I used This { SparkFun RS232 Shifter - SMD - PRT-00449 - SparkFun Electronics }handy little level shifter from spark fun to make sure the RS232 speaking xsense could talk to my 5V TTL arduino. If you don't have a level shifter you're going to have a bad day because occasionally things like the preamble, BID, and MID will come through correctly then you get Gobbly Gook after that (Sorry about the technical terminology).

SECOND: When you read from the xsense you're reading a float (which is made of 4 bytes) one byte at a time. I couldn't figure out how to put these together so after some searching through C coding sites I found that you could define a variable as both a float AND an array of bytes[]. For example

union {
  byte asByte[4];
  float asFloat;
} xsensLatitude;

So you can read in one byte at a time xsensLatitude.asByte[3] = Serial.read(); then read out one float Serial.print(xsensLatitude.asFloat);

LAST: It is much easier to get the output mode you want using the MT Manager. The outputmode will stay on your xsens even after it's shut down so then all you have to do is read it with the arduino rather than configuring it with the arduino (which is possible if you want to go there.

EXAMPLE TIME: So I'm going to post the code that I used. Before running this on the arduino I used MT Manager to set up the Xsens so that it only put out position and and status (make sure to select none under angles if you're running this exactly). I used ArduinoMEGA2560 with the Xsens on Serial1 and the computer plugged in through USB.

EDIT I accidentally posted the wrong sketch the first time. This is what I meant to post EDIT

//0xFA preamble
byte gotoConfig[] = {0xFA,0xFF,0x30,0x00,0xD1};
byte gotoMeasurement[] = {0xFA,0xFF,0x10,0x00,0xF1};
byte setOutputSkipFactor[] = {0xFA,0x01,0xD4,0x02,0xFF,0xFF,0x2B};
byte reqData[] = {0xFA,0x01,0x34,0x00,0xCB};

byte inByte = 0x00;
byte xsensBID,xsensMID,xsensLEN,xsensCHECKSUM,xsensSTATUS;

union {
  byte asByte[4];
  float asFloat;
} xsensLatitude;
union {
  byte asByte[4];
  float asFloat;
} xsensLongitude;
union {
  byte asByte[4];
  float asFloat;
} xsensAltitude;

union {
  byte asByte[2];
  unsigned int asInt;
} xsensSample;


void setup(){
Serial.begin(115200);
Serial1.begin(115200);
xsensConfigure();
}

void loop() {
  /*poll xsens for a message*/
xsensGetData();
delay(1000);

}


/*********************************************************
This function is to take the MTData message from xsens and
place it into global variables which can be recorded by the 
microcontroller
**********************************************************/

void xsensGetData(){
byte xsensBuffer[100];
int i = 0;
Serial1.write(reqData,5);
//Serial.print("\nRequest Data");
while(Serial1.available() ==0){}
while(Serial1.available()){
  xsensBuffer[i] = Serial1.read();
  i++; 
}
xsensBID=xsensBuffer[1];
xsensMID=xsensBuffer[2];
xsensLEN=xsensBuffer[3];

if(xsensBID==0xFF && xsensMID == 0x32){
  //Latitude, longitude, altitude  
  xsensLatitude.asByte[3] = xsensBuffer[4];
  xsensLatitude.asByte[2] = xsensBuffer[5];
  xsensLatitude.asByte[1] = xsensBuffer[6];
  xsensLatitude.asByte[0] = xsensBuffer[7];
  
  xsensLongitude.asByte[3] = xsensBuffer[8];
  xsensLongitude.asByte[2] = xsensBuffer[9];
  xsensLongitude.asByte[1] = xsensBuffer[10];
  xsensLongitude.asByte[0] = xsensBuffer[11];
  
  xsensAltitude.asByte[3] = xsensBuffer[12];
  xsensAltitude.asByte[2] = xsensBuffer[13];
  xsensAltitude.asByte[1] = xsensBuffer[14];
  xsensAltitude.asByte[0] = xsensBuffer[15];
  
  xsensSTATUS = xsensBuffer[16];
  
  xsensSample.asByte[1] = xsensBuffer[17];
  xsensSample.asByte[0] = xsensBuffer[18];
  xsensCHECKSUM = xsensBuffer[19];
  byte byteSum = 0;
  for(i = 1;i<=19;i++){
    byteSum += xsensBuffer[i];
  }
  /*The following will print GPS Active - 1, Kalman filter active -1, latitude, longitude, altidude, sample number*/ 
  
  Serial.print("MSG-0:");Serial.print(byteSum,HEX);
  Serial.print("\tGPS:");Serial.print(bitRead(xsensSTATUS,2));
  Serial.print("\tKXF:");Serial.print(bitRead(xsensSTATUS,1));
  Serial.print("\t");Serial.print(xsensLatitude.asFloat,DEC);
  Serial.print("\t");Serial.print(xsensLongitude.asFloat,DEC);
  Serial.print("\t");Serial.print(xsensAltitude.asFloat,DEC);
  Serial.print("\t");Serial.print(xsensSample.asInt,DEC);
  
  
  xsensLatitude.asFloat = 0;
  xsensLongitude.asFloat = 0;
  xsensAltitude.asFloat = 0;
  xsensSample.asInt = 0;

}//end if

  Serial.print("\tBID:");
  Serial.print(xsensBID,HEX);
  Serial.print("\tMID:");
  Serial.print(xsensMID,HEX);
  Serial.print("\tLEN:");
  Serial.print(xsensLEN,DEC);
  Serial.print("\n");

}

/********************************************************
the purpose of this function is to configure the
xsens to only send data when it is polled
*********************************************************/
void xsensConfigure(){
byte xsensByte = 0x00;
int i = 0; //i is a generic counter
Serial.println("\nConfiguration Mode\n");
/*set xsens to configuration mode*/
Serial1.write(gotoConfig,5);
while(Serial1.available() ==0){}
while(Serial1.available()){
  xsensByte = Serial1.read();
  Serial.print(xsensByte);
  Serial.print("\t");
  delay(5);
}
/*Tells xsens to only send messagewhen polled*/
Serial.println("\nOnly send data when polled\n");
Serial1.write(setOutputSkipFactor,7);
while(Serial1.available() ==0){}
while(Serial1.available()){
  xsensByte = Serial1.read();
  Serial.print(xsensByte);
  Serial.print("\t");
  delay(5);
}
/*set xsens to configuration mode*/
Serial.println("\nMeasurement Mode\n");
Serial1.write(gotoMeasurement,5);

while(Serial1.available() ==0){}
while(Serial1.available()){
  xsensByte = Serial1.read();
  Serial.print(xsensByte);
  Serial.print("\t");
  delay(5);
}
Serial.print("\n");
}






/*Message Order
1. GPSPVT Data
2.Temp
3. Calibrated Data
4. Orientation Data
5. Auxiliary Data
6. Position (float - 4 BYTE)
7. Velocity (float - 4 BYTE)
8. Status
9. Sample (unsigned int 2 BYTES)
*/
union {
  byte asByte[4];
  float asFloat;
} xsensLatitude;
union {
  byte asByte[4];
  float asFloat;
} xsensLongitude;
union {
  byte asByte[4];
  float asFloat;
} xsensAltitude;

You only need one union. You can create as many instances of the union as you like.

byte xsensBuffer[100];
int i = 0;

What are these for? They are never used.

UH OH! I posted the wrong code! The code I posted was an adjustment I was using to test different settings. This is the code that actually prints out the data. I modified the code in my original post.

Thank you PaulS for the advice on the union. I definitely face palmed when I read it.

thegreatmongoose,

How did you connect the wire between MTi and Arduino?