Magnetometer problems

Hello All. I am a bit of a noob, but please bear with me.

I'm trying to use Honeywell's HMC5883L as a digital compass. I've compiled a piece of code from random sites on the internet to drive it, and output x, y and z values down the serial. That part works satisfactorily. However, I've been trying to make additions to that code that calculates a vector (only from the x and y at this point, although I'd like z too) as an angle and a magnitude.
It also should calculate which of eight outputs to use, to light up some LED's.

I'm having a problem with the numbers, though- for some inputs the output numbers get stuck on 0.00, or values like -0.79. If I change the number types up the top (I've been experimenting with having m, a and o as long or float numbers) it can get stuck on -2147483648.

Why is this? It's really annoying. Also, does anyone know how I could integrate the z axis, and how I could make the arduino output to the relevant LED?

Here is the code I am using. Only the comments that directly mention me are mine, I am aware some of them hide code, or are in stupid places. Also attached is a representation of my magnetometer and arduino setup. I am using a duemilanove with a 328, via Mac OSX 10.6.8.

// HMC5883L Arduino Sketch, compiled on 22/06/2012 by Marlo Webber from other people's code

#include <Wire.h>
#define address 0x1E //0011110b, I2C 7bit address of HMC5883

//defining numbers. I added m, a, and o here.
int cnt=0;
int x,y,z; 
long xx,yy,zz;
long m,a,o;

void setup(){
  //Initialize Serial and I2C communications
  Serial.begin(9600);
  Wire.begin();  
  //Put the HMC5883 IC into the correct operating mode
  Wire.beginTransmission(address); //open communication with HMC5883
  Wire.write(0x02); //select mode register
  Wire.write(0x00); //continuous measurement mode
  Wire.endTransmission();
}
void loop(){  
  //Tell the HMC5883 where to begin reading data
  Wire.beginTransmission(address);
  Wire.write(0x03); //select register 3, X MSB register
  Wire.endTransmission();
  //Read data from each axis, 2 registers per axis
  Wire.requestFrom(address, 6);
  while(Wire.available() < 6);
  x = Wire.read()<<8; //X msb
  x |= Wire.read(); //X lsb
  z = Wire.read()<<8; //Z msb
  z |= Wire.read(); //Z lsb
  y = Wire.read()<<8; //Y msb
  y |= Wire.read(); //Y lsb
  
  //if(zz>4800)
  
  //corrections?
  float heading=atan2(yy,zz);  //zz instead of xx
  
  //Print out values of each axis
  Serial.print("x: ");
  Serial.print(x);
  Serial.print("  y: ");
  Serial.print(y);
  Serial.print("  z: ");
  Serial.print(z);
//repeatable within 1 deg, but off by 90 deg?

//The code I've added
  m = sqrt((x*x) + (y*y));
  a = atan2(y,x);
  o = (a/(4/PI));

  Serial.print(" m: ");
  Serial.print(m);
  Serial.print(" a: ");
  Serial.print(a);
  Serial.print(" o: ");
  Serial.println(o);
  
if (zz > 4800)
{  //near level 5200 max down, in MA
  //Serial.print(xx);Serial.write(32);Serial.println(yy);
  float heading=atan2(yy,xx);
  heading*=(180/M_PI); 
  //declination here degrees
  if(heading<0)heading+=360;
  if(heading>360)heading-=360;  //for + declination only   
  //upside down on board so...
  heading=360-heading;
  Serial.println(heading);
  }
delay(50);  //50 or 150 more acc 3s
}
boolean avgxyz(){
const int N=20;  //matches delay 20*50=1s
boolean b;
cnt++;
if(cnt%N==1)xx=yy=zz=0;
xx+=x;
yy+=y;
zz+=z;
if(b=!(cnt%N)){
  xx=xx*10/N;yy=yy*10/N;zz=zz*10/N; }
return(b);
}

Screen shot 2012-06-22 at 10.37.28 AM.png

You should probably start with the basics. Are the raw X, Y and Z values you get from the magnetometer exactly what you expect?

int x,y,z; 
long xx,yy,zz;

This is not off to a good start.

  float heading=atan2(yy,zz);  //zz instead of xx

Where did yy and zz get values?

  a = atan2(y,x);

Are you sure that y and x are proper inputs to atan2, to get a valid output? Are y and x in degrees or radians?

if (zz > 4800)
{  //near level 5200 max down, in MA

Where did zz get a value?

if(b=!(cnt%N)){

This is an assignment (a strange one at that), not a conditional test. Is that what you wanted? I doubt it.

Put more clearly you must use float variables for non-integer values or it'll round down to an integer value. So the results of sqrt and atan2 should be in float variables.

The xyz values I get are pretty much what I expect.

@PaulS I know a lot of the code is mildly retarded. I didn't write the xx, yy, zz parts, nor the heading parts. I will try cutting the code down to it's basics and see if it helps. I believe y, x is the proper input to atan2; they define a point on a plane, and atan2 returns the angle from 0,0 to that point.

Thanks Markt. I'll try that and see if it helps.

http://www.nongnu.org/avr-libc/user-manual/group__avr__math.html#ga054230cd7e4c12958dbfac75ab6886e5

double atan2 ( double __y,
double __x
)

The atan2() function computes the principal value of the arc tangent of __y / __x, using the signs of both arguments to determine the quadrant of the return value. The returned value is in the range [-pi, +pi] radians.

I think the compiler will cast the ints x and y into doubles (which in Arduino are 32 bit floats) for atan2() but you should check.
And then comes the return which is a double between -3.14... to +3.14... which gets stuffed into a long integer. So it could be -3, -2, -1, 0, 1, 2 or 3.

You don't happen to see any problem there?

And then there's the rest of what Paul pointed out that blowing off does nothing to fix.