Go Down

### Topic: Serial Communication (Read 8580 times)previous topic - next topic

#### BlackDragon

#15
##### Apr 30, 2009, 06:57 pm
The continuation mode was set to assure that reasonable data was being received. Thanks to your coding, all works well and numbers look reasonable. I just need to convert a ASCII DEC printout to normal decimals (1.00 g) for example. I was trying to find this last night. I will convert to polled mode--for this is what we will use for our application. For proper organization, dont i need a BYTE output other than a y with a double dot on top?

thanks

#### BlackDragon

#16
##### May 02, 2009, 07:24 pm
Well I have made huge progress and have 1 more step. I have all incoming bytes organized by the accelerations and angular rates. The final code I am using is the following. It simply organizes the incoming bytes to their corresponding desired outputs. I want to get the 4 inputs organized into 1. My understanding is the followin: You have 4 bytes (8 bit words) for Acceleration X for example, correct. If the first 2 bits are say (10000001) and (00011000), then add 8 zeros to the first word resulting in (1000000100000000) and add to second bit resulting in 1000000100011000 ( a 16 bit word). This value precedes the decimal point. You repeat the procedure for the last two bits resulting in a value after the decimal point. The combination of Result1.Result2  produces the final 32 bit word resulting in the single result of Acceleration X when read in DEC.

If my procedure is correct, can you use the shift command to tack on the following zeros?

float incomingByte = 0;     // for incoming serial data
int i;
char a;
char b;
char c;
char d;
char e;
void setup()
{
Serial.begin(115200);
//e = 250;               // DEC for 0xFA   stop continuous mode
//Serial.print(e);
//a = 196;               // DEC for 0xC4   start continuous
//Serial.print(a);
//b = 193;                // DEC for 0xC1           dont change
//Serial.print(b);
//c = 41;               // DEC for 0x29        dont change
Serial.print(c);
d = 194;               // DEC for 0xC4     change for desired output (see Microstrain Protocal)
Serial.print(d);
delay(1000);
if (Serial.available() >= 0) {
for (i=0; i<1; i++) {// read the incoming byte:
Serial.println(incomingByte*10);
}
Serial.print("\n");

for (i=1; i<5; i++) {
Serial.print("Acceleration in X: ");
Serial.println(incomingByte*10);
}
Serial.print("\n");

for (i=5; i<9; i++) {
Serial.print("Acceleration in Y: ");
Serial.println(incomingByte*10);
}
Serial.print("\n");

for (i=9; i<13; i++) {
Serial.print("Acceleration in Z: ");
Serial.println(incomingByte*10);
}
Serial.print("\n");

for (i=13; i<17; i++) {
Serial.print("Angular Rate in X: ");
Serial.println(incomingByte*10);
}
Serial.print("\n");

for (i=17; i<21; i++) {
Serial.print("Angular Rate in Y: ");
Serial.println(incomingByte*10);
}
Serial.print("\n");

for (i=21; i<25; i++) {
Serial.print("Angular Rate in Z: ");
Serial.println(incomingByte*10);
}
Serial.print("\n");

for (i=25; i<29; i++) {
Serial.print("Check Sum: ");
Serial.println(incomingByte*10);
}
Serial.print("\n");

for (i=29; i<31; i++) {
Serial.print("Timer: ");
Serial.println(incomingByte*10);
}
}
}
void loop() {}

Thank you. This is my last step

#### shelleycat

#17
##### May 02, 2009, 11:27 pmLast Edit: May 02, 2009, 11:58 pm by shelleycat Reason: 1
I have some suggestions, which I hope you will take in good part.

1  Your code as posted is all in setup, so it is going to run once and stop.  Is that what you wanted?  Perhaps it should be inside loop.

2 You are assuming that as soon as 1 serial byte is ready for reading (the available() call), all the rest will follow slightly slower than you can process them - that is dangerous.  Serial.read() will return -1 if no data is present.   You could start analysing as soon as
available() > 31, then you know you can safely read 32 bytes.

3  I would do the data manipulation like this, otherwise you will overflow the byte.  The spec for the output seems to be a 4 byte value, in a floating point format, and I think you are intending to use command 0xC1.  I haven't got one to try!  Also, print does not work on float.

