Good day all,
I'm currently trying to build a device which turns to a given magnetic heading and have come across a hurdle.
I am able to interface the stepper motor with no issues, and am able to calibrate the magnetometer in one sketch to use in another sketch, but I am unable to calibrate the compass and use that calibration in the same sketch. Ideally I would like the system to turn 360 degrees performing a calibration run (with appropriate delays to prevent weird fields put out by the stepper motor), followed by being able to use that calibration data to know where North is, etc.
The calibration and heading sketches are examples in the LSM303.h library.
Calibration sketch: (works well)
#include <Wire.h>
#include <LSM303.h>
LSM303 compass;
LSM303::vector<int16_t> running_min = {32767, 32767, 32767}, running_max = {-32768, -32768, -32768};
char report[80];
void setup() {
Serial.begin(9600);
Wire.begin();
compass.init();
compass.enableDefault();
}
void loop() {
compass.read();
running_min.x = min(running_min.x, compass.m.x);
running_min.y = min(running_min.y, compass.m.y);
running_min.z = min(running_min.z, compass.m.z);
running_max.x = max(running_max.x, compass.m.x);
running_max.y = max(running_max.y, compass.m.y);
running_max.z = max(running_max.z, compass.m.z);
snprintf(report, sizeof(report), "min: {%+6d, %+6d, %+6d} max: {%+6d, %+6d, %+6d}",
running_min.x, running_min.y, running_min.z,
running_max.x, running_max.y, running_max.z);
Serial.println(report);
delay(100);
}
Heading determining sketch: (following separate calibration sketch) (works well)
#include <Wire.h>
#include <LSM303.h>
LSM303 compass;
void setup() {
Serial.begin(9600);
Wire.begin();
compass.init();
compass.enableDefault();
/*
Calibration values; the default values of +/-32767 for each axis
lead to an assumed magnetometer bias of 0. Use the Calibrate example
program to determine appropriate values for your particular unit.
*/
compass.m_min = (LSM303::vector<int16_t>){-32767, -32767, -32767};
compass.m_max = (LSM303::vector<int16_t>){+32767, +32767, +32767};
}
void loop() {
compass.read();
/*
When given no arguments, the heading() function returns the angular
difference in the horizontal plane between a default vector and
north, in degrees.
The default vector is chosen by the library to point along the
surface of the PCB, in the direction of the top of the text on the
silkscreen. This is the +X axis on the Pololu LSM303D carrier and
the -Y axis on the Pololu LSM303DLHC, LSM303DLM, and LSM303DLH
carriers.
To use a different vector as a reference, use the version of heading()
that takes a vector argument; for example, use
compass.heading((LSM303::vector<int>){0, 0, 1});
to use the +Z axis as a reference.
*/
float heading = compass.heading();
Serial.println(heading);
delay(100);
}
My current test sketch: (my coding is not the strongest, though I have persisted thus far and got quite a lot done! (insert proud face))
#include <AccelStepper.h>
#include <Wire.h>
#include <LSM303.h>
// Define a stepper and the pins it will use
AccelStepper stepper(AccelStepper::DRIVER, 2, 3);
#define MS1 3
#define MS2 4
#define MS3 5
//Setting up Compass Variables
LSM303 compass;
LSM303::vector<int16_t> running_min = {32767, 32767, 32767}, running_max = { -32768, -32768, -32768};
float heading;
long azimuth;
long where;
void setup()
{
stepper.setMaxSpeed(800.0);
stepper.setAcceleration(8000.0);
pinMode(MS1, OUTPUT);
pinMode(MS2, OUTPUT);
pinMode(MS3, OUTPUT);
digitalWrite(MS1, LOW);
digitalWrite(MS2, LOW);
digitalWrite(MS3, LOW);
stepper.setCurrentPosition(0);
Wire.begin();
compass.init();
compass.enableDefault();
}
void loop()
{
for (int i = 0; i < 72; i++)
{
stepper.runToNewPosition(876);
delay(100);
stepper.setCurrentPosition(0);
compass.read();
running_min.x = min(running_min.x, compass.m.x); //Local Magnetic Field x axis Minimum value in MicroTeslas
running_min.y = min(running_min.y, compass.m.y); //Local Magnetic Field y axis Minimum value in MicroTeslas
running_min.z = min(running_min.z, compass.m.z); //Local Magnetic Field z axis Minimum value in MicroTeslas
running_max.x = max(running_max.x, compass.m.x); //Local Magnetic Field x axis Maximum value in MicroTeslas
running_max.y = max(running_max.y, compass.m.y); //Local Magnetic Field y axis Maximum value in MicroTeslas
running_max.z = max(running_max.z, compass.m.z); //Local Magnetic Field z axis Maximum value in MicroTeslas
delay(100);
}
delay(500);
compass.read();
where = compass.heading();
azimuth = 0 - ((where / 360) * 63000);
delay(500);
stepper.runToNewPosition(azimuth);
delay(10000);
}
The system does turn as expected, though doesn't then make the final turn after the 'for loop' where I would expect about a 90 degree turn from where I have set this up on the table (facing East before and at end of 360 degree calibration spin).
From what I gather, the vectors used are a special type which have been created for this library, where an array might traditionally be used?
Just wondering if anyone has any suggestions or if anyone knows what might be going wrong with I've typed up? If all else fails I'll just run a calibration whenever I want to run this system, though I would of course love this to be integrated into the main code for a better user experience!
p.s. the calculation at the end for azimuth is because my stepper setup requires 63000 steps to complete a single rotation of the system. Hope that helps for clarity!
LSM303 github page:
AccelStepper library link:
http://www.airspayce.com/mikem/arduino/AccelStepper/classAccelStepper.html
Thanks all!
Chris