#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
// ===== LCD 20x4 I2C =====
// En Wokwi suele funcionar 0x27. Si no, prueba 0x3F.
LiquidCrystal_I2C lcd(0x27, 20, 4);
// ===== Keypad 4x4 =====
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
{ '1','2','3','A' },
{ '4','5','6','B' },
{ '7','8','9','C' },
{ '*','0','#','D' }
};
// Pines según diagram.json
byte rowPins[ROWS] = { 13, 12, 14, 27 };
byte colPins[COLS] = { 26, 25, 33, 32 };
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
// ===== Menú / Estados =====
enum Screen {
SCR_HOME = 0,
SCR_MENU,
SCR_PIN,
SCR_INFO
};
Screen screen = SCR_HOME;
int menuIndex = 0;
const int MENU_COUNT = 3;
const char* menuItems[MENU_COUNT] = {
"1) Estado GPIO",
"2) Ingresar PIN",
"3) Info sistema"
};
String pinBuffer = "";
const String correctPIN = "1234";
// ===== Helpers =====
void lcdClearLine(int row) {
lcd.setCursor(0, row);
for (int i = 0; i < 20; i++) lcd.print(' ');
}
void drawHome() {
lcd.clear();
lcd.setCursor(0,0); lcd.print("ESP32 + LCD20x4");
lcd.setCursor(0,1); lcd.print("Keypad 4x4 Wokwi");
lcd.setCursor(0,2); lcd.print("D=Menu C=Info");
lcd.setCursor(0,3); lcd.print("A/B navega dentro");
}
void drawMenu() {
lcd.clear();
lcd.setCursor(0,0); lcd.print("== MENU (A/B) ==");
// Mostramos 2 items por pantalla
int first = menuIndex;
int second = (menuIndex + 1) % MENU_COUNT;
lcd.setCursor(0,1); lcd.print("> "); lcd.print(menuItems[first]);
lcd.setCursor(0,2); lcd.print(" "); lcd.print(menuItems[second]);
lcd.setCursor(0,3); lcd.print("D=Sel C=Atras");
}
void drawPin() {
lcd.clear();
lcd.setCursor(0,0); lcd.print("== INGRESAR PIN ==");
lcd.setCursor(0,1); lcd.print("PIN: ");
lcd.setCursor(5,1);
for (size_t i = 0; i < pinBuffer.length(); i++) lcd.print('*');
lcd.setCursor(0,2); lcd.print("*=Borrar #=OK");
lcd.setCursor(0,3); lcd.print("C=Atras");
}
void drawInfo() {
lcd.clear();
lcd.setCursor(0,0); lcd.print("== INFO ==");
lcd.setCursor(0,1); lcd.print("ESP32 DevKit (sim)");
lcd.setCursor(0,2); lcd.print("I2C SDA=21 SCL=22");
lcd.setCursor(0,3); lcd.print("C=Atras");
}
void showToast(const String& msg) {
lcdClearLine(3);
lcd.setCursor(0,3);
lcd.print(msg.substring(0, 20));
}
void setup() {
Wire.begin(21, 22);
lcd.init();
lcd.backlight();
drawHome();
}
void loop() {
char k = keypad.getKey();
if (!k) return;
// Teclas recomendadas:
// A = arriba / anterior
// B = abajo / siguiente
// C = back
// D = enter / menu
if (screen == SCR_HOME) {
if (k == 'D') { screen = SCR_MENU; menuIndex = 0; drawMenu(); }
else if (k == 'C') { screen = SCR_INFO; drawInfo(); }
return;
}
if (screen == SCR_MENU) {
if (k == 'A') {
menuIndex = (menuIndex - 1 + MENU_COUNT) % MENU_COUNT;
drawMenu();
} else if (k == 'B') {
menuIndex = (menuIndex + 1) % MENU_COUNT;
drawMenu();
} else if (k == 'C') {
screen = SCR_HOME;
drawHome();
} else if (k == 'D') {
// Selección
if (menuIndex == 0) {
lcd.clear();
lcd.setCursor(0,0); lcd.print("== ESTADO GPIO ==");
lcd.setCursor(0,1); lcd.print("GPIO 2: (demo)");
lcd.setCursor(0,2); lcd.print("No hardware real");
lcd.setCursor(0,3); lcd.print("C=Atras");
screen = SCR_INFO; // reutilizamos pantalla simple
} else if (menuIndex == 1) {
pinBuffer = "";
screen = SCR_PIN;
drawPin();
} else if (menuIndex == 2) {
screen = SCR_INFO;
drawInfo();
}
}
return;
}
if (screen == SCR_PIN) {
if (k == 'C') {
screen = SCR_MENU;
drawMenu();
return;
}
if (k >= '0' && k <= '9') {
if (pinBuffer.length() < 8) pinBuffer += k;
drawPin();
return;
}
if (k == '*') {
if (pinBuffer.length() > 0) pinBuffer.remove(pinBuffer.length() - 1);
drawPin();
return;
}
if (k == '#') {
if (pinBuffer == correctPIN) {
showToast("PIN OK! Acceso granted");
} else {
showToast("PIN incorrecto");
}
delay(700);
drawPin();
return;
}
return;
}
// SCR_INFO o pantallas simples: C regresa
if (screen == SCR_INFO) {
if (k == 'C') {
screen = SCR_MENU;
drawMenu();
}
return;
}
}
110v
L1
L2
L3
T1
T2
T3
A1
A2
BOVINA
MOTOR 2
INTERRUPTOR
MOTOR 1
RC SNUBBER
CONTACTOR