/*
* Control de secuencia SIMPLE (M1 -> Pausa -> M2)
* con Dashboard Profesional en TFT ILI9341 e Interrupciones.
*
* **************************************************************************
* * CÓDIGO CORREGIDO para ESP32 Core 3.x (API de Timer actualizada) *
* **************************************************************************
*/
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <SPI.h>
#define TFT_CS 15
#define TFT_DC 2
#define TFT_RST 4
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
#define M1_STEP_PIN 12
#define M1_DIR_PIN 14
#define M2_STEP_PIN 26
#define M2_DIR_PIN 27
hw_timer_t *timerMotor1 = NULL;
hw_timer_t *timerMotor2 = NULL;
#define MOTOR_STEP_PERIOD_US 1000
void IRAM_ATTR onTimerMotor1() {
digitalWrite(M1_STEP_PIN, !digitalRead(M1_STEP_PIN));
}
void IRAM_ATTR onTimerMotor2() {
digitalWrite(M2_STEP_PIN, !digitalRead(M2_STEP_PIN));
}
enum EstadoSecuencia {
MOTOR1_CORRIENDO,
PAUSA,
MOTOR2_CORRIENDO
};
EstadoSecuencia estadoActual = MOTOR1_CORRIENDO;
unsigned long tiempoUltimoCambio = 0;
const long DURACION_M1 = 3000;
const long DURACION_PAUSA = 4000;
const long DURACION_M2 = 4000;
#define BAR_WIDTH 140
#define BAR_HEIGHT 30
#define BAR1_X 10
#define BAR_Y 150
#define BAR2_X 170
void drawDashboardLayout();
void updateMotorStatus(int motorNum, String status, uint16_t color);
void updateProgressBar(int motorNum, long elapsed, long total, uint16_t color);
void updateTimerDisplay(int motorNum, long elapsed, long total, uint16_t color); // <-- NUEVO PROTOTIPO
void clearTimerDisplay(int motorNum); // <-- NUEVO PROTOTIPO
// =========================================================================
// SETUP
// =========================================================================
void setup() {
Serial.begin(115200);
pinMode(M1_STEP_PIN, OUTPUT);
pinMode(M1_DIR_PIN, OUTPUT);
pinMode(M2_STEP_PIN, OUTPUT);
pinMode(M2_DIR_PIN, OUTPUT);
digitalWrite(M1_DIR_PIN, HIGH);
digitalWrite(M2_DIR_PIN, LOW);
tft.begin();
tft.setRotation(1);
drawDashboardLayout();
timerMotor1 = timerBegin(1000000);
timerAttachInterrupt(timerMotor1, &onTimerMotor1);
timerAlarm(timerMotor1, MOTOR_STEP_PERIOD_US / 2, true, 0);
timerMotor2 = timerBegin(1000000);
timerAttachInterrupt(timerMotor2, &onTimerMotor2);
timerAlarm(timerMotor2, MOTOR_STEP_PERIOD_US / 2, true, 0);
timerStop(timerMotor1);
timerStop(timerMotor2);
Serial.println("Iniciando secuencia...");
updateMotorStatus(1, "RUNNING", ILI9341_GREEN);
updateMotorStatus(2, "IDLE", ILI9341_DARKGREY);
timerStart(timerMotor1);
tiempoUltimoCambio = millis();
}
// =========================================================================
// LOOP
// =========================================================================
void loop() {
unsigned long tiempoActual = millis();
unsigned long tiempoTranscurrido = tiempoActual - tiempoUltimoCambio;
switch (estadoActual) {
case MOTOR1_CORRIENDO:
if (tiempoTranscurrido >= DURACION_M1) {
Serial.println("Motor 1 -> Pausa");
timerStop(timerMotor1);
estadoActual = PAUSA;
tiempoUltimoCambio = tiempoActual;
tiempoTranscurrido = 0;
updateMotorStatus(1, "PAUSED", ILI9341_YELLOW);
updateMotorStatus(2, "PAUSED", ILI9341_YELLOW);
}
break;
case PAUSA:
if (tiempoTranscurrido >= DURACION_PAUSA) {
Serial.println("Pausa -> Motor 2");
estadoActual = MOTOR2_CORRIENDO;
tiempoUltimoCambio = tiempoActual;
tiempoTranscurrido = 0;
updateMotorStatus(1, "IDLE", ILI9341_DARKGREY);
updateMotorStatus(2, "RUNNING", ILI9341_GREEN);
updateProgressBar(1, 0, DURACION_PAUSA, ILI9341_BLACK);
timerStart(timerMotor2);
}
break;
case MOTOR2_CORRIENDO:
if (tiempoTranscurrido >= DURACION_M2) {
Serial.println("Motor 2 -> Motor 1");
timerStop(timerMotor2);
estadoActual = MOTOR1_CORRIENDO;
tiempoUltimoCambio = tiempoActual;
tiempoTranscurrido = 0;
updateMotorStatus(1, "RUNNING", ILI9341_GREEN);
updateMotorStatus(2, "IDLE", ILI9341_DARKGREY);
updateProgressBar(2, 0, DURACION_M2, ILI9341_BLACK);
timerStart(timerMotor1);
}
break;
}
// 2. LÓGICA DE ACTUALIZACIÓN GRÁFICA (GRÁFICOS)
// (Se ejecuta en CADA ciclo del loop para mostrar progreso)
if (estadoActual == MOTOR1_CORRIENDO) {
// Motor 1 Activo
updateProgressBar(1, tiempoTranscurrido, DURACION_M1, ILI9341_GREEN);
updateTimerDisplay(1, tiempoTranscurrido, DURACION_M1, ILI9341_GREEN);
// Motor 2 Inactivo
clearTimerDisplay(2);
} else if (estadoActual == PAUSA) {
// Ambos en Pausa
updateProgressBar(1, tiempoTranscurrido, DURACION_PAUSA, ILI9341_YELLOW);
updateProgressBar(2, tiempoTranscurrido, DURACION_PAUSA, ILI9341_YELLOW);
updateTimerDisplay(1, tiempoTranscurrido, DURACION_PAUSA, ILI9341_YELLOW);
updateTimerDisplay(2, tiempoTranscurrido, DURACION_PAUSA, ILI9341_YELLOW);
} else if (estadoActual == MOTOR2_CORRIENDO) {
// Motor 1 Inactivo
clearTimerDisplay(1);
// Motor 2 Activo
updateProgressBar(2, tiempoTranscurrido, DURACION_M2, ILI9341_GREEN);
updateTimerDisplay(2, tiempoTranscurrido, DURACION_M2, ILI9341_GREEN);
}
}
// =========================================================================
// FUNCIONES DEL DASHBOARD (UI)
// =========================================================================
/**
* @brief Dibuja el layout estático del dashboard. Se llama 1 vez en setup().
*/
void drawDashboardLayout() {
tft.fillScreen(ILI9341_BLACK);
tft.setTextSize(2);
tft.setTextColor(ILI9341_WHITE);
tft.setCursor(35, 20);
tft.print("MOTOR 1");
tft.setCursor(195, 20);
tft.print("MOTOR 2");
tft.drawFastVLine(160, 0, 240, ILI9341_WHITE);
tft.drawRect(BAR1_X, BAR_Y, BAR_WIDTH, BAR_HEIGHT, ILI9341_WHITE);
tft.drawRect(BAR2_X, BAR_Y, BAR_WIDTH, BAR_HEIGHT, ILI9341_WHITE);
}
/**
* @brief Actualiza el texto de estado (RUNNING, PAUSED, IDLE) para un motor.
* @param motorNum 1 o 2
* @param status El texto a mostrar
* @param color Color del texto
*/
void updateMotorStatus(int motorNum, String status, uint16_t color) {
int xPos = (motorNum == 1) ? 20 : 180;
int yPos = 70;
tft.fillRect(xPos - 5, yPos - 5, 130, 60, ILI9341_BLACK);
tft.setTextSize(2);
tft.setTextColor(ILI9341_WHITE);
tft.setCursor(xPos, yPos);
tft.println("ESTADO:");
tft.setTextSize(2);
tft.setTextColor(color);
tft.setCursor(xPos, yPos + 25);
tft.print(status);
}
/**
* @brief Dibuja la barra de progreso. Se llama continuamente en loop().
* @param motorNum 1 o 2
* @param elapsed Tiempo transcurrido
* @param total Duración total del estado
* @param color Color de la barra
*/
void updateProgressBar(int motorNum, long elapsed, long total, uint16_t color) {
int barX = (motorNum == 1) ? BAR1_X : BAR2_X;
if (elapsed > total) {
elapsed = total;
}
int fillWidth = map(elapsed, 0, total, 0, BAR_WIDTH);
tft.fillRect(barX, BAR_Y, fillWidth, BAR_HEIGHT, color);
tft.fillRect(barX + fillWidth, BAR_Y, BAR_WIDTH - fillWidth, BAR_HEIGHT, ILI9341_BLACK);
}
/**
* @brief Dibuja el temporizador numérico (countdown) sobre la barra.
* @param motorNum 1 o 2
* @param elapsed Tiempo transcurrido
* @param total Duración total
* @param color Color del texto
*/
void updateTimerDisplay(int motorNum, long elapsed, long total, uint16_t color) {
long tiempoRestante = total - elapsed;
if (tiempoRestante < 0) {
tiempoRestante = 0;
}
float segundosRestantes = (float)tiempoRestante / 1000.0;
// Formatear el texto (ej. "2.8s")
String timerText = String(segundosRestantes, 1) + "s";
int xPos = (motorNum == 1) ? 40 : 200;
int yPos = 120;
// Limpiar el área anterior para evitar parpadeo/superposición
tft.fillRect(xPos, yPos, 80, 25, ILI9341_BLACK);
tft.setTextSize(2);
tft.setTextColor(color);
tft.setCursor(xPos, yPos);
tft.print(timerText);
}
/**
* @brief Limpia el área del temporizador numérico para un motor.
* @param motorNum 1 o 2
*/
void clearTimerDisplay(int motorNum) {
int xPos = (motorNum == 1) ? 40 : 200;
int yPos = 120;
tft.fillRect(xPos, yPos, 80, 25, ILI9341_BLACK);
}