Try the sparki.compass(); function.
e.g.
int direction = sparki.compass();
Try the sparki.compass(); function.
e.g.
int direction = sparki.compass();
The compass function is not supported yet. We will release it in the documentation when it is supported.
I think the Polar Vortex Has twisted Sparki’s flux capacitor!!! I looked over the web and found many ways to determine a heading based on magnetometer x,y values. I have attached a sketch which prints values to the LCD. I also tried the unsupported sparki.compass(). I have not seen any headings that are correct, not even close. I believe that my magnetometer may be a little wacky. It is very unstable. I get readings all over the chart, mostly on the y values. Would others please try this sketch and see if your values jump all over the place even when sparki is setting in one location for some time. Thanks, please post your findings here. Anyone with ideas on how to slow down the magnetometer or stabilize the readouts that would help also. One last thing can anyone tell me the orientation of the magnetometer? This may not matter, but on a digital compass it does.
Sketch Name: compass_heading
By: Ralph Ward
Date: 2/21/2014
Process: an attempt to find and print the heading of a Sparki Robot
Uses: Magnetometer, LCD,
#include <Sparki.h> // include the sparki library
//CALCULATES THE HEADING OF SPARKI
void setup(){
sparki.servo(SERVO_CENTER); // center the servo
}
void loop() {
sparki.clearLCD(); // wipe the screen
float x = sparki.magX(); // measure the Magnetometer x-axis
float y = sparki.magY(); // measure the Magnetometer y-axis
float z = sparki.magZ(); // measure the Magnetometer z-axis
float Pi = 3.14159;
// Calculate the angle of the vector y,x
float heading = 180 - (atan(x/y))*180/Pi;
// Normalize to 0-359
if (heading < 0)
{
heading = 359 + heading;
}
// write the measurements to the screen
sparki.print("Calulation y,x: ");
sparki.println(180-(atan2(y,x)* 180)/Pi);
sparki.print("Mag X: ");
sparki.println(x);
sparki.print("Mag Y: ");
sparki.println(y);
sparki.print("Heading: ");
sparki.println(heading);
sparki.updateLCD(); // display all of the information written to the screen
delay(1000); // wait
}
What I saw when I played with the reading yesterday was that the x never went from positive to negative when rotating all the way around. Seems like you need to calibrate it some way and not just use the raw readings.
Also, in your code, I noticed that you need to do something about the quadrants. If x and y are both positive, you’ll get the same results from atan as if they are both negative. (seems like I’ve seen an atan function that does that for you, but probably wasn’t arduino).
I tested your code as-is and I get these numbers:
approx
north: 255deg
east: 142
south: 185
west: 180
When I get a chance, I’m going to play with this a bit more. I’ll post here if I make any headway.
I modified the code a bit to keep the max and min values and normalize the outputs. Also put in something to ignore the ‘bad’ values it seems to spit out every so often.
once you start it, you need to rotate the sparki a couple of times before the numbers work (i.e. calibrate)
[code]#include <Sparki.h> // include the sparki library
//CALCULATES THE HEADING OF SPARKI
float maxx = -9999.9;
float maxy = -9999.9;
float minx = 9999.9;
float miny = 9999.9;
float badval = 1000.0; //anything over this we ignore…
void setup(){
sparki.servo(SERVO_CENTER); // center the servo
}
void loop() {
sparki.clearLCD(); // wipe the screen
float x = sparki.magX(); // measure the Magnetometer x-axis
float y = sparki.magY(); // measure the Magnetometer y-axis
float z = sparki.magZ(); // measure the Magnetometer z-axis
float Pi = 3.14159;
boolean gotbadval = false;
//find max and min of sensors, keep for normalization code below…
if(abs(x)<badval)
{
if(x>maxx) maxx=x;
if(x<minx) minx=x;
}
else gotbadval = true;
if(abs(y)<badval)
{
if(y>maxy) maxy=y;
if(y<miny) miny=y;
}
else gotbadval = true;
if(!gotbadval) //only update display if reading are reasonable
{
//normalize to -1.0 to +1.0…
float nx = -1.0 + 2.0*(x-minx)/(maxx-minx);
float ny = -1.0 + 2.0*(y-miny)/(maxy-miny);
// Calculate the angle of the vector y,x
float heading = (atan2(-nx, ny))*180.0/Pi; // negative x so that it comes out ‘clockwise’ from north (east = +90)
while(heading<0) heading += 360; //instead of +/-180…
// write the measurements to the screen
sparki.println(“Minimums / Maximums:”);
sparki.print(minx); sparki.print(" “); sparki.println(maxx);
sparki.print(miny); sparki.print(” "); sparki.println(maxy);
sparki.print("Mag NX: ");
sparki.println(nx);
sparki.print("Mag NY: ");
sparki.println(ny);
sparki.print("Heading: ");
sparki.println(heading);
sparki.updateLCD(); // display all of the information written to the screen
//point head towards north… (this doesn’t work, seems to like east instead)
//sparki.servo(90 - (int)(heading) % 180);
}
delay(250); // wait
}[/code]
There is a bug in the Magnometer code which sometimes gives erronerous readings, see
It’s easy to fix the code manually, just move one line of code outside of the function. Additionaly here is a function I wrote to calculate an average of readings over a sample period (using the sparki.compass() as input).
/*
* Filter min/max Magnometer samples and calculate average heading over sample period
*/
int curr_heading_index = 0;
float heading[] = {0,0,0,0,0};
int heading_len = (sizeof(heading)/sizeof(float));
float get_heading() {
float avg_heading = 0, min_heading = 360, max_heading = 0;
// Get heading and sample it
heading[curr_heading_index] = sparki.compass();
// Increment index
if(++curr_heading_index > heading_len)
curr_heading_index = 0;
// Accumulate all sample values
for(int i = 0; i < heading_len; i++) {
avg_heading += heading[i];
if(heading[i] < min_heading)
min_heading = heading[i];
if(heading[i] > max_heading)
max_heading = heading[i];
// Serial.print(heading[i]); Serial.print(" ");
}
// Serial.print("min = "); Serial.print(min_heading); Serial.print(", max = "); Serial.print(max_heading);
// Deduct max and min values
avg_heading -= min_heading;
avg_heading -= max_heading;
// Calculate average
avg_heading = avg_heading / (heading_len - 2);
return avg_heading;
}
Thanks for the help. I just looked at “sparki.ccp” I think it has just now been revised by ArcBotics. It look like line 723 has been removed. There is a revision note about the magnetometer buffer array. What does this do roboalchemist? Should I reload the software?
I uploaded Ingemar’s sketch. It seemed to have some bugs that I’m not wise enough to debug. I did rotate the sparki 3 complete circles and that is giving me some better reads from the magnetometer then I got before. I think the calculations are garbage, so I will have to find one that will give me the correct deg. and I need some way to sample and average like Ingemar’s sketch is doing. The compass function is spitting out nothing useful it appears. thanks everyone for your help, any additional comments or sketches or calculations would still be very helpful.
Yup, as people pointed out, it looks like we made a booboo in passing a locally-declared array. It works most of the time, which is why we didn’t catch it earlier. We’ve patched it in the github, and will be rolling it out in the next update later this week. You can also go ahead and download a new Sparki.cpp and copy it over right now here if you’re so able:
github.com/ArcBotics/Sparki/blo … Sparki.cpp
There is a very strong magnet underneath Sparki in the form of the motor, that makes it tricky to use it as a compass without first figuring out how strong said magnet is. I think a neat calibration would be to let Sparki do a 360 turn, measuring the mins and maxes, the write those values to the eeprom, doing a little beep-beep head-jiggle at the end. That’s what we’re planning for the next update.
Cutest. Calibration. Ever.
Rwardjr5: It’s not a complete sketch just a function that can be used to calculate an average of samples and it’s working for me doing just that, however the input to is faulty (sparki.compass()). The sparki.compass() function and magnometer driver is not fully working yet so we have to wait a little bit so Joe can sort that out.
Another problem with Magnometer driver is that whenever you call the magX(), magY() or magZ() you are actually making a new readout from the Magnometer so the X,Y and Z values does not correlate with each other. Another way is to call the sparki.readMag() once and read out the global variables like this:
sparki.readMag();
float x = sparki.xAxisMag; // measure the Magnetometer x-axis
float y = sparki.yAxisMag; // measure the Magnetometer y-axis
float z = sparki.zAxisMag; // measure the Magnetometer z-axis
Edit: Additionally you can configure the Magnometer to by itself do an average sample calculation (on 8 values). This will give you much more stable readings. I have made that change as well to jle’s modificed version of your sketch (see setup):
#include <Sparki.h> // include the sparki library
//CALCULATES THE HEADING OF SPARKI
float maxx = -9999.9;
float maxy = -9999.9;
float minx = 9999.9;
float miny = 9999.9;
float badval = 1000.0; //anything over this we ignore...
void setup(){
sparki.servo(SERVO_CENTER); // center the servo
// Set magnometer to calculate average of 8 reads
sparki.WireWrite(0, 0x70);
}
void loop() {
sparki.clearLCD(); // wipe the screen
sparki.readMag();
float x = sparki.xAxisMag; // measure the Magnetometer x-axis
float y = sparki.yAxisMag; // measure the Magnetometer y-axis
float z = sparki.zAxisMag; // measure the Magnetometer z-axis
float Pi = 3.14159;
boolean gotbadval = false;
//find max and min of sensors, keep for normalization code below..
if(abs(x)<badval)
{
if(x>maxx) maxx=x;
if(x<minx) minx=x;
}
else gotbadval = true;
if(abs(y)<badval)
{
if(y>maxy) maxy=y;
if(y<miny) miny=y;
}
else gotbadval = true;
if(!gotbadval) //only update display if reading are reasonable
{
//normalize to -1.0 to +1.0...
float nx = -1.0 + 2.0*(x-minx)/(maxx-minx);
float ny = -1.0 + 2.0*(y-miny)/(maxy-miny);
// Calculate the angle of the vector y,x
float heading = (atan2(-nx, ny))*180.0/Pi; // negative x so that it comes out 'clockwise' from north (east = +90)
while(heading<0) heading += 360; //instead of +/-180...
// write the measurements to the screen
sparki.println("Minimums / Maximums:");
sparki.print(minx); sparki.print(" "); sparki.println(maxx);
sparki.print(miny); sparki.print(" "); sparki.println(maxy);
sparki.print("Mag NX: ");
sparki.println(nx);
sparki.print("Mag NY: ");
sparki.println(ny);
sparki.print("Heading: ");
sparki.println(heading);
sparki.updateLCD(); // display all of the information written to the screen
//point head towards north... (this doesn't work, seems to like east instead)
//sparki.servo(90 - (int)(heading) % 180);
}
delay(250); // wait
}
I think i understand. Thank you, again.
This is technically true, but the readings happen so fast it wouldn’t matter in a realistic sense. It samples at 120 times second.
[quote=“roboalchemist”]
This is technically true, but the readings happen so fast it wouldn’t matter in a realistic sense. It samples at 120 times second.[/quote]
I agree in most cases it should not matter, but if the reading is bad and should be filtered out you actually discard 3 readings where 2 could be good. Configuration register A is never set in the driver which should leave the output data rate in the HMC5883L to its default of 15hz (15 times a second) so in fact the output will propably not have changed between the readings because of this. 
Okay. This is now officially wrecking my head. I’ve scripted to read the min and max x, y & z axis & the total magnetic field. i.e. -> Sqrt(sq(x)+sq(y)+sq(z)).
Through more than 360º, the min & max total magnetic field values are 466 & 757 milligauss.
I’ve also tried calculating the heading using the arctan method. I’m not really getting anything tangible. I’ve tried including my magnetic declination & magnetic field strength for Ireland.
Not sure where to go from here. I’ve got my iPhone & Sparki on a sheet of paper & I’m rotating them through 360º. I’m in danger of needing a white jacket 
Joseph, you suggested writing the min & max to the EEPROM. How can we utilise this & do you have any concerns about the finite number of times you can write & erase from there?
Is it a case that that the magnets in wheels are just too much of a problem? You mentioned you were working on the calibration sketch…are you any further along with that?
Thanks,
Trevor.
Did you use the min for each axis as the offset for that axis before using the arctan method?
EEPROM is something like 10,000+ writes, I don’t really worry about it.
Still working on that along with a lot of other things. Should be in the update late this week.
Nope…will do now though
Thanks.
Nope. I’m officially lost. Anyone able to get a compass heading?
I only have one thing to throw at this that I believe is meaningful. That is that there are 360 deg we are dealing with, we are starting with 0 and so there are only 359 more degrees. Deg = 0 to 359. This is not going to change the fact that we are working with whacked out x,y readings. I have seen close to the correct headings flash on the LCD for all 4 of the main directions. But this could be a fluke. I’ve looked at the LCD so long that I may be seeing what I want to see and not the true headings I am getting. That said I have gotten those when;
1. I had the Sparki spin 3 full circles to calabrate the magnetometer.
2. Sparki was sitting still ( not moving ) although I do not know if that has anything to do with why the headings appear to be stable albeit wrong.
I am not a good enough programmer to get this figured out but has anyone collected say ten samples of both the x and y readings and the thrown out the lowest three of each and highest three of each and then averaged the remaing 4. Taking these averages and using the averages to do your calculation might give you something useful.
This looks like a lost cause to me. I ran more experiments tonight.
Please take everything I observed with a pinch of salt & please correct me if I’m wrong. I’m definitely no expert!
Here’s my observations:
-> With Sparki turned on, but not moving, results remain fairly constant for any given heading, although incorrect.
-> Comparing heading angle on Sparki against another compass, I observed that the angle is always off by (roughly) 45º, but in different planes as you rotate through 360º. I’m wondering if this has anything to do with the arctan, as I believe it calculates the angle of the x y tangent at an angle of 45º to x (Is this correct???)
-> Still with Sparki turned on, I rotated (by hand) each of his wheels slowly & independently of each other. The heading varied throughout the full 360º as each wheel was rotated. Is this as a result of the strong magnetic field from the stepper motors?
I’m tempted to go to the outdoor shop, buy a compass & superglue it to his ass! 
Trevor: If the heading remains constant in a given direction I would say you come a long way, so don’t give up! For that it sounds more like you have an offset that you need to take in to the calculation (for some reason) as you point out.
On my Sparki I get the same readings wherever I turn my Sparki and the magnometer on my phone shows a pretty decent earth magnetic force of 40-50 mikro Tesla. However from the Sparki I only get a maximum of -15 to +10 milli Gauss on any given axis. Thats with the wheels not turning, when I turn the wheels however the readings goes all over the place and far more than a couple of milli gauss, so the only conclusion I have drawn from my testing is that the motors should not turn when you do the readings from the magnometer since they will be faulty. Apart from that my readings are so small I can only assume my magnometer is not working properly, but I am hoping Joe can weave some magic with the driver code!