LSM303AGR and OLED fluctuating compass

I connected an Adafruit LSM303AGR to a Nano board and uploaded the LIS2MDL>compass sketch from examples in the Arduino IDE. It worked corrrectly and displayed a stable compass reading that changed as I rotated the sensor.

I then added code for a 128x32 OLED and connected the OLED to the I2C pins along with the LSM303AGR. I uploaded and it displays the compass heading on the serial monitor as well as the OLED, except now the compass heading jumps all over the place. Not minor fluctuations, hundreds of degrees.

Any Idea why this is happening?

I want to post my code and I think I should attach it in the proper format. Hopefully I did it right.

LSM303AGRcompass_OLED_test_230212.ino (3.1 KB)

Thanks for your help,
Jim

Sorry I don't have a link to the cheap AliExpress 128x32 OLED

/**************************************************************************
  This is an example for our Monochrome OLEDs based on SSD1306 drivers

  Pick one up today in the adafruit shop!
  ------> http://www.adafruit.com/category/63_98

  This example is for a 128x64 pixel display using I2C to communicate
  3 pins are required to interface (two I2C and one reset).

  Adafruit invests time and resources providing this open
  source code, please support Adafruit and open-source
  hardware by purchasing products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries,
  with contributions from the open source community.
  BSD license, check license.txt for more information
  All text above, and the splash screen below must be
  included in any redistribution.
 **************************************************************************/

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_LIS2MDL.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels

#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

Adafruit_LIS2MDL mag = Adafruit_LIS2MDL(12345);
float cHeading = 0;



void setup() {
  Serial.begin(9600);
  Serial.println("Magnetometer Test"); Serial.println("");

  //*****OLED Display***********
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;); // Don't proceed, loop forever
  }
  delay(2000);
  display.clearDisplay();
  //  Modify these lines to change the splash screen on startup
  display.setTextSize(3);
  display.setTextColor(WHITE);
  display.setCursor(0, 8);
  // Display static text
  display.println("Pegasus");
  display.display();
  delay(2000);
  //*****END OLED Display***********

}
//*****LOOP***********
void loop() {

  /* Get a new sensor event */
  sensors_event_t event;
  mag.getEvent(&event);

  float Pi = 3.14159;

  // Calculate the angle of the vector y,x
  float heading = (atan2(event.magnetic.y, event.magnetic.x) * 180) / Pi;

  // Normalize to 0-360
  if (heading < 0)
  {
    heading = 360 + heading;
  }
  Serial.print("Compass Heading: ");
  Serial.println(heading);
  cHeading = heading;
  Display();
  delay(2000);
}

////*******DISPLAY*****************************
void Display()
{
  //  char buff[10];
  display.clearDisplay();
  display.setTextSize(3);
  display.setTextColor(WHITE);
  display.setCursor(30, 8);
  //  sprintf(buff, "%d" , disp);
  // Display Throttle Setting on OLED
  //  if (disp == 0) {
  //    display.println("IDLE");
  //  } else {
  display.println(cHeading);
  //  }
  //  display.println((char*)disp);
  display.display();
  Serial.println(cHeading);
  //}
}

Please edit your post to add the code in line. Format it with CTRL-T in the Arduino IDE, cut and paste into code tags ("</>" forum editor button).

Also, please post links to the module and display product pages, and a photo of a hand drawn wiring diagram, with pins and connections clearly labeled.

All this is explained in the "How to get the best out of this forum" post.

I don't see anything obviously wrong with the code or the wiring. You might be running out of memory, as the display buffer takes about half of RAM.

Please post the memory report that appears on the IDE screen when compile/link is finished. The display buffer memory is allocated at run time so it won't show up in the report.

Thanks for your reply. Here is the report after compiling:

I hope that's not the problem since this is just a testing sketch. My plan is to add the LSM303AGR code to a considerably larger sketch. I might need a different processor with more memory.

