Pages: [1]   Go Down
Author Topic: HMC5883L 3 axis compass, Processing, and sending signed integer data  (Read 1685 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 36
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have a Processing sketch that plots the x/y/z/heading and Bt.  I'm using scaled values to determin my heading and getting pretty good results.

My problem is that I'm using Serial.write() to send the signed integers to Processing.  Since each axis has two registers, and sends 2 bytes, I'm getting mixed values on the Processing end. 
How do I go about sending each axis MSB and LSB,  and then join the two once they are read in Processing?  I have searched forums and reference pages for days, and have not made
any progress.  I am storing the incoming bytes into an array in processing.  Any help would be appreciated.

Also, the good results I am getting are via the HMC5883L library instead of calling addresses on the I2C directly.  I'm allowing the library to do the heavy lifting.  Thanks in advance for
any assistance
Logged

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 287
Posts: 25681
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Seeing your code would help people to help you.
Logged

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

UK
Offline Offline
Shannon Member
****
Karma: 222
Posts: 12524
-
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Using an ascii message format isn't especially efficient, but makes it very easy to encode and decade messages across platforms. I suggest you change to a text-based messaging format such as CSV. This solves all your problems of message alignment, field alignment and so on.
Logged

I only provide help via the forum - please do not contact me for private consultancy.

Offline Offline
Newbie
*
Karma: 0
Posts: 36
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

OK, here's the huge problem I'm having.  The magnetometer's library makes it simple to read the x/y/z values into a variable (either a float or int). For example, to grab the Bx value from the magnetometer I simply write this ..

int xscaled = (int)scaled.XAxis;

The same goes for the y and z axis.  I am processing them as signed integers and the values can range from -4096 to 4096.  I'm also calculating the heading in degrees and sending it out as an integer (0 - 360).  To send them via serial from the arduino, I have tried two methods with no success receiving the same values sent in Processing.

Serial.write(xscaled);  or  Serial.print(xscaled);

The values come out of the magnetometer's registers as two bytes (in 2's complement form) for each axis register.  Now, when using the serial monitor in the Arduino environment, the numbers print exactly as they should.  The library takes care of the multiple bytes, signed integers, and 2's complement notation.  Receiving them in Processing is a nightmare.  I setup an array to store the incoming magnetometer data for each cycle and read it like this...

int[] serialInArray = new int[4];  (before void Setup)    //  using Serial.write on the arduino to send

int inByte = myPort.read();

Bx = serialInArray[0];
By = serialInArray[1];
Bz = serialInArray[2];
heading = serialInArray[3];

where Bx, By, Bz, and heading are all signed integers.  I've tried sending and accepting in other formats including chars, strings, bytes with no success.  I know I need to expand my serialInArray to 8 since each serial.write will be values greater than 255 and sometimes negative numbers.

What would be the simplest way to send these signed integers with 16 bit lengths to processing, and then reconstruct them back into the same original integers?  At this point, and after days of researching and experimenting, my mind is now toast.  Any guidance would be GREATLY appreciated!
Logged

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 287
Posts: 25681
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
inline size_t write(unsigned long n) { return write((uint8_t)n); }
    inline size_t write(long n) { return write((uint8_t)n); }
    inline size_t write(unsigned int n) { return write((uint8_t)n); }
    inline size_t write(int n) { return write((uint8_t)n); }
Can you see why "write" is not a good choice for what you're trying to do?
Logged

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Offline Offline
Newbie
*
Karma: 0
Posts: 36
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks, and yes i see what a hassle write would be.  This may sound like a stupid question, (or maybe my mind is fried from working on this for days) but what is the correct method for receiving strings in Processing if I use Serial.print to send the signed integers as ASCII ?  I tried some experimental sketches and when I read the port it only sends one ASCII encoded part of the entire number at a time...

i.e.  Serial.write(123); on the arduino

       int x = myPort.read();  in Processing

returns 49, 50, & 51  -  the ASCII values of each digit originally sent.  I guess I'm not understanding how to properly receive these signed integers on the Processing end.  Very frustrating !!
      
