Yes, I kept the permanent magnet in front of the magnetometer, while (between sets) moving it closer to the magnetometer, until it was about 1/4 of an inch away.
I’ve been rereading two sketches like the one at the bottom of this reply, though I don’t really understand how they work. I’ll call both of them Example7-I2C_Sensor_Offset_Modified. Still, two users (one at another forum, and another at a Discord page) said I should try them, to correct or calibrate Example2-I2C_Digital_compass. I think I know how to get numbers from Example7-I2C_Sensor_Offset_Modified and insert those numbers into Example2-I2C_Digital_compass. However, if one sketch (Example2-I2C_Digital_compass) can’t enable a heading accuracy of plus-minus 0.5 degrees (which SparkFun says the digital compass has), how can I be sure two (Example2-I2C_Digital_compass and Example7-I2C_Sensor_Offset_Modified) can? In other words, how many sketches do I need to get that accuracy?
Thank you.
/*
Removing the bridge offset from the MMC5983MA
By: Paul Clark
SparkFun Electronics
Date: February 9th, 2023
License: SparkFun code, firmware, and software is released under the MIT License(http://opensource.org/licenses/MIT).
Feel like supporting our work? Buy a board from SparkFun!
https://www.sparkfun.com/products/19034
This example shows how to remove the sensor (bridge) offset.
Press a key (send any character) to perform the set-reset and update the offset.
Hardware Connections:
Plug a Qwiic cable into the sensor and a RedBoard
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper
(https://www.sparkfun.com/products/17912) Open the serial monitor at 115200 baud to see the output
From the datasheet:
USING SET AND RESET TO REMOVE BRIDGE OFFSET
The integrated SET and RESET functions of the MMC5893VA enables the user to remove the error
associated with bridge Offset change as a function of temperature, thereby enabling more precise heading
measurements over a wider temperature than competitive technologies. The SET and RESET
functions effectively alternately flip the magnetic sensing polarity of the sensing elements of the device.
1) The most accurate magnetic field measurements can be obtained by using the protocol described
as follows: Perform SET. This sets the internal magnetization of the sensing resistors in the
direction of the SET field.
2) Perform MEASUREMENT. This measurement will contain not only the sensors response to the
external magnetic field, H, but also the Offset; in other words,
Output1 = +H + Offset.
3) Perform RESET. This resets the internal magnetization of the sensing resistors in the
direction of the RESET field, which is opposite to the SET field (180o opposed).
4) Perform MEASUREMENT. This measurement will contain both the sensors response to the
external field and also the Offset. In other words,
Output2 = -H + Offset.
5) Finally, calculate H by subtracting the two measurements and dividing by 2. This procedure
effectively eliminates the Offset from the measurement and therefore any changes in the
Offset over temperature.
Note: To calculate and store the offset; add the two measurements and divide by 2. This calculated offset
value can be subtracted from subsequent measurements to obtain H directly from each measurement.
*/
#include <Wire.h>
#include <SparkFun_MMC5983MA_Arduino_Library.h> //Click here to get the library: http://librarymanager/All#SparkFun_MMC5983MA
SFE_MMC5983MA myMag;
void setup()
{
Serial.begin(115200);
// Most Serial prints have been commented out - to allow the data to be plotted nicely using Tools \ Serial Plotter
//Serial.println("MMC5983MA Example");
Wire.begin();
if (myMag.begin() == false)
{
Serial.println("MMC5983MA did not respond - check your wiring. Freezing.");
while (true)
;
}
myMag.softReset();
//Serial.println("MMC5983MA connected");
//Serial.println("Press a key (send any character) to perform the set-reset and update the offset.");
while (Serial.available())
Serial.read(); // Empty the Serial RX buffer
}
void loop()
{
// The magnetic field values are 18-bit unsigned. The zero (mid) point is 2^17 (131072).
// Use static variables to hold the offset. Set to 131072 initially.
static uint32_t offsetX = 131072;
static uint32_t offsetY = 131072;
static uint32_t offsetZ = 131072;
// Has the user pressed a key?
if (Serial.available())
{
updateOffset(&offsetX, &offsetY, &offsetZ); // Update the offsets
while (Serial.available())
Serial.read(); // Empty the Serial RX buffer
}
uint32_t currentX = 0;
uint32_t currentY = 0;
uint32_t currentZ = 0;
// This reads the X, Y and Z channels simultaneously
myMag.getMeasurementXYZ(¤tX, ¤tY, ¤tZ);
// The magnetic field values are 18-bit unsigned.
// The zero (mid) point should be 2^17 (131072).
// Here we subtract the offset, then normalize each field to +/- 1.0,
// then multiply by 8 to convert to Gauss
double normalizedX = (double)currentX - (double)offsetX; // Convert to double _before_ subtracting
normalizedX /= 131072.0;
normalizedX *= 8.0;
double normalizedY = (double)currentY - (double)offsetY; // Convert to double _before_ subtracting
normalizedY /= 131072.0;
normalizedY *= 8.0;
double normalizedZ = (double)currentZ - (double)offsetZ; // Convert to double _before_ subtracting
normalizedZ /= 131072.0;
normalizedZ *= 8.0;
// Print the three channels with commas in between so the Serial Plotter can plot them
Serial.print(normalizedX, 5); // Print with 5 decimal places
Serial.print(",");
Serial.print(normalizedY, 5);
Serial.print(",");
Serial.println(normalizedZ, 5);
}
bool updateOffset(uint32_t *offsetX, uint32_t *offsetY, uint32_t *offsetZ) // Update the offsets
{
bool success = true; // Use AND (&=) to record if any one command fails
success &= myMag.performSetOperation(); // Perform the SET operation
uint32_t setX = 131072;
uint32_t setY = 131072;
uint32_t setZ = 131072;
success &= myMag.getMeasurementXYZ(&setX, &setY, &setZ); // Read all three channels
success &= myMag.getMeasurementXYZ(&setX, &setY, &setZ); // Do it twice - just in case there is noise on the first
success &= myMag.performResetOperation(); // Perform the RESET operation
uint32_t resetX = 131072;
uint32_t resetY = 131072;
uint32_t resetZ = 131072;
success &= myMag.getMeasurementXYZ(&resetX, &resetY, &resetZ); // Read all three channels
success &= myMag.getMeasurementXYZ(&resetX, &resetY, &resetZ); // Do it twice - just in case there is noise on the first
// Calculate the offset - as per the datasheet.
// The measurements are 18-bit so it's OK to add them directly.
if (success)
{
*offsetX = (setX + resetX) / 2;
*offsetY = (setY + resetY) / 2;
*offsetZ = (setZ + resetZ) / 2;
Serial.print("Offsets: ")
Serial.print((setX + resetX) / 2);
Serial.print(", ");
Serial.print((setY + resetY) / 2);
Serial.print(", ");
Serial.print((setZ + resetZ) / 2);
Serial.println();
}
return success;
}
The example sketches Example2-I2C_Digital_compass and Example7-I2C_Sensor_Offset (both of which are at https://github.com/sparkfun/SparkFun_MMC5983MA_Magnetometer_Arduino_Library/tree/main/examples) both mention (in comments) the tutorial (for correcting and calibrating) at https://thecavepearlproject.org/2015/05/22/calibrating-any-compass-or-accelerometer-for-arduino/. However, I couldn't find a sketch for my board in that tutorial. I could use help making a sketch (for my board) using that tutorial, or finding a sketch (that already works with my board) that is similar to the sketch in that tutorial.
However, I couldn't find a sketch for my board in that tutorial. I could use help making a sketch ... using that tutorial, or finding a sketch ... that is similar to the sketch in that tutorial
Magnetic calibration parameters are typically not calculated within Arduino sketches directly (although it is possible).
Most of the time, magnetic data is sent from the Arduino to a PC via USB that runs calibration software. This calibration software spits out a 3 element bias vector and 3x3 scaling/orthogonality matrix. The bias vector is to account for the presence of any hard-iron/permanent magnets while the matrix is to account for soft-iron/sensor-axis-misalignment errors. Much of this is covered in the Cave Pearl Project link.
This might help: https://learn.adafruit.com/adafruit-sensorlab-magnetometer-calibration/magnetometer-calibration (should be fairly straight forward to update the sketches to work with your particular sensors)
Thank you.
If I get 9 numbers from a 3x3 matrix, can you tell me how I can use those 9 numbers to change Example2-I2C_Digital_compass (which the first link in post #224 goes to)?
Also, how can I compare the heading accuracies that I can get using the Adafruit pages Faraday just gave me a link to, that I can get using the example sketch Example7-I2C_Sensor_Offset (which the first link in post #224 also goes to), and of plus-minus 0.5 degrees (which SparkFun says it enables)?
If I get 9 numbers from a 3x3 matrix, can you tell me how I can use those 9 numbers...
First, take your measurements in each axis (X, Y, and Z in the sensor coordinate frame - frame axes are usually labeled on the PCB) and subtract the 3 element bias vector. Then, take the unbiased measurement vector and apply the 3x3 matrix to get a fully calibrated measurement. To "apply the matrix", you have to do matrix multiplication where the calibrated measurement = 3x3 matrix * unbiased measurement.
NOTE: Order of operation matters when doing matrix multiplication!
Some (untested) code to make the explanation clearer (shamelessly ripped from this post):
// correction constants from Magneto
float M_B[3]
{ -922.31, 2199.41, 373.17};
float M_Ainv[3][3]
{ { 1.04492, 0.03452, -0.01714},
{ 0.03452, 1.05168, 0.00644},
{ -0.01714, 0.00644, 1.07005}
};
float Mxyz[3],temp[3];
...
// get magnetometer data
Mxyz[0] = imu.mx;
Mxyz[1] = imu.my;
Mxyz[2] = imu.mz;
//apply mag offsets (bias) and scale factors from Magneto
for (int i = 0; i < 3; i++) temp[i] = (Mxyz[i] - M_B[i])
Mxyz[0] = M_Ainv[0][0] * temp[0] + M_Ainv[0][1] * temp[1] + M_Ainv[0][2] * temp[2];
Mxyz[1] = M_Ainv[1][0] * temp[0] + M_Ainv[1][1] * temp[1] + M_Ainv[1][2] * temp[2];
Mxyz[2] = M_Ainv[2][0] * temp[0] + M_Ainv[2][1] * temp[1] + M_Ainv[2][2] * temp[2];
Thank you.
Will those measurements be raw values, scaled values, or headings?
I’ve been giving a lot of misleading information, including x instead of y.
When I saw a 3x3 matrix, I thought I’d have to put 9 numbers into Example2-I2C_Digital_compass. But a 3x3 matrix times a 3x1 matrix is a 3x1 matrix, which has 3 (not 9) numbers in it. There are 3, one for each of the x, y, and z axes. So that makes sense to me now.
Will those measurements be raw values, scaled values, or headings?
Raw field measurements in each orthogonal direction
When I saw a 3x3 matrix, I thought I’d have to put 9 numbers into Example2-I2C_Digital_compass
You need to put 12 numbers into the "example" - 9 for the scaling matrix and 3 for the bias vector
Also: I had to edit the code snippet in my previous post due to a bug. It's fixed now
I've skimmed through previous posts here before and had a few thoughts on the matter that you may or may not find beneficial:
- A magnetometer-only compass sensor is most likely not sufficient to meet the your project requirements. A few alternatives that will be more accurate/reliable:
- Instead of a compass, use a high quality Inertial Measurement Unit (IMU)
- This will give you higher sensor orientation accuracy, but you might have to break the bank to purchase a quality IMU unless...
- You might be able to make your own using this sensor board and this code
- Use an infrared (IR) motion capture system
- This is the same type of system movie/videogame companies use to do real-world motion capture with black suits and attached white balls
- I believe you've linked to a video before of such a system used on ballet dancers for medical purposes: this would be the same system, just repurposed for your project
- This will break the bank even harder than the IMU solution, but will reliably give very, very accurate heading values for multiple dancers (or even parts of dancers) in an indoor environment (even with obstacles like columns)
- Use 3 or more cameras mounted around the room similar to the IR motion capture system
- You could use cameras designed for machine vision (pricey, but not bad) or USB cameras if on a tight budget
- The video feeds would all go to a single PC for processing:
- The PC will use the simultaneously captured images from the various cameras and triangulate common features in the scene (OpenCV can help with this)
- These common features could then be identified as the right arm of dancer A, etc. (I'm honestly not 100% how to do this part)
- The orientation of the features could then be reported to an Arduino for LED control
- Note that the triangulation algorithm will need to know each camera's exact location and orientation to work
- IMHO this is the best option for the money, but will take the most work by far
- Instead of a compass, use a high quality Inertial Measurement Unit (IMU)
- Unless you use the IR motion capture system mentioned above, math (specifically linear algebra) will 100% be required to do this project
- This can be seen with the matrix multiplication required for calibration, but it doesn't stop there
- Basic familiarity with other math concepts like trigonometry, Euler angles, and coordinate frame transformations will be needed, too
- The good news is that the math looks a lot more scary than it really is
- I might be pushing for unnecessary amounts of accuracy in my suggestions above, so if you disregard them, I totally understand
- This is a pretty cool project and I hope you can get it working
Please share your ideas if you think of anything more.
I made the following sketch before realizing that one of Faraday’s links may go to one that does the same thing and more. But maybe someone will find it easy. Even if it works, I welcome ideas about how the sketch can be written better. It does the following.
/*
Includes a function that is supposed to help make hard and soft iron corrections to the MMC5983MA_Magnetometer
Modified from sketches made by other people
*/
#define Serial SerialUSB // My microcontroller needs this line to work, but it might stop yours from working
double results;
void setup() {
Serial.begin(9600);
}
void loop() {
delay(7000); // Give me time to open the serial monitor
results = calibration((uint32_t)2, (uint32_t)3, (uint32_t)2); // The numbers can be used to make a matrix in which a11 = 2, a21 = 3, and a31 = 2.
Serial.print(results);
while (true); // pause
}
double calibration(uint32_t rawValueX, uint32_t rawValueY, uint32_t rawValueZ)
// Get the parameters from the following, in the Example2-I2C_Digital_compass example sketch
// myMag.getMeasurementXYZ(&rawValueX, &rawValueY, &rawValueZ);
// I'm assuming the &s should be left out
{
static float rawValues[3];
rawValues[0] = (float)rawValueX;
rawValues[1] = (float)rawValueY;
rawValues[2] = (float)rawValueZ;
static float hardIronCorrection[3]
{1, 1, 1};
static float softIronCorrection[3][3]
{ {1, 2, 1},
{4, 5, 1},
{2, 1, 3}
};
static float products[3][4]; // Used to get and add dot products
// Subtract hardIronCorrection from rawValues
for (int i = 0; i < 3; i++) {
rawValues[i] = rawValues[i] - hardIronCorrection[i];
}
// Apply softIronCorrection
for (int i = 0; i < 3; i++) { // rows of dot products
for (int j = 0; j < 4; j++) { // 3 columns of dot products, and the 4th is the sum of the dot products in a row
if (j != 3 ) {
products[i][j] = softIronCorrection[i][j] * rawValues[j]; // The softIron column is the row of the other matrix
} else { //There are three products in a row, and it is time to add them
products[i][3] = products[i][0] + products[i][1] + products[i][2];
}
}
}
Serial.println("Dot products in the same row will be added together");
for (int i = 0; i < 3; i++) {
Serial.println(" ");
for (int j = 0; j < 3; j++) {
Serial.print(products[i][j]);
Serial.print(" ");
}
}
Serial.println(" ");
Serial.println("A 3x1 matrix has a11, a21, and a31 in it");
Serial.print("a11 ");
Serial.println(products[0][3]);
Serial.print("a21 ");
Serial.println(products[1][3]);
Serial.print("a31 ");
Serial.println(products[2][3]);
// Use the rest of Example2-I2C_Digital_compass here instead of in the example sketch
double scaledX = 0;
double scaledY = 0;
double scaledZ = 0;
double heading = 0;
scaledX = (double)products[0][3] - 131072.0;
scaledX /= 131072.0;
scaledY = (double)products[1][3] - 131072.0;
scaledY /= 131072.0;
scaledZ = (double)products[2][3] - 131072.0;
scaledZ /= 131072.0;
// Magnetic north is oriented with the Y axis
// Convert the X and Y fields into heading using atan2 (Arc Tangent 2)
heading = atan2(scaledX, 0 - scaledY);
// atan2 returns a value between +PI and -PI
// Convert to degrees
heading /= PI;
heading *= 180;
heading += 180;
// Serial.print("Heading: ");
// Serial.println(heading, 1);
delay(100);
return heading;
}
Didn't look through the code very carefully, but the notes look correct except for one thing. I'm pretty sure you want to handle all operations with 131072.0 before calibrating. I'm not super familiar with the sensor you're using, but I think the true "raw" value is after you subtract and divide by 131072.0.
The page named “Install SensorLab” (which that link goes to) says to install SensorLab. However, to use SensorLab, I wasn’t able to get the library dependencies from the computer that has my sketches on it. So I got the dependencies from another computer, before putting them into Documents -> Arduino -> Libraries (on the computer that has my sketches on it). I don’t know whether that caused my problem.
The page named “Magnetic Calibration with MotionCal” (which that link also goes to) says to upload imucal. However, after I uploaded imucal, it printed the following.
Sensor Lab - IMU Calibration!
Calibration filesys test
Failed to initialize calibration helper
Here is the imucal sketch.
/***************************************************************************
This is an example for the Adafruit SensorLab library
It will look for a supported magnetometer and output
PJRC Motion Sensor Calibration Tool-compatible serial data
PJRC & Adafruit invest time and resources providing this open source code,
please support PJRC and open-source hardware by purchasing products
from PJRC!
Written by PJRC, adapted by Limor Fried for Adafruit Industries.
***************************************************************************/
#define Serial SerialUSB // For my microcontroller
#include <Adafruit_SensorLab.h>
#include <Adafruit_Sensor_Calibration.h>
Adafruit_SensorLab lab;
#if defined(ADAFRUIT_SENSOR_CALIBRATION_USE_EEPROM)
Adafruit_Sensor_Calibration_EEPROM cal;
#else
Adafruit_Sensor_Calibration_SDFat cal;
#endif
Adafruit_Sensor *mag = NULL, *gyro = NULL, *accel = NULL;
sensors_event_t mag_event, gyro_event, accel_event;
int loopcount = 0;
void setup(void) {
Serial.begin(115200);
delay(7000); // instead of delay(10);
// while (!Serial) delay(10); // will pause Zero, Leonardo, etc until serial console opens
Serial.println(F("Sensor Lab - IMU Calibration!"));
lab.begin();
Serial.println("Calibration filesys test");
if (!cal.begin()) {
Serial.println("Failed to initialize calibration helper");
while (1) yield();
}
if (! cal.loadCalibration()) {
Serial.println("No calibration loaded/found... will start with defaults");
} else {
Serial.println("Loaded existing calibration");
}
Serial.println("Looking for a magnetometer");
mag = lab.getMagnetometer();
if (! mag) {
Serial.println(F("Could not find a magnetometer, skipping!"));
} else {
mag->printSensorDetails();
}
Serial.println("Looking for a gyroscope");
gyro = lab.getGyroscope();
if (! gyro) {
Serial.println(F("Could not find a gyroscope, skipping!"));
} else {
gyro->printSensorDetails();
}
Serial.println("Looking for a accelerometer");
accel = lab.getAccelerometer();
if (! accel) {
Serial.println(F("Could not find a accelerometer, skipping!"));
} else {
accel->printSensorDetails();
}
}
void loop() {
if (mag && ! mag->getEvent(&mag_event)) {
return;
}
if (gyro && ! gyro->getEvent(&gyro_event)) {
return;
}
if (accel && ! accel->getEvent(&accel_event)) {
return;
}
// 'Raw' values to match expectation of MOtionCal
Serial.print("Raw:");
Serial.print(int(accel_event.acceleration.x * 8192 / 9.8)); Serial.print(",");
Serial.print(int(accel_event.acceleration.y * 8192 / 9.8)); Serial.print(",");
Serial.print(int(accel_event.acceleration.z * 8192 / 9.8)); Serial.print(",");
Serial.print(int(gyro_event.gyro.x * Adafruit_SensorLab::DEGREES_PER_RADIAN * 16)); Serial.print(",");
Serial.print(int(gyro_event.gyro.y * Adafruit_SensorLab::DEGREES_PER_RADIAN * 16)); Serial.print(",");
Serial.print(int(gyro_event.gyro.z * Adafruit_SensorLab::DEGREES_PER_RADIAN * 16)); Serial.print(",");
Serial.print(int(mag_event.magnetic.x * 10)); Serial.print(",");
Serial.print(int(mag_event.magnetic.y * 10)); Serial.print(",");
Serial.print(int(mag_event.magnetic.z * 10)); Serial.println("");
// unified data
Serial.print("Uni:");
Serial.print(accel_event.acceleration.x); Serial.print(",");
Serial.print(accel_event.acceleration.y); Serial.print(",");
Serial.print(accel_event.acceleration.z); Serial.print(",");
Serial.print(gyro_event.gyro.x, 4); Serial.print(",");
Serial.print(gyro_event.gyro.y, 4); Serial.print(",");
Serial.print(gyro_event.gyro.z, 4); Serial.print(",");
Serial.print(mag_event.magnetic.x); Serial.print(",");
Serial.print(mag_event.magnetic.y); Serial.print(",");
Serial.print(mag_event.magnetic.z); Serial.println("");
loopcount++;
receiveCalibration();
// occasionally print calibration
if (loopcount == 50 || loopcount > 100) {
Serial.print("Cal1:");
for (int i = 0; i < 3; i++) {
Serial.print(cal.accel_zerog[i], 3);
Serial.print(",");
}
for (int i = 0; i < 3; i++) {
Serial.print(cal.gyro_zerorate[i], 3);
Serial.print(",");
}
for (int i = 0; i < 3; i++) {
Serial.print(cal.mag_hardiron[i], 3);
Serial.print(",");
}
Serial.println(cal.mag_field, 3);
loopcount++;
}
if (loopcount >= 100) {
Serial.print("Cal2:");
for (int i = 0; i < 9; i++) {
Serial.print(cal.mag_softiron[i], 4);
if (i < 8) Serial.print(',');
}
Serial.println();
loopcount = 0;
}
delay(10);
}
/********************************************************/
byte caldata[68]; // buffer to receive magnetic calibration data
byte calcount = 0;
void receiveCalibration() {
uint16_t crc;
byte b, i;
while (Serial.available()) {
b = Serial.read();
if (calcount == 0 && b != 117) {
// first byte must be 117
return;
}
if (calcount == 1 && b != 84) {
// second byte must be 84
calcount = 0;
return;
}
// store this byte
caldata[calcount++] = b;
if (calcount < 68) {
// full calibration message is 68 bytes
return;
}
// verify the crc16 check
crc = 0xFFFF;
for (i = 0; i < 68; i++) {
crc = crc16_update(crc, caldata[i]);
}
if (crc == 0) {
// data looks good, use it
float offsets[16];
memcpy(offsets, caldata + 2, 16 * 4);
cal.accel_zerog[0] = offsets[0];
cal.accel_zerog[1] = offsets[1];
cal.accel_zerog[2] = offsets[2];
cal.gyro_zerorate[0] = offsets[3];
cal.gyro_zerorate[1] = offsets[4];
cal.gyro_zerorate[2] = offsets[5];
cal.mag_hardiron[0] = offsets[6];
cal.mag_hardiron[1] = offsets[7];
cal.mag_hardiron[2] = offsets[8];
cal.mag_field = offsets[9];
cal.mag_softiron[0] = offsets[10];
cal.mag_softiron[1] = offsets[13];
cal.mag_softiron[2] = offsets[14];
cal.mag_softiron[3] = offsets[13];
cal.mag_softiron[4] = offsets[11];
cal.mag_softiron[5] = offsets[15];
cal.mag_softiron[6] = offsets[14];
cal.mag_softiron[7] = offsets[15];
cal.mag_softiron[8] = offsets[12];
if (! cal.saveCalibration()) {
Serial.println("**WARNING** Couldn't save calibration");
} else {
Serial.println("Wrote calibration");
}
cal.printSavedCalibration();
calcount = 0;
return;
}
// look for the 117,84 in the data, before discarding
for (i = 2; i < 67; i++) {
if (caldata[i] == 117 && caldata[i + 1] == 84) {
// found possible start within data
calcount = 68 - i;
memmove(caldata, caldata + i, calcount);
return;
}
}
// look for 117 in last byte
if (caldata[67] == 117) {
caldata[0] = 117;
calcount = 1;
} else {
calcount = 0;
}
}
}
uint16_t crc16_update(uint16_t crc, uint8_t a)
{
int i;
crc ^= a;
for (i = 0; i < 8; i++) {
if (crc & 1) {
crc = (crc >> 1) ^ 0xA001;
} else {
crc = (crc >> 1);
}
}
return crc;
}
Thank you.
Looks like your Arduino can't initialize the calibration helper object or something like that. It will likely be easiest to rewrite the sketch to simply stream your raw data to the USB serial port.
According to this part of the tutorial, all the code needs to do is format the raw magnetometer output with the following format:
Raw:0,0,0,0,0,0,-375,-159,-24
Note that you don't need the Uni:... outputs. Also, the formatting of the output is as Adafruit says:
The first three numbers are accelerometer data - if you don't have an accelerometer, they will be 0
The middle three numbers are gyroscope data - if you don't have an gyroscope, they will be 0
The last three numbers are magnetometer, they should definitely not be zeros!
Therefore, the Raw:... line will be zeros except for the last three. Lastly, I think the intended units for the magnetometer data fields is nanoTeslas (nT) or Gauss (G). This is important because both of these units result in large field values that can easily be represented in integers. If your outputs are small floats, all of the interesting data will be clipped by the typecasting from floats to ints. The easy fix would be to test, look at the output, and multiply by an appropriate factor of 10 until you get good results.
Were you able to install MotionCal.exe properly?
See the updates
Thank you.
It is going to take me a while to understand that.
Am I supposed to use part of the Example2-I2C_Digital_compass sketch (at https://github.com/sparkfun/SparkFun_MMC5983MA_Magnetometer_Arduino_Library/blob/main/examples/Example2-I2C_Digital_compass/Example2-I2C_Digital_compass.ino) with part of the imucal sketch (in post #232)? If so, what should I get from Example2-I2C_Digital_compass, and where should I put it into imucal? Does the answer have to do with what Faraday was saying (in post #233) about nanoTeslas (nT) and Gauss (G)?
I think so. What does MotionCal.exe want from imucal?
The idea is to use code like the following (untested!):
#include <Wire.h>
#include <SparkFun_MMC5983MA_Arduino_Library.h> //Click here to get the library: http://librarymanager/All#SparkFun_MMC5983MA
SFE_MMC5983MA myMag;
void setup()
{
Serial.begin(115200);
Serial.println("MMC5983MA Example");
Wire.begin();
if (myMag.begin() == false)
{
Serial.println("MMC5983MA did not respond - check your wiring. Freezing.");
while (true)
;
}
myMag.softReset();
Serial.println("MMC5983MA connected");
}
void loop()
{
uint32_t rawValueX = 0;
uint32_t rawValueY = 0;
uint32_t rawValueZ = 0;
double scaledX = 0;
double scaledY = 0;
double scaledZ = 0;
double heading = 0;
// Read all three channels simultaneously
myMag.getMeasurementXYZ(&rawValueX, &rawValueY, &rawValueZ);
// The magnetic field values are 18-bit unsigned. The _approximate_ zero (mid) point is 2^17 (131072).
// Here we scale each field to +/- 1.0 to make it easier to calculate an approximate heading.
//
// Please note: to properly correct and calibrate the X, Y and Z channels, you need to determine true
// offsets (zero points) and scale factors (gains) for all three channels. Futher details can be found at:
// https://thecavepearlproject.org/2015/05/22/calibrating-any-compass-or-accelerometer-for-arduino/
scaledX = (double)rawValueX - 131072.0;
scaledX /= 131072.0;
scaledY = (double)rawValueY - 131072.0;
scaledY /= 131072.0;
scaledZ = (double)rawValueZ - 131072.0;
scaledZ /= 131072.0;
Serial.print("Raw:");
Serial.print(0); Serial.print(",");
Serial.print(0); Serial.print(",");
Serial.print(0); Serial.print(",");
Serial.print(0); Serial.print(",");
Serial.print(0); Serial.print(",");
Serial.print(0); Serial.print(",");
Serial.print(int(scaledX * 10)); Serial.print(",");
Serial.print(int(scaledY * 10)); Serial.print(",");
Serial.print(int(scaledZ * 10)); Serial.println("");
delay(10);
}
And then connect MotionCal.exe to the COM port of your Arduino and let MotionCal.exe calculate the calibration parameters. Once they're calculated, you can put those parameters (all 12 of them) into your project code to calibrate your measurements and get an accurate heading.
The nT vs G vs whatever units your sensor works in was just to say you need to be careful about the range of typical values of scaledX, scaledY, and scaledZ
Thank you.
I used the following.
Serial.print(int(scaledX * 10)); Serial.print(",");
All I got was zeros. Like I think Faraday said, multiplying the 10 by one 10 after another makes it better. If we don’t know what number to multiply scaledX by, what range should I try to get in the print?
Probably in the 10,000s range
Thank you.
I multiplied each one by 1000000.
I got MotionCal to plot points, but they didn’t wind up looking like a sphere. Is there a maximum amount of time it takes the points to look like a sphere?
You have to rotate the sensor around in all different directions (while keeping it's location stationary). That will cause it to more or less trace a full sphere
Thank you.
I still can’t get it to look like a sphere. Is my hand enough to keep its location stationary?
