Mini Attitude Indicator

I have hooked up a .96" mini display accessing it through the Adafruit_SSD1306.h library and an MPU-650 3 axis Gyro to a UNO to give me a mini Attitude indicator (attached). They are both wired into A5/A4 pins of an arduino UNO and the ground/5v.

The three troubles:
The Y (pitch) indicator takes about 1 minute to settle on startup and is quick to respond afterwards but slow to come back to original whereas X (the the bank indicator) is much more responsive.

If shaken up too quickly the display sometimes goes a bit crazy and the program crashes. This has happened with the screen in other applications when I ran out of memory on the board.

The 3axis unit occasionally stops changing x/y/z positions as if it's just given up.

Questions:

  • After compiling the program it is at 79% program storage. Could the chip be doing other things in the base system that is causing the memory to run out? Should I find a board with a chip that has more memory?
  • Can I even hook 2 analog boards with different ID #s up in serial?
  • Can the display, the UNO and the MPU-630 handle the workload or am I expecting too much from this trio?

IMG_9305.JPG

Please post your sketch inline with code tags, not as an attachment.

Edit - also please post the correct sketch. You posted a DHT11 sketch.

Generally, displays like that allocate a buffer for mirroring what's going to be sent to the display. That's likely what's eating your memory. Hard to say though because the code doesn't mention the display or the library.

The good news is though that there isn't going to be any more RAM allocated as you use the display, so you may be able to keep your utilization within the Uno's limits.

The sketch you posted has nothing to do with the problem.

With the MPU-6050, yaw will always drift. The combination of the display code and the MPU-6050 code will take up most of the memory, and if you use String objects, the memory problems with Strings will cause it to crash.

jremington:
The sketch you posted has nothing to do with the problem.

With the MPU-6050, yaw will always drift. The combination of the display code and the MPU-6050 code will take up most of the memory, and if you use String objects, the memory problems with Strings will cause it to crash.

You're right, Ill try and post the correct one within a code block here. It has a library in it I created which is used to find the intersections between the horizon line and points on the circle:

#include <DQCircular.h>

#include <SPI.h>
#include <Wire.h>
#include <MPU6050_light.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

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

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
MPU6050 mpu(Wire);

float basex;
float basey;
int samples = 0;
/*
   Attitude Indicator on supper small 128/64 screen
*/
void setup() {
  Serial.begin(115200);

  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for (;;); // Don't proceed, loop forever
  }
  byte status = mpu.begin();
  while (status != 0) { } // stop everything if could not connect to MPU6050
  mpu.calcOffsets(true, true); // gyro and accelero

  // Show initial display buffer contents on the screen --
  // the library initializes this with an Adafruit splash screen.
  display.display();
  delay(250); // Pause for 2 seconds

  // Clear the buffer
  display.clearDisplay();
  display.display();
  delay(200);
  // Let user know wer are initializing variables
  display.clearDisplay();
  display.setTextSize(1); // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(10, 0);
  display.print(F("Initializing"));
  display.setCursor(10, 20);
  display.print(F("Unit"));
  display.display();      // Show initial text
  delay(100);
  display.startscrolldiagright(0x00, 0x07);
  bool changes = true;
  double startup = millis();
  while (changes && millis() - startup  < (30000))
  {
    mpu.update();
    basex += mpu.getAngleX();
    basey += mpu.getAngleY();
    samples++;
    delay(100);
  }
  basex = basex / samples;
  basey = basey / samples;
  delay(2000);
  display.stopscroll();

  show(mpu.getAngleX() - basex, mpu.getAngleY() - basey);
  delay(1000);
}

void loop() {
  mpu.update();
  //Serial.print("X: "); Serial.println(mpu.getAngleX());
  //Serial.print("Y: "); Serial.println(mpu.getAngleY());
  show(mpu.getAngleX() - basex, mpu.getAngleY() - basey);
  delay(500);
}


