/*
  MPU6050 and LCD 20x4 with Franzininho
  by Anderson Costa with ❤ for the Wokwi community
  Visit https://wokwi.com to learn about the Wokwi Simulator
  Visit https://franzininho.com.br to learn about the Franzininho
*/

#include <TinyWireM.h>
#include <LiquidCrystal_I2C.h>

#define MPU_ADDR 0x68                    // Endereco I2C do MPU6050

LiquidCrystal_I2C lcd(0x27, 20, 4);      // Define endereco e 20 caracteres/4 linhas

// Variaveis para armazenar valores dos sensores
int accelX, accelY, accelZ, temp;
int gyroX, gyroY, gyroZ;

// Variaveis para corrigir o espaço do valor anterior
int lastAccelX, lastAccelY, lastAccelZ, lastTemp;
int lastGyroX, lastGyroY, lastGyroZ;

unsigned long timerRequest;

void setup() {
  // Inicializa o LCD
  lcd.begin(20, 4);
  lcd.init();                            // Inicializa o display
  lcd.backlight();                       // Acende a luz de fundo do display
  lcd.clear();                           // Limpa o display

  // Inicializa o MPU-6050
  TinyWireM.begin();
  TinyWireM.beginTransmission(MPU_ADDR);
  TinyWireM.write(0x6B);                 // Endereço de configuração de energia
  TinyWireM.write(0);                    // Desperta o MPU-6050
  TinyWireM.endTransmission();

  // Informações iniciais do display
  lcd.setCursor(0, 0);
  lcd.print("Acelerometro");
  lcd.setCursor(0, 2);
  lcd.print("Giroscopio");
}

void loop() {
  if (millis() - timerRequest >= 200) {
    // Reseta o temporizador de requisição
    timerRequest = millis();

    TinyWireM.beginTransmission(MPU_ADDR);
    TinyWireM.write(0x3B);                 // Iniciando no registrador 0x3B
    TinyWireM.endTransmission();

    // Solicita os dados do sensor
    TinyWireM.requestFrom(MPU_ADDR, 14);   // Requisição de 14 bytes

    // Armazena os valores retornados nas variaveis correspondentes
    accelX = TinyWireM.read() << 8 | TinyWireM.read(); // 0x3B - ACCEL_XOUT_H | 0x3C - ACCEL_XOUT_L
    accelY = TinyWireM.read() << 8 | TinyWireM.read(); // 0x3D - ACCEL_YOUT_H | 0x3E - ACCEL_YOUT_L
    accelZ = TinyWireM.read() << 8 | TinyWireM.read(); // 0x3F - ACCEL_ZOUT_H | 0x40 - ACCEL_ZOUT_L
    temp = TinyWireM.read() << 8 | TinyWireM.read();   // 0x41 - TEMP_OUT_H   | 0x42 - TEMP_OUT_L
    gyroX = TinyWireM.read() << 8 | TinyWireM.read();  // 0x43 - GYRO_XOUT_H  | 0x44 - GYRO_XOUT_L
    gyroY = TinyWireM.read() << 8 | TinyWireM.read();  // 0x45 - GYRO_YOUT_H  | 0x46 - GYRO_YOUT_L
    gyroZ = TinyWireM.read() << 8 | TinyWireM.read();  // 0x47 - GYRO_ZOUT_H  | 0x48 - GYRO_ZOUT_L

    // Corrige o espaço usado pelo último valor de accelX
    if (lastAccelX != accelX) {
      lastAccelX = accelX;
      lcd.setCursor(2, 1);
      lcd.print("     ");
    }

    // Corrige o espaço usado pelo último valor de accelY
    if (lastAccelY != accelY) {
      lastAccelY = accelY;
      lcd.setCursor(9, 1);
      lcd.print("    ");
    }

    // Corrige o espaço usado pelo último valor de accelZ
    if (lastAccelZ != accelZ) {
      lastAccelZ = accelZ;
      lcd.setCursor(15, 1);
      lcd.print("     ");
    }

    // Corrige o espaço usado pelo último valor da temperatura
    if (lastTemp != temp) {
      lastTemp = temp;
      lcd.setCursor(15, 0);
      lcd.print("     ");
    }

    // Corrige o espaço usado pelo último valor de gyroX
    if (lastGyroX != gyroX) {
      lastGyroX = gyroX;
      lcd.setCursor(2, 3);
      lcd.print("     ");
    }

    // Corrige o espaço usado pelo último valor de gyroY
    if (lastGyroY != gyroY) {
      lastGyroY = gyroY;
      lcd.setCursor(9, 3);
      lcd.print("    ");
    }

    // Corrige o espaço usado pelo último valor de gyroZ
    if (lastGyroZ != gyroZ) {
      lastGyroZ = gyroZ;
      lcd.setCursor(15, 3);
      lcd.print("     ");
    }

    lcd.setCursor(0, 1);
    lcd.print("X=");
    lcd.setCursor(2, 1);
    // Envia valor X do acelerômetro para o LCD
    lcd.print(mapd(accelX, -2.0, 2.0));

    lcd.setCursor(7, 1);
    lcd.print("Y=");
    lcd.setCursor(9, 1);
    // Envia valor Y do acelerômetro para o LCD
    lcd.print(mapd(accelY, -2.0, 2.0));

    lcd.setCursor(13, 1);
    lcd.print("Z=");
    lcd.setCursor(15, 1);
    // Envia valor Z do acelerômetro para o LCD
    lcd.print(mapd(accelZ, -2.0, 2.0));

    lcd.setCursor(13, 0);
    lcd.print("T:");
    lcd.setCursor(15, 0);
    // Envia valor da temperatura para o LCD
    // Calcula a temperatura em graus Celsius
    lcd.print(temp / 340.00 + 36.53);

    lcd.setCursor(0, 3);
    lcd.print("X=");
    lcd.setCursor(2, 3);
    // Envia valor X do giroscópio para o LCD
    lcd.print(mapi(gyroX, -250, 250)); // 2 bytes

    lcd.setCursor(7, 3);
    lcd.print("Y=");
    lcd.setCursor(9, 3);
    // Envia valor Y do giroscópio para o LCD
    lcd.print(mapi(gyroY, -250, 250));

    lcd.setCursor(13, 3);
    lcd.print("Z=");
    lcd.setCursor(15, 3);
    // Envia valor Z do giroscópio para o LCD
    lcd.print(mapi(gyroZ, -250, 250));
  }
}

// map double (2 bytes)
double mapd(int x, float out_min, float out_max) {
  return (x - -32768) * (out_max - out_min) / (32767 - -32768) + out_min;
}

// map int (2 bytes)
int mapi(int x, int out_min, int out_max) {
  return (x - -32768) * (out_max + 1 - out_min) / (32767 - -32768) + out_min;
}