(Solved) Getting ovf returns in a function

Hello everyone, the Forum has changed some since I was last here, so if this in the wrong section, feel free to move it or let me know.

Anyway, I have a strange problem that I can't seem to figure out. I have a function that reads a magnetometer, applies hard and soft iron corrections, and returns a heading. Pretty straightforward stuff. During the development, I wrote the following stand-alone sketch.:

#include <LiquidCrystal_I2C.h>
#include <Adafruit_MMC56x3.h>
#include <Wire.h> 
#include <EEPROMVar.h>                                          //Used for EEPROM access
#include <EEPROMex.h>                                           //Used for EEPROM access
//===================================================================================================================================================================

#define COMPASS_POWER 12
#define SWITCH_PIN_1 A0
#define SWITCH_PIN_2 A1


// Hard-iron calibration settings
const float hard_iron[3] = {EEPROM.readFloat(HARD_IRON_X_OFFSET_ADDRESS),EEPROM.readFloat(HARD_IRON_Y_OFFSET_ADDRESS),EEPROM.readFloat(HARD_IRON_Z_OFFSET_ADDRESS)};

// Soft-iron calibration settings
const float soft_iron[3][3] = {
  {EEPROM.readFloat(SOFT_IRON_MATRIX_VALUE1_ADDRESS), EEPROM.readFloat(SOFT_IRON_MATRIX_VALUE2_ADDRESS), EEPROM.readFloat(SOFT_IRON_MATRIX_VALUE3_ADDRESS)},
  {EEPROM.readFloat(SOFT_IRON_MATRIX_VALUE4_ADDRESS), EEPROM.readFloat(SOFT_IRON_MATRIX_VALUE5_ADDRESS), EEPROM.readFloat(SOFT_IRON_MATRIX_VALUE6_ADDRESS)},
  {EEPROM.readFloat(SOFT_IRON_MATRIX_VALUE7_ADDRESS), EEPROM.readFloat(SOFT_IRON_MATRIX_VALUE8_ADDRESS), EEPROM.readFloat(SOFT_IRON_MATRIX_VALUE9_ADDRESS)},};

const float mag_decl = EEPROM.readFloat(LOCAL_MAGNETIC_DECLINATION_ADDRESS);
unsigned long lcdTimer;


