// =========================================================================
// == PROYECTO TAREA 3: SECUENCIA DE BLOQUE MÓVIL (VERSIÓN SIN PARPADEO) ==
// =========================================================================
// Autor: Josue Cardenas Aguirre (Adaptado por Gemini)
// Descripción: Simulación de un bloque de 4 LEDs que se mueve desde el
// centro a los extremos y viceversa, apagando su rastro.
// =========================================================================
// --- 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 = 16;
const int SPEED_MIN = 50;
const int SPEED_MAX = 400;
const int PAUSE_FACTOR = 3;
// --- CONFIGURACIÓN VISUAL (TFT) ---
#define FOOTER_HEIGHT 40
#define COLOR_BACKGROUND 0xDEFB // Gris claro
#define COLOR_FOOTER 0x2104 // Azul oscuro
#define COLOR_TEXT_TITLE ILI9341_WHITE
#define COLOR_TEXT_AUTHOR ILI9341_LIGHTGREY
#define COLOR_LED_ON ILI9341_GREEN
#define COLOR_LED_OFF 0x9CF3
#define COLOR_BORDER 0x4A49
const int LED_DIAMETER = 12;
const int LED_SPACING = 4;
// =========================================================================
// == 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 { EXPANDING, PAUSE_EXTREMES, CONTRACTING, PAUSE_CENTER };
AnimationState currentState = EXPANDING;
const char* statusMessage = "Iniciando...";
unsigned long lastUpdateTime = 0;
int animationStep = 0;
long cycleCount = 0;
int startX, startY;
// =========================================================================
// == FUNCIONES DE VISUALIZACIÓN ==
// =========================================================================
void drawTFTFooter() {
tft.fillRect(0, tft.height() - FOOTER_HEIGHT, tft.width(), FOOTER_HEIGHT, COLOR_FOOTER);
tft.setTextSize(2);
tft.setTextColor(COLOR_TEXT_TITLE);
tft.setCursor(35, tft.height() - 28);
tft.print("Control de 16 Leds");
tft.setTextSize(1);
tft.setTextColor(COLOR_TEXT_AUTHOR);
tft.setCursor(80, tft.height() - 12);
tft.print("JOSUE CARDENAS AGUIRRE");
}
void updateOLED(long speed_ms) {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
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)");
int barWidth = map(speedPercent, 0, 100, 0, 100);
display.drawRect(14, 15, 102, 8, SSD1306_WHITE);
display.fillRect(15, 16, barWidth, 6, SSD1306_WHITE);
display.setCursor(0, 32);
display.print("Estado: ");
display.print(statusMessage);
display.setCursor(0, 48);
display.print("Ciclo Completado #: ");
display.print(cycleCount);
display.display();
}
void drawLed(int index, uint16_t fillColor) {
int ledX_center = startX + index * (LED_DIAMETER + LED_SPACING) + LED_DIAMETER / 2;
tft.fillCircle(ledX_center, startY, LED_DIAMETER / 2, fillColor);
tft.drawCircle(ledX_center, startY, LED_DIAMETER / 2, COLOR_BORDER);
}
void clearAllLeds() {
for (int i = 0; i < NUM_LEDS; i++) {
drawLed(i, COLOR_LED_OFF);
}
}
// =========================================================================
// == PROGRAMA PRINCIPAL ==
// =========================================================================
void setup() {
tft.begin();
tft.setRotation(1);
tft.fillScreen(COLOR_BACKGROUND);
drawTFTFooter();
int totalLedsWidth = NUM_LEDS * LED_DIAMETER + (NUM_LEDS - 1) * LED_SPACING;
int drawingAreaHeight = tft.height() - FOOTER_HEIGHT;
startX = (tft.width() - totalLedsWidth) / 2;
startY = (drawingAreaHeight / 2);
clearAllLeds();
Wire.begin();
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { for(;;); }
}
void loop() {
long animationInterval = map(analogRead(POT_PIN), 0, 4095, SPEED_MAX, SPEED_MIN);
updateOLED(animationInterval);
unsigned long interval = (currentState == PAUSE_EXTREMES || currentState == PAUSE_CENTER) ? (animationInterval * PAUSE_FACTOR) : animationInterval;
if (millis() - lastUpdateTime < interval) return;
lastUpdateTime = millis();
switch (currentState) {
case EXPANDING: {
statusMessage = "Expandiendo...";
clearAllLeds(); // Limpiar antes de dibujar el nuevo paso
int left1 = 6 - (animationStep * 2);
int left2 = 7 - (animationStep * 2);
int right1 = 8 + (animationStep * 2);
int right2 = 9 + (animationStep * 2);
drawLed(left1, COLOR_LED_ON);
drawLed(left2, COLOR_LED_ON);
drawLed(right1, COLOR_LED_ON);
drawLed(right2, COLOR_LED_ON);
animationStep++;
if (animationStep > 3) {
animationStep = 0; // Reiniciar para la próxima fase
currentState = PAUSE_EXTREMES;
}
break;
}
case PAUSE_EXTREMES:
// En este estado solo esperamos, no redibujamos.
statusMessage = "Pausa Extremos";
currentState = CONTRACTING;
break;
case CONTRACTING: {
statusMessage = "Contrayendo...";
clearAllLeds(); // Limpiar antes de dibujar el nuevo paso
int left1 = 2 + (animationStep * 2);
int left2 = 3 + (animationStep * 2);
int right1 = 12 - (animationStep * 2);
int right2 = 13 - (animationStep * 2);
drawLed(left1, COLOR_LED_ON);
drawLed(left2, COLOR_LED_ON);
drawLed(right1, COLOR_LED_ON);
drawLed(right2, COLOR_LED_ON);
animationStep++;
if (animationStep > 2) {
animationStep = 0; // Reiniciar para la próxima fase
currentState = PAUSE_CENTER;
}
break;
}
case PAUSE_CENTER:
// En este estado solo esperamos y actualizamos el ciclo
statusMessage = "Pausa Centro";
cycleCount++;
currentState = EXPANDING;
break;
}
}