#include <Arduino.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <U8g2lib.h>
#include <Wire.h>
// Definición de pines para ESP32-C6
#define SDA_PIN 19
#define SCL_PIN 18
// Pines disponibles: 13,12,23,20,19,18,9,0,1,2,3,4,5
const int voltagePin = 1; // ADC para lectura de voltaje
const int currentPin = 2; // ADC para lectura de corriente
const int controlPin = 3; // Pin para control de carga
const int statusLedPin = 4; // LED de estado
const int buttonOnOffPin = 5; // Botón de encendido/apagado
const int buttonResetPin = 9; // Botón de reset
// Constructor para pantalla OLED con I2C
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
// 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;
// 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";
// 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;
// Variables para la pantalla OLED
int displayPage = 0;
unsigned long displayChangeTime = 0;
const int displayPageDuration = 3000; // Duración de cada página en ms
// Tiempos de actualización
unsigned long lastVoltageReadTime = 0;
unsigned long lastCurrentReadTime = 0;
unsigned long lastControlTime = 0;
unsigned long lastMqttPublishTime = 0;
unsigned long lastDisplayUpdateTime = 0;
// Objetos para WiFi y MQTT
WiFiClient espClient;
PubSubClient client(espClient);
// Función para generar ID de cliente aleatorio
String generateRandomClientId() {
String clientId = "ESP32C6Client-";
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);
// Mostrar en OLED
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawStr(0, 10, "Conectando WiFi...");
u8g2.drawStr(0, 25, ssid);
u8g2.sendBuffer();
WiFi.begin(ssid, password);
int dots = 0;
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
if (dots % 10 == 0) {
u8g2.clearBuffer();
u8g2.drawStr(0, 10, "Conectando WiFi...");
u8g2.drawStr(0, 25, ssid);
}
// Dibujar puntos de progreso
for (int i = 0; i < (dots % 10); i++) {
u8g2.drawStr(i*10, 40, ".");
}
u8g2.sendBuffer();
dots++;
}
Serial.println("");
Serial.println("WiFi conectado");
Serial.print("Dirección IP: ");
Serial.println(WiFi.localIP());
// Mostrar en OLED
u8g2.clearBuffer();
u8g2.drawStr(0, 10, "WiFi conectado!");
u8g2.drawStr(0, 25, "IP:");
u8g2.drawStr(20, 40, WiFi.localIP().toString().c_str());
u8g2.sendBuffer();
delay(2000);
}
// Reconexión al servidor MQTT
void reconnectMQTT() {
// Mostrar en OLED
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawStr(0, 10, "Conectando MQTT...");
u8g2.drawStr(0, 25, mqtt_server);
u8g2.sendBuffer();
int attempts = 0;
while (!client.connected() && attempts < 5) {
Serial.print("Conectando al servidor MQTT...");
// 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");
// Mostrar en OLED
u8g2.clearBuffer();
u8g2.drawStr(0, 10, "MQTT conectado");
u8g2.drawStr(0, 25, "Suscrito a:");
u8g2.drawStr(0, 40, "control/reset");
u8g2.sendBuffer();
delay(1000);
} else {
Serial.print(" - falló, rc=");
Serial.print(client.state());
Serial.println(" intentando de nuevo en 2 segundos");
// Mostrar en OLED
u8g2.clearBuffer();
u8g2.drawStr(0, 10, "Fallo MQTT!");
u8g2.drawStr(0, 25, "Reintentando...");
char buf[10];
sprintf(buf, "RC: %d", client.state());
u8g2.drawStr(0, 40, buf);
u8g2.sendBuffer();
delay(2000);
attempts++;
}
}
if (!client.connected()) {
// Mostrar en OLED
u8g2.clearBuffer();
u8g2.drawStr(0, 10, "MQTT no conectado");
u8g2.drawStr(0, 25, "Continuando sin");
u8g2.drawStr(0, 40, "conexión MQTT");
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...");
// Mostrar en OLED
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawStr(0, 20, "RESET por MQTT");
u8g2.drawStr(0, 40, "Reiniciando...");
u8g2.sendBuffer();
delay(1000);
resetSystem();
}
}
}
// Función para reiniciar el sistema
void resetSystem() {
Serial.println("🔄 Reiniciando sistema...");
// Mostrar en OLED
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");
}
// Actualizar OLED
u8g2.clearBuffer();
u8g2.drawStr(0, 20, "SISTEMA APAGADO");
u8g2.drawStr(0, 40, "PARA REINICIAR");
u8g2.sendBuffer();
delay(2000);
// Encender el sistema nuevamente
delay(3000); // 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");
// Mostrar en OLED
u8g2.clearBuffer();
u8g2.drawStr(0, 20, "SISTEMA ACTIVO");
u8g2.drawStr(0, 40, "DATOS VALIDOS");
u8g2.sendBuffer();
if (client.connected()) {
client.publish(topicEstado, "ACTIVO");
}
} else {
Serial.println("❌ Error en medición. Verifique sensores");
// Mostrar en OLED
u8g2.clearBuffer();
u8g2.drawStr(0, 20, "ERROR DE SENSORES");
u8g2.drawStr(0, 40, "SISTEMA DESACTIVADO");
u8g2.sendBuffer();
systemEnabled = false;
digitalWrite(controlPin, LOW);
digitalWrite(statusLedPin, LOW);
if (client.connected()) {
client.publish(topicEstado, "ERROR");
}
}
delay(2000);
}
// 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
void updateDisplay() {
unsigned long currentMillis = millis();
// Cambiar página automáticamente cada displayPageDuration ms
if (currentMillis - displayChangeTime >= displayPageDuration) {
displayPage = (displayPage + 1) % 3; // 3 páginas en total
displayChangeTime = currentMillis;
}
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr);
// Página 1: Datos principales
if (displayPage == 0) {
u8g2.drawStr(0, 10, "MONITOR DE ENERGIA");
u8g2.drawLine(0, 12, 127, 12);
char buffer[20];
sprintf(buffer, "Voltaje: %.1f V", VAC);
u8g2.drawStr(0, 25, buffer);
sprintf(buffer, "Corriente: %.1f A", AmpAC);
u8g2.drawStr(0, 37, buffer);
sprintf(buffer, "Potencia: %.1f W", Power);
u8g2.drawStr(0, 49, buffer);
u8g2.drawStr(0, 61, systemEnabled ? "Estado: ACTIVO" : "Estado: INACTIVO");
}
// Página 2: Gráfico de barras
else if (displayPage == 1) {
u8g2.drawStr(0, 10, "NIVELES DE MEDICION");
u8g2.drawLine(0, 12, 127, 12);
// Barra de voltaje (referencia 220V)
u8g2.drawStr(0, 25, "V:");
u8g2.drawFrame(15, 18, 112, 8);
int voltBar = map(constrain(VAC, 0, 220), 0, 220, 0, 112);
u8g2.drawBox(15, 18, voltBar, 8);
// Barra de corriente (referencia 25A)
u8g2.drawStr(0, 40, "A:");
u8g2.drawFrame(15, 33, 112, 8);
int currBar = map(constrain(AmpAC, 0, 25), 0, 25, 0, 112);
u8g2.drawBox(15, 33, currBar, 8);
// Barra de potencia (referencia 5000W)
u8g2.drawStr(0, 55, "W:");
u8g2.drawFrame(15, 48, 112, 8);
int powBar = map(constrain(Power, 0, 5000), 0, 5000, 0, 112);
u8g2.drawBox(15, 48, powBar, 8);
char buffer[20];
if (systemEnabled) {
sprintf(buffer, "Sistema ACTIVO");
} else {
sprintf(buffer, "Sistema INACTIVO");
}
u8g2.drawStr(0, 62, buffer);
}
// Página 3: Información de estado y red
else if (displayPage == 2) {
u8g2.drawStr(0, 10, "INFORMACION DE RED");
u8g2.drawLine(0, 12, 127, 12);
// Estado de WiFi
u8g2.drawStr(0, 24, "WiFi:");
u8g2.drawStr(40, 24, WiFi.status() == WL_CONNECTED ? "Conectado" : "Desconectado");
// Estado de MQTT
u8g2.drawStr(0, 36, "MQTT:");
u8g2.drawStr(40, 36, client.connected() ? "Conectado" : "Desconectado");
// IP asignada
u8g2.drawStr(0, 48, "IP:");
if (WiFi.status() == WL_CONNECTED) {
u8g2.drawStr(20, 48, WiFi.localIP().toString().c_str());
} else {
u8g2.drawStr(20, 48, "Sin conexion");
}
// Mensaje de estado
char statusMsg[20] = "Estado: ";
if (!systemEnabled) {
strcat(statusMsg, "INACTIVO");
} else if (VAC < minVoltage) {
strcat(statusMsg, "BAJO_V");
} else if (VAC > maxVoltage) {
strcat(statusMsg, "SOBRE_V");
} else if (AmpAC > maxCurrent) {
strcat(statusMsg, "SOBRE_A");
} else {
strcat(statusMsg, "NORMAL");
}
u8g2.drawStr(0, 60, statusMsg);
}
u8g2.sendBuffer();
}
// Publicar datos por MQTT
void publishMQTTData() {
if (!client.connected()) {
return;
}
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
if (!systemEnabled) {
client.publish(topicEstado, "INACTIVO");
} else if (VAC < minVoltage) {
client.publish(topicEstado, "BAJO_VOLTAJE");
} else if (VAC > maxVoltage) {
client.publish(topicEstado, "SOBREVOLTAJE");
} else if (AmpAC > maxCurrent) {
client.publish(topicEstado, "SOBRECORRIENTE");
} else {
client.publish(topicEstado, "ACTIVO");
}
}
// 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 en OLED
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawStr(0, 20, systemEnabled ? "ENCENDIENDO..." : "APAGANDO...");
u8g2.drawStr(0, 40, "MANUAL");
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;
}
void setup() {
Serial.begin(115200);
Serial.println("Inicializando sistema...");
// Inicializar el generador de números aleatorios
randomSeed(analogRead(0));
// Configurar resolución del ADC
analogReadResolution(12);
// Configurar pines
pinMode(controlPin, OUTPUT);
pinMode(statusLedPin, OUTPUT);
pinMode(buttonOnOffPin, INPUT_PULLUP);
pinMode(buttonResetPin, INPUT_PULLUP);
digitalWrite(controlPin, LOW);
digitalWrite(statusLedPin, LOW);
// Inicializar I2C con los pines específicos
Wire.begin(SDA_PIN, SCL_PIN);
// Inicializar OLED
u8g2.begin();
// Mostrar mensaje de inicio
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawStr(5, 20, "INICIANDO SISTEMA");
u8g2.drawStr(10, 35, "MonitorPotencia");
u8g2.drawStr(20, 50, "ESP32-C6");
u8g2.sendBuffer();
delay(2000);
// Configurar WiFi
setupWiFi();
// Configurar MQTT
client.setServer(mqtt_server, mqtt_port);
client.setCallback(callback);
Serial.println("Sistema listo");
// Mostrar mensaje de sistema listo
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawStr(0, 20, "SISTEMA LISTO");
u8g2.drawStr(0, 40, "Monitoreando...");
u8g2.sendBuffer();
delay(1000);
}
void loop() {
unsigned long currentMillis = millis();
// Verificar conexión MQTT
if (!client.connected()) {
reconnectMQTT();
}
client.loop();
// Manejar botones
handleButtons();
// Leer voltaje cada 100ms
if (currentMillis - lastVoltageReadTime >= 100) {
VAC = readVoltageFromADC();
lastVoltageReadTime = currentMillis;
}
// Leer corriente cada 100ms
if (currentMillis - lastCurrentReadTime >= 100) {
AmpAC = readCurrentFromADC();
lastCurrentReadTime = currentMillis;
}
// Calcular potencia
Power = calculatePower(VAC, AmpAC);
// Control de potencia cada 200ms
if (currentMillis - lastControlTime >= 200) {
controlPower();
lastControlTime = currentMillis;
}
// Mostrar datos en Serial
displayData();
// Actualizar pantalla OLED cada 500ms
if (currentMillis - lastDisplayUpdateTime >= 500) {
updateDisplay();
lastDisplayUpdateTime = currentMillis;
}
// Publicar datos MQTT cada segundo (1000ms)
if (currentMillis - lastMqttPublishTime >= 1000) {
publishMQTTData();
lastMqttPublishTime = currentMillis;
}
delay(5);
}Voltage Sensor
Current Sensor
SSR Out
POWER ON/OFF
RESET