// =========================================================================
// == PROYECTO FINAL V2: SIMULADOR TFT CON MONITOR OLED Y TÍTULO EN TFT ==
// =========================================================================
// Autor: Josue Cardenas Aguirre
// Descripción: La simulación y el título se muestran en la pantalla TFT.
// La pantalla OLED actúa como monitor de datos independiente.
// =========================================================================
// --- LIBRERÍAS ---
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <Adafruit_SSD1306.h>
// =========================================================================
// == CONFIGURACIÓN GENERAL ==
// =========================================================================
// --- PINES DE CONEXIÓN ---
#define TFT_CS 15
#define TFT_DC 2
#define TFT_RST 4
#define POT_PIN 34
// --- CONFIGURACIÓN OLED ---
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
// --- CONFIGURACIÓN DE LA ANIMACIÓN ---
const int NUM_LEDS = 8;
const int SPEED_MIN = 50;
const int SPEED_MAX = 400;
const int PAUSE_FACTOR = 2;
// --- CONFIGURACIÓN VISUAL (TFT) ---
#define FOOTER_HEIGHT 40 // Espacio en la parte inferior para el título
#define COLOR_BACKGROUND ILI9341_YELLOW
#define COLOR_FOOTER 0x2104 // Azul oscuro para el pie de página
#define COLOR_TEXT_TITLE ILI9341_WHITE
#define COLOR_TEXT_AUTHOR ILI9341_LIGHTGREY
#define COLOR_LED_ON ILI9341_RED
#define COLOR_LED_OFF 0x31A6
#define COLOR_BORDER 0x528A
const int LED_WIDTH = 25;
const int LED_HEIGHT = 25;
const int LED_SPACING = 8;
const int LED_CORNER_RADIUS = 5;
// =========================================================================
// == INSTANCIAS Y VARIABLES ==
// =========================================================================
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
enum AnimationState { CENTER_OUT, PAUSE_ALL_ON, EXTREMES_IN, PAUSE_ALL_OFF };
AnimationState currentState = CENTER_OUT;
const char* statusMessage = "Iniciando...";
unsigned long lastUpdateTime = 0;
int animationStep = 0;
long cycleCount = 0;
int startX, startY;
// =========================================================================
// == FUNCIONES DE VISUALIZACIÓN ==
// =========================================================================
/**
* @brief Dibuja el pie de página con título y autor en la pantalla TFT.
*/
void drawTFTFooter() {
// Dibuja el rectángulo del pie de página
tft.fillRect(0, tft.height() - FOOTER_HEIGHT, tft.width(), FOOTER_HEIGHT, COLOR_FOOTER);
// Escribe el título
tft.setTextSize(2);
tft.setTextColor(COLOR_TEXT_TITLE);
tft.setCursor(50, tft.height() - 28);
tft.print("Control de 8 Leds");
// Escribe el nombre del autor
tft.setTextSize(1);
tft.setTextColor(COLOR_TEXT_AUTHOR);
tft.setCursor(80, tft.height() - 12);
tft.print("JOSUE CARDENAS AGUIRRE");
}
/**
* @brief Actualiza toda la información en la pantalla OLED.
*/
void updateOLED(long speed_ms) {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
// --- Velocidad ---
int speedPercent = map(speed_ms, SPEED_MAX, SPEED_MIN, 0, 100);
display.setCursor(0, 2);
display.print("Velocidad: ");
display.print(speedPercent);
display.print("% (");
display.print(speed_ms);
display.print("ms)");
// --- Barra de Velocidad ---
int barWidth = map(speedPercent, 0, 100, 0, 100);
display.drawRect(14, 15, 102, 8, SSD1306_WHITE);
display.fillRect(15, 16, barWidth, 6, SSD1306_WHITE);
// --- Estado y Ciclos ---
display.setCursor(0, 32);
display.print("Estado: ");
display.print(statusMessage);
display.setCursor(0, 48);
display.print("Ciclo Completado #: ");
display.print(cycleCount);
display.display();
}
/**
* @brief Dibuja un LED en la pantalla TFT.
*/
void drawLed(int index, uint16_t fillColor) {
int ledX = startX + index * (LED_WIDTH + LED_SPACING);
tft.fillRoundRect(ledX, startY, LED_WIDTH, LED_HEIGHT, LED_CORNER_RADIUS, fillColor);
tft.drawRoundRect(ledX, startY, LED_WIDTH, LED_HEIGHT, LED_CORNER_RADIUS, COLOR_BORDER);
}
// =========================================================================
// == PROGRAMA PRINCIPAL ==
// =========================================================================
void setup() {
// --- INICIAR PANTALLA TFT ---
tft.begin();
tft.setRotation(1);
tft.fillScreen(COLOR_BACKGROUND);
drawTFTFooter(); // Dibuja el pie de página en la TFT
// Recalcular la posición Y para centrar los LEDs en el área amarilla
int drawingAreaHeight = tft.height() - FOOTER_HEIGHT;
int totalLedsWidth = (NUM_LEDS * LED_WIDTH) + ((NUM_LEDS - 1) * LED_SPACING);
startX = (tft.width() - totalLedsWidth) / 2;
startY = (drawingAreaHeight - LED_HEIGHT) / 2;
for (int i = 0; i < NUM_LEDS; i++) {
drawLed(i, COLOR_LED_OFF);
}
// --- INICIAR PANTALLA OLED ---
Wire.begin();
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { for(;;); }
updateOLED(map(analogRead(POT_PIN), 0, 4095, SPEED_MAX, SPEED_MIN));
}
void loop() {
// --- LECTURA Y CÁLCULO DE VELOCIDAD ---
int potValue = analogRead(POT_PIN);
long animationInterval = map(potValue, 0, 4095, SPEED_MAX, SPEED_MIN);
// --- ACTUALIZAR PANTALLA DE DATOS (OLED) ---
updateOLED(animationInterval);
// --- CONTROL DE TIEMPO DE LA ANIMACIÓN (TFT) ---
unsigned long currentTime = millis();
unsigned long interval = (currentState == PAUSE_ALL_ON || currentState == PAUSE_ALL_OFF) ? (animationInterval * PAUSE_FACTOR) : animationInterval;
if (currentTime - lastUpdateTime < interval) return;
lastUpdateTime = currentTime;
// --- MÁQUINA DE ESTADOS (CONTROL DE LEDs) ---
switch (currentState) {
case CENTER_OUT:
if (animationStep == 0) statusMessage = "Expandiendo...";
drawLed((NUM_LEDS / 2) - 1 - animationStep, COLOR_LED_ON);
drawLed((NUM_LEDS / 2) + animationStep, COLOR_LED_ON);
animationStep++;
if (animationStep >= NUM_LEDS / 2) {
animationStep = 0;
currentState = PAUSE_ALL_ON;
}
break;
case PAUSE_ALL_ON:
statusMessage = "Pausa (ON)";
currentState = EXTREMES_IN;
break;
case EXTREMES_IN:
if (animationStep == 0) statusMessage = "Contrayendo...";
drawLed(animationStep, COLOR_LED_OFF);
drawLed(NUM_LEDS - 1 - animationStep, COLOR_LED_OFF);
animationStep++;
if (animationStep >= NUM_LEDS / 2) {
animationStep = 0;
currentState = PAUSE_ALL_OFF;
}
break;
case PAUSE_ALL_OFF:
statusMessage = "Pausa (OFF)";
cycleCount++;
currentState = CENTER_OUT;
break;
}
}