Alok Yadav
Published

ORBIT-Q: MPU6050 on STM32

Orbit-Q STM32F4 project using a Kalman filter to smooth MPU6050 sensor readings and display accelerometer and gyroscope data on an OLED.

ORBIT-Q: MPU6050 on STM32

Things used in this project

Hardware components

ORBIT-Q
ORBIT-Q
×1
Jumper wires (generic)
Jumper wires (generic)
×1
MPU6050(GY-521) Sensor Module
×1

Software apps and online services

Arduino IDE
Arduino IDE
STM32CUBEPROG
STMicroelectronics STM32CUBEPROG
Adafruit SSD1306
Adafruit GFX Library
Wire/TwoWire

Hand tools and fabrication machines

ST-Link debugger

Story

Read more

Schematics

Circuit Diagram

Code

Using a Kalman Filter on MPU6050

C/C++
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH  128
#define SCREEN_HEIGHT 32
#define OLED_RESET    -1
#define OLED_ADDRESS  0x3C

#define MPU_ADDR      0x68
#define WHO_AM_I      0x75
#define PWR_MGMT_1    0x6B
#define ACCEL_CONFIG  0x1C
#define GYRO_CONFIG   0x1B
#define ACCEL_XOUT_H  0x3B

#define STATUS_LED    PC13

TwoWire Wire2(PB11, PB10);
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

bool ledState = false;

// Independent 1D Kalman filter for smoothing each sensor axis.
class Kalman1D {
public:
  Kalman1D(float processNoise, float measurementNoise)
    : q(processNoise), r(measurementNoise),
      estimate(0.0f), estimateError(1.0f), initialized(false) {}

  float update(float measurement) {
    if (!initialized) {
      estimate = measurement;
      initialized = true;
      return estimate;
    }
    estimateError += q;
    float kalmanGain = estimateError / (estimateError + r);
    estimate += kalmanGain * (measurement - estimate);
    estimateError *= (1.0f - kalmanGain);
    return estimate;
  }

private:
  float q;
  float r;
  float estimate;
  float estimateError;
  bool initialized;
};

// Starting tuning values; each axis has its own filter.
Kalman1D filterAx(0.08f, 1.0f);
Kalman1D filterAy(0.08f, 1.0f);
Kalman1D filterAz(0.08f, 1.0f);
Kalman1D filterGx(0.5f, 4.0f);
Kalman1D filterGy(0.5f, 4.0f);
Kalman1D filterGz(0.5f, 4.0f);

bool writeRegister(uint8_t reg, uint8_t value) {
  Wire2.beginTransmission(MPU_ADDR);
  Wire2.write(reg);
  Wire2.write(value);
  return Wire2.endTransmission() == 0;
}

bool readRegisters(uint8_t reg, uint8_t *data, uint8_t length) {
  Wire2.beginTransmission(MPU_ADDR);
  Wire2.write(reg);
  if (Wire2.endTransmission(false) != 0) return false;

  uint8_t received = Wire2.requestFrom(MPU_ADDR, length);
  if (received != length) {
    while (Wire2.available()) Wire2.read();
    return false;
  }
  for (uint8_t i = 0; i < length; i++) data[i] = Wire2.read();
  return true;
}

int16_t readInt16(uint8_t highByte, uint8_t lowByte) {
  return (int16_t)((uint16_t)highByte << 8 | lowByte);
}

void showError(const char *message) {
  display.clearDisplay();
  display.setCursor(0, 0);
  display.println("MPU-6050 ERROR");
  display.println(message);
  display.display();
}

void setup() {
  Serial.begin(115200);
  pinMode(STATUS_LED, OUTPUT);
  digitalWrite(STATUS_LED, HIGH); // Active-low LED OFF

  Wire.setSCL(PB6);
  Wire.setSDA(PB7);
  Wire.begin();

  display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS);
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.println("Starting MPU-6050");
  display.display();

  Wire2.begin();
  Wire2.setClock(100000);

  uint8_t identity;
  if (!readRegisters(WHO_AM_I, &identity, 1) || identity != 0x68) {
    showError("Check sensor/I2C");
    while (true) delay(500);
  }
  if (!writeRegister(PWR_MGMT_1, 0x00)) {
    showError("Wake-up failed");
    while (true) delay(500);
  }

  writeRegister(ACCEL_CONFIG, 0x00); // +/- 2g
  writeRegister(GYRO_CONFIG, 0x00);  // +/- 250 degrees/second
  delay(100);
}

void loop() {
  uint8_t data[14];
  if (!readRegisters(ACCEL_XOUT_H, data, 14)) {
    showError("I2C read failed");
    delay(500);
    return;
  }

  int16_t rawAx = readInt16(data[0], data[1]);
  int16_t rawAy = readInt16(data[2], data[3]);
  int16_t rawAz = readInt16(data[4], data[5]);
  int16_t rawGx = readInt16(data[8], data[9]);
  int16_t rawGy = readInt16(data[10], data[11]);
  int16_t rawGz = readInt16(data[12], data[13]);

  float ax = rawAx / 16384.0f * 9.80665f;
  float ay = rawAy / 16384.0f * 9.80665f;
  float az = rawAz / 16384.0f * 9.80665f;
  float gx = rawGx / 131.0f;
  float gy = rawGy / 131.0f;
  float gz = rawGz / 131.0f;

  // Apply Kalman filters only; preserve output names, units and format.
  ax = filterAx.update(ax);
  ay = filterAy.update(ay);
  az = filterAz.update(az);
  gx = filterGx.update(gx);
  gy = filterGy.update(gy);
  gz = filterGz.update(gz);

  display.clearDisplay();
  display.setCursor(0, 0);
  display.print("Ax:"); display.print(ax, 1);
  display.print(" Ay:"); display.println(ay, 1);
  display.print("Az:"); display.println(az, 1);
  display.print("Gx:"); display.print(gx, 1);
  display.print(" Gy:"); display.println(gy, 1);
  display.print("Gz:"); display.println(gz, 1);
  display.display();

  ledState = !ledState;
  digitalWrite(STATUS_LED, ledState ? LOW : HIGH);

  Serial.print("Accel m/s2: ");
  Serial.print(ax); Serial.print(", ");
  Serial.print(ay); Serial.print(", ");
  Serial.println(az);

  Serial.print("Gyro deg/s: ");
  Serial.print(gx); Serial.print(", ");
  Serial.print(gy); Serial.print(", ");
  Serial.println(gz);

  delay(200);
}

Credits

Alok Yadav
1 project • 1 follower
Thanks to Siddhant Gupta .

Comments