Thanks for your responses. My full code is included bellow. Pitch is defined as the rotation around the the y-axis from -90deg to +90deg, in my code I have kept it in radians because I return that value to be used in calculating my compensated magnetic field components. I convert to degrees just to print it.
I'm planning on implementing auto-calibration for the accelerometer eventually, but wanted to get the tilt working to some degree before I added it.
//////////LSM303C//////////////
/*START signal
next byte of data is the address of slave in first 7 bits
8th bit indicates if it is read or write
*/
#include<Wire.h>
#include<math.h>
//mag sensor
#define Madd 0x1E//address of slave magnetic sensor
#define msad_read 0x3D // SAD read condition
#define msad_write 0x3C //SAD write
//Accel sensor
#define Aadd 0x1D//address of slave accelorometer sensor
#define asad_read 0x3B
#define asad_write 0x3A
void setup() {
Wire.begin();
Serial.begin(9600);
Serial.println("LSM303C Digital Magnetometer");
///////SET ACCELEROMETER REGISTERS//////////////// make a loop for all this. form: slave, sub, message
set_regs(Aadd, 0x20, 0xEF);// 1110 1111, high resolution, 800Hz ODR, enable all axis, bdu on to prevent diferent reads from different samples*
set_regs(Aadd, 0x21, 0xC0);// 1100 0000 LPF At ODR(800) / 400 = 2Hz, most aggressive filer for now
set_regs(Aadd, 0x22, 0x00);// 0000 0000
set_regs(Aadd, 0x23, 0x04);// 0000 0100
set_regs(Aadd, 0x24, 0x00);// 0000 0000
set_regs(Aadd, 0x25, 0x00);// 0000 0000
///////SET MAGNETOMETER REGISTERS//////////////////
set_regs(Madd, 0x20, 0x5C);// 0101 1100, ulta-high performance mode, ODR=80Hz CTRL_REG1_M
set_regs(Madd, 0x21, 0x60);// 0110 0000 full scale +-16G, and reset before operation CTRL_REG2_M
set_regs(Madd, 0x22, 0x00);// 0000 0000 CTRL_REG3_M
set_regs(Madd, 0x23, 0x08);// 0000 1000 z-axis ultra-high performance, lsb at lower address CTRL_REG4_M
set_regs(Madd, 0x24, 0x00);// 0000 0000 continous update of magnetic data, 0100 0000 (0x40) will wait until data has been read before updating CTRL_REG5_M
}
//global variables
int M_xl=0, M_yl=0, M_zl=0, M_xh=0, M_yh=0, M_zh=0;
int Mx=0, My=0, Mz=0;
int A_xl=0, A_yl=0, A_zl=0, A_xh=0, A_yh=0, A_zh=0;
float Ax=0, Ay=0, Az=0;
float Ax_Raw =0, Ay_Raw=0, Az_Raw=0;
float A_cal_x=0, A_cal_y=0, A_cal_z=0;
float M_cal_x =0, M_cal_y=0, M_cal_z=0;
void loop() {
///Check Status
int Mstat = getdata(Madd, 0x27);
int Astat = getdata(Aadd, 0x27);
//GET ACCELEROMETER DATA
///getdata(master, slave)////////
if ((Astat & 0x08) == 0x08){
A_xl = getdata(Aadd, 0x28);
A_xh = getdata(Aadd, 0x29);
A_yl = getdata(Aadd, 0x2A);
A_yh = getdata(Aadd, 0x2B);
A_zl = getdata(Aadd, 0x2C);
A_zh = getdata(Aadd, 0x2D);
}
//GET MAGNETOMETER DATA
if ((Mstat & 0x08) == 0x08){
M_xl = getdata(Madd, 0x28);//
M_xh = getdata(Madd, 0x29);//
M_yl = getdata(Madd, 0x2A);//
M_yh = getdata(Madd, 0x2B);//
M_zl = getdata(Madd, 0x2C);//
M_zh = getdata(Madd, 0x2D);//
}
//WHO AM I
int WhoM = getdata(Madd, 0x0F);
int WhoA = getdata(Aadd, 0x0F);
//combine lsb and msb
Mx = ((M_xh<<8) | M_xl)*(0.0464);//[0.58mG/LSB], ODR=80Hz, converts output to gauss
My = ((M_yh<<8) | M_yl)*(0.0464);//0.00058*80=0.0464
Mz = ((M_zh<<8) | M_zl)*(0.0464);
Ax = ((int16_t)(A_xh<<8) | A_xl)*(0.000061);//0.000061 for 2g, 0.000122 for 4g, 0.000244 for 8g
Ay = ((int16_t)(A_yh<<8) | A_yl)*(0.000061);//
Az = ((int16_t)(A_zh<<8) | A_zl)*(0.000061);
//GET ACC OFFSET VALUES
// apply offset bias
/*offsets calcualted by Mageneto 1.2*/
Mx = Mx - 2878.527880;
My = My - 183.571936;
Mz = Mz - 1506.301507;
Ax = Ax - 0.040266;
Ay = Ay - 0.067500;
Az = Az - 0.004826;
//apply combined scaled factors
/* calculated by magneto 1.2*/
M_cal_x = Mx*(0.003499) + My*(0.000001) + Mz*(0.000001);
M_cal_y = Mx*(0.000001) + My*(0.003501) - Mz*(0.000001);
M_cal_z = Mx*(0.000001) - My*(0.000001) + Mz*(0.000339);
A_cal_x = Ax*(1.108194) - Ay*(0.010513) - Az*(0.006387);
A_cal_y = -Ax*(0.010513) + Ay*(1.163415) + Az*(0.011668);
A_cal_z = -Ax*(0.006387) + Ay*(0.011558) + Az*(0.919439);
//normalize results
float A_norm = sqrt(pow(A_cal_x, 2.0) + pow(A_cal_y, 2.0) + pow(A_cal_z, 2.0));
float M_norm = sqrt(pow(A_cal_x, 2.0) + pow(A_cal_y, 2.0) + pow(A_cal_z, 2.0));
A_cal_x = A_cal_x/A_norm;
A_cal_y = A_cal_y/A_norm;
A_cal_z = A_cal_z/A_norm;
M_cal_x = M_cal_x/M_norm;
M_cal_y = M_cal_y/M_norm;
M_cal_z = M_cal_z/M_norm;
A_cal_y = -1.0*A_cal_y;//because chip defines +Z as up
A_cal_z = -1.0*A_cal_z;//to keep the correct right-hand coordinate system
//digital low-pass filter
/* set in the control register for ACC slave */
float alpha = 0.15; //smoothing factor do not set too low or it will take too long to stabilize and real-time results will be lost, 0<alpha<1
A_cal_x = (alpha*A_cal_x) + (1-alpha)*(A_cal_x);
A_cal_y = (alpha*A_cal_y) + (1-alpha)*(A_cal_y);
A_cal_z = (alpha*A_cal_z) + (1-alpha)*(A_cal_z);
//CALCULATE RAW ACCELERATION MAGNITUDE
float Gx = getAcc(A_cal_x);
float Gy = getAcc(A_cal_y);
float Gz = getAcc(A_cal_z);
//CALCULATE PITCH AND ROLL
//float pitch = pitchCalc(A_cal_x, A_cal_y, A_cal_z);
float pitch = pitchCalc(Gx, Gy, Gz);
//float roll = rollCalc(A_cal_x, A_cal_y, A_cal_z, pitch);
float roll = rollCalc(Gx, Gz, pitch);
//CALCULATE TILT ADJUSTED MAGNETIC FIELDS
//only need x and y for heading calc
float Mx_comp = M_cal_x*cos(pitch) + M_cal_z*sin(pitch);
float My_comp = M_cal_x*sin(pitch)*sin(roll) + M_cal_y*cos(roll) - M_cal_z*sin(roll)*cos(pitch);
float Mz_comp = -M_cal_x*cos(roll)*sin(pitch) + M_cal_y*sin(roll) + M_cal_z*cos(roll)*cos(pitch);
//float Mx_comp = M_cal_x*cos(pitch) + M_cal_y*sin(roll)*sin(pitch) + M_cal_z*sin(pitch)*cos(roll);
//float My_comp = M_cal_y*cos(roll) - M_cal_z*sin(roll);
//float Mx_comp = M_cal_x*cos(pitch) + M_cal_z*sin(pitch);
//float My_comp = M_cal_y*cos(roll) - M_cal_z*sin(roll);
//CALCULATE HEADING W/ TILT CORRECTION
float Heading = headingCalc(Mx_comp, My_comp);
//CALCULATE FIELD STRENGTH
float Fstrength = sqrt(pow(M_cal_x, 2.0) + pow(M_cal_y, 2.0) + pow(M_cal_z, 2.0));
printOut(Heading,Fstrength, M_cal_x, M_cal_y, M_cal_z, A_cal_x, A_cal_y, A_cal_z, WhoM, WhoA);
//TEST PRINTS
Serial.print("Pitch:");Serial.println(pitch*(180/PI));
Serial.print("Roll:");Serial.println(roll*(180/PI));
delay(1000);
}
//SET REGISTERS//
////this function will be used to set all the control registers in setup
void set_regs(int slave,int sub, int message){
Wire.beginTransmission(slave);
Wire.write(sub);
Wire.write(message);
Wire.endTransmission();
}
//GET DATA//
int getdata(int slave, int sub){
Wire.beginTransmission(slave);
Wire.write(sub);
Wire.endTransmission();
Wire.requestFrom(slave,1);
int data = Wire.read();
return (data);
}
//HEADING CALCULATIONS
float headingCalc(float M_cal_x, float M_cal_y){
float head;
if ((M_cal_x == 0) && (M_cal_y < 0)){
head = 90;
} else if ((M_cal_x ==0) && (M_cal_y>0)){
head = 0;
}
head = ((atan2(M_cal_y, M_cal_x)) * (180/PI))-17.14; //magnetic declination is -17.14, for true north delete
if (head < 0){
head = head + 360;
} else if (head > 360){
head = head - 360;
}
return head;
}
//ACCELERATION
float getAcc(float raw_acc){
float Gacc = (raw_acc)*(0.0001220703125);//(raw)*(Range/(2^(resolution-1)) 2^15 = 32,768
return Gacc;
}
//PITCH CALCULATIONS
float pitchCalc(float AGx, float AGy, float AGz){
// pitchCalc(float Ax_cal, float Ay_cal, float Az_cal){
//float p = atan2(Ax_cal, sqrt(sq(Ay_cal) + sq(Az_cal)));
float p = atan2(AGy, sqrt(sq(AGx) + sq(AGz)));
//float p = asin(Ax_cal);
return p;
}
float rollCalc(float GxA, float GzA, float Pitch){
//float rollCalc (float Ax_cal, float Ay_cal, float Az_cal,float Pitch){
//float R = atan2(Ay_cal, sqrt(sq(Ax_cal) + sq(Az_cal)));
float R = atan2(GxA, -GzA);
//float R = -asin(Ay_cal/cos(Pitch));
if ((Pitch == 90) || (Pitch ==-90)){
R = 0;
}
return R;
}
//PRINT OUTS
void printOut(float Heading, float Fstrength, float M_cal_x, float M_cal_y, float M_cal_z, float A_cal_x, float A_cal_y, float A_cal_z, int WhoM, int WhoA){
// Serial.println("LSM303 Outputs");
// Serial.println(" ");
Serial.print("Heading: "); Serial.println(Heading);
// Serial.print("Field Strength: "); Serial.print(Fstrength);Serial.println("[G]");
}
Note that for the pitch and roll functions I have multiple versions I have been trying that are commented out. It looks a bit confusing.