We've had support for the hmc5883 magnetometer for some time. This measures the magnetic field strength on 3 axis, which with a little bit of trigonometry can be turned into a compass. We used this on SniffBot so that it could be instructed to face North when in idle mode. This worked brilliantly sometimes, but a few times we tried it, it just behaved very badly. We put this down to the generally messy magnetic field caused by all the metal and electronic equipment we have around here.
However we were prompted to revisit it, by the announcement of the BBC Microbit. Despite this being promised to arrive in schools in the next few weeks, no one has yet seen one... slightly worrying. However we do know it includes a mag3110 magnetometer. This does exactly the same job as the hmc5883, and in fact they both run on i2c, so for all practical purposes its a direct drop in replacement. So in Sniff R21 we've added support for the mag3110. With the new driver you should be able to just change:
make magnetometer hmc5883 device
make magnetometer mag3110 device
And you're good to go.
When we tried it, we found that the results were pretty unreliable, but a bit of research suggested we could improve things by calbrating the sensor. If you've got an iPhone and used the compass app, then you'll be familiar with this, where it asks you to wave the phone around. The problem is that the chip tends to be biased, outputting the results with a fixed offset.
From our experience (sample size 1) on the hmc5883 this is sometimes quite a small error, so we got OK results... sometimes! On the mag3110 this is typically quite a larger error so we need always need to compensate for it.
While your phone does some complex stuff, including using the accelerometer to track the movement of your phone during calibration, we can do a basic calibration by getting you to wave the chip around. If you move it enough we'll get max and minimum values on each axis which are Offset+field, and Offset-field. Taking the average gives is the offset, which we can then subtract from subsequent readings.
To make this happen we've added a new variable compassCalibrate. If you set it to "yes" then you can still use the compass as normal but the readings are also used for calibration. Provided you're operating in a relatively constant magnetic field, this works really well, so if you just need a generic compass just set it to "yes".
.set compassCalibrate to yes
..tell magnetometer to "read"
..wait 0.1 secs
In the compass.sniff example code, we set compassCalibrate to yes, then take 100 readings over 10 seconds to get a baseline calibration. During this time you should turn the chip around as much as possible. However even after we leave this setup phase, and start collecting "real" readings, we leave calibration on, and in fact the accuracy does seem to improve with further use.
..tell magnetometer to "read"
..say join "Heading " [ heading ]
..wait 1 secs
Where this approach will fail is if you actually want to measure a changing magnetic field. Large changes in the field will be partially calibrated out, as the sensor tries to correct for something it actually should be measuring. In which case you should probably calibrate initially in a constant field, then turn calibration mode off.
With this code in place the Mag3110 worked really well, so we went back and added the same calibration modes to the Hmc5883. Your old code should work just as it always did, but if you add a calibration phase, or simple let the device calibrate as you're using it you should see much better results.