#include <LiquidCrystal_PCF8574.h>
#include <WiFi.h>
#include <Bounce2.h>
LiquidCrystal_PCF8574 lcd(0x27);
const char* ssid = "ESP32";
const char* password = "123456789";
String request;
unsigned long currentTime = millis();
unsigned long previousTime = 0;
const long timeoutTime = 2000;
WiFiServer server(80);
// Definición de Entradas (I)
#define I0 36
#define I1 39
#define I2 34
#define I3 35
#define I4 15
#define I5 13
#define I6 12
// Definición de Salidas (Q)
#define Q0 23
#define Q1 19
#define Q2 18
#define Q3 17
#define Q4 16
#define Q5 4
#define Q6 2
// Instanciamos un objeto Bounce para cada entrada
Bounce debouncer0 = Bounce();
Bounce debouncer1 = Bounce();
Bounce debouncer2 = Bounce();
Bounce debouncer3 = Bounce();
Bounce debouncer4 = Bounce();
Bounce debouncer5 = Bounce();
Bounce debouncer6 = Bounce();
// Variables para control de estado de salida
boolean estado_Q0 = LOW;
boolean estado_Q1 = LOW;
boolean estado_Q2 = LOW;
boolean estado_Q3 = LOW;
boolean estado_Q4 = LOW;
boolean estado_Q5 = LOW;
boolean estado_Q6 = LOW;
byte alto[] = { B11111, B11111, B11111, B11111, B11111, B11111, B11111, B00000 };
byte bajo[] = { B11111, B10001, B10001, B10001, B10001, B10001, B11111, B00000 };
void estados() {
lcd.setCursor(0,0);
lcd.print("IP: ");
lcd.print(WiFi.softAPIP());
lcd.setCursor(0,1);
lcd.print("I: ");
// Usamos .read() de Bounce para leer el estado estable en el LCD
lcd.write(debouncer0.read() == LOW ? 1 : 0); lcd.print(" ");
lcd.write(debouncer1.read() == LOW ? 1 : 0); lcd.print(" ");
lcd.write(debouncer2.read() == LOW ? 1 : 0); lcd.print(" ");
lcd.write(debouncer3.read() == LOW ? 1 : 0); lcd.print(" ");
lcd.write(debouncer4.read() == LOW ? 1 : 0); lcd.print(" ");
lcd.write(debouncer5.read() == LOW ? 1 : 0); lcd.print(" ");
lcd.write(debouncer6.read() == LOW ? 1 : 0);
lcd.setCursor(3,2);
lcd.print("0 1 2 3 4 5 6");
lcd.setCursor(0,3);
lcd.print("Q: ");
lcd.write(estado_Q0); lcd.print(" ");
lcd.write(estado_Q1); lcd.print(" ");
lcd.write(estado_Q2); lcd.print(" ");
lcd.write(estado_Q3); lcd.print(" ");
lcd.write(estado_Q4); lcd.print(" ");
lcd.write(estado_Q5); lcd.print(" ");
lcd.write(estado_Q6);
}
void setup() {
// Configuración de Hardware de Entradas
pinMode(I0, INPUT); // Externo 10k Pull-up
pinMode(I1, INPUT); // Externo 10k Pull-up
pinMode(I2, INPUT); // Externo 10k Pull-up
pinMode(I3, INPUT); // Externo 10k Pull-up
pinMode(I4, INPUT_PULLUP);
pinMode(I5, INPUT_PULLUP);
pinMode(I6, INPUT_PULLUP);
// Configuramos Bounce para cada pin
// .attach (pin) y .interval (milisegundos de rebote)
debouncer0.attach(I0); debouncer0.interval(25);
debouncer1.attach(I1); debouncer1.interval(25);
debouncer2.attach(I2); debouncer2.interval(25);
debouncer3.attach(I3); debouncer3.interval(25);
debouncer4.attach(I4); debouncer4.interval(25);
debouncer5.attach(I5); debouncer5.interval(25);
debouncer6.attach(I6); debouncer6.interval(25);
pinMode(Q0, OUTPUT);
pinMode(Q1, OUTPUT);
pinMode(Q2, OUTPUT);
pinMode(Q3, OUTPUT);
pinMode(Q4, OUTPUT);
pinMode(Q5, OUTPUT);
pinMode(Q6, OUTPUT);
lcd.begin(20, 4);
lcd.createChar(0, bajo);
lcd.createChar(1, alto);
delay(200);
lcd.setBacklight(255);
WiFi.softAP(ssid, password);
server.begin();
}
void loop() {
// 1. ACTUALIZAR LOS DEBOUNCERS (Muy importante al inicio del loop)
debouncer0.update();
debouncer1.update();
debouncer2.update();
debouncer3.update();
debouncer4.update();
debouncer5.update();
debouncer6.update();
// 2. LÓGICA DE PULSADOR TOGGLE USANDO .fell()
// .fell() devuelve TRUE si el pin pasó de HIGH a LOW (presionado)
if (debouncer0.fell()) estado_Q0 = !estado_Q0;
if (debouncer1.fell()) estado_Q1 = !estado_Q1;
if (debouncer2.fell()) estado_Q2 = !estado_Q2;
if (debouncer3.fell()) estado_Q3 = !estado_Q3;
if (debouncer4.fell()) estado_Q4 = !estado_Q4;
if (debouncer5.fell()) estado_Q5 = !estado_Q5;
if (debouncer6.fell()) estado_Q6 = !estado_Q6;
WiFiClient client = server.available();
// Actualizamos salidas físicas
digitalWrite(Q0, estado_Q0);
digitalWrite(Q1, estado_Q1);
digitalWrite(Q2, estado_Q2);
digitalWrite(Q3, estado_Q3);
digitalWrite(Q4, estado_Q4);
digitalWrite(Q5, estado_Q5);
digitalWrite(Q6, estado_Q6);
estados();
if (client) {
currentTime = millis();
previousTime = currentTime;
String currentLine = "";
while (client.connected() && currentTime - previousTime <= timeoutTime) {
currentTime = millis();
if (client.available()) {
char c = client.read();
request += c;
if (c == '\n') {
if (currentLine.length() == 0) {
// Lógica Web Server
if (request.indexOf("/Q0_on") != -1) estado_Q0 = HIGH;
if (request.indexOf("/Q0_off") != -1) estado_Q0 = LOW;
if (request.indexOf("/Q1_on") != -1) estado_Q1 = HIGH;
if (request.indexOf("/Q1_off") != -1) estado_Q1 = LOW;
if (request.indexOf("/Q2_on") != -1) estado_Q2 = HIGH;
if (request.indexOf("/Q2_off") != -1) estado_Q2 = LOW;
if (request.indexOf("/Q3_on") != -1) estado_Q3 = HIGH;
if (request.indexOf("/Q3_off") != -1) estado_Q3 = LOW;
if (request.indexOf("/Q4_on") != -1) estado_Q4 = HIGH;
if (request.indexOf("/Q4_off") != -1) estado_Q4 = LOW;
if (request.indexOf("/Q5_on") != -1) estado_Q5 = HIGH;
if (request.indexOf("/Q5_off") != -1) estado_Q5 = LOW;
if (request.indexOf("/Q6_on") != -1) estado_Q6 = HIGH;
if (request.indexOf("/Q6_off") != -1) estado_Q6 = LOW;
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Connection: close");
client.println();
client.println("<!DOCTYPE HTML><html><head>");
client.println("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">");
client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
client.println(".button {display: inline-table;width: 80px;border: none;color: white;padding: 13px 30px;}");
client.println(".button-on {background-color: #1abc9c;} .button-off {background-color: #34495e;}</style></head>");
client.println("<body><h1>ESP32 Control</h1>");
// Botones Web
for(int i=0; i<=6; i++){
bool est;
if(i==0) est = estado_Q0; else if(i==1) est = estado_Q1; else if(i==2) est = estado_Q2;
else if(i==3) est = estado_Q3; else if(i==4) est = estado_Q4; else if(i==5) est = estado_Q5;
else est = estado_Q6;
if(est) client.printf("<a class=\"button button-on\" href=\"/Q%d_off\">Q%d: ON</a>", i, i);
else client.printf("<a class=\"button button-off\" href=\"/Q%d_on\">Q%d: OFF</a>", i, i);
}
client.println("</body></html>");
break;
} else currentLine = "";
} else if (c != '\r') currentLine += c;
}
}
request = "";
client.stop();
}
}