void show(int deg, int attitude) {
  // fill in all the circle to start
  display.fillCircle(display.height() / 2, display.height() / 2, (int) display.height() / 2, SSD1306_WHITE);
  Intercepts ipt = Intercepts(display.height() / 2, deg, attitude);

  // find out if the left or right is the highest point (we will be blackening up to here with a rectangle);
  Point rtop = ipt.right;
  Point rbot = ipt.left;
  if (ipt.left.Y > rtop.Y)
  {
    rtop = ipt.left;
    rbot = ipt.right;
  }
  Point rtopfixed = ipt.refix(rtop, display.height() , display.height() );
  Point rbotfixed = ipt.refix(rbot, display.height() , display.height() );

  display.fillRect(0, 0, display.height() , rtopfixed.Y, SSD1306_BLACK);

  // if the highest point also has the highest x value then we draw a triangle from that point down to the x intercept and back up to 0,0
  Point tri_pt[3];
  if (ipt.left.Y > ipt.right.Y)
  {
    tri_pt[0] = ipt.refix(ipt.left, display.height() , display.height() );
    Point fig1 = ipt.refix(ipt.right, display.height() , display.height());
    float m = (tri_pt[0].Y - fig1.Y) / (tri_pt[0].X - fig1.X);
    float b = fig1.Y - fig1.X * m;
    tri_pt[1] = Point(display.height(), display.height() * m + b);
    tri_pt[2] = Point(display.height() , tri_pt[0].Y);
  }
  else
  {
    // our triangle goes from right point to x intercept to (0, right.Y)
    ipt.left.prt();
    ipt.right.prt();
    //ipt->refix(ipt->left, display.height(), display.height())->prt();
    //ipt->refix(ipt->right, display.height(), display.height())->prt();
    tri_pt[0] = ipt.refix(ipt.right, display.height() , display.height() );
    Point fig1 = ipt.refix(ipt.left, display.height() , display.height() );
    float m = (tri_pt[0].Y - fig1.Y) / (tri_pt[0].X - fig1.X);
    float b = fig1.Y - fig1.X * m;
    tri_pt[1] = Point(0, b);
    tri_pt[2] = Point(0, tri_pt[0].Y);
  }

  display.fillTriangle(tri_pt[0].X, tri_pt[0].Y, tri_pt[1].X, tri_pt[1].Y, tri_pt[2].X, tri_pt[2].Y, SSD1306_BLACK);
  display.drawCircle(display.height() / 2, display.height() / 2, (int) display.height() / 2, SSD1306_WHITE);
  // clear text area for our angle display and then display it
  display.fillRect(display.height() + 1, 0, (display.width() - (display.height() + 1)), display.height(), SSD1306_BLACK);
  //display.display();
  display.setCursor(display.height() * 1.1, display.height() / 6);
  display.print("Roll: ");
  display.print(deg);
  display.setCursor(display.height() * 1.1, display.height() / 6 * 5);
  display.print("Pt: ");
  display.print(0 - attitude);
  display.println("");

  int midbarwidth = display.height() * .65;
  display.fillRect(display.height() / 2 - midbarwidth / 2, display.height() / 2 - 1, midbarwidth, 2, SSD1306_INVERSE);
  const float w_mul[] = {15, 55, 15, 55};
  for (int ii = 0; ii < 3; ii++)
  {
    // print a + and - line at 5 degree pitch intervals
    Intercepts pos = Intercepts(display.height() / 2, 0, (ii + 1) * 5);
    Intercepts neg = Intercepts(display.height() / 2, 0, (ii + 1) * -5);
    midbarwidth = display.height() * w_mul[ii] / 100;
    Point posleft = Point(display.height() / 2 - midbarwidth / 2, pos.refix(pos.left, display.height(), display.height()).Y);
    Point posright = Point(display.height() / 2 + midbarwidth / 2, pos.refix(pos.right, display.height(), display.height()).Y);
    Point negleft = Point(display.height() / 2 - midbarwidth / 2, neg.refix(neg.left, display.height(), display.height()).Y);
    Point negright = Point(display.height() / 2 + midbarwidth / 2, neg.refix(neg.right, display.height(), display.height()).Y);
    display.drawLine(posleft.X, posleft.Y, posright.X, posright.Y, SSD1306_INVERSE);
    display.drawLine(negleft.X, negleft.Y, negright.X, negright.Y, SSD1306_INVERSE);
  }
  display.display();
}

jremington:
The sketch you posted has nothing to do with the problem.

With the MPU-6050, yaw will always drift. The combination of the display code and the MPU-6050 code will take up most of the memory, and if you use String objects, the memory problems with Strings will cause it to crash.

I suppose it depends on how it's mounted but I am not handling yaw (which is my Z axis), but the Y axis is pretty much completely unreliable.

And when using a breadboard and when shaking the breadboard and when expecting the breadboard connections to remain good during the shaking. Well don't. Breadboard connections are not very good when the breadboard is sitting still, expecting the connections to remain reliable during movement is a bit of a wild stretch.


When I used MPU6050's or MPU9250's I bought 3 to find one good one.

If you want to use the MPU-6050 to measure pitch and roll for relatively stable orientations, there is no need to use the gyro.

This is all you need. Any axis can be defined as vertical, but if not Z, then of course the code will need to be changed accordingly.

// minimal MPU-6050 tilt and roll (sjr)
// works perfectly with GY-521, pitch and roll signs agree with arrows on sensor module 7/2019
// tested with eBay Pro Mini, **no external pullups on SDA and SCL** (works with internal pullups!)
// A4 = SDA, A5 = SCL

