#include <Arduino.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <EmonLib.h> // Incluir la biblioteca EmonLib para mediciones de energía
// 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* topicPotenciaActiva = "medicion/potencia_activa";
const char* topicPotenciaReactiva = "medicion/potencia_reactiva";
const char* topicPotenciaAparente = "medicion/potencia_aparente";
const char* topicFactorPotencia = "medicion/factor_potencia";
const char* topicEstado = "medicion/estado";
const char* topicControlReset = "control/reset";
// Definición de pines para medición de potencia
const int voltagePin = 1; // Pin ADC para el sensor de voltaje
const int currentPin = 2; // Pin ADC para el sensor de corriente
const int controlPin = 7; // Salida para control de relé
const int statusLedPin = 8; // LED indicador de estado
const int buttonOnOffPin = 4; // Botón de encendido/apagado
const int buttonResetPin = 5; // Botón de reinicio
const int zeroCrossPin = 25; // Pin para detección de cruce por cero (para cálculo de factor de potencia)
// Constantes de calibración
const float VCAL = 234.26; // Constante de calibración para el voltaje (230V AC)
const float ICAL = 111.1; // Constante de calibración para la corriente
const float PHASECAL = 1.7; // Calibración de fase
// 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;
float minPowerFactor = 0.85; // Factor de potencia mínimo aceptable
// Variables de medición
float VAC = 0.0, lastVAC = -1;
float AmpAC = 0.0, lastAmpAC = -1;
float ActivePower = 0.0, lastActivePower = -1;
float ReactivePower = 0.0, lastReactivePower = -1;
float ApparentPower = 0.0, lastApparentPower = -1;
float PowerFactor = 0.0, lastPowerFactor = -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 lastPowerCalcTime = 0;
unsigned long lastControlTime = 0;
unsigned long lastMqttPublishTime = 0;
unsigned long lastFrequencyCalcTime = 0;
unsigned long lastZeroCrossTime = 0;
volatile unsigned long zeroCrossTimes[10] = {0};
volatile byte zeroCrossCount = 0;
// Objetos para WiFi y MQTT
WiFiClient espClient;
PubSubClient client(espClient);
EnergyMonitor emon1; // Crear una instancia de EnergyMonitor
// Función para generar ID de cliente aleatorio
String generateRandomClientId() {
String clientId = "ESP32Client-";
clientId += String(random(0xffff), HEX);
return clientId;
}
// ISR para la detección de cruce por cero
void IRAM_ATTR zeroCrossDetected() {
unsigned long now = micros();
// Almacenar el tiempo para cálculo de frecuencia
zeroCrossTimes[zeroCrossCount % 10] = now;
zeroCrossCount++;
}
// Cálculo de frecuencia basado en cruces por cero
float calculateFrequency() {
if (zeroCrossCount < 10) return 50.0; // Valor predeterminado
unsigned long totalTime = 0;
for (int i = 1; i < 10; i++) {
unsigned long diff = zeroCrossTimes[i % 10] - zeroCrossTimes[(i - 1) % 10];
// Solo considerar períodos válidos (evitar wraparound o mediciones erróneas)
if (diff > 0 && diff < 25000) { // 25000 microsegundos = 40Hz mínimo
totalTime += diff;
}
}
// Calcular frecuencia: 1/(2*período promedio) * 1,000,000
// Factor 2 porque cada período tiene 2 cruces por cero
float frequency = 1000000.0 / (totalTime / 9.0 * 2);
return (frequency > 40.0 && frequency < 70.0) ? frequency : 50.0;
}
// 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, 6);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi conectado");
Serial.print("Dirección IP: ");
Serial.println(WiFi.localIP());
}
// Reconexión al servidor MQTT
void reconnectMQTT() {
while (!client.connected()) {
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");
} else {
Serial.print(" - falló, rc=");
Serial.print(client.state());
Serial.println(" intentando de nuevo en 5 segundos");
delay(5000);
}
}
}
// 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 medir el voltaje AC
float measureVoltage() {
return emon1.Vrms;
}
// Función para medir la corriente AC
float measureCurrent() {
return emon1.Irms;
}
// Función para reiniciar el sistema
void resetSystem() {
Serial.println("🔄 Reiniciando sistema...");
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
client.publish(topicEstado, "REINICIANDO");
// Encender el sistema nuevamente
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
emon1.calcVI(20, 2000); // Calcular (20 medias-ondas, 2000ms timeout)
delay(100);
float tempVAC = emon1.Vrms;
float tempAmpAC = emon1.Irms;
if (tempVAC > 0 && tempAmpAC >= 0) {
VAC = tempVAC;
AmpAC = tempAmpAC;
ActivePower = emon1.realPower;
ReactivePower = emon1.apparentPower * sin(acos(emon1.powerFactor));
ApparentPower = emon1.apparentPower;
PowerFactor = emon1.powerFactor;
Serial.println("✅ Datos válidos. Sistema listo para operar");
client.publish(topicEstado, "ACTIVO");
} else {
Serial.println("❌ Error en medición. Verifique sensores");
systemEnabled = false;
digitalWrite(controlPin, LOW);
digitalWrite(statusLedPin, LOW);
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");
client.publish(topicEstado, "INACTIVO");
}
lastSystemState = systemEnabled;
return;
}
bool withinLimits = (VAC >= minVoltage && VAC <= maxVoltage &&
AmpAC <= maxCurrent && PowerFactor >= minPowerFactor);
if (withinLimits) {
digitalWrite(controlPin, HIGH);
digitalWrite(statusLedPin, HIGH);
if (lastSystemState != systemEnabled) {
Serial.println("✅ Carga activada: Condiciones óptimas");
client.publish(topicEstado, "ACTIVO");
}
} else {
digitalWrite(controlPin, LOW);
digitalWrite(statusLedPin, LOW);
if (lastSystemState != systemEnabled) {
if (VAC < minVoltage) {
Serial.println("⚠️ Bajo voltaje: Carga desactivada");
client.publish(topicEstado, "BAJO_VOLTAJE");
}
else if (VAC > maxVoltage) {
Serial.println("⚠️ Sobrevoltaje: Carga desactivada");
client.publish(topicEstado, "SOBREVOLTAJE");
}
else if (AmpAC > maxCurrent) {
Serial.println("⚠️ Sobrecorriente: Carga desactivada");
client.publish(topicEstado, "SOBRECORRIENTE");
}
else if (PowerFactor < minPowerFactor) {
Serial.println("⚠️ Factor de potencia bajo: Carga desactivada");
client.publish(topicEstado, "BAJO_FP");
}
}
}
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 activePowerChanged = fabs(ActivePower - lastActivePower) > 1.0;
bool reactivePowerChanged = fabs(ReactivePower - lastReactivePower) > 1.0;
bool apparentPowerChanged = fabs(ApparentPower - lastApparentPower) > 1.0;
bool powerFactorChanged = fabs(PowerFactor - lastPowerFactor) > 0.01;
bool frequencyChanged = fabs(Frequency - lastFrequency) > 0.1;
if (voltageChanged || currentChanged || activePowerChanged ||
reactivePowerChanged || apparentPowerChanged || powerFactorChanged || 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 Activa: "); Serial.print(ActivePower, 2); Serial.println(" W");
Serial.print("Potencia Reactiva: "); Serial.print(ReactivePower, 2); Serial.println(" VAR");
Serial.print("Potencia Aparente: "); Serial.print(ApparentPower, 2); Serial.println(" VA");
Serial.print("Factor de Potencia: "); Serial.println(PowerFactor, 3);
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;
lastActivePower = ActivePower;
lastReactivePower = ReactivePower;
lastApparentPower = ApparentPower;
lastPowerFactor = PowerFactor;
lastFrequency = Frequency;
}
}
// Publicar datos por MQTT
void publishMQTTData() {
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 activa
dtostrf(ActivePower, 6, 2, buffer);
client.publish(topicPotenciaActiva, buffer);
// Publicar potencia reactiva
dtostrf(ReactivePower, 6, 2, buffer);
client.publish(topicPotenciaReactiva, buffer);
// Publicar potencia aparente
dtostrf(ApparentPower, 6, 2, buffer);
client.publish(topicPotenciaAparente, buffer);
// Publicar factor de potencia
dtostrf(PowerFactor, 5, 3, buffer);
client.publish(topicFactorPotencia, 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");
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 avanzado de medición...");
// Inicializar el generador de números aleatorios
randomSeed(analogRead(0));
// Configurar resolución ADC
analogReadResolution(12);
// Configurar pines
pinMode(controlPin, OUTPUT);
pinMode(statusLedPin, OUTPUT);
pinMode(buttonOnOffPin, INPUT_PULLUP);
pinMode(buttonResetPin, INPUT_PULLUP);
pinMode(zeroCrossPin, INPUT_PULLUP);
digitalWrite(controlPin, LOW);
digitalWrite(statusLedPin, LOW);
// Inicializar detector de cruce por cero
attachInterrupt(digitalPinToInterrupt(zeroCrossPin), zeroCrossDetected, RISING);
// Inicializar EmonLib
emon1.voltage(voltagePin, VCAL, PHASECAL); // Voltage: pin, calibración, desfase
emon1.current(currentPin, ICAL); // Corriente: pin, calibración
// Configurar WiFi
setupWiFi();
// Configurar MQTT
client.setServer(mqtt_server, mqtt_port);
client.setCallback(callback);
Serial.println("Sistema avanzado de medición listo");
}
void loop() {
unsigned long currentMillis = millis();
// Verificar conexión MQTT
if (!client.connected()) {
reconnectMQTT();
}
client.loop();
handleButtons();
// Realizar medición cada 1 segundo
if (currentMillis - lastPowerCalcTime >= 1000) {
// Calcular valores eléctricos (20 medias-ondas, 2000ms timeout)
emon1.calcVI(20, 2000);
// Actualizar variables
VAC = emon1.Vrms;
AmpAC = emon1.Irms;
ActivePower = emon1.realPower;
ApparentPower = emon1.apparentPower;
PowerFactor = emon1.powerFactor;
// Calcular potencia reactiva
ReactivePower = ApparentPower * sin(acos(PowerFactor));
lastPowerCalcTime = currentMillis;
}
// Calcular frecuencia cada 1 segundo
if (currentMillis - lastFrequencyCalcTime >= 1000) {
Frequency = calculateFrequency();
lastFrequencyCalcTime = currentMillis;
}
// Control de potencia
if (currentMillis - lastControlTime >= 200) {
controlPower();
lastControlTime = currentMillis;
}
displayData();
// Publicar datos MQTT cada segundo (1000ms)
if (currentMillis - lastMqttPublishTime >= 1000) {
publishMQTTData();
lastMqttPublishTime = currentMillis;
}
delay(5);
}Loading
esp32-c6-devkitc-1
esp32-c6-devkitc-1
Voltage Sensor
Current Sensor
SSR Out
POWER ON/OFF
RESET