Objetivos de la Práctica
-
Implementar control ON/OFF de temperatura
-
Monitorear temperatura en tiempo real con MAX6675
-
Crear interfaz HMI en LabVIEW para control térmico
-
Implementar histéresis para evitar ciclado rápido
-
Registrar datos y generar alertas
Materiales Requeridos
Hardware:
-
ESP32 Dev Board
-
Módulo MAX6675 + Termopar Tipo K
-
Relé de estado sólido o mecánico
-
Calentador (resistencia 100W o bombilla)
-
Fuente de alimentación 12V/5A
-
Protoboard y cables
-
Caja de control
-
Disipador de calor (opcional)
Software:
-
LabVIEW
-
Arduino IDE
-
VI Package Manager
-
LIFA Toolkit
Parte 1: Esquema de Conexiones
Diagrama de Conexiones ESP32:
// MAX6675 MAX6675 → ESP32 VCC → 3.3V GND → GND SO → GPIO 19 CS → GPIO 5 SCK → GPIO 18 // Relé RELAY → GPIO 12 // LED Indicador LED → GPIO 2 (LED interno)
Esquema de Potencia:
Fuente 12V ────┐
│
┌─────┴─────┐
│ RELÉ │
└─────┬─────┘
│
CALENTADOR
(100W 12V)
Parte 2: Programación del ESP32
Código Principal (Control_Temperatura_ESP32.ino):
#include <max6675.h> // Pines MAX6675 int thermoSO = 19; int thermoCS = 5; int thermoSCK = 18; // Pin Relé y LED int relayPin = 12; int ledPin = 2; MAX6675 thermocouple(thermoSCK, thermoCS, thermoSO); // Variables de control float setpoint = 25.0; // Temperatura objetivo float hysteresis = 2.0; // Histéresis bool heating = false; // Estado del calentador bool autoMode = true; // Modo automático // Variables de monitoreo float currentTemp = 0.0; unsigned long lastRead = 0; const unsigned long readInterval = 1000; // Leer cada 1 segundo unsigned long heatingStartTime = 0; float maxTemp = 0.0; float minTemp = 100.0; // Buffers para comunicación String inputString = ""; bool stringComplete = false; void setup() { // Configurar pines pinMode(relayPin, OUTPUT); pinMode(ledPin, OUTPUT); // Inicialmente apagar relé digitalWrite(relayPin, LOW); // LOW = Apagado (depende del relé) digitalWrite(ledPin, LOW); // Comunicación serial Serial.begin(115200); // Esperar inicialización MAX6675 delay(500); Serial.println("CONTROL_TEMPERATURA_READY"); Serial.println("Comandos: SET=xx.x, HYST=xx.x, AUTO_ON, AUTO_OFF, HEATER_ON, HEATER_OFF, STATUS"); } void loop() { // Leer temperatura cada intervalo if (millis() - lastRead >= readInterval) { readTemperature(); lastRead = millis(); } // Control ON/OFF automático if (autoMode) { controlHeater(); } // Enviar datos a LabVIEW cada 2 segundos static unsigned long lastDataSend = 0; if (millis() - lastDataSend >= 2000) { sendDataToLabVIEW(); lastDataSend = millis(); } // Procesar comandos seriales processSerialCommands(); delay(100); } void readTemperature() { float temp = thermocouple.readCelsius(); // Verificar error de lectura if (isnan(temp)) { Serial.println("ERROR: Lectura de temperatura fallida"); return; } currentTemp = temp; // Actualizar max y min if (temp > maxTemp) maxTemp = temp; if (temp < minTemp) minTemp = temp; // LED indicador (parpadeo según temperatura) updateStatusLED(); } void controlHeater() { if (currentTemp < (setpoint - hysteresis) && !heating) { // Encender calentador digitalWrite(relayPin, HIGH); // HIGH = Encendido heating = true; heatingStartTime = millis(); Serial.println("CALENTADOR: ENCENDIDO"); } else if (currentTemp > (setpoint + hysteresis) && heating) { // Apagar calentador digitalWrite(relayPin, LOW); // LOW = Apagado heating = false; unsigned long heatingTime = (millis() - heatingStartTime) / 1000; Serial.print("CALENTADOR: APAGADO - Tiempo encendido: "); Serial.print(heatingTime); Serial.println(" segundos"); } } void updateStatusLED() { static unsigned long lastBlink = 0; static bool ledState = false; if (heating) { // Parpadeo rápido cuando está calentando if (millis() - lastBlink >= 200) { ledState = !ledState; digitalWrite(ledPin, ledState); lastBlink = millis(); } } else { // LED apagado cuando no está calentando digitalWrite(ledPin, LOW); } } void sendDataToLabVIEW() { Serial.print("DATA:"); Serial.print(currentTemp, 2); Serial.print(","); Serial.print(setpoint, 2); Serial.print(","); Serial.print(heating ? "1" : "0"); Serial.print(","); Serial.print(autoMode ? "1" : "0"); Serial.print(","); Serial.print(maxTemp, 2); Serial.print(","); Serial.print(minTemp, 2); Serial.print(","); Serial.print(hysteresis, 2); Serial.println(); } void processSerialCommands() { if (Serial.available()) { char inChar = (char)Serial.read(); inputString += inChar; if (inChar == '\n') { stringComplete = true; } } if (stringComplete) { inputString.trim(); if (inputString.startsWith("SET=")) { setpoint = inputString.substring(4).toFloat(); Serial.print("SETPOINT: "); Serial.println(setpoint); resetMinMax(); // Resetear min/max al cambiar setpoint } else if (inputString.startsWith("HYST=")) { hysteresis = inputString.substring(5).toFloat(); Serial.print("HYSTERESIS: "); Serial.println(hysteresis); } else if (inputString == "AUTO_ON") { autoMode = true; Serial.println("MODO: AUTOMATICO"); } else if (inputString == "AUTO_OFF") { autoMode = false; Serial.println("MODO: MANUAL"); } else if (inputString == "HEATER_ON") { digitalWrite(relayPin, HIGH); heating = true; Serial.println("CALENTADOR: ENCENDIDO (MANUAL)"); } else if (inputString == "HEATER_OFF") { digitalWrite(relayPin, LOW); heating = false; Serial.println("CALENTADOR: APAGADO (MANUAL)"); } else if (inputString == "STATUS") { sendDetailedStatus(); } else if (inputString == "RESET_MINMAX") { resetMinMax(); Serial.println("MIN/MAX RESETEADO"); } else { Serial.println("ERROR: Comando no reconocido"); } inputString = ""; stringComplete = false; } } void sendDetailedStatus() { Serial.println("=== CONTROL TEMPERATURA STATUS ==="); Serial.print("Temperatura Actual: "); Serial.print(currentTemp, 2); Serial.println(" °C"); Serial.print("Setpoint: "); Serial.print(setpoint, 2); Serial.println(" °C"); Serial.print("Histéresis: "); Serial.print(hysteresis, 2); Serial.println(" °C"); Serial.print("Calentador: "); Serial.println(heating ? "ENCENDIDO" : "APAGADO"); Serial.print("Modo: "); Serial.println(autoMode ? "AUTOMATICO" : "MANUAL"); Serial.print("Temperatura Máxima: "); Serial.print(maxTemp, 2); Serial.println(" °C"); Serial.print("Temperatura Mínima: "); Serial.print(minTemp, 2); Serial.println(" °C"); Serial.print("Banda de Control: "); Serial.print(setpoint - hysteresis, 2); Serial.print(" - "); Serial.print(setpoint + hysteresis, 2); Serial.println(" °C"); Serial.println("================================="); } void resetMinMax() { maxTemp = currentTemp; minTemp = currentTemp; }
Parte 3: Librería MAX6675 (si es necesaria)
max6675.h (si no está disponible):
#ifndef MAX6675_h #define MAX6675_h #include <Arduino.h> class MAX6675 { public: MAX6675(int8_t SCLK, int8_t CS, int8_t MISO); float readCelsius(void); float readFahrenheit(void); private: int8_t sclk, miso, cs; uint8_t spiread(void); }; #endif
max6675.cpp:
#include "max6675.h" MAX6675::MAX6675(int8_t SCLK, int8_t CS, int8_t MISO) { sclk = SCLK; cs = CS; miso = MISO; pinMode(cs, OUTPUT); pinMode(sclk, OUTPUT); pinMode(miso, INPUT); digitalWrite(cs, HIGH); } float MAX6675::readCelsius(void) { uint16_t v; digitalWrite(cs, LOW); delayMicroseconds(10); v = spiread(); v <<= 8; v |= spiread(); digitalWrite(cs, HIGH); if (v & 0x4) { return NAN; // Termopar abierto } v >>= 3; return v * 0.25; } float MAX6675::readFahrenheit(void) { return readCelsius() * 9.0 / 5.0 + 32; } uint8_t MAX6675::spiread(void) { int i; uint8_t d = 0; for (i = 7; i >= 0; i--) { digitalWrite(sclk, LOW); delayMicroseconds(10); if (digitalRead(miso)) { d |= (1 << i); } digitalWrite(sclk, HIGH); delayMicroseconds(10); } return d; }
Parte 4: Programación en LabVIEW
VI Principal: Control_Temperatura_ONOFF.vi
Front Panel Design:
// GRUPO: INDICADORES PRINCIPALES - Termómetro: Temperatura Actual (0-300°C) - Numérico: Setpoint Actual - Numérico: Histéresis - LED: Estado Calentador - LED: Modo Automático - LED: Alarma Temperatura // GRUPO: GRÁFICOS - Waveform Chart: Temperatura vs Tiempo - Waveform Chart: Setpoint y Bandas - Waveform Chart: Estado Calentador // GRUPO: CONTROL - Botón: Modo Automático/Manual - Botón: Encender Calentador (manual) - Botón: Apagar Calentador (manual) - Selector Numérico: Nuevo Setpoint - Selector Numérico: Nueva Histéresis - Botón: Aplicar Configuración // GRUPO: ESTADÍSTICAS - Numérico: Temperatura Máxima - Numérico: Temperatura Mínima - Numérico: Tiempo Encendido Acumulado - Numérico: Ciclos de Trabajo // GRUPO: CONFIGURACIÓN COMUNICACIÓN - String: Puerto COM - Numérico: Baud Rate (115200) - Botón: Conectar/Desconectar - LED: Estado Conexión
Diagrama de Bloques Principal:
// ESTRUCTURA PRINCIPAL While Loop con [Stop] button // INICIALIZACIÓN Case Structure: First Call? - VISA Configure Serial Port - Inicializar variables - Configurar gráficos // ESTADO DE LA MÁQUINA Case Structure dentro del While Loop: Estado 0: Desconectado Estado 1: Conectando Estado 2: Conectado - Monitoreo Estado 3: Error - Reconexión // PROCESAMIENTO DE DATOS Event Structure: - Timeout: 100ms → Leer datos seriales - Value Change: Botones → Enviar comandos // SUBVIs - "Parsear Datos Temperatura.vi" - "Controlar Calentador.vi" - "Calcular Estadísticas.vi" - "Gestión Alertas.vi"
SubVI: Parsear Datos Temperatura.vi
// Input: String serial (ej: "DATA:25.50,25.00,1,1,30.00,20.00,2.00") // Output: Cluster con datos parseados // Algoritmo: 1. Buscar "DATA:" en string 2. Extraer substring después de "DATA:" 3. Separar por comas → Array de strings 4. Convertir a números: [0] = Temperatura actual [1] = Setpoint [2] = Estado calentador (0/1) [3] = Modo automático (0/1) [4] = Temp máxima [5] = Temp mínima [6] = Histéresis
SubVI: Controlar Calentador.vi
// Inputs: Modo (Auto/Manual), Comando Manual, Temperatura, Setpoint, Histéresis
// Output: Comando a enviar
Case Structure:
- Modo Automático:
* Si Temp < (Setpoint - Histéresis) → "HEATER_ON"
* Si Temp > (Setpoint + Histéresis) → "HEATER_OFF"
- Modo Manual:
* Seguir comando manual del usuario
SubVI: Gestión Alertas.vi
// Inputs: Temperatura, Setpoint, Histéresis // Outputs: Alerta, Nivel de Alerta Case Structure: - Si Temperatura > (Setpoint + 3*Histéresis): "ALERTA: SOBRETEMPERATURA" - Si Temperatura < (Setpoint - 3*Histéresis): "ALERTA: SUBTEMPERATURA" - Si error de sensor: "ALERTA: SENSOR DESCONECTADO" // Acciones: - Cambiar color de indicadores - Guardar en log de eventos - Reproducir sonido de alerta
Parte 5: Configuración y Calibración
Procedimiento de Puesta en Marcha:
-
Conexiones Físicas:
-
Verificar polaridad del termopar
-
Conectar relé correctamente (NO/NC)
-
Asegurar aislamiento eléctrico
-
-
Configuración ESP32:
cpp// Cargar código // Abrir Monitor Serial // Verificar mensaje "CONTROL_TEMPERATURA_READY"
-
Configuración LabVIEW:
-
Establecer puerto COM correcto
-
Baud rate 115200
-
Configurar límites de temperatura
-
Calibración del Sistema:
-
Prueba de Sensor:
-
Comparar con termómetro de referencia
-
Verificar respuesta a cambios de temperatura
-
Ajustar compensación si es necesario
-
-
Ajuste de Histéresis:
-
Para sistemas lentos: histéresis 2-5°C
-
Para sistemas rápidos: histéresis 0.5-2°C
-
Evitar ciclado excesivo del relé
-
Parte 6: Pruebas y Validación
Pruebas de Funcionamiento:
-
Prueba de Control ON/OFF:
-
Establecer setpoint a temperatura ambiente + 10°C
-
Verificar encendido del calentador
-
Monitorear alcanze del setpoint
-
Verificar apagado del calentador
-
-
Prueba de Histéresis:
-
Configurar histéresis de 2°C
-
Verificar banda de control
-
Contar ciclos por hora
-
-
Prueba de Modos:
-
Cambio entre automático y manual
-
Control manual del calentador
-
Respuesta a cambios de setpoint
-
Ejemplo de Datos de Prueba:
// Datos esperados durante operación: Temperatura: 25.0°C → Calentador: ON Temperatura: 36.5°C → Calentador: ON (Setpoint: 37°C, Histéresis: 2°C) Temperatura: 38.5°C → Calentador: OFF Temperatura: 36.0°C → Calentador: ON
Parte 7: Mejoras y Extensiones
Características Avanzadas:
-
Control PID (opcional):
cpp// En lugar de ON/OFF, implementar PID double error = setpoint - currentTemp; double output = Kp * error + Ki * integral + Kd * derivative;
-
Suavizado de Temperatura:
cpp// Filtro media móvil float readSmoothedTemperature() { static float readings[10]; static int index = 0; readings[index] = thermocouple.readCelsius(); index = (index + 1) % 10; float sum = 0; for (int i = 0; i < 10; i++) { sum += readings[i]; } return sum / 10; }
-
Web Server ESP32:
cpp#include <WebServer.h> WebServer server(80); void setupServer() { server.on("/", HTTP_GET, []() { String html = "<html><body>"; html += "<h1>Control Temperatura</h1>"; html += "<p>Temperatura: " + String(currentTemp) + " °C</p>"; html += "</body></html>"; server.send(200, "text/html", html); }); server.begin(); }
Parte 8: Evaluación de la Práctica
Criterios de Evaluación:
-
✅ Comunicación serial estable
-
✅ Lectura correcta de temperatura con MAX6675
-
✅ Control ON/OFF funcional con histéresis
-
✅ Interfaz LabVIEW intuitiva y completa
-
✅ Cambio correcto entre modos auto/manual
-
✅ Sistema de alertas operativo
-
✅ Registro de datos y estadísticas
Entregables:
-
Código completo ESP32 + MAX6675
-
VI de LabVIEW funcionando
-
Diagramas de conexión
-
Reporte de pruebas con gráficos
-
Análisis de estabilidad del sistema
Métricas de Desempeño:
-
Precisión: ±1°C del setpoint
-
Estabilidad: Menos de 5 ciclos/hora
-
Respuesta: Tiempo para alcanzar setpoint
-
Confiabilidad: 0 falsas alarmas
Esta práctica proporciona una base sólida para el control térmico ON/OFF con capacidades industriales. ¿Te gustaría que agregue alguna funcionalidad específica o que profundice en alguna parte del sistema?
