#include <Arduino.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>

// Pines de la pantalla TFT
#define TFT_DC 0
#define TFT_CS 15
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

// Pines de botones y potenciómetro
#define PIN_BUTTON_DIS 17
#define PIN_BUTTON_DIS_COP 5
#define PIN_POTENTIOMETER 35

// Pines de LEDs
#define PIN_DIS_LED1 4
#define PIN_DIS_LED2 16
#define PIN_DIS_COP_LED1 25
#define PIN_DIS_COP_LED2 33
#define PIN_DIS_COP_LED3 32
#define PIN_DIS_COP_LED4 26

// Variables compartidas
volatile bool modeDIS = false;
volatile bool modeDIS_COP = false;
volatile float targetSpeed = 0; // Velocidad objetivo
volatile float currentSpeed = 0; // Velocidad actual

// Prototipos de funciones
void taskDIS(void *parameter);
void taskDIS_COP(void *parameter);
void taskUpdateDisplay(void *parameter);
void checkButtons(void *parameter);
int getDelayFromPotentiometer();
void drawDial();
void drawNeedle(float currentSpeed, float targetSpeed);

void setup() {
  // Configuración de pines
  pinMode(PIN_BUTTON_DIS, INPUT_PULLDOWN);
  pinMode(PIN_BUTTON_DIS_COP, INPUT_PULLDOWN);
  pinMode(PIN_DIS_LED1, OUTPUT);
  pinMode(PIN_DIS_LED2, OUTPUT);
  pinMode(PIN_DIS_COP_LED1, OUTPUT);
  pinMode(PIN_DIS_COP_LED2, OUTPUT);
  pinMode(PIN_DIS_COP_LED3, OUTPUT);
  pinMode(PIN_DIS_COP_LED4, OUTPUT);

  // Configuración de la pantalla TFT
  tft.begin();
  tft.setRotation(1);
  drawDial(); // Mostrar el velocímetro al iniciar

  // Crear tareas FreeRTOS
  xTaskCreate(taskDIS, "Task DIS", 1024, NULL, 1, NULL);
  xTaskCreate(taskDIS_COP, "Task DIS COP", 1024, NULL, 1, NULL);
  xTaskCreate(taskUpdateDisplay, "Task Update Display", 2048, NULL, 1, NULL);
  xTaskCreate(checkButtons, "Check Buttons", 1024, NULL, 2, NULL);
}

void loop() {
  // El loop principal queda vacío porque usamos FreeRTOS
}

// Tarea para manejar el modo DIS
void taskDIS(void *parameter) {
  while (true) {
    if (modeDIS) {
      int delayTime = getDelayFromPotentiometer();
      targetSpeed = map(delayTime, 100, 2000, 240, 0);

      digitalWrite(PIN_DIS_LED1, HIGH);
      digitalWrite(PIN_DIS_LED2, LOW);
      vTaskDelay(pdMS_TO_TICKS(delayTime));

      digitalWrite(PIN_DIS_LED1, LOW);
      digitalWrite(PIN_DIS_LED2, HIGH);
      vTaskDelay(pdMS_TO_TICKS(delayTime));
    } else {
      digitalWrite(PIN_DIS_LED1, LOW);
      digitalWrite(PIN_DIS_LED2, LOW);
      vTaskDelay(pdMS_TO_TICKS(100));
    }
  }
}

// Tarea para manejar el modo DIS COP
void taskDIS_COP(void *parameter) {
  int sequence[] = {PIN_DIS_COP_LED1, PIN_DIS_COP_LED3, PIN_DIS_COP_LED4, PIN_DIS_COP_LED2};
  while (true) {
    if (modeDIS_COP) {
      int delayTime = getDelayFromPotentiometer();
      targetSpeed = map(delayTime, 100, 2000, 240, 0);

      for (int i = 0; i < 4; i++) {
        digitalWrite(sequence[i], HIGH);
        vTaskDelay(pdMS_TO_TICKS(delayTime));
        digitalWrite(sequence[i], LOW);
      }
    } else {
      for (int i = 0; i < 4; i++) {
        digitalWrite(sequence[i], LOW);
      }
      vTaskDelay(pdMS_TO_TICKS(100));
    }
  }
}

