#include <Arduino.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <U8g2lib.h>
#include <Wire.h>
// Credenciales WiFi
const char* ssid = "Wokwi-GUEST";
const char* password = "";
// Configuración MQTT
const char* mqtt_server = "broker.emqx.io";
const int mqtt_port = 1883;
// Sin ID de cliente fijo
// Tópicos MQTT
const char* topicVoltaje = "medicion/voltaje";
const char* topicCorriente = "medicion/corriente";
const char* topicFrecuencia = "medicion/frecuencia";
const char* topicPotencia = "medicion/potencia";
const char* topicEstado = "medicion/estado";
const char* topicControlReset = "control/reset";
// Definición de pines
const int voltagePin = 1;
const int currentPin = 2;
const int controlPin = 3;
const int statusLedPin = 9;
const int buttonOnOffPin = 4;
const int buttonResetPin = 5;
// Definición de pines para OLED
#define SDA_PIN 19
#define SCL_PIN 18
// Constructor para pantalla OLED con I2C
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
// Variables de estado de los botones
bool lastButtonState = HIGH;
bool lastResetState = HIGH;
// Límites de seguridad
float minVoltage = 108.5;
float maxVoltage = 110.5;
float maxCurrent = 25.0;
// Variables de medición
float VAC = 0.0, lastVAC = -1;
float AmpAC = 0.0, lastAmpAC = -1;
float Power = 0.0, lastPower = -1;
float Frequency = 50.0, lastFrequency = -1;
bool systemEnabled = true, lastSystemState = true;
// Tiempos de actualización
unsigned long lastVoltageReadTime = 0;
unsigned long lastCurrentReadTime = 0;
unsigned long lastControlTime = 0;
unsigned long lastMqttPublishTime = 0;
unsigned long lastOledUpdateTime = 0;
unsigned long lastWiFiCheckTime = 0;
// Variables para control de pantalla
int displayMode = 0; // 0=Datos, 1=Gráfico, 2=Estado
unsigned long displaySwitchTime = 0;
const long displaySwitchInterval = 5000; // Cambiar de vista cada 5 segundos
// Objetos para WiFi y MQTT
WiFiClient espClient;
PubSubClient client(espClient);
// Función para generar ID de cliente aleatorio
String generateRandomClientId() {
String clientId = "ESP32Client-";
clientId += String(random(0xffff), HEX);
return clientId;
}
// Lectura de voltaje simulada
float readVoltageFromADC() {
int adcValue = analogRead(voltagePin);
return map(adcValue, 0, 4095, 0, 1300) / 10.0;
}
// Lectura de corriente simulada
float readCurrentFromADC() {
int adcValue = analogRead(currentPin);
return map(adcValue, 0, 4095, 0, 250) / 10.0;
}
// Cálculo de potencia
float calculatePower(float voltage, float current) {
return voltage * current;
}
// Configuración de la conexión WiFi
void setupWiFi() {
delay(10);
Serial.println();
Serial.print("Conectando a WiFi: ");
Serial.println(ssid);
WiFi.begin(ssid, password);
// Mostrar intento de conexión en la pantalla OLED
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawStr(0, 10, "Conectando a WiFi...");
u8g2.drawStr(0, 25, ssid);
u8g2.sendBuffer();
int dotCount = 0;
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
// Actualizar pantalla con puntos para mostrar progreso
if (dotCount % 5 == 0) {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawStr(0, 10, "Conectando a WiFi...");
u8g2.drawStr(0, 25, ssid);
}
String dots = "";
for (int i = 0; i < (dotCount % 5) + 1; i++) {
dots += ".";
}
u8g2.drawStr(0, 40, dots.c_str());
u8g2.sendBuffer();
dotCount++;
}
Serial.println("");
Serial.println("WiFi conectado");
Serial.print("Dirección IP: ");
Serial.println(WiFi.localIP());
// Mostrar información de conexión en la pantalla
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawStr(0, 10, "WiFi conectado!");
u8g2.drawStr(0, 25, "IP:");
u8g2.drawStr(20, 25, WiFi.localIP().toString().c_str());
u8g2.drawStr(0, 40, "Conectando MQTT...");
u8g2.sendBuffer();
}
// Reconexión al servidor MQTT
void reconnectMQTT() {
int attempts = 0;
while (!client.connected() && attempts < 3) {
Serial.print("Conectando al servidor MQTT...");
// Mostrar intento en pantalla
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawStr(0, 10, "Conectando MQTT");
u8g2.drawStr(0, 25, mqtt_server);
u8g2.drawStr(0, 40, "Intento:");
u8g2.drawStr(50, 40, String(attempts + 1).c_str());
u8g2.sendBuffer();
// Generar un ID de cliente aleatorio
String clientId = generateRandomClientId();
Serial.print("ID de cliente: ");
Serial.print(clientId);
if (client.connect(clientId.c_str())) {
Serial.println(" - conectado");
// Suscribirse al tópico de control
client.subscribe(topicControlReset);
Serial.println("Suscrito al tópico de control/reset");
// Actualizar pantalla con conexión exitosa
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawStr(0, 10, "MQTT Conectado!");
u8g2.drawStr(0, 25, "Sistema listo");
u8g2.drawStr(0, 40, "Iniciando monitoreo");
u8g2.sendBuffer();
delay(1000);
} else {
Serial.print(" - falló, rc=");
Serial.print(client.state());
Serial.println(" intentando de nuevo en 2 segundos");
// Actualizar pantalla con fallo
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawStr(0, 10, "Error conexión MQTT");
u8g2.drawStr(0, 25, "Reintentando...");
u8g2.sendBuffer();
delay(2000);
}
attempts++;
}
if (!client.connected()) {
// Si falló después de varios intentos, mostrar error
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawStr(0, 10, "ERROR MQTT");
u8g2.drawStr(0, 25, "Continuando sin");
u8g2.drawStr(0, 40, "conexión al broker");
u8g2.sendBuffer();
delay(2000);
}
}
// Callback para procesar mensajes MQTT entrantes
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Mensaje recibido en tópico: ");
Serial.println(topic);
// Convertir payload a string para comparar
char mensaje[length + 1];
for (unsigned int i = 0; i < length; i++) {
mensaje[i] = (char)payload[i];
}
mensaje[length] = '\0';
Serial.print("Mensaje: ");
Serial.println(mensaje);
// Procesar comando de reset
if (strcmp(topic, topicControlReset) == 0) {
if (strcmp(mensaje, "reset") == 0) {
Serial.println("🔄 Reiniciando sistema por comando MQTT...");
resetSystem();
}
}
}
// Función para reiniciar el sistema
void resetSystem() {
Serial.println("🔄 Reiniciando sistema...");
// Mostrar reinicio en pantalla
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawStr(0, 20, "REINICIANDO");
u8g2.drawStr(0, 40, "Sistema...");
u8g2.sendBuffer();
delay(500);
// Apagar el sistema antes de reiniciar
systemEnabled = false;
digitalWrite(controlPin, LOW);
digitalWrite(statusLedPin, LOW);
Serial.println("⚠️ Sistema APAGADO durante reinicio");
// Publicar estado en MQTT
if (client.connected()) {
client.publish(topicEstado, "REINICIANDO");
}
// Encender el sistema nuevamente
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawStr(0, 20, "ESPERE");
u8g2.drawStr(0, 40, "Reiniciando...");
u8g2.sendBuffer();
delay(5000); // Un pequeño retraso para simular apagado/encendido
systemEnabled = true;
digitalWrite(controlPin, HIGH);
digitalWrite(statusLedPin, HIGH);
Serial.println("🔵 Sistema ENCENDIDO después del reinicio");
// Verificar si los datos de los sensores son válidos
float tempVAC = readVoltageFromADC();
float tempAmpAC = readCurrentFromADC();
if (tempVAC > 0 && tempAmpAC >= 0) {
VAC = tempVAC;
AmpAC = tempAmpAC;
Power = calculatePower(VAC, AmpAC);
Serial.println("✅ Datos válidos. Sistema listo para operar");
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawStr(0, 20, "SISTEMA LISTO");
u8g2.drawStr(0, 40, "Operando normalmente");
u8g2.sendBuffer();
if (client.connected()) {
client.publish(topicEstado, "ACTIVO");
}
} else {
Serial.println("❌ Error en medición. Verifique sensores");
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawStr(0, 20, "ERROR SENSORES");
u8g2.drawStr(0, 40, "Verifique conexiones");
u8g2.sendBuffer();
systemEnabled = false;
digitalWrite(controlPin, LOW);
digitalWrite(statusLedPin, LOW);
if (client.connected()) {
client.publish(topicEstado, "ERROR");
}
}
}
// Control de potencia
void controlPower() {
if (!systemEnabled) {
digitalWrite(controlPin, LOW);
digitalWrite(statusLedPin, LOW);
if (lastSystemState != systemEnabled) {
Serial.println("🔴 Sistema APAGADO manualmente: Carga desactivada");
if (client.connected()) {
client.publish(topicEstado, "INACTIVO");
}
}
lastSystemState = systemEnabled;
return;
}
bool withinLimits = (VAC >= minVoltage && VAC <= maxVoltage && AmpAC <= maxCurrent);
if (withinLimits) {
digitalWrite(controlPin, HIGH);
digitalWrite(statusLedPin, HIGH);
if (lastSystemState != systemEnabled) {
Serial.println("✅ Carga activada: Condiciones óptimas");
if (client.connected()) {
client.publish(topicEstado, "ACTIVO");
}
}
} else {
digitalWrite(controlPin, LOW);
digitalWrite(statusLedPin, LOW);
if (lastSystemState != systemEnabled) {
if (VAC < minVoltage) {
Serial.println("⚠️ Bajo voltaje: Carga desactivada");
if (client.connected()) {
client.publish(topicEstado, "BAJO_VOLTAJE");
}
}
else if (VAC > maxVoltage) {
Serial.println("⚠️ Sobrevoltaje: Carga desactivada");
if (client.connected()) {
client.publish(topicEstado, "SOBREVOLTAJE");
}
}
else if (AmpAC > maxCurrent) {
Serial.println("⚠️ Sobrecorriente: Carga desactivada");
if (client.connected()) {
client.publish(topicEstado, "SOBRECORRIENTE");
}
}
}
}
lastSystemState = systemEnabled;
}
// Mostrar datos en Serial solo si cambian
void displayData() {
bool voltageChanged = fabs(VAC - lastVAC) > 0.5;
bool currentChanged = fabs(AmpAC - lastAmpAC) > 0.1;
bool powerChanged = fabs(Power - lastPower) > 1.0;
bool frequencyChanged = fabs(Frequency - lastFrequency) > 0.1;
if (voltageChanged || currentChanged || powerChanged || frequencyChanged) {
Serial.println("------- DATOS DEL SISTEMA -------");
Serial.print("Voltaje: "); Serial.print(VAC, 2); Serial.println(" V");
Serial.print("Corriente: "); Serial.print(AmpAC, 2); Serial.println(" A");
Serial.print("Potencia: "); Serial.print(Power, 2); Serial.println(" W");
Serial.print("Frecuencia: "); Serial.print(Frequency, 1); Serial.println(" Hz");
Serial.print("Estado del sistema: "); Serial.println(systemEnabled ? "ACTIVO" : "INACTIVO");
Serial.println("--------------------------------");
lastVAC = VAC;
lastAmpAC = AmpAC;
lastPower = Power;
lastFrequency = Frequency;
}
}
// Actualizar pantalla OLED con información relevante
void updateOLED() {
unsigned long currentMillis = millis();
// Cambiar de pantalla automáticamente cada cierto tiempo
if (currentMillis - displaySwitchTime >= displaySwitchInterval) {
displayMode = (displayMode + 1) % 3; // Rotar entre 3 vistas
displaySwitchTime = currentMillis;
}
// Determinar qué pantalla mostrar según el modo
switch (displayMode) {
case 0: // Pantalla de datos principales
showMainDataScreen();
break;
case 1: // Pantalla de gráfico/barras
showGraphScreen();
break;
case 2: // Pantalla de estado y WiFi/MQTT
showStatusScreen();
break;
}
}
// Pantalla 1: Datos principales
void showMainDataScreen() {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr);
// Título
u8g2.drawStr(0, 10, "MEDICION ELECTRICA");
u8g2.drawLine(0, 12, 127, 12);
// Datos principales
char buffer[20];
// Voltaje
u8g2.drawStr(0, 25, "Voltaje:");
sprintf(buffer, "%.1f V", VAC);
u8g2.drawStr(75, 25, buffer);
// Corriente
u8g2.drawStr(0, 35, "Corriente:");
sprintf(buffer, "%.1f A", AmpAC);
u8g2.drawStr(75, 35, buffer);
// Potencia
u8g2.drawStr(0, 45, "Potencia:");
sprintf(buffer, "%.1f W", Power);
u8g2.drawStr(75, 45, buffer);
// Frecuencia
u8g2.drawStr(0, 55, "Frecuencia:");
sprintf(buffer, "%.1f Hz", Frequency);
u8g2.drawStr(75, 55, buffer);
// Indicador de página
u8g2.drawStr(0, 64, "Pag 1/3");
// Estado del sistema (activo/inactivo)
if (systemEnabled) {
u8g2.drawStr(80, 64, "ACTIVO");
} else {
u8g2.drawStr(80, 64, "INACTIVO");
}
u8g2.sendBuffer();
}
// Pantalla 2: Gráfico de barras
void showGraphScreen() {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr);
// Título
u8g2.drawStr(0, 10, "NIVELES");
u8g2.drawLine(0, 12, 127, 12);
// Barra de voltaje (normalizada entre mínimo y máximo)
int voltageBarWidth = 0;
if (VAC >= minVoltage && VAC <= maxVoltage) {
// Si está dentro del rango, calcular tamaño proporcional
voltageBarWidth = map(VAC, minVoltage, maxVoltage, 0, 100);
} else if (VAC < minVoltage) {
voltageBarWidth = 0; // Bajo voltaje
} else {
voltageBarWidth = 100; // Sobre voltaje
}
u8g2.drawStr(0, 25, "V:");
u8g2.drawFrame(15, 17, 105, 10);
u8g2.drawBox(15, 17, voltageBarWidth, 10);
// Barra de corriente (0 a maxCurrent)
int currentBarWidth = map(AmpAC, 0, maxCurrent, 0, 100);
currentBarWidth = constrain(currentBarWidth, 0, 100);
u8g2.drawStr(0, 40, "A:");
u8g2.drawFrame(15, 32, 105, 10);
u8g2.drawBox(15, 32, currentBarWidth, 10);
// Barra de potencia (0 a maxVoltage*maxCurrent o un valor razonable)
float maxPower = maxVoltage * maxCurrent;
int powerBarWidth = map(Power, 0, maxPower, 0, 100);
powerBarWidth = constrain(powerBarWidth, 0, 100);
u8g2.drawStr(0, 55, "W:");
u8g2.drawFrame(15, 47, 105, 10);
u8g2.drawBox(15, 47, powerBarWidth, 10);
// Indicador de página
u8g2.drawStr(0, 64, "Pag 2/3");
u8g2.sendBuffer();
}
// Pantalla 3: Estado y conexiones
void showStatusScreen() {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr);
// Título
u8g2.drawStr(0, 10, "ESTADO DEL SISTEMA");
u8g2.drawLine(0, 12, 127, 12);
// Estado WiFi
u8g2.drawStr(0, 25, "WiFi:");
if (WiFi.status() == WL_CONNECTED) {
u8g2.drawStr(60, 25, "Conectado");
} else {
u8g2.drawStr(60, 25, "Desconectado");
}
// Estado MQTT
u8g2.drawStr(0, 35, "MQTT:");
if (client.connected()) {
u8g2.drawStr(60, 35, "Conectado");
} else {
u8g2.drawStr(60, 35, "Desconectado");
}
// Estado del sistema
u8g2.drawStr(0, 45, "Sistema:");
// Determinar mensaje de estado
const char* statusMsg;
if (!systemEnabled) {
statusMsg = "APAGADO";
} else if (VAC < minVoltage) {
statusMsg = "BAJO VOLTAJE";
} else if (VAC > maxVoltage) {
statusMsg = "ALTO VOLTAJE";
} else if (AmpAC > maxCurrent) {
statusMsg = "SOBRECORRIENTE";
} else {
statusMsg = "ACTIVO OK";
}
u8g2.drawStr(60, 45, statusMsg);
// Botones disponibles
u8g2.drawStr(0, 55, "BTN4: On/Off BTN5: Reset");
// Indicador de página
u8g2.drawStr(0, 64, "Pag 3/3");
u8g2.sendBuffer();
}
// Publicar datos por MQTT
void publishMQTTData() {
if (!client.connected()) {
return; // No publicar si no hay conexión
}
char buffer[10];
// Publicar voltaje
dtostrf(VAC, 6, 2, buffer);
client.publish(topicVoltaje, buffer);
// Publicar corriente
dtostrf(AmpAC, 6, 2, buffer);
client.publish(topicCorriente, buffer);
// Publicar potencia
dtostrf(Power, 6, 2, buffer);
client.publish(topicPotencia, buffer);
// Publicar frecuencia
dtostrf(Frequency, 6, 2, buffer);
client.publish(topicFrecuencia, buffer);
// Publicar estado
client.publish(topicEstado, systemEnabled ? "ACTIVO" : "INACTIVO");
}
// Manejo de botones
void handleButtons() {
bool buttonState = digitalRead(buttonOnOffPin);
bool resetState = digitalRead(buttonResetPin);
// Botón ON/OFF
if (buttonState == LOW && lastButtonState == HIGH) {
systemEnabled = !systemEnabled;
Serial.println(systemEnabled ? "🔵 Sistema ENCENDIDO manualmente" : "🔴 Sistema APAGADO manualmente");
// Mostrar cambio en OLED
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB10_tr);
u8g2.drawStr(0, 25, systemEnabled ? "ENCENDIDO" : "APAGADO");
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawStr(0, 45, "Cambio de estado");
u8g2.sendBuffer();
delay(1000);
if (client.connected()) {
client.publish(topicEstado, systemEnabled ? "ACTIVO" : "INACTIVO");
}
}
lastButtonState = buttonState;
// Botón RESET
if (resetState == LOW && lastResetState == HIGH) {
resetSystem();
}
lastResetState = resetState;
}
// Verificar estado de WiFi
void checkWiFiConnection() {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("Conexión WiFi perdida. Reconectando...");
// Mostrar reconexión en pantalla
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawStr(0, 20, "WiFi desconectado");
u8g2.drawStr(0, 35, "Reconectando...");
u8g2.sendBuffer();
WiFi.disconnect();
WiFi.begin(ssid, password);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 10) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("WiFi reconectado");
} else {
Serial.println("Falló la reconexión WiFi");
}
}
}
void setup() {
Serial.begin(115200);
Serial.println("Inicializando sistema...");
// Inicializar el generador de números aleatorios
randomSeed(analogRead(0));
analogReadResolution(12);
pinMode(controlPin, OUTPUT);
pinMode(statusLedPin, OUTPUT);
pinMode(buttonOnOffPin, INPUT_PULLUP);
pinMode(buttonResetPin, INPUT_PULLUP);
digitalWrite(controlPin, LOW);
digitalWrite(statusLedPin, LOW);
// Inicializar la pantalla OLED
Wire.begin(SDA_PIN, SCL_PIN);
u8g2.begin();
// Pantalla de bienvenida
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB10_tr);
u8g2.drawStr(5, 20, "ESP32-C6");
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawStr(0, 35, "Sistema de Monitoreo");
u8g2.drawStr(20, 50, "Iniciando...");
u8g2.sendBuffer();
delay(2000);
// Configurar WiFi
setupWiFi();
// Configurar MQTT
client.setServer(mqtt_server, mqtt_port);
client.setCallback(callback);
// Conectar a MQTT
if (WiFi.status() == WL_CONNECTED) {
reconnectMQTT();
}
Serial.println("Sistema listo");
}
void loop() {
unsigned long currentMillis = millis();
// Verificar estado de WiFi cada 30 segundos
if (currentMillis - lastWiFiCheckTime >= 30000) {
checkWiFiConnection();
lastWiFiCheckTime = currentMillis;
}
// Verificar conexión MQTT
if (WiFi.status() == WL_CONNECTED && !client.connected()) {
reconnectMQTT();
}
client.loop();
handleButtons();
if (currentMillis - lastVoltageReadTime >= 100) {
VAC = readVoltageFromADC();
lastVoltageReadTime = currentMillis;
}
if (currentMillis - lastCurrentReadTime >= 100) {
AmpAC = readCurrentFromADC();
lastCurrentReadTime = currentMillis;
}
Power = calculatePower(VAC, AmpAC);
if (currentMillis - lastControlTime >= 200) {
controlPower();
lastControlTime = currentMillis;
}
displayData();
// Publicar datos MQTT cada segundo (1000ms)
if (currentMillis - lastMqttPublishTime >= 1000) {
publishMQTTData();
lastMqttPublishTime = currentMillis;
}
// Actualizar la pantalla OLED cada 500ms
if (currentMillis - lastOledUpdateTime >= 500) {
updateOLED();
lastOledUpdateTime = currentMillis;
}
delay(5);
}Loading
esp32-c6-devkitc-1
esp32-c6-devkitc-1
Voltage Sensor
Current Sensor
SSR Out
POWER ON/OFF
RESET