long incomingvalue; //should by 4 bytes long
float z;
int int_part_of_float;
int frac_part_of_float;

// for each block of data, as you coded it
incomingvalue = 0;
for (i=9; i<13; i++) {
incomingvalue <<= 8; // shift contents 8 bits = multiply by 256
incomingvalue += Serial.read(); // add byte in
}
z = float(incomingvalue);
int_part_of_float = (int)z;
frac_part_of_float = 100 * (z - (float)int_part_of_float);

Serial.print("Acceleration in Z: ");
Serial.print(int_part_of_float);
Serial.print('.');
Serial.println(frac_part_of_float);
Serial.print("\n");

4  According to the device specs, you get an 8 byte response back when you set continuous mode.  At the moment your code is ignoring this, you need to eat those bytes in the setup part.  Perhaps

if (Serial.available() > 7)
{
for(i = 0; i < 8; i++)
{
}
}

Good luck.

#### BlackDragon

#18
##### May 03, 2009, 01:23 am
well thank you very much. I am trying to perform my work in polled mode--one time display of the values. When I ran your code, I receive a number of about 12,000.  One concern was whether your coding involved the 32 bit output of the Inertia Link in IEEE 754 format-sign (+/-) is first byte, exponent follows in the next byte, then rest are base value. This is how I understand it. I was concerned that the shifting is overriding this format?

#### shelleycat

#19
##### May 03, 2009, 07:05 pmLast Edit: May 04, 2009, 01:09 am by shelleycat Reason: 1
You are right, I'm looking for a way to convert the 4 bytes into a float format.  The cast (float)incomingvalue won't work.  Try the code following. I've tested putting some bytes into the array, and it gives the same value as a float.

Edit: I've looked at page http://babbage.cs.qc.edu/IEEE-754/32bit.html  I don't know which order your device will send the bytes, you may have to tinker, by reversing the order the 4 bytes are read into the array.   I've posted the sketch I was using to try and convert bytes to and from floats.  It gives the same value as the calculated values from the page, e.g. 100 is hex 42c80000, which is loaded as the values 0 0 200 66.

Code: [Select]
`union t_long_fl { // this assign the byte array on the same memory as the floatbyte b_arr[4];float f_var;} ;t_long_fl t1;int j; int int_part_of_float;int frac_part_of_float;// for each block of data, as you coded it  j=0;  for (i=9; i<13; i++)  {    t1.b_arr[j] = Serial.read(); // add byte into array    j += 1;  }  int_part_of_float = (int)t1.f_var;  frac_part_of_float = 100 * (t1.f_var - (float)int_part_of_float);  Serial.print("Acceleration in Z: ");  Serial.print(int_part_of_float);  Serial.print('.');  Serial.println(frac_part_of_float);  Serial.print("\n");`

Code: [Select]
`long readnumber(){  char c;  long res= 0;  int count = 0;  boolean pos = true;  while (true) {   if (Serial.available())   {     c = Serial.read();     count ++;     if (c >= '0' && c <= '9')     {       res *= 10;       res += (c - '0');     }     else if (c = '-' && count == 1)     {        pos = false;     }     else     else     {       break;     }   }  }  return pos ? res : -res;}union t_long_fl {byte  b_arr[4];float f_var;} t1 ;void print_ba(){  for (int i = 0; i < 4; i++)  {     Serial.print(" ");     Serial.print((int)t1.b_arr[i]);  }}void print_float(float f){  long i_i = (long)f;  int i_f = 100 * (f - (float)i_i);  Serial.print(i_i);  Serial.print('.');  Serial.print(i_f);}void setup(){  Serial.begin(9600);  Serial.print("enter [Fii ff ] or [Imm mm mm mm ] or [P]\nii ff integer,fractional part for float\nmm values for each byte (dec)\n");}void loop(){  long int_part;  int int_frac;  if (Serial.available())  {    char c = Serial.read();    Serial.print(c);    switch (c)    {      case 'I':        for (int i = 0; i < 4 ; i++)        {          t1.b_arr[i] = readnumber();        }        print_ba();        Serial.println();        break;      case 'F':        int_part = readnumber();        Serial.print("int part:");        Serial.print(int_part);        int_frac = readnumber();        Serial.print(" frac part:");        Serial.println(int_frac);        t1.f_var = int_part + float(int_frac)/100.0;        break;      case 'P':        print_float(t1.f_var);        print_ba();        Serial.println();        break;      default:        Serial.println(" bad char ");        break;    } // end switch  }} // end loop`

