Petit tour effectué, c'est un échec ! D'après la littérature il manque quelque chose en plus pour faire un inclinomètre pour moto. Par contre pour du franchissement en 4x4 c'est parfait.
Code : Tout sélectionner
#include <EEPROM.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#define DEBUG_SERIAL
// -- CONSTANTS --
//Digital input
const int DI_PUSH_BUTTON = 2;
//Display
const int LCD_I2C_ADDRESS = 0x27;
const int LCD_CHAR_PER_LINE = 16;
const int LCD_LINES = 2;
//Accelerometer and gyroscope
const int MPU6050_I2C_ADDRESS = 0x68;
const int MPU6050_REGISTER_ADDRESS_PWR_MGMT_1 = 0x6B;
const int MPU6050_REGISTER_ADDRESS_ACCEL_CONFIG = 0x1C;
const int MPU6050_REGISTER_ADDRESS_GYRO_CONFIG = 0x1B;
const int MPU6050_REGISTER_ADDRESS_ACCEL_XOUT_H = 0x3B;
const float MPU6050_ACCEL_LSB_SENSITIVITY = 16384.0;
const int MPU6050_REGISTER_ADDRESS_GYRO_XOUT_H = 0x43;
const float MPU6050_GYRO_LSB_SENSITIVITY = 131.0;
const int MPU6050_CALIBRATION_SAMPLE_COUNT = 200;
//Misc
const int MAX_ROLL = 75;
const int MAX_PITCH = 75;
// -- TYPES --
struct AccCorrectionOffset
{
float X;
float Y;
};
struct GyroCorrectionOffset
{
float X;
float Y;
float Z;
};
// -- VARIABLES --
//Raw values
static float g_accX, g_accY, g_accZ; //Accelerometer values
static float g_gyroX, g_gyroY, g_gyroZ; //Gyroscope values
//Temporary values
static float g_accAngleX, g_accAngleY = 0.0;
static float g_gyroAngleX, g_gyroAngleY, g_gyroAngleZ;
static float g_elapsedTime, g_currentTime, g_previousTime;
//Final values
static float g_roll, g_pitch, g_yaw;
//Statistics
static int g_statisticLeftRoll = 0;
static int g_statisticRightRoll = 0;
static int g_statisticLeftPitch = 0;
static int g_statisticRightPitch = 0;
static float g_accMinX, g_accMaxX;
static float g_accMinY, g_accMaxY;
//Correction values
static AccCorrectionOffset g_accCorrectionOffset;
static GyroCorrectionOffset g_gyroCorrectionOffset;
//Display
static int g_updateDisplay = 0;
static int g_displayMode = 0;
//Lcd
LiquidCrystal_I2C g_lcd(LCD_I2C_ADDRESS, LCD_CHAR_PER_LINE, LCD_LINES);
// -- FUNCTIONS --
//Setup
void setup()
{
#ifdef DEBUG_SERIAL
Serial.begin(19200);
#endif
//LCD Display init
g_lcd.init();
g_lcd.backlight();
//Push button init
pinMode(DI_PUSH_BUTTON, INPUT_PULLUP);
//MPU6050 init
Wire.begin();
Wire.beginTransmission(MPU6050_I2C_ADDRESS);
Wire.write(MPU6050_REGISTER_ADDRESS_PWR_MGMT_1);
Wire.write(0x00); // Make reset and use internal clock
Wire.endTransmission(true);
delay(20);
//Init corrections
if(digitalRead(DI_PUSH_BUTTON) != LOW)
{//Read corrections saved in EEPROM for accelerometer and gyroscope
int eeAddress = 0;
EEPROM.get( eeAddress, g_accCorrectionOffset);
eeAddress = sizeof(AccCorrectionOffset);
EEPROM.get( eeAddress, g_gyroCorrectionOffset);
}
else
{//Enter in calibration mode
g_lcd.clear();
g_lcd.setCursor(0, 0);
g_lcd.print("AUTO CALIBRATION");
g_lcd.setCursor(0, 1);
g_lcd.print("Don't move...");
delay(2000);
//Start calibration
AutoCalibrate();
//Save corrections in EEPROM
int eeAddress = 0;
EEPROM.put( eeAddress, g_accCorrectionOffset);
eeAddress = sizeof(AccCorrectionOffset);
EEPROM.put( eeAddress, g_gyroCorrectionOffset);
//Show details if keep button down
if(digitalRead(DI_PUSH_BUTTON) == LOW)
{
g_lcd.clear();
g_lcd.setCursor(0, 0);
g_lcd.print("AX offset");
g_lcd.setCursor(0, 1);
g_lcd.print(g_accCorrectionOffset.X);
delay(1500);
g_lcd.clear();
g_lcd.setCursor(0, 0);
g_lcd.print("AY offset");
g_lcd.setCursor(0, 1);
g_lcd.print(g_accCorrectionOffset.Y);
delay(1500);
g_lcd.clear();
g_lcd.setCursor(0, 0);
g_lcd.print("GX offset");
g_lcd.setCursor(0, 1);
g_lcd.print(g_gyroCorrectionOffset.X);
delay(1500);
g_lcd.clear();
g_lcd.setCursor(0, 0);
g_lcd.print("GY offset");
g_lcd.setCursor(0, 1);
g_lcd.print(g_gyroCorrectionOffset.Y);
delay(1500);
g_lcd.clear();
g_lcd.setCursor(0, 0);
g_lcd.print("GZ offset");
g_lcd.setCursor(0, 1);
g_lcd.print(g_gyroCorrectionOffset.Z);
delay(1500);
}
}
g_lcd.clear();
g_lcd.setCursor(0, 0);
g_lcd.print(" INCLINOMETER ");
g_lcd.setCursor(9, 1);
g_lcd.print("V1.0");
delay(3000);
DisplayMode();
}
//Loop
void loop()
{
static int pushButtonDownCount;
//RAZ statitics if push button down
while(digitalRead(DI_PUSH_BUTTON) == LOW)
{
pushButtonDownCount++;
delay(100);
if(pushButtonDownCount > 20)
{
g_lcd.clear();
g_lcd.setCursor(0, 0);
g_lcd.print("RESET ALL");
delay(1500);
//Start RAZ
ResetStatistics();
pushButtonDownCount = 0;
}
}
if(pushButtonDownCount > 0)
{
if(++g_displayMode > 2)
g_displayMode = 0;
DisplayMode();
pushButtonDownCount = 0;
}
//Read acceleromter data
Wire.beginTransmission(MPU6050_I2C_ADDRESS);
Wire.write(MPU6050_REGISTER_ADDRESS_ACCEL_XOUT_H);
Wire.endTransmission(false);
Wire.requestFrom(MPU6050_I2C_ADDRESS, 6, true); //Read 6 registers from ACCEL_XOUT_H (ACCEL_XOUT_H, ACCEL_XOUT_L, ACCEL_YOUT_H, ACCEL_YOUT_L, ACCEL_ZOUT_H, ACCEL_ZOUT_L)
//For a range of +-2g, divide raw values by ACCEL_LSB_SENSITIVITY (according to the datasheet)
g_accX = (Wire.read() << 8 | Wire.read()) / MPU6050_ACCEL_LSB_SENSITIVITY;
g_accY = (Wire.read() << 8 | Wire.read()) / MPU6050_ACCEL_LSB_SENSITIVITY;
g_accZ = (Wire.read() << 8 | Wire.read()) / MPU6050_ACCEL_LSB_SENSITIVITY;
// Calculating Roll and Pitch from the accelerometer data
g_accAngleX = (atan(g_accY / sqrt(pow(g_accX, 2) + pow(g_accZ, 2))) * 180 / PI) + g_accCorrectionOffset.X;
g_accAngleY = (atan(-1 * g_accX / sqrt(pow(g_accY, 2) + pow(g_accZ, 2))) * 180 / PI) + g_accCorrectionOffset.Y;
//Compute period from last acquisition
g_previousTime = g_currentTime; // Previous time is stored before the actual time read
g_currentTime = millis(); // Current time actual time read
g_elapsedTime = (g_currentTime - g_previousTime) / 1000; // Divide by 1000 to get seconds
//Read gyroscope data
Wire.beginTransmission(MPU6050_I2C_ADDRESS);
Wire.write(MPU6050_REGISTER_ADDRESS_GYRO_XOUT_H);
Wire.endTransmission(false);
Wire.requestFrom(MPU6050_I2C_ADDRESS, 6, true); // Read 6 registers from GYRO_XOUT_H (GYRO_XOUT_H, GYRO_XOUT_L, GYRO_YOUT_H, GYRO_YOUT_L, GYRO_ZOUT_H, GYRO_ZOUT_L)
//For a 250deg/s range, divide raw value by GYRO_LSB_SENSITIVITY (according to the datasheet)
g_gyroX = (Wire.read() << 8 | Wire.read()) / MPU6050_GYRO_LSB_SENSITIVITY;
g_gyroY = (Wire.read() << 8 | Wire.read()) / MPU6050_GYRO_LSB_SENSITIVITY;
g_gyroZ = (Wire.read() << 8 | Wire.read()) / MPU6050_GYRO_LSB_SENSITIVITY;
//Correct the outputs with the calculated error values
g_gyroX = g_gyroX + g_gyroCorrectionOffset.X;
g_gyroY = g_gyroY + g_gyroCorrectionOffset.Y;
g_gyroZ = g_gyroZ + g_gyroCorrectionOffset.Z;
//Currently the raw values are in degrees per seconds, deg/s, so we need to multiply by sendonds (s) to get the angle in degrees
g_gyroAngleX = g_gyroAngleX + g_gyroX * g_elapsedTime; // deg/s * s = deg
g_gyroAngleY = g_gyroAngleY + g_gyroY * g_elapsedTime;
g_yaw = g_yaw + g_gyroZ * g_elapsedTime;
//Complementary filter - combine acceleromter and gyro angle values
g_gyroAngleX = 0.96 * g_gyroAngleX + 0.04 * g_accAngleX;
g_gyroAngleY = 0.96 * g_gyroAngleY + 0.04 * g_accAngleY;
g_roll = g_gyroAngleX;
g_pitch = g_gyroAngleY;
#ifdef DEBUG_SERIAL
// Print the values on the serial monitor
Serial.print("accX=");
Serial.print(g_accX);
Serial.print("/accY=");
Serial.print(g_accY);
Serial.print("/accZ=");
Serial.print(g_accZ);
Serial.print(" ");
Serial.print("roll=");
Serial.print(g_roll);
Serial.print("/pitch=");
Serial.print(g_pitch);
Serial.print("/yaw=");
Serial.println(g_yaw);
#endif
UpdateStatistics();
g_updateDisplay++;
if(g_updateDisplay >= 100)
{//Do not display value eachtime to avoid LCD to blink
g_updateDisplay = 0;
switch(g_displayMode)
{
case 0:
DisplayRollAndLinear();
break;
case 1:
DisplayRollAndPitch();
break;
case 2:
DisplayLinearAndCentrifugal();
break;
}
}
}
//Function
void AutoCalibrate()
{
static float AccX, AccY, AccZ = 0.0;
static float GyroX, GyroY, GyroZ = 0.0;
static float AccErrorX, AccErrorY = 0.0;
static float GyroErrorX, GyroErrorY, GyroErrorZ = 0.0;
//Read accelerometer values X times
int sample = 0;
while (sample < MPU6050_CALIBRATION_SAMPLE_COUNT)
{
Wire.beginTransmission(MPU6050_I2C_ADDRESS);
Wire.write(MPU6050_REGISTER_ADDRESS_ACCEL_XOUT_H);
Wire.endTransmission(false);
Wire.requestFrom(MPU6050_I2C_ADDRESS, 6, true);
AccX = (Wire.read() << 8 | Wire.read()) / MPU6050_ACCEL_LSB_SENSITIVITY;
AccY = (Wire.read() << 8 | Wire.read()) / MPU6050_ACCEL_LSB_SENSITIVITY;
AccZ = (Wire.read() << 8 | Wire.read()) / MPU6050_ACCEL_LSB_SENSITIVITY;
//Sum all readings
AccErrorX = AccErrorX + ((atan((AccY) / sqrt(pow((AccX), 2) + pow((AccZ), 2))) * 180 / PI));
AccErrorY = AccErrorY + ((atan(-1 * (AccX) / sqrt(pow((AccY), 2) + pow((AccZ), 2))) * 180 / PI));
sample++;
}
AccErrorX = AccErrorX / MPU6050_CALIBRATION_SAMPLE_COUNT;
AccErrorY = AccErrorY / MPU6050_CALIBRATION_SAMPLE_COUNT;
//Read gyroscope values X times
sample = 0;
while (sample < MPU6050_CALIBRATION_SAMPLE_COUNT)
{
Wire.beginTransmission(MPU6050_I2C_ADDRESS);
Wire.write(MPU6050_REGISTER_ADDRESS_GYRO_XOUT_H);
Wire.endTransmission(false);
Wire.requestFrom(MPU6050_I2C_ADDRESS, 6, true);
GyroX = Wire.read() << 8 | Wire.read();
GyroY = Wire.read() << 8 | Wire.read();
GyroZ = Wire.read() << 8 | Wire.read();
// Sum all readings
GyroErrorX = GyroErrorX + (GyroX / MPU6050_GYRO_LSB_SENSITIVITY);
GyroErrorY = GyroErrorY + (GyroY / MPU6050_GYRO_LSB_SENSITIVITY);
GyroErrorZ = GyroErrorZ + (GyroZ / MPU6050_GYRO_LSB_SENSITIVITY);
sample++;
}
GyroErrorX = GyroErrorX / MPU6050_CALIBRATION_SAMPLE_COUNT;
GyroErrorY = GyroErrorY / MPU6050_CALIBRATION_SAMPLE_COUNT;
GyroErrorZ = GyroErrorZ / MPU6050_CALIBRATION_SAMPLE_COUNT;
g_accCorrectionOffset.X = AccErrorX * -1;
g_accCorrectionOffset.Y = AccErrorY * -1;
g_gyroCorrectionOffset.X = GyroErrorX * -1;
g_gyroCorrectionOffset.Y = GyroErrorY * -1;
g_gyroCorrectionOffset.Z = GyroErrorZ * -1;
#ifdef DEBUG_SERIAL
// Print the error values on the Serial Monitor
Serial.print("AccErrorX: ");
Serial.println(AccErrorX);
Serial.print("AccErrorY: ");
Serial.println(AccErrorY);
Serial.print("GyroErrorX: ");
Serial.println(GyroErrorX);
Serial.print("GyroErrorY: ");
Serial.println(GyroErrorY);
Serial.print("GyroErrorZ: ");
Serial.println(GyroErrorZ);
#endif
}
void ResetStatistics()
{
g_statisticLeftRoll = 0;
g_statisticRightRoll = 0;
g_statisticLeftPitch = 0;
g_statisticRightPitch = 0;
g_accMinX = 0;
g_accMaxX = 0;
g_accMinY = 0;
g_accMaxY = 0;
}
void UpdateStatistics()
{
if(g_roll < g_statisticLeftRoll)
{
g_statisticLeftRoll = g_roll;
}
else if(g_roll > g_statisticRightRoll)
{
g_statisticRightRoll = g_roll;
}
if(g_pitch < g_statisticLeftPitch)
{
g_statisticLeftPitch = g_pitch;
}
else if(g_pitch > g_statisticRightPitch)
{
g_statisticRightPitch = g_pitch;
}
if(g_accX < g_accMinX)
g_accMinX = g_accX;
else if(g_accX > g_accMaxX)
g_accMaxX = g_accX;
if(g_accY < g_accMinY)
g_accMinY = g_accY;
else if(g_accY > g_accMaxY)
g_accMaxY = g_accY;
}
//Display
void DisplayMode()
{
switch(g_displayMode)
{
case 0:
g_lcd.clear();
g_lcd.setCursor(0, 0);
g_lcd.print("ROLL");
g_lcd.setCursor(0, 1);
g_lcd.print("& LINEAR");
delay(1000);
break;
case 1:
g_lcd.clear();
g_lcd.setCursor(0, 0);
g_lcd.print("ROLL");
g_lcd.setCursor(0, 1);
g_lcd.print("& PITCH");
delay(1000);
break;
case 2:
g_lcd.clear();
g_lcd.setCursor(0, 0);
g_lcd.print("LINEAR");
g_lcd.setCursor(0, 1);
g_lcd.print("& CENTRIFUGAL");
delay(1000);
break;
}
}
void DisplayRollAndPitch()
{
int roll = g_roll;
int pitch = g_pitch;
roll = abs(roll);
if(roll > MAX_ROLL)
roll = MAX_ROLL;
pitch = abs(pitch);
if(pitch > MAX_PITCH)
pitch = MAX_PITCH;
int statisticLeftRoll = abs(g_statisticLeftRoll);
if(statisticLeftRoll > MAX_ROLL)
statisticLeftRoll = MAX_ROLL;
int statisticRightRoll = abs(g_statisticRightRoll);
if(statisticRightRoll > MAX_ROLL)
statisticRightRoll = MAX_ROLL;
int statisticLeftPitch = abs(g_statisticLeftPitch);
if(statisticLeftPitch > MAX_PITCH)
statisticLeftPitch = MAX_PITCH;
int statisticRightPitch = abs(g_statisticRightPitch);
if(statisticRightPitch > MAX_PITCH)
statisticRightPitch = MAX_PITCH;
char buffer[3];
g_lcd.clear();
g_lcd.setCursor(0, 0);
g_lcd.print("R:");
snprintf(buffer,sizeof(buffer), "%d", roll);
g_lcd.print(buffer);
g_lcd.print((char)223);
g_lcd.setCursor(11, 0);
snprintf(buffer,sizeof(buffer), "%02d", statisticLeftRoll);
g_lcd.print(buffer);
g_lcd.print("/");
snprintf(buffer,sizeof(buffer), "%02d", statisticRightRoll);
g_lcd.print(buffer);
g_lcd.setCursor(0, 1);
g_lcd.print("P:");
snprintf(buffer,sizeof(buffer), "%d", pitch);
g_lcd.print(buffer);
g_lcd.print((char)223);
g_lcd.setCursor(11, 1);
snprintf(buffer,sizeof(buffer), "%02d", statisticLeftPitch);
g_lcd.print(buffer);
g_lcd.print("/");
snprintf(buffer,sizeof(buffer), "%02d", statisticRightPitch);
g_lcd.print(buffer);
}
void DisplayLinearAndCentrifugal()
{
g_lcd.clear();
g_lcd.setCursor(0, 0);
g_lcd.print("L:");
if(g_accX < 0)
g_lcd.setCursor(2, 0);
else
g_lcd.setCursor(3, 0);
g_lcd.print(g_accX, 1);
g_lcd.print("g");
g_lcd.setCursor(8, 0);
g_lcd.print(g_accMinX,1);
g_lcd.print("/");
g_lcd.print(g_accMaxX,1);
g_lcd.setCursor(0, 1);
g_lcd.print("C:");
if(g_accY < 0)
g_lcd.setCursor(2, 1);
else
g_lcd.setCursor(3, 1);
g_lcd.print(g_accY, 1);
g_lcd.print("g");
g_lcd.setCursor(8, 1);
g_lcd.print(g_accMinY,1);
g_lcd.print("/");
g_lcd.print(g_accMaxY,1);
}
void DisplayRollAndLinear()
{
g_lcd.clear();
int roll = g_roll;
roll = abs(roll);
if(roll > MAX_ROLL)
roll = MAX_ROLL;
int statisticLeftRoll = abs(g_statisticLeftRoll);
if(statisticLeftRoll > MAX_ROLL)
statisticLeftRoll = MAX_ROLL;
int statisticRightRoll = abs(g_statisticRightRoll);
if(statisticRightRoll > MAX_ROLL)
statisticRightRoll = MAX_ROLL;
char buffer[3];
g_lcd.clear();
g_lcd.setCursor(0, 0);
g_lcd.print("R: ");
snprintf(buffer,sizeof(buffer), "%d", roll);
g_lcd.print(buffer);
g_lcd.print((char)223);
g_lcd.setCursor(11, 0);
snprintf(buffer,sizeof(buffer), "%02d", statisticLeftRoll);
g_lcd.print(buffer);
g_lcd.print("/");
snprintf(buffer,sizeof(buffer), "%02d", statisticRightRoll);
g_lcd.print(buffer);
g_lcd.setCursor(0, 1);
g_lcd.print("L:");
if(g_accX < 0)
g_lcd.setCursor(2, 1);
else
g_lcd.setCursor(3, 1);
g_lcd.print(g_accX, 1);
g_lcd.print("g");
g_lcd.setCursor(8, 1);
g_lcd.print(g_accMinX,1);
g_lcd.print("/");
g_lcd.print(g_accMaxX,1);
}