And to make matters even more complicated, I must send positive and negative numbers as large as 4096 !!!
« Last Edit: February 02, 2013, 04:36:35 pm by relic1974 » Logged

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 287
Posts: 25681
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Serial.write(123); on the arduino

       int x = myPort.read();  in Processing

returns 49, 50, & 51 
Now I'm surprised.
Which version of the IDE are you using?
Logged

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Offline Offline
Newbie
*
Karma: 0
Posts: 36
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm using Arduino IDE v 1.0.3 and Processing 2.0b7. 

Yes, it's very unusual and not what I expected at all from the serial port.  As stated, the ints show fine in the arduino serial monitor, or in Tera Term.  But trying to read the port in Processing is proving to be impossible.  And only to send some simple signed integers!!!
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 36
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

PROBLEM SOVLED !!  smiley-cool

With determination and a LOT of experiments and researching I have found a cure.  Posting my bit of code for anyone else who might have a similar problem.  It would seem that when sending strings over serial using serial.println on the Arduino, several things must be considered to receive the data as a signed integer in Processing...

1. a myPort.read() in processing proves to only grab one character/byte at a time and in ASCII format.  
2. using serial.println, the whitespace characters are also sent as part of the string (i.e \n or other formatters)
3. when you do receive all of the data and assemble the full string, a simple (int) conversion from a (str) does not work.
4. I avoided using void SerialEvent() because of timing issues and the way draw() and SerialEvent() continue to loop simultaneously

So on the Arduino side here's what I did...first of all a handshaking method like so:

void establishContact()                  // Serial handshaking method
{
  while (Serial.available() <= 0)        // If nothing has been heard from the PC
  {
    Serial.println("A");   // send a capital A
    delay(20);
  }
}

When processing wants new magnetometer data, it simply sends a myPort.write('A'); and the Arduino checks this with this...

if (inChar == 'A')                   // our RTS signal received
    {
      firstContact = true;
      digitalWrite(statusLED, HIGH);     // Handshaking Success Status LED
    }
    else
    {
      firstContact = false;              // demands another handshake from processing
      digitalWrite(statusLED, LOW);
    }

So while firstContact is true, the data is sent using headers to identify WHAT magnetometer value is being sent.  I had to do this else the data ended up being stored in random variables in processing.  Here's the arduino code I'm using to send the magnetometer data and data headers:

if (firstContact == true)
  {
     Serial.flush();
     Serial.print("x");
     Serial.println(xscaled);            // send x data over serial
     Serial.print("y");
     Serial.println(yscaled);
     Serial.print("z");
     Serial.println(zscaled);
     Serial.print("d");
     Serial.println(degree);                        
  }

Now to Processing to receive and digest the data.  I used a similar handshaking method as on the arduino.  Again, I avoided using the SerialEvent() function and wrote this:


void getData()
{
  while(myPort.available() > 0)         // process serial data until buffer is empty.  each axis is tagged with
  {                                               // a header character so it ends up in the right variable
    int Header = myPort.read();        // this also makes sure the buffer gets totally cleared   
    switch(Header)
    {
      case 'x':
       bxString = myPort.readStringUntil(lf);
       break;
      
      case 'y':
       byString = myPort.readStringUntil(lf);
       break;
      
      case 'z':
       bzString = myPort.readStringUntil(lf);
       break;
      
      case 'd':
       degreeString = myPort.readStringUntil(lf);
       break;
    }
  }
}

With the addition of the header tags, the data now always ends up where it should.  The string is compiled until it reaches the \n character, or ASCII 10 (linefeed).
Now all that is needed is to use trim() to remove the whitespace characters from the strings and convert them to integers.  Code example below:

if(bxString != null)
    {
      bxString = trim(bxString);                       // trim off the newline char
      Bx = Integer.valueOf(bxString).intValue(); // convert the string to an integer
    }
    if(byString != null)
    {
      byString = trim(byString);
      By = Integer.valueOf(byString).intValue();
    }

This is working great now for signed and unsigned integers.  I'm sure there is an easier way, but I'm going to stick with what (finally) works!  I hope someone can find this useful.
« Last Edit: February 04, 2013, 06:35:43 am by relic1974 » Logged

Pages: [1]   Go Up
Jump to: