Skip to content

Commit

Permalink
Merge pull request mprograms#2 from TheNitek/master
Browse files Browse the repository at this point in the history
Add calibration functions
  • Loading branch information
mprograms authored Jun 27, 2020
2 parents 1226226 + fef8836 commit 4cb4c1b
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 9 deletions.
83 changes: 83 additions & 0 deletions examples/calibration/calibration.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
===============================================================================================================
QMC5883LCompass.h Library XYZ Example Sketch
Learn more at [https://github.com/mprograms/QMC5883LCompass]
Generate calibration values for your QMC5883L
===============================================================================================================
Release under the GNU General Public License v3
[https://www.gnu.org/licenses/gpl-3.0.en.html]
===============================================================================================================
*/
#include <QMC5883LCompass.h>

QMC5883LCompass compass;

int calibrationData[3][2];
bool changed = false;

void setup() {
Serial.begin(115200);
compass.init();

Serial.println("Slowly move your magnetometer in all directions until the output does not change anymore");
Serial.println("Afterwards just copy the most recent output line into your sketch to use calibration data");
}

void loop() {
int x, y, z;

// Read compass values
compass.read();

// Return XYZ readings
x = compass.getX();
y = compass.getY();
z = compass.getZ();

changed = false;

if(x < calibrationData[0][0]) {
calibrationData[0][0] = x;
changed = true;
}
if(x > calibrationData[0][1]) {
calibrationData[0][1] = x;
changed = true;
}

if(y < calibrationData[1][0]) {
calibrationData[1][0] = y;
changed = true;
}
if(y > calibrationData[1][1]) {
calibrationData[1][1] = y;
changed = true;
}

if(z < calibrationData[2][0]) {
calibrationData[2][0] = z;
changed = true;
}
if(z > calibrationData[2][1]) {
calibrationData[2][1] = z;
changed = true;
}

if(changed) {
Serial.print("compass.setCalibration(");
Serial.print(calibrationData[0][0]);
Serial.print(", ");
Serial.print(calibrationData[0][1]);
Serial.print(", ");
Serial.print(calibrationData[1][0]);
Serial.print(", ");
Serial.print(calibrationData[1][1]);
Serial.print(", ");
Serial.print(calibrationData[2][0]);
Serial.print(", ");
Serial.print(calibrationData[2][1]);
Serial.println(");");
}
}
2 changes: 1 addition & 1 deletion examples/xyz/xyz.ino
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
===============================================================================================================
QMC5883LCompass.h Library XYZ Example Sketch
Learn more at [https://github.com/mprograms/QMC5883Compas]
Learn more at [https://github.com/mprograms/QMC5883LCompass]
This example shows how to get the XYZ values from the sensor.
Expand Down
9 changes: 7 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ void setup(){
```
#### Change Mode, Data Rate, Scale, Sample Ratio

You can also change the mode, sensitivity, sample rate and output rate of the QMC5583L chip. To do this, simply call `compass.setMode(MODE, ODR, RNG, OSR)l` after you have called `compass.init()`. Note that each value must be a byte.
You can also change the mode, sensitivity, sample rate and output rate of the QMC5583L chip. To do this, simply call `compass.setMode(MODE, ODR, RNG, OSR);` after you have called `compass.init()`. Note that each value must be a byte.

The values to set each mode are in the table below and were taken from the [QST QMC5583L datasheet](https://nettigo.pl/attachments/440).

Expand Down Expand Up @@ -238,6 +238,11 @@ The values to set each mode are in the table below and were taken from the [QST
| 512 | 0x00 |

---

## Calibrating The Sensor

It is possible to set calibration values specific for your QMC5883L to improve the quality of the readings. Just call `compass.setCalibration(X_MIN, X_MAX, Y_MIN, Y_MAX, Z_MIN, Z_MAX);` once. To find the values specific for your sensor flash the calibration example and watch the output on the serial monitor.

## Smoothing Sensor Output

Smoothing can help in cases where sensor readings seem to bounce around. QMC5883L Compass Library uses a rolling average function to store (n) sensor readings and return the average of each axis. This averaging also places smoothing on azimuth and directional output as well.
Expand All @@ -255,7 +260,7 @@ To enable smoothing call `compass.setSmoothing(STEPS, ADVANCED);` before the loo
```
void setup(){
compass.init();
setSmoothing(10, true);
compass.setSmoothing(10, true);
}
```

Expand Down
70 changes: 64 additions & 6 deletions src/QMC5883LCompass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,21 @@ void QMC5883LCompass::setSmoothing(byte steps, bool adv){
_smoothAdvanced = (adv == true) ? true : false;
}

/**
SET CALIBRATION
Set calibration values for more accurate readings
**/
void QMC5883LCompass::setCalibration(int x_min, int x_max, int y_min, int y_max, int z_min, int z_max){
_calibrationUse = true;

_vCalibration[0][0] = x_min;
_vCalibration[0][1] = x_max;
_vCalibration[1][0] = y_min;
_vCalibration[1][1] = y_max;
_vCalibration[2][0] = z_min;
_vCalibration[2][1] = z_max;
}


/**
READ
Expand All @@ -144,20 +159,52 @@ void QMC5883LCompass::read(){
Wire.write(0x00);
int err = Wire.endTransmission();
if (!err) {
Wire.requestFrom(_ADDR, (byte)7);
Wire.requestFrom(_ADDR, (byte)6);
_vRaw[0] = (int)(int16_t)(Wire.read() | Wire.read() << 8);
_vRaw[1] = (int)(int16_t)(Wire.read() | Wire.read() << 8);
_vRaw[2] = (int)(int16_t)(Wire.read() | Wire.read() << 8);

if ( _calibrationUse ) {
_applyCalibration();
}

if ( _smoothUse ) {
_smoothing();
}

byte overflow = Wire.read() & 0x02;
//byte overflow = Wire.read() & 0x02;
//return overflow << 2;
}
}

/**
APPLY CALIBRATION
This function uses the calibration data provided via @see setCalibration() to calculate more
accurate readings
Based on this awesome article:
https://appelsiini.net/2018/calibrate-magnetometer/
**/
void QMC5883LCompass::_applyCalibration(){
int x_offset = (_vCalibration[0][0] + _vCalibration[0][1])/2;
int y_offset = (_vCalibration[1][0] + _vCalibration[1][1])/2;
int z_offset = (_vCalibration[2][0] + _vCalibration[2][1])/2;
int x_avg_delta = (_vCalibration[0][1] - _vCalibration[0][0])/2;
int y_avg_delta = (_vCalibration[1][1] - _vCalibration[1][0])/2;
int z_avg_delta = (_vCalibration[2][1] - _vCalibration[2][0])/2;

int avg_delta = (x_avg_delta + y_avg_delta + z_avg_delta) / 3;

float x_scale = (float)avg_delta / x_avg_delta;
float y_scale = (float)avg_delta / y_avg_delta;
float z_scale = (float)avg_delta / z_avg_delta;

_vCalibrated[0] = (_vRaw[0] - x_offset) * x_scale;
_vCalibrated[1] = (_vRaw[1] - y_offset) * y_scale;
_vCalibrated[2] = (_vRaw[2] - z_offset) * z_scale;
}


/**
SMOOTH OUTPUT
Expand Down Expand Up @@ -187,7 +234,7 @@ void QMC5883LCompass::_smoothing(){
if ( _vTotals[i] != 0 ) {
_vTotals[i] = _vTotals[i] - _vHistory[_vScan][i];
}
_vHistory[_vScan][i] = _vRaw[i];
_vHistory[_vScan][i] = ( _calibrationUse ) ? _vCalibrated[i] : _vRaw[i];
_vTotals[i] = _vTotals[i] + _vHistory[_vScan][i];

if ( _smoothAdvanced ) {
Expand Down Expand Up @@ -219,7 +266,7 @@ void QMC5883LCompass::_smoothing(){
@return int x axis
**/
int QMC5883LCompass::getX(){
return ( _smoothUse ) ? _vSmooth[0] : _vRaw[0];
return _get(0);
}


Expand All @@ -231,7 +278,7 @@ int QMC5883LCompass::getX(){
@return int y axis
**/
int QMC5883LCompass::getY(){
return ( _smoothUse ) ? _vSmooth[1] : _vRaw[1];
return _get(1);
}


Expand All @@ -243,10 +290,21 @@ int QMC5883LCompass::getY(){
@return int z axis
**/
int QMC5883LCompass::getZ(){
return ( _smoothUse ) ? _vSmooth[2] : _vRaw[2];
return _get(2);
}

int QMC5883LCompass::_get(int index){
if ( _smoothUse )
return _vSmooth[index];

if ( _calibrationUse )
return _vCalibrated[index];

return _vRaw[index];
}



/**
GET AZIMUTH
Calculate the azimuth (in degrees);
Expand Down
6 changes: 6 additions & 0 deletions src/QMC5883LCompass.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class QMC5883LCompass{
void setADDR(byte b);
void setMode(byte mode, byte odr, byte rng, byte osr);
void setSmoothing(byte steps, bool adv);
void setCalibration(int x_min, int x_max, int y_min, int y_max, int z_min, int z_max);
void setReset();
void read();
int getX();
Expand All @@ -24,6 +25,7 @@ class QMC5883LCompass{

private:
void _writeReg(byte reg,byte val);
int _get(int index);
bool _smoothUse = false;
byte _smoothSteps = 5;
bool _smoothAdvanced = false;
Expand All @@ -34,6 +36,10 @@ class QMC5883LCompass{
long _vTotals[3] = {0,0,0};
int _vSmooth[3] = {0,0,0};
void _smoothing();
bool _calibrationUse = false;
int _vCalibration[3][2];
int _vCalibrated[3];
void _applyCalibration();
const char _bearings[16][3] = {
{' ', ' ', 'N'},
{'N', 'N', 'E'},
Expand Down

0 comments on commit 4cb4c1b

Please sign in to comment.