Sketch uses 18536 bytes (60%) of program storage space. Maximum is 30720 bytes.
Global variables use 624 bytes (30%) of dynamic memory, leaving 1424 bytes for local variables. Maximum is 2048 bytes

    Using Port                    : COM8
     Using Programmer              : arduino
     Overriding Baud Rate          : 115200
     AVR Part                      : ATmega328P
     Chip Erase delay              : 9000 us
     PAGEL                         : PD7
     BS2                           : PC2
     RESET disposition             : dedicated
     RETRY pulse                   : SCK
     serial program mode           : yes
     parallel program mode         : yes
     Timeout                       : 200
     StabDelay                     : 100
     CmdexeDelay                   : 25
     SyncLoops                     : 32
     ByteDelay                     : 0
     PollIndex                     : 3
     PollValue                     : 0x53
     Memory Detail                 :

                              Block Poll               Page                       Polled
       Memory Type Mode Delay Size  Indx Paged  Size   Size #Pages MinW  MaxW   ReadBack
       ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
       eeprom        65    20     4    0 no       1024    4      0  3600  3600 0xff 0xff
       flash         65     6   128    0 yes     32768  128    256  4500  4500 0xff 0xff
       lfuse          0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
       hfuse          0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
       efuse          0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
       lock           0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
       calibration    0     0     0    0 no          1    0      0     0     0 0x00 0x00
       signature      0     0     0    0 no          3    0      0     0     0 0x00 0x00

     Programmer Type : Arduino
     Description     : Arduino
     Hardware Version: 3
     Firmware Version: 4.4
     Vtarget         : 0.3 V
     Varef           : 0.3 V
     Oscillator      : 28.800 kHz
     SCK period      : 3.3 us

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.00s

avrdude: Device signature = 0x1e950f (probably m328p)
avrdude: reading input file "C:\Users\jspri\AppData\Local\Temp\arduino_build_343087/LSM303AGRcompass_OLED_test_230212.ino.hex"
avrdude: writing flash (18536 bytes):

Writing | ################################################## | 100% 2.91s

avrdude: 18536 bytes of flash written
avrdude: verifying flash memory against C:\Users\jspri\AppData\Local\Temp\arduino_build_343087/LSM303AGRcompass_OLED_test_230212.ino.hex:
avrdude: load data flash data from input file C:\Users\jspri\AppData\Local\Temp\arduino_build_343087/LSM303AGRcompass_OLED_test_230212.ino.hex:
avrdude: input file C:\Users\jspri\AppData\Local\Temp\arduino_build_343087/LSM303AGRcompass_OLED_test_230212.ino.hex contains 18536 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 2.28s

avrdude: verifying ...
avrdude: 18536 bytes of flash verified

What I'm looking for is the IDE report, like this one (in this case, for the ATtiny85):

Sketch uses 314 bytes (3%) of program storage space. Maximum is 8192 bytes.
Global variables use 9 bytes (1%) of dynamic memory, leaving 503 bytes for local variables. Maximum is 512 bytes.

Sorry, I sent too much. Here is my report:

Sketch uses 18536 bytes (60%) of program storage space. Maximum is 30720 bytes.
Global variables use 624 bytes (30%) of dynamic memory, leaving 1424 bytes for local variables. Maximum is 2048 bytes

I don't see an obvious problem with memory.

OK, Thanks for looking at it.

Do the heading values shown on the serial monitor agree with the heading values shown on the display? That is, do both fluctuate wildly?

Yes, they are the same. I've been trying a couple other sketches from Examples in Arduino.

My code is based on a few added lines of code to the compass sketch below. When I upload that sketch it seems pretty stable but since there isn't any code for the OLED it doesn't display on the OLED screen.

Examples > Adafruit LIS2MDL >compass

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_LIS2MDL.h>


Adafruit_LIS2MDL mag = Adafruit_LIS2MDL(12345);

void setup(void)
{
  Serial.begin(9600);
  Serial.println("Magnetometer Test"); Serial.println("");

  /* Initialise the sensor */
  if(!mag.begin())
  {
    /* There was a problem detecting the LIS2MDL ... check your connections */
    Serial.println("Ooops, no LIS2MDL detected ... Check your wiring!");
    while(1);
  }
}

void loop(void)
{
  /* Get a new sensor event */
  sensors_event_t event;
  mag.getEvent(&event);

  float Pi = 3.14159;

  // Calculate the angle of the vector y,x
  float heading = (atan2(event.magnetic.y,event.magnetic.x) * 180) / Pi;

  // Normalize to 0-360
  if (heading < 0)
  {
    heading = 360 + heading;
  }
  Serial.print("Compass Heading: ");
  Serial.println(heading);
  delay(500);
}

