#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"
#include <Wire.h>

// Пины для дисплея
#define TFT_DC 2
#define TFT_CS 3

// Инициализация дисплея
Adafruit_ILI9341 tft(TFT_CS, TFT_DC);

// Пины энкодера
#define ENCODER_A PA0
#define ENCODER_B PA1

// Пин для DS18B20
#define ONE_WIRE_BUS PA2

// Команды для DS18B20
#define READ_SCRATCHPAD 0xBE
#define CONVERT_T 0x44
#define SKIP_ROM 0xCC

// Адрес MPU6050
#define MPU6050_ADDRESS 0x68

volatile int encoderPosition = 0;
volatile int lastEncoded = 0;
int lastDisplayState = -1;

void setup() {
  tft.begin();
  tft.setRotation(3);
  tft.fillScreen(ILI9341_BLACK);

  // Инициализация пинов энкодера
  pinMode(ENCODER_A, INPUT_PULLUP);
  pinMode(ENCODER_B, INPUT_PULLUP);

  // Прерывания для энкодера
  attachInterrupt(digitalPinToInterrupt(ENCODER_A), updateEncoder, CHANGE);
  attachInterrupt(digitalPinToInterrupt(ENCODER_B), updateEncoder, CHANGE);

  // Инициализация I2C для MPU6050
  Wire.begin();
  Wire.beginTransmission(MPU6050_ADDRESS);
  Wire.write(0x6B); // PWR_MGMT_1 register
  Wire.write(0);    // Set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);

  // Инициализация DS18B20
  pinMode(ONE_WIRE_BUS, INPUT_PULLUP);

  // Изначальное отображение данных
  displaySensorData();
}

void loop() {
  int displayState = encoderPosition % 3;

  if (displayState != lastDisplayState) {
    displaySensorData();
    lastDisplayState = displayState;
  }
}

void displaySensorData() {
  tft.fillScreen(ILI9341_BLACK);
  int displayState = encoderPosition % 3;

  tft.setCursor(20, 120);
  tft.setTextSize(2);

  switch (displayState) {
    case 0:
      displayAccelerometerData();
      break;
    case 1:
      displayGyroscopeData();
      break;
    case 2:
      int temperatureC = readTemperature();
      tft.setTextColor(ILI9341_YELLOW);
      tft.print("Temp: ");
      tft.print(temperatureC / 100); // Целая часть
      tft.print('.');
      tft.print(temperatureC % 100); // Дробная часть
      tft.println(" C");
      break;
  }
}

void displayAccelerometerData() {
  int16_t ax, ay, az;
  readMPU6050(&ax, &ay, &az, NULL, NULL, NULL);
  tft.setTextColor(ILI9341_RED);
  tft.print("Accel X: ");
  tft.println(ax);
  tft.print("Accel Y: ");
  tft.println(ay);
  tft.print("Accel Z: ");
  tft.println(az);
}

void displayGyroscopeData() {
  int16_t gx, gy, gz;
  readMPU6050(NULL, NULL, NULL, &gx, &gy, &gz);
  tft.setTextColor(ILI9341_GREEN);
  tft.print("Gyro X: ");
  tft.println(gx);
  tft.print("Gyro Y: ");
  tft.println(gy);
  tft.print("Gyro Z: ");
  tft.println(gz);
}

void updateEncoder() {
  int MSB = digitalRead(ENCODER_A);
  int LSB = digitalRead(ENCODER_B);
  int encoded = (MSB << 1) | LSB;
  int sum = (lastEncoded << 2) | encoded;

  if (sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) encoderPosition++;
  if (sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) encoderPosition--;

  lastEncoded = encoded;
}

void writeOneWire(uint8_t data) {
  for (uint8_t i = 0; i < 8; i++) {
    if (data & 0x01) {
      pinMode(ONE_WIRE_BUS, OUTPUT);
      digitalWrite(ONE_WIRE_BUS, LOW);
      delayMicroseconds(10);
      pinMode(ONE_WIRE_BUS, INPUT);
      delayMicroseconds(55);
    } else {
      pinMode(ONE_WIRE_BUS, OUTPUT);
      digitalWrite(ONE_WIRE_BUS, LOW);
      delayMicroseconds(65);
      pinMode(ONE_WIRE_BUS, INPUT);
      delayMicroseconds(5);
    }
    data >>= 1;
  }
}

uint8_t readOneWire() {
  uint8_t result = 0;
  for (uint8_t i = 0; i < 8; i++) {
    pinMode(ONE_WIRE_BUS, OUTPUT);
    digitalWrite(ONE_WIRE_BUS, LOW);
    delayMicroseconds(3);
    pinMode(ONE_WIRE_BUS, INPUT);
    delayMicroseconds(10);
    if (digitalRead(ONE_WIRE_BUS)) {
      result |= (1 << i);
    }
    delayMicroseconds(53);
  }
  return result;
}

void resetOneWire() {
  pinMode(ONE_WIRE_BUS, OUTPUT);
  digitalWrite(ONE_WIRE_BUS, LOW);
  delayMicroseconds(480);
  pinMode(ONE_WIRE_BUS, INPUT);
  delayMicroseconds(70);
  delayMicroseconds(410);
}

int readTemperature() {
  resetOneWire();
  writeOneWire(SKIP_ROM);
  writeOneWire(CONVERT_T);
  delay(750); // Время для конверсии температуры

  resetOneWire();
  writeOneWire(SKIP_ROM);
  writeOneWire(READ_SCRATCHPAD);

  uint8_t lsb = readOneWire();
  uint8_t msb = readOneWire();
  int16_t temp = (msb << 8) | lsb;

  return (temp * 625) / 100; // Возвращаем температуру в целых и дробных частях
}

void readMPU6050(int16_t* ax, int16_t* ay, int16_t* az, int16_t* gx, int16_t* gy, int16_t* gz) {
  Wire.beginTransmission(MPU6050_ADDRESS);
  Wire.write(0x3B); // Начать чтение с регистра акселерометра
  Wire.endTransmission(false);
  Wire.requestFrom(MPU6050_ADDRESS, 14, true);

  if (ax) *ax = (Wire.read() << 8) | Wire.read();
  if (ay) *ay = (Wire.read() << 8) | Wire.read();
  if (az) *az = (Wire.read() << 8) | Wire.read();
  Wire.read(); Wire.read(); // Пропускаем температуру
  if (gx) *gx = (Wire.read() << 8) | Wire.read();
  if (gy) *gy = (Wire.read() << 8) | Wire.read();
  if (gz) *gz = (Wire.read() << 8) | Wire.read();
}