#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
// =====================================================
// TAREA 6 - BANDA TRANSPORTADORA CON ESP32 + TFT
// Autor: Denis Zarate Saenz
// Funcionamiento:
// INICIO -> inicia/reinicia conteo
// SENSOR -> simula el sensor IR, cada pulsacion cuenta 1 pieza
// PAUSA -> detiene el sistema
// Al llegar a 12 piezas: se detiene la banda, caja completa,
// enciende LED amarillo y activa 2da banda por 3 segundos.
// =====================================================
// ==========================
// TFT ILI9341 - ESP32
// ==========================
#define TFT_CS 5
#define TFT_DC 2
#define TFT_RST 4
Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST);
// ==========================
// ENTRADAS
// ==========================
#define BTN_INICIO 15
#define BTN_PAUSA 33
#define BTN_SENSOR 27 // En Wokwi simula el sensor IR
// ==========================
// SALIDAS / INDICADORES
// ==========================
#define LED_VERDE 13 // Banda principal encendida
#define LED_ROJO 14 // Sistema detenido o pausado
#define LED_AMARILLO 21 // Caja completa
#define LED_AZUL 22 // Segunda banda activa
// Pines opcionales para motor real con L298N
#define MOTOR_IN1 25
#define MOTOR_IN2 26
// ==========================
// VARIABLES DEL SISTEMA
// ==========================
const int LIMITE_PIEZAS = 12;
int contador = 0;
enum EstadoSistema {
DETENIDO,
FUNCIONANDO,
PAUSADO,
CAJA_COMPLETA
};
EstadoSistema estado = DETENIDO;
bool segundaBanda = false;
int tiempoSegundaBanda = 0; // se cuenta con el temporizador
// Temporizador TMR1 / timer del ESP32
hw_timer_t *timer = NULL;
volatile bool tickTimer = false;
// Antirrebote por software
bool ultimoInicio = HIGH;
bool ultimoPausa = HIGH;
bool ultimoSensor = HIGH;
unsigned long tInicio = 0;
unsigned long tPausa = 0;
unsigned long tSensor = 0;
const unsigned long DEBOUNCE = 180;
// =====================================================
// INTERRUPCION DEL TEMPORIZADOR
// Cada 100 ms activa una bandera para controlar tiempos.
// =====================================================
void IRAM_ATTR onTimer() {
tickTimer = true;
}
// =====================================================
// CONTROL DE SALIDAS
// =====================================================
void actualizarSalidas() {
bool bandaON = (estado == FUNCIONANDO);
digitalWrite(LED_VERDE, bandaON ? HIGH : LOW);
digitalWrite(LED_ROJO, (estado == DETENIDO || estado == PAUSADO || estado == CAJA_COMPLETA) ? HIGH : LOW);
digitalWrite(LED_AMARILLO, (estado == CAJA_COMPLETA) ? HIGH : LOW);
digitalWrite(LED_AZUL, segundaBanda ? HIGH : LOW);
// Motor real: gira cuando la banda esta funcionando
if (bandaON) {
digitalWrite(MOTOR_IN1, HIGH);
digitalWrite(MOTOR_IN2, LOW);
} else {
digitalWrite(MOTOR_IN1, LOW);
digitalWrite(MOTOR_IN2, LOW);
}
}
// =====================================================
// PANTALLA TFT
// =====================================================
void mostrarPantalla() {
tft.fillScreen(ILI9341_BLACK);
tft.setTextColor(ILI9341_CYAN);
tft.setTextSize(2);
tft.setCursor(15, 10);
tft.println("BANDA TRANSPORTADORA");
tft.drawLine(0, 38, 320, 38, ILI9341_WHITE);
tft.setTextSize(2);
tft.setTextColor(ILI9341_WHITE);
tft.setCursor(25, 55);
tft.print("Piezas: ");
tft.setTextColor(ILI9341_YELLOW);
tft.print(contador);
tft.print("/");
tft.println(LIMITE_PIEZAS);
tft.setTextColor(ILI9341_WHITE);
tft.setCursor(25, 90);
tft.print("Estado: ");
if (estado == DETENIDO) {
tft.setTextColor(ILI9341_RED);
tft.println("DETENIDO");
} else if (estado == FUNCIONANDO) {
tft.setTextColor(ILI9341_GREEN);
tft.println("BANDA ON");
} else if (estado == PAUSADO) {
tft.setTextColor(ILI9341_ORANGE);
tft.println("PAUSADO");
} else if (estado == CAJA_COMPLETA) {
tft.setTextColor(ILI9341_YELLOW);
tft.println("CAJA LLENA");
}
tft.setTextColor(ILI9341_WHITE);
tft.setCursor(25, 125);
tft.print("Caja: ");
if (estado == CAJA_COMPLETA) {
tft.setTextColor(ILI9341_YELLOW);
tft.println("COMPLETA");
} else if (estado == FUNCIONANDO) {
tft.setTextColor(ILI9341_GREEN);
tft.println("LLENANDO");
} else {
tft.setTextColor(ILI9341_RED);
tft.println("ESPERA");
}
// Dibujo de banda transportadora
tft.drawRoundRect(30, 160, 260, 38, 8, ILI9341_WHITE);
for (int x = 50; x <= 270; x += 35) {
tft.fillCircle(x, 205, 6, ILI9341_BLUE);
}
// Barra de progreso
int ancho = map(contador, 0, LIMITE_PIEZAS, 0, 250);
tft.drawRect(35, 220, 250, 12, ILI9341_WHITE);
tft.fillRect(35, 220, ancho, 12, ILI9341_GREEN);
// Indicacion inferior
tft.setTextSize(1);
tft.setTextColor(ILI9341_WHITE);
tft.setCursor(10, 238);
tft.print("INICIO=GPIO15 SENSOR=GPIO27 PAUSA=GPIO33");
}
// =====================================================
// FUNCIONES DEL PROCESO
// =====================================================
void iniciarSistema() {
contador = 0;
segundaBanda = false;
tiempoSegundaBanda = 0;
estado = FUNCIONANDO;
actualizarSalidas();
mostrarPantalla();
Serial.println("Sistema iniciado. Banda funcionando.");
}
void pausarSistema() {
segundaBanda = false;
estado = PAUSADO;
actualizarSalidas();
mostrarPantalla();
Serial.println("Sistema pausado.");
}
void completarCaja() {
estado = CAJA_COMPLETA;
segundaBanda = true;
tiempoSegundaBanda = 0;
actualizarSalidas();
mostrarPantalla();
Serial.println("Caja completa: 12 piezas. Banda detenida.");
}
void contarPieza() {
if (estado == FUNCIONANDO) {
contador++;
Serial.print("Pieza detectada: ");
Serial.println(contador);
if (contador >= LIMITE_PIEZAS) {
contador = LIMITE_PIEZAS;
completarCaja();
} else {
mostrarPantalla();
}
}
}
// =====================================================
// LECTURA DE BOTONES
// =====================================================
void leerBotones() {
bool lecturaInicio = digitalRead(BTN_INICIO);
bool lecturaPausa = digitalRead(BTN_PAUSA);
bool lecturaSensor = digitalRead(BTN_SENSOR);
// Boton INICIO: siempre reinicia desde 0 y arranca la banda
if (lecturaInicio == LOW && ultimoInicio == HIGH && millis() - tInicio > DEBOUNCE) {
tInicio = millis();
iniciarSistema();
}
// Boton PAUSA: detiene el sistema
if (lecturaPausa == LOW && ultimoPausa == HIGH && millis() - tPausa > DEBOUNCE) {
tPausa = millis();
pausarSistema();
}
// Boton SENSOR: cuenta una pieza por pulsacion
if (lecturaSensor == LOW && ultimoSensor == HIGH && millis() - tSensor > DEBOUNCE) {
tSensor = millis();
contarPieza();
}
ultimoInicio = lecturaInicio;
ultimoPausa = lecturaPausa;
ultimoSensor = lecturaSensor;
}
// =====================================================
// SETUP
// =====================================================
void setup() {
Serial.begin(115200);
pinMode(BTN_INICIO, INPUT_PULLUP);
pinMode(BTN_PAUSA, INPUT_PULLUP);
pinMode(BTN_SENSOR, INPUT_PULLUP);
pinMode(LED_VERDE, OUTPUT);
pinMode(LED_ROJO, OUTPUT);
pinMode(LED_AMARILLO, OUTPUT);
pinMode(LED_AZUL, OUTPUT);
pinMode(MOTOR_IN1, OUTPUT);
pinMode(MOTOR_IN2, OUTPUT);
tft.begin();
tft.setRotation(1);
// Temporizador interno del ESP32: 100 ms
// Compatible con ESP32 Arduino Core 3.x usado por Wokwi
timer = timerBegin(1000000); // frecuencia: 1 MHz = 1 us por conteo
timerAttachInterrupt(timer, &onTimer);
timerAlarm(timer, 100000, true, 0); // 100000 us = 100 ms, repetitivo
actualizarSalidas();
mostrarPantalla();
}
// =====================================================
// LOOP PRINCIPAL
// =====================================================
void loop() {
leerBotones();
// TMR1: control de segunda banda sin usar delay
if (tickTimer) {
tickTimer = false;
if (segundaBanda) {
tiempoSegundaBanda++; // cada incremento equivale a 100 ms
// 30 x 100 ms = 3 segundos
if (tiempoSegundaBanda >= 30) {
segundaBanda = false;
actualizarSalidas();
mostrarPantalla();
Serial.println("Segunda banda detenida.");
}
}
}
}