Skip to content

Commit

Permalink
Merge pull request #3 from mprograms/1.1.x
Browse files Browse the repository at this point in the history
1.1.0
  • Loading branch information
mprograms authored Jun 27, 2020
2 parents 1226226 + f98aa18 commit fa0a4d0
Show file tree
Hide file tree
Showing 6 changed files with 251 additions and 10 deletions.
10 changes: 9 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,21 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).


## [v1.1.0]
### Added
- Added calibration functions to library
- Added /examples/calibration/calibration.ino utility sketch.
### Changed
- Modified readme.md to reflect new calibration functions.

## [v1.0.3]
### Changed
- Modified readme.md to correct type in getAzimuth() function.

## [v1.0.2]
### Changed
- Modified readme.md to make is clearer that wire.h is required to run.
- Modified readme.md to make it clearer that wire.h is required to run.

## [v1.0.1]
### Changed
Expand Down
127 changes: 127 additions & 0 deletions examples/calibration/calibration.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
QMC5883LCompass.h Library Calibration Example Sketch
Learn more at [https://github.com/mprograms/QMC5883LCompass]
Upload this calibration sketch onto your Arduino to provide calibration for your QMC5883L chip.
After upload, run the serial monitor and follow the directions.
When prompted, copy the last line into your project's actual sketch.
===============================================================================================================
Release under the GNU General Public License v3
[https://www.gnu.org/licenses/gpl-3.0.en.html]
===============================================================================================================
UTILITY SKETCH
NO SERVICABLE PARTS BELOW
*/
#include <QMC5883LCompass.h>

QMC5883LCompass compass;

int calibrationData[3][2];
bool changed = false;
bool done = false;
int t = 0;
int c = 0;

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

Serial.println("This will provide calibration settings for your QMC5883L chip. When prompted, move the magnetometer in all directions until the calibration is complete.");
Serial.println("Calibration will begin in 5 seconds.");
delay(5000);

}

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 && !done) {
Serial.println("CALIBRATING... Keep moving your sensor around.");
c = millis();
}
t = millis();


if ( (t - c > 5000) && !done) {
done = true;
Serial.println("DONE. Copy the line below and paste it into your projects sketch.);");
Serial.println();

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
30 changes: 28 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,7 @@ The values to set each mode are in the table below and were taken from the [QST
| 512 | 0x00 |

---

## 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,8 +256,33 @@ To enable smoothing call `compass.setSmoothing(STEPS, ADVANCED);` before the loo
```
void setup(){
compass.init();
setSmoothing(10, true);
compass.setSmoothing(10, true);
}
```


## Calibrating The Sensor

QMC5883LCompass library includes a calibration function and utility sketch to help you calibrate your QMC5883L chip. Calibration is a two-step process.

### Step 1: Run Calibration Sketch

1. Ensure that your QMC5883L chip is connected.
2. Locate the included calibration sketch under EXAMPLES > QMC5883LCOMPASS > CALIBRATION.
3. Upload the calibration sketch to your arduino and then open the serial monitor.
4. Follow the directions on the screen by moving your sensor around when the calibration process starts.
5. Once all calibration data has been collected, the sketch will tell provide you with some code that will look like `compass.setCalibration(-1537, 1266, -1961, 958, -1342, 1492);` Copy this code. You may want to save it for future reference.

### Step 2: Using Calibration Data

1. Open your project's sketch and paste the line of code you copied directly below the `compass.init()` call.
2. Use the QMC5883LCompass library as normal.

It is recommended that you use the provided calibration sketch to generate your sensor's min and max values but you can also add your own by using the `compass.setCalibration(X_MIN, X_MAX, Y_MIN, Y_MAX, Z_MIN, Z_MAX);` function.


## Contributions

Special thanks is given to the following individuals who have contributed to this library:

- Claus Näveke : [TheNitek](https://github.com/TheNitek) for adding calibration functions to the library.
86 changes: 80 additions & 6 deletions src/QMC5883LCompass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,26 @@ void QMC5883LCompass::setSmoothing(byte steps, bool adv){
_smoothAdvanced = (adv == true) ? true : false;
}

/**
SET CALIBRATION
Set calibration values for more accurate readings
@author Claus Näveke - TheNitek [https://github.com/TheNitek]
@since v1.1.0
**/
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 +164,56 @@ 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
@author Claus Näveke - TheNitek [https://github.com/TheNitek]
Based on this awesome article:
https://appelsiini.net/2018/calibrate-magnetometer/
@since v1.1.0
**/
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 +243,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 +275,7 @@ void QMC5883LCompass::_smoothing(){
@return int x axis
**/
int QMC5883LCompass::getX(){
return ( _smoothUse ) ? _vSmooth[0] : _vRaw[0];
return _get(0);
}


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


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

/**
GET SENSOR AXIS READING
Get the smoothed, calibration, or raw data from a given sensor axis
@since v1.1.0
@return int sensor axis value
**/
int QMC5883LCompass::_get(int i){
if ( _smoothUse )
return _vSmooth[i];

if ( _calibrationUse )
return _vCalibrated[i];

return _vRaw[i];
}



/**
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 fa0a4d0

Please sign in to comment.