// Tarea para actualizar la pantalla TFT
void taskUpdateDisplay(void *parameter) {
  while (true) {
    // Si ninguno de los modos está activo, establecer la velocidad objetivo en 0
    if (!modeDIS && !modeDIS_COP) {
      targetSpeed = 0;
    }

    // Interpolar suavemente entre la velocidad actual y la velocidad objetivo
    currentSpeed += (targetSpeed - currentSpeed) * 0.1;
    drawNeedle(currentSpeed, targetSpeed);

    vTaskDelay(pdMS_TO_TICKS(20)); // Actualización cada 20ms
  }
}

// Tarea para verificar los botones
void checkButtons(void *parameter) {
  static bool lastStateDIS = false;
  static bool lastStateDIS_COP = false;

  while (true) {
    bool currentStateDIS = digitalRead(PIN_BUTTON_DIS);
    bool currentStateDIS_COP = digitalRead(PIN_BUTTON_DIS_COP);

    if (currentStateDIS && !lastStateDIS && !modeDIS_COP) {
      modeDIS = !modeDIS;
    }

    if (currentStateDIS_COP && !lastStateDIS_COP && !modeDIS) {
      modeDIS_COP = !modeDIS_COP;
    }

    lastStateDIS = currentStateDIS;
    lastStateDIS_COP = currentStateDIS_COP;

    vTaskDelay(pdMS_TO_TICKS(50));
  }
}

// Función para obtener el tiempo de retraso del potenciómetro
int getDelayFromPotentiometer() {
  int analogValue = analogRead(PIN_POTENTIOMETER);
  return map(analogValue, 0, 4095, 100, 2000);
}

// Dibujar el dial en la pantalla TFT con rango máximo de 8 (x1000 RPM)
void drawDial() {
  tft.fillScreen(ILI9341_BLACK);

  // Círculos principales
  tft.drawCircle(160, 120, 100, ILI9341_WHITE);
  tft.drawCircle(160, 120, 95, ILI9341_WHITE);

  // Dibujar marcas principales y secundarias
  for (int i = 0; i <= 32; i++) { // Dividir hasta 8 en incrementos de 0.25
    float angle = map(i, 0, 32, -225, 45) * 3.14159 / 180; // Ajustar ángulo
    int x1 = 160 + cos(angle) * 90;
    int y1 = 120 + sin(angle) * 90;
    int x2 = 160 + cos(angle) * (i % 4 == 0 ? 80 : 85); // Marcas largas cada 1 unidad
    int y2 = 120 + sin(angle) * (i % 4 == 0 ? 80 : 85);
    tft.drawLine(x1, y1, x2, y2, ILI9341_WHITE);

    // Solo números en las marcas principales (cada 1 unidad)
    if (i % 4 == 0) {
      int textX = 160 + cos(angle) * 65 - 5;
      int textY = 120 + sin(angle) * 65 - 5;
      tft.setCursor(textX, textY);
      tft.setTextColor(ILI9341_WHITE);
      tft.setTextSize(1);
      tft.print(i / 4); // Mostrar valores de 0 a 8
    }
  }

  // Dibujar la zona roja (máximo)
  for (int i = 28; i <= 32; i++) { // Resaltar zona roja de 7 a 8
    float angle = map(i, 0, 32, -225, 45) * 3.14159 / 180;
    int x1 = 160 + cos(angle) * 90;
    int y1 = 120 + sin(angle) * 90;
    int x2 = 160 + cos(angle) * 80;
    int y2 = 120 + sin(angle) * 80;
    tft.drawLine(x1, y1, x2, y2, ILI9341_RED);
  }

  // Título de RPM
  tft.setCursor(140, 160);
  tft.setTextColor(ILI9341_WHITE);
  tft.setTextSize(2);
  tft.print("RPM");
}

// Dibujar la aguja del velocímetro
void drawNeedle(float currentSpeed, float targetSpeed) {
  static float prevAngle = -255;

  // Calcular el ángulo actual
  float angle = map(currentSpeed, 0, 8000, -225, 45); // Rango ajustado hasta 8000 RPM
  float radian = angle * 3.14159 / 180;

  // Borrar la aguja anterior
  float prevRadian = prevAngle * 3.14159 / 180;
  int xPrev = 160 + cos(prevRadian) * 70;
  int yPrev = 120 + sin(prevRadian) * 70;
  tft.drawLine(160, 120, xPrev, yPrev, ILI9341_BLACK);

  // Dibujar la nueva aguja
  int x = 160 + cos(radian) * 70;
  int y = 120 + sin(radian) * 70;
  tft.drawLine(160, 120, x, y, ILI9341_ORANGE);

  prevAngle = angle;
}
$abcdeabcde151015202530354045505560fghijfghij