//====================================================================================================================================================================
LiquidCrystal_I2C lcd(0x27,20,4); //0x3f
Adafruit_MMC5603 mag = Adafruit_MMC5603(12345);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup() 
{
lcd.init();                      
lcd.backlight();
Serial.begin(115200);
pinMode(COMPASS_POWER,OUTPUT);
pinMode(SWITCH_PIN_1, INPUT_PULLUP);
pinMode(SWITCH_PIN_2, INPUT_PULLUP);
digitalWrite(COMPASS_POWER,HIGH);
if(mag.begin(MMC56X3_DEFAULT_ADDRESS, &Wire))
{
  lcd.setCursor(0,0);
  lcd.print("MMC5603 Compass chip");
  lcd.setCursor(5,1);
  lcd.print("detected.");
  delay(3000);
  lcd.clear();
}
else
{
  lcd.setCursor(2,0);
  lcd.print("No Compass chip");
  lcd.setCursor(5,1);
  lcd.print("detected.");
  delay(3000);
  lcd.clear();
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////  
void loop() 
{
  if(digitalRead(SWITCH_PIN_1) == LOW) EnterCompassCalibrationData();
  if(digitalRead(SWITCH_PIN_2) == LOW) DisplayData();
  
  static float hi_cal[3];
  static float heading = 0;
  
  // Get new sensor event with readings in uTesla and output for MotionCal
  sensors_event_t event;
  mag.getEvent(&event);
  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(event.magnetic.x*10)); Serial.print(",");
  Serial.print(int(event.magnetic.y*10)); Serial.print(",");
  Serial.print(int(event.magnetic.z*10)); Serial.println("");
  
  // Put raw magnetometer readings into an array
  float mag_data[] = {event.magnetic.x,
                      event.magnetic.y,
                      event.magnetic.z};

  // Apply hard-iron offsets
  for (int i = 0; i < 3; i++  ) {
    hi_cal[i] = mag_data[i] - hard_iron[i];
  }

  // Apply soft-iron scaling
  for (int i = 0; i < 3; i++  ) 
  {
    mag_data[i] = (soft_iron[i][0] * hi_cal[0]) + (soft_iron[i][1] * hi_cal[1]) + (soft_iron[i][2] * hi_cal[2]);
  }

  // Calculate angle for heading, assuming board is parallel to
  // the ground and  Y points toward heading.
  heading = -1 * (atan2(mag_data[0], mag_data[1]) * 180) / PI;

  // Apply magnetic declination to convert magnetic heading
  // to geographic heading
  heading  += mag_decl;

  // Convert heading to 0..360 degrees
  if (heading < 0) heading  += 360;
 
if(millis() - lcdTimer > 500)
{
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Heading: ");
  lcd.setCursor(9,0);
  lcd.print(round(heading));  
  lcdTimer = millis();
}
delay(10);
 }

This works great. I then copied the loop code into a function in the main program as follows:


int GetHeading()
{
  static float heading;
  static float hi_cal[3]; 
 
  sensors_event_t event;
  mag.getEvent(&event);  
  float mag_data[] = {event.magnetic.x, event.magnetic.y, event.magnetic.z};

  
  for (int i = 0; i < 3; i++) {
    hi_cal[i] = mag_data[i] - hard_iron[i];
  }
  Serial.println("Soft Iron values");
  Serial.print(soft_iron[0][0],3);
  Serial.print(", ");
  Serial.print(soft_iron[0][1],3);
  Serial.print(", ");
  Serial.print(soft_iron[0][2],3);
  Serial.print(", ");
  Serial.print(soft_iron[1][0],3);
  Serial.print(", ");
  Serial.print(soft_iron[1][1],3);
  Serial.print(", ");
  Serial.print(soft_iron[1][2],3);
  Serial.print(", ");
  Serial.print(soft_iron[2][0],3);
  Serial.print(", ");
  Serial.print(soft_iron[2][1],3);
  Serial.print(", ");
  Serial.println(soft_iron[2][2],3);
  Serial.println("Hard Iron Corrections");
  Serial.print(mag_data[0],3);
  Serial.print(", ");
  Serial.print(mag_data[1],3);
  Serial.print(", ");
  Serial.println(mag_data[2],3);
 
  
  
 
  for (int i = 0; i < 3; i++  ) 
  {
    mag_data[i] = (soft_iron[i][0] * hi_cal[0]) + (soft_iron[i][1] * hi_cal[1]) + (soft_iron[i][2] * hi_cal[2]);
  }
  Serial.println("Soft Iron Corrections");
  Serial.print(mag_data[0],3);
  Serial.print(", ");
  Serial.print(mag_data[1],3);
  Serial.print(", ");
  Serial.println(mag_data[2],3);
 
  heading = 1 * (atan2(mag_data[0], mag_data[1]) * 180) / PI;  
  heading  += declination;  
  heading += compassCorrection;
  if (heading < 0) heading += 360;
  delay(100);
  return (int)heading);

The corrections are written to the EEPROM in both versions, but the serial monitor shows the following when the function is run:

Soft Iron values
0.982, -0.005, ovf, -0.005, 1.002, -0.004, 0.002, -0.004, 1.016
Hard Iron Corrections
43.219, -31.056, 54.769
Soft Iron Corrections
ovf, ovf, ovf

Sorry for the long post, but I tried to be as clear as I could. Does anyone have a reason for the overflow returns on the soft iron correction?

are the above equivalaent?

gcjr, no they are not. I only have the serial prints for diagnostic reasons anyway. The event.magnetic.x,y.z are the raw readings from the magnetometer, while the mag_data values represent the raw values with the corrections.

Delta_G, I tried that, making the soft iron array a local variable, entering the values directly without involving the EEPROM, but no change. I have the same setup in the stand-alone sketch, and it works fine, The stand alone sketch has everything in the main loop, whereas the function form of it in the larger sketch just gets called as needed. Something isn't getting calculated right in the soft-iron correction part, but I can't seem to figure out what.

sounds like the code is not equivalent

did the corrections cause the overflows?

You were right, the EEPROM was the problem. Defining the correction arrays as global constant floats solved the problem. Now I have to figure out why the data in the EEPROM was getting corrupted. The stand-alone sketch stores the same values in EEPROM, and works perfectly. Something in the other, larger sketch is confounding things.

At least I now have some idea where the problem is, and where to look for a solution. Thank you very much to everyone that replied!

I am concerned about buffer overruns and/or memory collisions as well. Here is the output from compiling the larger program:

Sketch uses 224972 bytes (88%) of program storage space. Maximum is 253952 bytes.
Global variables use 2443 bytes (29%) of dynamic memory, leaving 5749 bytes for local variables. Maximum is 8192 bytes.

I'll follow your advice about creating a stand-alone sketch for the EEPROM.

Edit...do I feel like an idiot! You stare at something so long that you don't even see it anymore.
Here is where I defined the EEPROM addresses:

#define HARD_IRON_X_OFFSET_ADDRESS                    524
#define HARD_IRON_Y_OFFSET_ADDRESS                    528
#define HARD_IRON_Z_OFFSET_ADDRESS                    532
#define SOFT_IRON_MATRIX_VALUE1_ADDRESS               536
#define SOFT_IRON_MATRIX_VALUE2_ADDRESS               538
#define SOFT_IRON_MATRIX_VALUE3_ADDRESS               542
#define SOFT_IRON_MATRIX_VALUE4_ADDRESS               544
#define SOFT_IRON_MATRIX_VALUE5_ADDRESS               548
#define SOFT_IRON_MATRIX_VALUE6_ADDRESS               552
#define SOFT_IRON_MATRIX_VALUE7_ADDRESS               556
#define SOFT_IRON_MATRIX_VALUE8_ADDRESS               560
#define SOFT_IRON_MATRIX_VALUE9_ADDRESS               564
#define MAGNETIC_DECLINATION_ADDRESS                  568

You can plainly see I only allocated 2 bytes to a float value with the 4th line. Duh.
Fixed it, and now everything is fine. I apologize for bothering the board with such a silly mistake.

1 Like

You did it in TWO places...

Yes, I saw.

Sometimes easier to just put all the variables into a single struct and then save/retrieve that from EEPROM.

I'd love to if I knew how. I'll look into it. Structures and unions are things I haven't done yet.

Thank you. I am looking at some information on structures now. It would seem to be a much neater solution than what I am doing. You have been most helpful, and I thank you!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.