// --- LIBRERÍAS COMUNES ---
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <ESP32Encoder.h>
// --- CONFIGURACIÓN DE PANTALLA OLED (I2C) ---
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// --- CONFIGURACIÓN DE ENCODERS ---
#define VOL_CLK_PIN 12
#define VOL_DT_PIN 13
#define GAIN_CLK_PIN 1 //TX
#define GAIN_DT_PIN 3 //RX
#define HIGH_CLK_PIN 34
#define HIGH_DT_PIN 35
#define MID_HIGH_CLK_PIN 32
#define MID_HIGH_DT_PIN 33
#define MID_LOW_CLK_PIN 25
#define MID_LOW_DT_PIN 26
#define LOW_CLK_PIN 27
#define LOW_DT_PIN 14
// --- PARÁMETROS DE FUNCIONAMIENTO ---
#define ENCODER_STEPS_PER_DB 2
// Rangos de Decibelios (dB) para los controles
const int VOL_MIN = -80, VOL_MAX = 10;
const int GAIN_MIN = -20, GAIN_MAX = 20;
const int EQ_MIN = -15, EQ_MAX = 15;
// ---> NUEVOS PARÁMETROS PARA LA LÓGICA DE PANTALLA
const unsigned long SCREEN_TIMEOUT_MS = 3000; // 3 segundos para volver a la pantalla de volumen
// ---> NUEVO ENUMERADOR PARA GESTIONAR EL ESTADO DE LA PANTALLA
enum DisplayState {
STATE_VOLUMEN,
STATE_GAIN,
STATE_HIGH,
STATE_MID_HIGH,
STATE_MID_LOW,
STATE_LOW
};
// --- OBJETOS GLOBALES ---
ESP32Encoder encoderVol;
ESP32Encoder encoderGain;
ESP32Encoder encoderHigh;
ESP32Encoder encoderMidHigh;
ESP32Encoder encoderLow;
ESP32Encoder encoderMidLow;
// --- VARIABLES DE ESTADO GLOBALES ---
// Para los controles
int currentVolDB = 0;
int currentGainDB = 0;
int currentHighDB = 0;
int currentMidHighDB = 0;
int currentMidLowDB = 0;
int currentLowDB = 0;
// ---> NUEVAS VARIABLES GLOBALES PARA LA LÓGICA DE PANTALLA
DisplayState currentDisplayState = STATE_VOLUMEN; // La pantalla inicia mostrando el volumen
unsigned long lastActivityTimestamp = 0; // Para el temporizador de inactividad
bool needsDisplayUpdate = true; // Para forzar la actualización de la pantalla cuando sea necesario
// --- PROTOTIPOS DE FUNCIONES ---
void actualizarPantallaParametro(const char *paramName, int currentValue, int minValue, int maxValue);
void updateDisplay(); // ---> NUEVO PROTOTIPO
void setup() {
//Serial.begin(115200);
// --- Inicialización de Buses ---
Wire.begin();
// --- Inicialización de Encoders ---
encoderVol.attachHalfQuad(VOL_DT_PIN, VOL_CLK_PIN);
encoderVol.setCount(VOL_MIN * ENCODER_STEPS_PER_DB);
currentVolDB = VOL_MIN;
encoderGain.attachHalfQuad(GAIN_DT_PIN, GAIN_CLK_PIN);
encoderGain.setCount(0 * ENCODER_STEPS_PER_DB);
encoderHigh.attachHalfQuad(HIGH_DT_PIN, HIGH_CLK_PIN);
encoderHigh.setCount(0 * ENCODER_STEPS_PER_DB);
encoderMidHigh.attachHalfQuad(MID_HIGH_DT_PIN, MID_HIGH_CLK_PIN);
encoderMidHigh.setCount(0 * ENCODER_STEPS_PER_DB);
encoderMidLow.attachHalfQuad(MID_LOW_DT_PIN, MID_LOW_CLK_PIN);
encoderMidLow.setCount(0 * ENCODER_STEPS_PER_DB);
encoderLow.attachHalfQuad(LOW_DT_PIN, LOW_CLK_PIN);
encoderLow.setCount(0 * ENCODER_STEPS_PER_DB);
// --- Inicialización de la pantalla OLED ---
display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.setCursor(15, 24);
display.println("PATRO DJ");
display.display();
delay(1500);
// ---> Mostrar la pantalla de volumen inicial
updateDisplay();
}
void loop() {
// --- 1. LEER ENCODERS Y ACTUALIZAR PARÁMETROS ---
long newVol = encoderVol.getCount() / ENCODER_STEPS_PER_DB;
if (newVol > VOL_MAX) { newVol = VOL_MAX; encoderVol.setCount(newVol * ENCODER_STEPS_PER_DB); }
if (newVol < VOL_MIN) { newVol = VOL_MIN; encoderVol.setCount(newVol * ENCODER_STEPS_PER_DB); }
if (newVol != currentVolDB) {
currentVolDB = newVol;
// ---> Si se mueve el volumen, se convierte en la pantalla activa inmediatamente
currentDisplayState = STATE_VOLUMEN;
needsDisplayUpdate = true;
}
long newGain = encoderGain.getCount() / ENCODER_STEPS_PER_DB;
if (newGain > GAIN_MAX) { newGain = GAIN_MAX; encoderGain.setCount(newGain * ENCODER_STEPS_PER_DB); }
if (newGain < GAIN_MIN) { newGain = GAIN_MIN; encoderGain.setCount(newGain * ENCODER_STEPS_PER_DB); }
if (newGain != currentGainDB) {
currentGainDB = newGain;
// ---> Si se mueve otro encoder, cambia la pantalla y reinicia el temporizador
currentDisplayState = STATE_GAIN;
lastActivityTimestamp = millis();
needsDisplayUpdate = true;
}
long newHigh = encoderHigh.getCount() / ENCODER_STEPS_PER_DB;
if (newHigh > EQ_MAX) { newHigh = EQ_MAX; encoderHigh.setCount(newHigh * ENCODER_STEPS_PER_DB); }
if (newHigh < EQ_MIN) { newHigh = EQ_MIN; encoderHigh.setCount(newHigh * ENCODER_STEPS_PER_DB); }
if (newHigh != currentHighDB) {
currentHighDB = newHigh;
currentDisplayState = STATE_HIGH;
lastActivityTimestamp = millis();
needsDisplayUpdate = true;
}
long newMidHigh = encoderMidHigh.getCount() / ENCODER_STEPS_PER_DB;
if (newMidHigh > EQ_MAX) { newMidHigh = EQ_MAX; encoderMidHigh.setCount(newMidHigh * ENCODER_STEPS_PER_DB); }
if (newMidHigh < EQ_MIN) { newMidHigh = EQ_MIN; encoderMidHigh.setCount(newMidHigh * ENCODER_STEPS_PER_DB); }
if (newMidHigh != currentMidHighDB) {
currentMidHighDB = newMidHigh;
currentDisplayState = STATE_MID_HIGH;
lastActivityTimestamp = millis();
needsDisplayUpdate = true;
}
long newMidLow = encoderMidLow.getCount() / ENCODER_STEPS_PER_DB;
if (newMidLow > EQ_MAX) { newMidLow = EQ_MAX; encoderMidLow.setCount(newMidLow * ENCODER_STEPS_PER_DB); }
if (newMidLow < EQ_MIN) { newMidLow = EQ_MIN; encoderMidLow.setCount(newMidLow * ENCODER_STEPS_PER_DB); }
if (newMidLow != currentMidLowDB) {
currentMidLowDB = newMidLow;
currentDisplayState = STATE_MID_LOW;
lastActivityTimestamp = millis();
needsDisplayUpdate = true;
}
long newLow = encoderLow.getCount() / ENCODER_STEPS_PER_DB;
if (newLow > EQ_MAX) { newLow = EQ_MAX; encoderLow.setCount(newLow * ENCODER_STEPS_PER_DB); }
if (newLow < EQ_MIN) { newLow = EQ_MIN; encoderLow.setCount(newLow * ENCODER_STEPS_PER_DB); }
if (newLow != currentLowDB) {
currentLowDB = newLow;
currentDisplayState = STATE_LOW;
lastActivityTimestamp = millis();
needsDisplayUpdate = true;
}
// --- 2. LÓGICA DE TIMEOUT DE PANTALLA ---
// Si la pantalla actual no es la de volumen y ha pasado el tiempo de timeout...
if (currentDisplayState != STATE_VOLUMEN && (millis() - lastActivityTimestamp > SCREEN_TIMEOUT_MS)) {
currentDisplayState = STATE_VOLUMEN; // ...vuelve al estado de volumen
needsDisplayUpdate = true;
}
// --- 3. ACTUALIZAR PANTALLA (SÓLO SI ES NECESARIO) ---
if (needsDisplayUpdate) {
updateDisplay();
needsDisplayUpdate = false; // Resetea el flag para no actualizar innecesariamente
}
delay(20); // Pequeño delay para no saturar el loop
}
// ---> NUEVA FUNCIÓN CENTRALIZADA PARA ACTUALIZAR LA PANTALLA
// Esta función decide qué dibujar basándose en la variable 'currentDisplayState'
void updateDisplay() {
switch (currentDisplayState) {
case STATE_VOLUMEN:
actualizarPantallaParametro("VOLUMEN", currentVolDB, VOL_MIN, VOL_MAX);
break;
case STATE_GAIN:
actualizarPantallaParametro("GAIN", currentGainDB, GAIN_MIN, GAIN_MAX);
break;
case STATE_HIGH:
actualizarPantallaParametro("HIGH", currentHighDB, EQ_MIN, EQ_MAX);
break;
case STATE_MID_HIGH:
actualizarPantallaParametro("MID HIGH", currentMidHighDB, EQ_MIN, EQ_MAX);
break;
case STATE_MID_LOW:
actualizarPantallaParametro("MID LOW", currentMidLowDB, EQ_MIN, EQ_MAX);
break;
case STATE_LOW:
actualizarPantallaParametro("LOW", currentLowDB, EQ_MIN, EQ_MAX);
break;
}
}
// Dibuja la pantalla de ajuste de un parámetro (Esta función no cambia)
void actualizarPantallaParametro(const char *paramName, int currentValue, int minValue, int maxValue) {
display.clearDisplay();
// 1. Dibuja el nombre y el valor numérico
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
int16_t x1, y1;
uint16_t w, h;
display.getTextBounds(paramName, 0, 0, &x1, &y1, &w, &h);
display.setCursor((SCREEN_WIDTH - w) / 2, 2);
display.println(paramName);
char dbStr[10];
sprintf(dbStr, "%+d dB", currentValue);
display.getTextBounds(dbStr, 0, 0, &x1, &y1, &w, &h);
display.setCursor((SCREEN_WIDTH - w) / 2, 20);
display.print(dbStr);
// 2. Dibuja la barra de progreso
int barWidth = map(currentValue, minValue, maxValue, 0, SCREEN_WIDTH);
display.drawRect(0, 42, SCREEN_WIDTH, 16, SSD1306_WHITE);
if (minValue < 0 && maxValue > 0) { // Si la barra es bipolar (ej. -15 a +15)
int zeroPoint = map(0, minValue, maxValue, 0, SCREEN_WIDTH);
display.drawFastVLine(zeroPoint, 40, 20, SSD1306_WHITE); // Línea central en 0
if (barWidth > zeroPoint) {
display.fillRect(zeroPoint, 42, barWidth - zeroPoint, 16, SSD1306_WHITE);
} else {
display.fillRect(barWidth, 42, zeroPoint - barWidth, 16, SSD1306_WHITE);
}
} else { // Si la barra es unipolar (ej. -80 a 0)
display.fillRect(0, 42, barWidth, 16, SSD1306_WHITE);
}
// 3. Muestra todo en la pantalla
display.display();
}