#include<Wire.h>
const int MPU_addr1 = 0x68;
float xa, ya, za, roll, pitch;

void setup() {

  Wire.begin();                                      //begin the wire communication
  Wire.beginTransmission(MPU_addr1);                 //begin, send the slave adress (in this case 68)
  Wire.write(0x6B);                                  //make the reset (place a 0 into the 6B register)
  Wire.write(0);
  Wire.endTransmission(true);                        //end the transmission
  Serial.begin(9600);
}

void loop() {

  Wire.beginTransmission(MPU_addr1);
  Wire.write(0x3B);  //send starting register address, accelerometer high byte
  Wire.endTransmission(false); //restart for read
  Wire.requestFrom(MPU_addr1, 6, true); //get six bytes accelerometer data
  int t = Wire.read();
  xa = (t << 8) | Wire.read();
  t = Wire.read();
  ya = (t << 8) | Wire.read();
  t = Wire.read();
  za = (t << 8) | Wire.read();
// formula from https://wiki.dfrobot.com/How_to_Use_a_Three-Axis_Accelerometer_for_Tilt_Sensing
  roll = atan2(ya , za) * 180.0 / PI;
  pitch = atan2(-xa , sqrt(ya * ya + za * za)) * 180.0 / PI; //account for roll already applied

  Serial.print("roll = ");
  Serial.print(roll,1);
  Serial.print(", pitch = ");
  Serial.println(pitch,1);
  delay(400);
}

jremington:
If you want to use the MPU-6050 to measure pitch and roll for relatively stable orientations, there is no need to use the gyro.

This is all you need. Any axis can be defined as vertical, but if not Z, then of course the code will need to be changed accordingly.

// minimal MPU-6050 tilt and roll (sjr)

// works perfectly with GY-521, pitch and roll signs agree with arrows on sensor module 7/2019
// tested with eBay Pro Mini, no external pullups on SDA and SCL (works with internal pullups!)
// A4 = SDA, A5 = SCL

#include<Wire.h>
const int MPU_addr1 = 0x68;
float xa, ya, za, roll, pitch;

void setup() {

Wire.begin();                                      //begin the wire communication
 Wire.beginTransmission(MPU_addr1);                 //begin, send the slave adress (in this case 68)
 Wire.write(0x6B);                                  //make the reset (place a 0 into the 6B register)
 Wire.write(0);
 Wire.endTransmission(true);                        //end the transmission
 Serial.begin(9600);
}

void loop() {

Wire.beginTransmission(MPU_addr1);
 Wire.write(0x3B);  //send starting register address, accelerometer high byte
 Wire.endTransmission(false); //restart for read
 Wire.requestFrom(MPU_addr1, 6, true); //get six bytes accelerometer data
 int t = Wire.read();
 xa = (t << 8) | Wire.read();
 t = Wire.read();
 ya = (t << 8) | Wire.read();
 t = Wire.read();
 za = (t << 8) | Wire.read();
// formula from How_to_Use_a_Three-Axis_Accelerometer_for_Tilt_Sensing-DFRobot
 roll = atan2(ya , za) * 180.0 / PI;
 pitch = atan2(-xa , sqrt(ya * ya + za * za)) * 180.0 / PI; //account for roll already applied

Serial.print("roll = ");
 Serial.print(roll,1);
 Serial.print(", pitch = ");
 Serial.println(pitch,1);
 delay(400);
}

I like this code better; it seems more raw. I'll go back and tweak it to see if it solves the slow responses I am getting for my Y without increasing the frequency of the other problems.

Motion will introduce errors into the pitch and roll angles, but they will instantly self-correct when the sensor is held still.

For increased accuracy, calibrate the accelerometer so that all axes are on the same scale, with zero offset. Tutorial for a different accelerometer.

Cute attitude display!

jremington:
Motion will introduce errors into the pitch and roll angles, but they will instantly self-correct when the sensor is held still.

Cute attitude display!

Maybe I ended up with a bad one, I'll see if I can pick another brand up from somewhere.
Thanks for the compliment, it's hard as heck to get a good photo of it but this one turned out decent.

Idahowalker:
And when using a breadboard and when shaking the breadboard and when expecting the breadboard connections to remain good during the shaking. Well don't. Breadboard connections are not very good when the breadboard is sitting still, expecting the connections to remain reliable during movement is a bit of a wild stretch.


When I used MPU6050's or MPU9250's I bought 3 to find one good one.

I'm not shaking it hard enough to mess up the connections as consistently as they do get messed up. Were they three different brands you bought and if so, do you remember the one you ended up satisfied with?

I just bought three on the same order of the same IMU each time I ordered a new thing.