#include "Wire.h"
#ifndef MPU6050_LIGHT_H
#define MPU6050_LIGHT_H
#include "Arduino.h"
#include "Wire.h"
#define MPU6050_ADDR 0x68
#define MPU6050_SMPLRT_DIV_REGISTER 0x19
#define MPU6050_CONFIG_REGISTER 0x1a
#define MPU6050_GYRO_CONFIG_REGISTER 0x1b
#define MPU6050_ACCEL_CONFIG_REGISTER 0x1c
#define MPU6050_PWR_MGMT_1_REGISTER 0x6b
#define MPU6050_GYRO_OUT_REGISTER 0x43
#define MPU6050_ACCEL_OUT_REGISTER 0x3B
#define RAD_2_DEG 57.29578 // [deg/rad]
#define CALIB_OFFSET_NB_MES 500
#define TEMP_LSB_2_DEGREE 340.0 // [bit/celsius]
#define TEMP_LSB_OFFSET 12412.0
#define DEFAULT_GYRO_COEFF 0.98
class MPU6050{
public:
// INIT and BASIC FUNCTIONS
MPU6050(TwoWire &w);
byte begin(int gyro_config_num=1, int acc_config_num=0);
byte writeData(byte reg, byte data);
byte readData(byte reg);
void calcOffsets(bool is_calc_gyro=true, bool is_calc_acc=true);
void calcGyroOffsets(){ calcOffsets(true,false); }; // retro-compatibility with v1.0.0
void calcAccOffsets(){ calcOffsets(false,true); }; // retro-compatibility with v1.0.0
void setAddress(uint8_t addr){ address = addr; };
uint8_t getAddress(){ return address; };
// MPU CONFIG SETTER
byte setGyroConfig(int config_num);
byte setAccConfig(int config_num);
void setGyroOffsets(float x, float y, float z);
void setAccOffsets(float x, float y, float z);
void setFilterGyroCoef(float gyro_coeff);
void setFilterAccCoef(float acc_coeff);
// MPU CONFIG GETTER
float getGyroXoffset(){ return gyroXoffset; };
float getGyroYoffset(){ return gyroYoffset; };
float getGyroZoffset(){ return gyroZoffset; };
float getAccXoffset(){ return accXoffset; };
float getAccYoffset(){ return accYoffset; };
float getAccZoffset(){ return accZoffset; };
float getFilterGyroCoef(){ return filterGyroCoef; };
float getFilterAccCoef(){ return 1.0-filterGyroCoef; };
// DATA GETTER
float getTemp(){ return temp; };
float getAccX(){ return accX; };
float getAccY(){ return accY; };
float getAccZ(){ return accZ; };
float getGyroX(){ return gyroX; };
float getGyroY(){ return gyroY; };
float getGyroZ(){ return gyroZ; };
float getAccAngleX(){ return angleAccX; };
float getAccAngleY(){ return angleAccY; };
float getAngleX(){ return angleX; };
float getAngleY(){ return angleY; };
float getAngleZ(){ return angleZ; };
// INLOOP UPDATE
void fetchData(); // user should better call 'update' that includes 'fetchData'
void update();
// UPSIDE DOWN MOUNTING
bool upsideDownMounting = false;
private:
TwoWire *wire;
uint8_t address = MPU6050_ADDR; // 0x68 or 0x69
float gyro_lsb_to_degsec, acc_lsb_to_g;
float gyroXoffset, gyroYoffset, gyroZoffset;
float accXoffset, accYoffset, accZoffset;
float temp, accX, accY, accZ, gyroX, gyroY, gyroZ;
float angleAccX, angleAccY;
float angleX, angleY, angleZ;
unsigned long preInterval;
float filterGyroCoef; // complementary filter coefficient to balance gyro vs accelero data to get angle
};
#endif
float rpy[3];
MPU6050 mpu(Wire);
unsigned long timer = 0;
void setup() {
Serial.begin(9600);
Wire.begin();
byte status = mpu.begin();
Serial.print(F("MPU6050 status: "));
Serial.println(status);
while(status!=0){
Serial.println("IMU'ya baglanamadim.");
} // stop everything if could not connect to MPU6050
Serial.println(F("Calculating offsets, do not move MPU6050"));//Calibration
delay(1000);
mpu.calcOffsets(); // gyro and accelero
Serial.println("Done!\n");
}
void loop() {
mpu.update();
IMUupdate();
Serial.print(rpy[0]);
Serial.print(" - ");
Serial.print(rpy[1]);
Serial.print(" - ");
Serial.print(rpy[2]);
Serial.println("");
}
void IMUupdate(){
mpu.update();
rpy[0] = mpu.getAngleX();
rpy[1] = mpu.getAngleY();
rpy[2] = mpu.getAngleZ();
};
#include "Arduino.h"
/* Wrap an angle in the range [-limit,+limit] (special thanks to Edgar Bonet!) */
static float wrap(float angle,float limit){
while (angle > limit) angle -= 2*limit;
while (angle < -limit) angle += 2*limit;
return angle;
}
/* INIT and BASIC FUNCTIONS */
MPU6050::MPU6050(TwoWire &w){
wire = &w;
setFilterGyroCoef(DEFAULT_GYRO_COEFF);
setGyroOffsets(0,0,0);
setAccOffsets(0,0,0);
}
byte MPU6050::begin(int gyro_config_num, int acc_config_num){
// changed calling register sequence [https://github.com/rfetick/MPU6050_light/issues/1] -> thanks to augustosc
byte status = writeData(MPU6050_PWR_MGMT_1_REGISTER, 0x01); // check only the first connection with status
writeData(MPU6050_SMPLRT_DIV_REGISTER, 0x00);
writeData(MPU6050_CONFIG_REGISTER, 0x00);
setGyroConfig(gyro_config_num);
setAccConfig(acc_config_num);
this->update();
angleX = this->getAccAngleX();
angleY = this->getAccAngleY();
preInterval = millis(); // may cause lack of angular accuracy if begin() is much before the first update()
return status;
}
byte MPU6050::writeData(byte reg, byte data){
wire->beginTransmission(address);
wire->write(reg);
wire->write(data);
byte status = wire->endTransmission();
return status; // 0 if success
}
// This method is not used internaly, maybe by user...
byte MPU6050::readData(byte reg) {
wire->beginTransmission(address);
wire->write(reg);
wire->endTransmission(true);
wire->requestFrom(address,(uint8_t) 1);
byte data = wire->read();
return data;
}
/* SETTER */
byte MPU6050::setGyroConfig(int config_num){
byte status;
switch(config_num){
case 0: // range = +- 250 deg/s
gyro_lsb_to_degsec = 131.0;
status = writeData(MPU6050_GYRO_CONFIG_REGISTER, 0x00);
break;
case 1: // range = +- 500 deg/s
gyro_lsb_to_degsec = 65.5;
status = writeData(MPU6050_GYRO_CONFIG_REGISTER, 0x08);
break;
case 2: // range = +- 1000 deg/s
gyro_lsb_to_degsec = 32.8;
status = writeData(MPU6050_GYRO_CONFIG_REGISTER, 0x10);
break;
case 3: // range = +- 2000 deg/s
gyro_lsb_to_degsec = 16.4;
status = writeData(MPU6050_GYRO_CONFIG_REGISTER, 0x18);
break;
default: // error
status = 1;
break;
}
return status;
}
byte MPU6050::setAccConfig(int config_num){
byte status;
switch(config_num){
case 0: // range = +- 2 g
acc_lsb_to_g = 16384.0;
status = writeData(MPU6050_ACCEL_CONFIG_REGISTER, 0x00);
break;
case 1: // range = +- 4 g
acc_lsb_to_g = 8192.0;
status = writeData(MPU6050_ACCEL_CONFIG_REGISTER, 0x08);
break;
case 2: // range = +- 8 g
acc_lsb_to_g = 4096.0;
status = writeData(MPU6050_ACCEL_CONFIG_REGISTER, 0x10);
break;
case 3: // range = +- 16 g
acc_lsb_to_g = 2048.0;
status = writeData(MPU6050_ACCEL_CONFIG_REGISTER, 0x18);
break;
default: // error
status = 1;
break;
}
return status;
}
void MPU6050::setGyroOffsets(float x, float y, float z){
gyroXoffset = x;
gyroYoffset = y;
gyroZoffset = z;
}
void MPU6050::setAccOffsets(float x, float y, float z){
accXoffset = x;
accYoffset = y;
accZoffset = z;
}
void MPU6050::setFilterGyroCoef(float gyro_coeff){
if ((gyro_coeff<0) or (gyro_coeff>1)){ gyro_coeff = DEFAULT_GYRO_COEFF; } // prevent bad gyro coeff, should throw an error...
filterGyroCoef = gyro_coeff;
}
void MPU6050::setFilterAccCoef(float acc_coeff){
setFilterGyroCoef(1.0-acc_coeff);
}
/* CALC OFFSET */
void MPU6050::calcOffsets(bool is_calc_gyro, bool is_calc_acc){
if(is_calc_gyro){ setGyroOffsets(0,0,0); }
if(is_calc_acc){ setAccOffsets(0,0,0); }
float ag[6] = {0,0,0,0,0,0}; // 3*acc, 3*gyro
for(int i = 0; i < CALIB_OFFSET_NB_MES; i++){
this->fetchData();
ag[0] += accX;
ag[1] += accY;
ag[2] += (accZ-1.0);
ag[3] += gyroX;
ag[4] += gyroY;
ag[5] += gyroZ;
delay(1); // wait a little bit between 2 measurements
}
if(is_calc_acc){
accXoffset = ag[0] / CALIB_OFFSET_NB_MES;
accYoffset = ag[1] / CALIB_OFFSET_NB_MES;
accZoffset = ag[2] / CALIB_OFFSET_NB_MES;
}
if(is_calc_gyro){
gyroXoffset = ag[3] / CALIB_OFFSET_NB_MES;
gyroYoffset = ag[4] / CALIB_OFFSET_NB_MES;
gyroZoffset = ag[5] / CALIB_OFFSET_NB_MES;
}
}
/* UPDATE */
void MPU6050::fetchData(){
wire->beginTransmission(address);
wire->write(MPU6050_ACCEL_OUT_REGISTER);
wire->endTransmission(false);
wire->requestFrom(address,(uint8_t) 14);
int16_t rawData[7]; // [ax,ay,az,temp,gx,gy,gz]
for(int i=0;i<7;i++){
rawData[i] = wire->read() << 8;
rawData[i] |= wire->read();
}
accX = ((float)rawData[0]) / acc_lsb_to_g - accXoffset;
accY = ((float)rawData[1]) / acc_lsb_to_g - accYoffset;
accZ = (!upsideDownMounting - upsideDownMounting) * ((float)rawData[2]) / acc_lsb_to_g - accZoffset;
temp = (rawData[3] + TEMP_LSB_OFFSET) / TEMP_LSB_2_DEGREE;
gyroX = ((float)rawData[4]) / gyro_lsb_to_degsec - gyroXoffset;
gyroY = ((float)rawData[5]) / gyro_lsb_to_degsec - gyroYoffset;
gyroZ = ((float)rawData[6]) / gyro_lsb_to_degsec - gyroZoffset;
}
void MPU6050::update(){
// retrieve raw data
this->fetchData();
// estimate tilt angles: this is an approximation for small angles!
float sgZ = accZ<0 ? -1 : 1; // allow one angle to go from -180 to +180 degrees
angleAccX = atan2(accY, sgZ*sqrt(accZ*accZ + accX*accX)) * RAD_2_DEG; // [-180,+180] deg
angleAccY = - atan2(accX, sqrt(accZ*accZ + accY*accY)) * RAD_2_DEG; // [- 90,+ 90] deg
unsigned long Tnew = millis();
float dt = (Tnew - preInterval) * 1e-3;
preInterval = Tnew;
// Correctly wrap X and Y angles (special thanks to Edgar Bonet!)
// https://github.com/gabriel-milan/TinyMPU6050/issues/6
angleX = wrap(filterGyroCoef*(angleAccX + wrap(angleX + gyroX*dt - angleAccX,180)) + (1.0-filterGyroCoef)*angleAccX,180);
angleY = wrap(filterGyroCoef*(angleAccY + wrap(angleY + sgZ*gyroY*dt - angleAccY, 90)) + (1.0-filterGyroCoef)*angleAccY, 90);
angleZ += gyroZ*dt; // not wrapped
}