This one below seems to work pretty well. It displays 3 numbers for the Accelerator and 3 numbers for the Accelerometer. Those numbers fluctuate a little, about 0.1 to 0.2 for the Accelerometer and about 1 to 2 for the Magnetometer. From what I understand the compass sketch takes those numbers and calculates the compass heading. Based on that minor? fluctuation I'm guessing it's fairly stable.

Examples > Adafruit LIS2MDL > LSM303AGR_oled

#include <Adafruit_SSD1306.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_LIS2MDL.h>
#include <Adafruit_LSM303_Accel.h>

Adafruit_LIS2MDL mag = Adafruit_LIS2MDL(12345);
Adafruit_LSM303_Accel_Unified accel = Adafruit_LSM303_Accel_Unified(54321);

Adafruit_SSD1306 display = Adafruit_SSD1306(128, 32, &Wire);

void setup() {
  Serial.begin(115200);
  //while (!Serial);
  Serial.println("LIS2MDL OLED demo");

  if(!mag.begin())
  {
    /* There was a problem detecting the LIS2MDL ... check your connections */
    Serial.println("Ooops, no LIS2MDL detected ... Check your wiring!");
    while(1);
  }
  if (!accel.begin()) {
    /* There was a problem detecting the LSM303 ... check your connections */
    Serial.println("Ooops, no LSM303 Accelerometer detected ... Check your wiring!");
    while (1)
      ;
  }
  
  Serial.println("Found LSM303AGR sensor");

  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }
  display.display();
  delay(500); // Pause for 2 seconds
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setRotation(0);
}


void loop() {
  sensors_event_t a, m;
  accel.getEvent(&a);
  mag.getEvent(&m);

  display.clearDisplay();
  display.setCursor(0,0);
  
  Serial.print("Accelerometer ");
  Serial.print("X: "); Serial.print(a.acceleration.x, 1); Serial.print(" m/s^2, ");
  Serial.print("Y: "); Serial.print(a.acceleration.y, 1); Serial.print(" m/s^2, ");
  Serial.print("Z: "); Serial.print(a.acceleration.z, 1); Serial.println(" m/s^2");

  display.println("Accelerometer - m/s^2");
  display.print(a.acceleration.x, 1); display.print(", ");
  display.print(a.acceleration.y, 1); display.print(", ");
  display.print(a.acceleration.z, 1); display.println("");

  Serial.print("Magnetometer ");
  Serial.print("X: "); Serial.print(m.magnetic.x, 1); Serial.print(" uT, ");
  Serial.print("Y: "); Serial.print(m.magnetic.y, 1); Serial.print(" uT, ");
  Serial.print("Z: "); Serial.print(m.magnetic.z, 1); Serial.println(" uT");

  display.println("Magnetometer - uT");
  display.print(m.magnetic.x, 1); display.print(", ");
  display.print(m.magnetic.y, 1); display.print(", ");
  display.print(m.magnetic.z, 1); display.println("");
  
  display.display();
  delay(100);

I really have no idea why that is happening. Since you are using Adafruit libraries, post on the Adafruit forum. Maybe someone there has seen this before.

Consider redoing all the connections. Maybe one is intermittent.

Thanks, I will

I think I have it working. I added the following code from the compass Example sketch. It seems odd since this code doesn't seem to do much.

  /* Initialise the sensor */
  if(!mag.begin())
  {
    /* There was a problem detecting the LIS2MDL ... check your connections */
    Serial.println("Ooops, no LIS2MDL detected ... Check your wiring!");
    while(1);
  }

Glad you found the solution! However, it is usually required to call the .begin method, if the library offers it.

Yes, I agree, that makes sense. But does that code snippet call begin or does it just check if it has begun?

This

if(!mag.begin()) {

calls the begin() method and tests the logically inverted value of the return parameter, which reports true or false for successful execution.

So, it is pretty important to do that.

OK, thanks for helping me understand that. I appreciate you taking the time to help.

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