#### BlackDragon

#20
##### May 05, 2009, 12:57 am
Thank you very much. I reviewed the code and I am sorry to say that I do not understand it completely. I compiled it alone to help understand the process but am having difficulty when implementing into my code as to where to place it and the expected output. From the Microstrain (Inertia Link manufacturer), by the coding involved in the following link (http://www.microstrain.com/inertia-link.aspx) under documentation: 3DM-GX2® Orientation SDK: SDK master: Windows: Code: C: Library: Source: utils: there is a function called FloatFromBytes. This function does the IEEE754 conversion. To compare values, they recommended I use the website you gave with all 4 of the outputs given by my last posted code into a single line HEX input into the website. Can the Arduino Software handle such a function such as FloatFromBytes, or is it necessary to use the coding you produced? (I am sure this took much time to create--i was surprised but I need some comments or background to its process since this is a bit above my C knowledge.

Thank you

#### shelleycat

#21
##### May 05, 2009, 10:45 pmLast Edit: May 05, 2009, 11:00 pm by shelleycat Reason: 1
The inertial device sends out a number of bytes.  4 of the bytes (4*8=32bits) are a representation of the floating value, which is also 4*8=32bits.

The arduino library does include float functions, which are also stored as 32bits, which is 4 consecutive bytes in memory.  If you declare a float variable, the compiler reserves 4 bytes space in memory for it.  It does not have a floatfrombytes as such.

What the code does is overlay a 4 byte array, at the same point in memory, as a float value.  Both take the same amount of room.  This is the union{...}.  The union allows access to the same 4 bytes as either an array, or as a float.

So when the 4 bytes are received in, they are put in the correct position to be read as a float.

The rest of the code, is to print out the float.  First it converts the float to a long, a long can only store the integer part, so a float of say 100.25 would convert to an long of 100.  The second conversion is to get the fractional part, which is done by taking 100.25 - 100 = 0.25 (this is done as a float operation).  Then multiply 0.25 by 100 to get the value 25.

The idea is that you declare these once:
union t_long_fl { // this assign the byte array on the same memory as the float
byte b_arr[4];
float f_var;
} ;

t_long_fl t1;
int j;

int int_part_of_float;
int frac_part_of_float;

at the top of your interpretation block.
The rest of the first code posted was to replace the blocks where you were reading in the successive bytes.  Obviously, you only need those code blocks where you are expecting a float.

Why not try to start with printing out the hex bytes in lines of 4, as the data, then put those values into the conversion website?  At least you will see if you are getting the sequencing right.

(To clarify, the first code example is for you to try; the second sketch was to verify the conversion was working.  It takes 3 commands:
enter the 4 bytes,  enter the float,  print out the bytes and the float.)

#### shelleycat

#22
##### May 05, 2009, 10:55 pm
I've looked at floatfrombytes.

You can code that on an arduino.
It takes  a pointer to 4 bytes stored adjacently.  By pretending they are an array, it then moves each byte in turn onto the correct position on a float variable - much like my code did.  Then it returns the pattern of bits as a float.

Its doing the same as my code with the union.

The testbyteorder is to sort out which order the bytes are sent.  It must depend on the architecture of the processor; in the absence of that, just stuff the bytes in either 0-0 1-1 2-2 3-3 or 0-3 1-2 2-1 3-0

#### BlackDragon

#23
##### May 06, 2009, 08:32 pm
i cannot get the following to compile and am concerned as to the unrecognition of "const" since it is not in brown text. Is this the reason for error? or is there another reason.

#include "i3dmgx2_Errors.h"
short convert2short(unsigned char *);
unsigned short convert2ushort(unsigned char *);
unsigned long convert2ulong(unsigned char *);
long convert2long(unsigned char*);
int calcChecksum(unsigned char *, int);

float FloatFromBytes(const unsigned char*);
char * explainError(int);
int TestByteOrder();

byte IMUByte =0;
float f;
float incomingByte = 0;     // for incoming serial data
int i;

char a;
char b;
char c;
char d;
char e;

char serOutString[11];
// array to hold string of arduino read values of digital pin

//int val=0;  // temporary value for inputs & outputs
//int val4=0;  //temp
//char val2=2;
//char val3=0;
//char val1=0;
//int p=0; // temporary value for pin

//read a string from the serial and store it in an array
//you must supply the array variable
void IMU_String (char *strArray) {
int i = 0;
if(Serial.available()) {
//Serial.print("reading Serial String: ");  //optional: for confirmation
while (Serial.available()){
i++;
// Serial.print(strArray[(i-1)]);         //optional: for confirmation
}
// Serial.println();                          //optional: for confirmation
}
}

//Print the whole string at once - will be performed only if thers is data inside it
//you must supply the array variable
void printSerialString(char *strArray) {
int i=0;
if (strArray != 0) {
while(strArray != 0) {
//Serial.print( strArray );
strArray = 0;                  // optional: flush the content
i++;
}
}
}

void setup()
{
Serial.begin(115200);
//e = 250;               // DEC for 0xFA   stop continuous mode
//Serial.print(e);

//a = 196;               // DEC for 0xC4   start continuous
//Serial.print(a);
//b = 193;                // DEC for 0xC1           dont change
//Serial.print(b);
//c = 41;               // DEC for 0x29        dont change
//Serial.print(c);
d = 194;               // DEC for 0xC4     change for desired output see Microstrain Protocal
Serial.print(d);

delay(1000);

if (Serial.available() >= 0) {

float incomingBytes(const unsigned char* pBytes);
{
float f = 0;

((BYTE*)(&f))[0] = pBytes[3];
((BYTE*)(&f))[1] = pBytes[2];
((BYTE*)(&f))[2] = pBytes[1];
((BYTE*)(&f))[3] = pBytes[0];

}

/*----------------------------------------------------------------------
* convert2long
* Convert four adjacent bytes to a signed long.
*
* parameters:  buffer : pointer to a 4 byte buffer.
* returns:     the converted value as a signed long.
*--------------------------------------------------------------------*/
long convert2long(unsigned char* plbyte) {
long l = 0;
{
l = (plbyte[0] <<24) + (plbyte[1] <<16) + (plbyte[2] << + (plbyte[3] & 0xFF);
}

return l;
}

}
}
}

void loop() {}

Thanks. This is our last step thankfully. Then after this is complete, then we need to organize the data by a record so we have AccelX = the float value, same for Y,Z? Do you need a record like they do, or is there another way?

#### shelleycat

#24
##### May 07, 2009, 12:55 am
Blackdragon, there are quite a few things wrong.  Can you get a local coder to help you.  I'm still not sure what you are trying to do...

1.  Structure needs correcting.  You cannot nest a function inside another function, hence you must move convert2long and incomingBytes out of setup().

2.  Unless you have put the header file i3dmgx2_Errors.h into your arduino ide in the correct place, it will error and not compile.  Try taking it out and see what else errors.

3  All the "prototypes", which define functions, short covert2short(unsigned char *) are probably not needed (in this environement, the arduino ide tries to generate them for you).  Try removing them.

4  IMU_String should add a 0 to the end of the string to terminate it.  See how printSerialString is looking for a 0 to stop processing.

5  const is OK, its a valid keyword.  Its probably not the problem. Remove it if you want.

6  brackets at the end do not look balanced.

7  You don't need printSerialString,
you get the same result with Serial.print(strArray).  The String library will print a string or a (null terminated) array of characters.

8 If you define a function, nothing happens unless you later call it.  For example convert2long would be a function definition if it were outside setup.  To call it - to actually make the computer do that processing -  you need a statement like this:

l = convert2long( char_array);

8  Can you plan out the overall code and show that?  Then code from that skeleton.

9  Have a close look at some examples of sketches.

I will try to have a look at the coding tomorrow, but its bedtime now.

#### BlackDragon

#25
##### May 07, 2009, 10:10 pm
We tried to simplify down your coding into the following. We now get data at least so that provides confidence that the setup on this end is correct.

{
char c;
char d;
long res= 0;
int count = 0;
boolean pos = true;
while (true) {
if (Serial.available())
{
count ++;
if (c >= '0' && c <= '9')
{
res *= 10;
res += (c - '0');
}
else if (c = '-' && count == 1)
{
pos = false;
}
else

{
break;
}
}
}
return pos ? res : -res;
}

union t_long_fl {
byte  b_arr[4];
float f_var;
} t1 ;

void print_ba()
{
for (int i = 0; i < 4; i++)
{
Serial.print(" ");
Serial.print((int)t1.b_arr);
}
}

void print_float(float f)
{
long i_i = (long)f;
int i_f = 100 * (f - (float)i_i);
Serial.print(i_i);
Serial.print('.');
Serial.print(i_f);
}

char d;
void setup()
{
Serial.begin(115200);
d=194;
Serial.print(d);
Serial.print("enter [Fii ff ] or [Imm mm mm mm ] or [P]\nii ff integer,fractional part for float\nmm values for each byte (dec)\n");
}

void loop()
{
long int_part;
int int_frac;
if (Serial.available())
{
char c = Serial.read();
Serial.print(c);

for (int i = 0; i < 4 ; i++)
{
}
print_ba();
Serial.println();

Serial.print("int part:");
Serial.print(int_part);
Serial.print(" frac part:");
Serial.println(int_frac);
t1.f_var = float(int_frac);

t1.f_var = float(int_part);

print_ba();
Serial.println();

}
} // end loop

Providing output of:

Âenter [Fii ff ] or [Imm mm mm mm ] or [P]
ii ff integer,fractional part for float
mm values for each byte (dec)
Â 0 0 0 0

int part:0 frac part:0

0 0 0 0

º 0 0 0 0

int part:0 frac part:0

0 0 0 0

þ 0 0 0 0

int part:0 frac part:0

0 0 0 0

( 0 0 0 0

int part:0 frac part:0

0 0 0 0

; 0 0 0 0

#### shelleycat

#26
##### May 08, 2009, 01:07 am
Sadly, all your data seems to be zeros.

Try this, I've tried to put some comments in.

Can you say how the device is connected - as far as I can see you must have it connected to the serial output line, so everything that is
a Serial.print("some string") is being sent to your device as well as the host computer.

Code: [Select]
`// declare a union to convert bytes to a floatunion t_long_fl {  byte  b_arr[4];  float f_var;}  t1 ;// prints the 4 bytes of the union byte array, for debugging to see what is coming backvoid print_ba(){  for (int i = 0; i < 4; i++)  {    Serial.print(" ");    Serial.print((int)t1.b_arr[i]);  }  Serial.println();}// prints a float with 2 decimal placesvoid print_float(float f){  long i_i = (long)f;  int i_f = 100 * (f - (float)i_i);  Serial.print(i_i);  Serial.print('.');  Serial.print(i_f);}char d;void setup(){  Serial.begin(115200);  //d=194;  d = 0xC2;  // this is the command value for Acceleration & Angular Rate (0xC2)  Serial.print(d);}void loop(){  int i;  boolean frame_all_done = false;  if (Serial.available())  {    char c = Serial.read();    if (c == 0xC2) {      // have got the first character of the frame we expect, wait for rest of frame to come it      // if the frame character is not an 0xC2 will not process in this block      while (Serial.available() < 30) {          // loop waits until there are 30 bytes read from the interface            // overall response frame is 31 long, but we already got the 1st one      }      Serial.println(" Received all frame bytes");      //Bytes 2-5   AccelX      Serial.print("Accel X:");      for (i = 0; i < 4 ; i++)      {        t1.b_arr[i] = Serial.read();      }      print_float(t1.f_var);      print_ba();      //Bytes 6-9   AccelY      Serial.print("Accel Y:");      for (i = 0; i < 4 ; i++)      {        t1.b_arr[i] = Serial.read();      }      print_float(t1.f_var);      print_ba();      //Bytes 10-13 AccelZ      Serial.print("Accel Z:");      for (i = 0; i < 4 ; i++)      {        t1.b_arr[i] = Serial.read();      }      print_float(t1.f_var);      print_ba();      //Bytes 14-17 AngRateX      Serial.print("AR X:");      for (i = 0; i < 4 ; i++)      {        t1.b_arr[i] = Serial.read();      }      print_float(t1.f_var);      print_ba();      //Bytes 18-21 AngRateY      Serial.print("AR Y:");      for (i = 0; i < 4 ; i++)      {        t1.b_arr[i] = Serial.read();      }      print_float(t1.f_var);      print_ba();      //Bytes 22-25 AngRateZ      Serial.print("AR Z:");      for (i = 0; i < 4 ; i++)      {        t1.b_arr[i] = Serial.read();      }      print_float(t1.f_var);      print_ba();      //Bytes 26-29 Timer      for (i = 0; i < 4 ; i++)      {        Serial.read(); // just discarding      }      //Bytes 30-31 Checksum      for (i = 0; i < 2 ; i++)      {        Serial.read(); // just discarding      }            frame_all_done = true; // set up flag to ask for another frame    } // end if frame start detected    else    {          Serial.print("First byte rx is not expected frame start:");          Serial.print((byte)c,HEX);        }      }  // end if serial data available      // frame is now finished, the loop is ready for another frame, so try asking for another frame    if (frame_all_done == true)    {      char d;      d = 0xC2;      delay(2000); // wait two seconds      Serial.print(d);      frame_all_done = false;    }      } // end loop`

#### BlackDragon

#27
##### May 08, 2009, 07:39 am
I am getting a better feel for your coding but am confused as to the lack of the mathematics for the IEEE 754 format. The website: http://www.h-schmidt.net/FloatApplet/IEEE754.html    calculates the decimal equivalent from the 32 bit long binary input with the following formula:   sign * 2exponent * mantissa with the following explanation

The sign is stored in bit 32. The exponent can be computed from bits 24-31 by subtracting 127. The mantissa (also known as significand or fraction) is stored in bits 1-23. An invisible leading bit (i.e. it is not actually stored) with value 1.0 is placed in front, then bit 23 has a value of 1/2, bit 22 has value 1/4 etc. As a result, the mantissa has a value between 1.0 and 2. If the exponent reaches -127 (binary 00000000), the leading 1 is no longer used to enable gradual underflow.
Rounding errors: Not every decimal number can be expressed exactly as a floating point number. This can be seen when entering "0.1" and examining its binary representation which is either slightly smaller or larger, depending on the last bit.

By using the following code and taking the acceleration Z values (putting zeros infront numbers to make 8 bits due to screen's truncation) in order, I get acceleration in Z to be -1.02g, a reasonable value. I need assistance in making the 32 bit word from the binary output of the unit and read off the sign, exponent, and mantissa to do the formula to produce such values.

Thank you soo much

#### BlackDragon

#28
##### May 08, 2009, 07:40 am
The code is:

float incomingByte;     // for incoming serial data
int i;

char d;

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

d = 194;               // DEC for 0xC2     Command byte to initiate communication: change for desired output see Microstrain Protocal
Serial.print(d);

delay(1000);

if (Serial.available() >= 0) {
for (i=0; i<1; i++) {// read the incoming byte:

Serial.println(incomingByte);
}
Serial.print("\n");

for (i=1; i<5; i++) {

Serial.print("Acceleration in X: ");
Serial.println(incomingByte, BIN);
}
Serial.print("\n");

for (i=5; i<9; i++) {

Serial.print("Acceleration in Y: ");
Serial.println(incomingByte, BIN);
}
Serial.print("\n");

for (i=9; i<13; i++) {

Serial.print("Acceleration in Z: ");
Serial.println(incomingByte, BIN);
}
Serial.print("\n");

}
}

void loop() {}

#### Grumpy_Mike

#29
##### May 08, 2009, 10:02 am
Is there any reason you want to use the mathematics for the IEEE 754 format. It is not a normal way of representing numbers although it does have advantages.

Also.