01 · Punto de partida
Qué vamos a construir
Un controlador para mantener a una temperatura fija un recinto pequeño: una incubadora casera, una caja para fermentar, un terrario. Un calefactor de 12 V y 20 W calienta; un sensor LM35 mide; el usuario elige la temperatura deseada con dos botones y ve en una pantalla de dos líneas la temperatura deseada, la medida y la potencia que se está entregando.
| Elemento | Comportamiento |
|---|---|
Botones + y − | Cambian la temperatura deseada de 0,5 en 0,5 °C entre 20 y 60 °C; si se mantienen apretados, repiten; el valor se guarda en la EEPROM 5 s después del último cambio |
| Pantalla (LCD 16×2) | Línea 1: SP 40.0 T 37.8 (deseada y medida). Línea 2: Pot 67 % PI o el mensaje de falla |
| Calefactor | Potencia continua de 0 a 100 % por PWM, con control PI (el modo por histéresis queda como opción de compilación) |
| Protecciones | Apagado inmediato si el sensor falla, si la temperatura pasa de 70 °C o si el programa se cuelga |
| Registro por serie | Una línea por segundo con deseada, medida y potencia, para graficar la respuesta |
Es el proyecto donde se ve por qué los temas anteriores fueron en ese orden: el ADC y su ruido (temas 28 y 36), el PWM (tema 25), la etapa de potencia (temas 38 y 39), los botones con antirrebote (temas 17 y 42), la estructura cooperativa con un tick de 1 ms (temas 41 y 42). Lo nuevo es la teoría del lazo: histéresis, acción proporcional e integral, saturación y un modelo de la planta.
02 · Antes de escribir código
El diseño: un lazo y cinco tareas
El programa sigue la estructura de los temas 41 y 42: un tick de 1 ms (Timer0), un planificador cooperativo y tareas cortas con su estado. Los tiempos de cada una se eligen según cuánto cambia lo que miran:
| Tarea | Período | Tiempo | Qué hace |
|---|---|---|---|
botones | 10 ms | 30 µs | Antirrebote, repetición, cambio de la deseada |
pantalla | 20 ms | 400 µs | Escribe 4 caracteres del LCD por llamada |
lectura | 100 ms | 1,8 ms | 16 conversiones, escalado, filtro, validación |
control | 1 s | 50 µs | Calcula la potencia y la aplica al PWM; vigila los límites |
registro | 1 s | 40 µs | Encola una línea para la UART (transmisión por interrupción) |
La utilización es de 4,1 % y el peor retraso de inicio de la tarea de botones (todas vencen a la vez) es 1,8 + 0,05 + 0,4 + 0,04 = 2,3 ms, lejos de los 10 ms de su período. La tarea que más pesa es la lectura (los 16 muestreos), y se mantiene corta porque cada conversión del ADC tarda 104 µs: ese es el motivo para no promediar 256 muestras de una vez.
¿Por qué el control corre una vez por segundo? Porque la planta es lenta. Si el recinto tiene una constante de tiempo de 15 minutos, recalcular la potencia cien veces por segundo no aporta nada y solo amplifica el ruido de la medida. La regla práctica es muestrear el control unas 10 a 20 veces por cada constante de tiempo del proceso, o más rápido si el retardo es corto; con 1 s sobra. La lectura va más rápida (100 ms) porque necesita un filtro con datos frescos.
03 · Paso 1
El cableado
| Pin | Conexión | Motivo |
|---|---|---|
| A0 (PC0) | Salida del LM35 con 100 Ω en serie y 100 nF a masa | El LM35 no maneja bien cargas capacitivas; el filtro RC amortigua y reduce el ruido |
| AREF | Solo un capacitor de 100 nF a masa (referencia interna de 1,1 V) | 10 mV/°C × 110 °C = 1,1 V: aprovecha todo el rango del ADC |
| D3 (PD3, OC2B) | 100 Ω → compuerta del MOSFET; 10 kΩ de compuerta a masa | Salida PWM del Timer2; el pull-down apaga el calefactor mientras el microcontrolador se reinicia |
| D8 a D13 (PB0 a PB5) | LCD: RS, E, D4, D5, D6, D7 (modo de 4 bits); RW a masa | 6 pines del puerto B; R/W a masa porque nunca se lee el LCD |
| D2 y D4 | Pulsadores a masa; pull-up interna | Botones + y − |
| D0, D1 | UART al monitor serie (9600 baudios) | Registro de la respuesta |
La etapa de potencia sigue lo del tema 38: fuente de 12 V separada para el calefactor, con la masa unida a la del microcontrolador en un solo punto, el MOSFET entre el calefactor y masa (conmutación del lado bajo), un capacitor de 470 µF cerca del MOSFET para absorber las variaciones de corriente y un fusible de 2 A en la línea de 12 V. Como la carga es una resistencia, no hace falta diodo de rueda libre (sí lo haría un motor, tema 38). El MOSFET debe ser de nivel lógico: con 5 V en la compuerta tiene que estar completamente encendido. Los cables del calefactor llevan 1,7 A y los del sensor, microamperios: se tienden por separado, y la masa del sensor se conecta lejos de la corriente del calefactor, para que la caída de tensión en los cables no se sume a la medida.
Una protección que no depende del software: un termostato de corte (fusible térmico o un termostato bimetálico de 80 °C) en serie con el calefactor. Si falla el microcontrolador, el MOSFET o el programa, el calor nunca pasará de ese límite. Todo diseño que pueda causar daño necesita, por lo menos, una protección que funcione sin el programa.
04 · Paso 2
Leer la temperatura
Con la referencia interna de 1,1 V, cada cuenta del ADC representa 1,1 V / 1024 = 1,074 mV, o sea 0,1074 °C del LM35 (tema 30). Esa resolución sirve para mostrar décimas de grado, pero es gruesa para un controlador: un cambio de una cuenta es un salto de 0,1 °C, y con el ruido típico del ADC (una o dos cuentas) la lectura vibra entre 0,2 y 0,3 °C. Dos técnicas lo resuelven, y se complementan:
- Sobremuestreo (tema 36): promediar
nconversiones reduce el ruido en√n. Con 16 muestras, el ruido baja a la cuarta parte y se ganan, con un ruido adecuado, dos bits de resolución efectiva. Cuesta 16 × 104 µs = 1,7 ms. - Filtro exponencial:
y ← y + (x − y)/4promedia a lo largo del tiempo. Reduce el ruido en un factor√(α/(2 − α))(0,38 para α = 1/4) y cuesta una resta y un desplazamiento. A cambio, atrasa la medida una constante de tiempo de (1/α − 1) muestras (0,3 s a 100 ms por muestra): despreciable frente a una planta de 15 minutos.
static int16_t temp_cc; // temperatura filtrada, en centésimas de °C (3781 = 37,81 °C)
static uint8_t temp_valida;
static int16_t offset_cc; // calibración, leída de la EEPROM
static void adc_init(void) {
ADMUX = (1 << REFS1) | (1 << REFS0); // referencia interna de 1,1 V, canal ADC0
ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // 16 MHz / 128 = 125 kHz
DIDR0 = (1 << ADC0D);
ADCSRA |= (1 << ADSC); while (ADCSRA & (1 << ADSC)) { } // primera conversión, descartada
}
static uint16_t adc_leer(void) {
ADCSRA |= (1 << ADSC);
while (ADCSRA & (1 << ADSC)) { }
return ADC;
}
void lectura_tarea(void) { // cada 100 ms
uint16_t suma = 0;
for (uint8_t i = 0; i < 16; i++) suma += adc_leer(); // 16 × 10 bits = 14 bits
int16_t x = (int16_t)(((uint32_t)suma * 11000UL) / 16384UL) + offset_cc; // cuentas·(1100/1024)·10 / 16
if (x < 200 || x > 10000) { // fuera de 2 a 100 °C: sensor abierto, en corto o cable roto
if (++fallas_sensor >= 5) temp_valida = 0;
return;
}
fallas_sensor = 0;
if (!temp_valida) { temp_cc = x; temp_valida = 1; }
else temp_cc += (x - temp_cc) / 4; // filtro exponencial
}
La cuenta de escala merece verificarse: suma es la suma de 16 cuentas. La temperatura en centésimas es cuenta_promedio × 1100/1024 × 10, o suma × 1100 × 10 / (1024 × 16) = suma × 11000 / 16384. Se usan centésimas de grado, y no décimas, para que el filtro y el controlador trabajen con una resolución más fina que la que se muestra. Se evita el punto flotante: el AVR no tiene unidad de coma flotante y cada operación cuesta decenas de ciclos y un par de kilobytes de biblioteca.
Un detalle importante es la validación: si el sensor se desconecta, la entrada del ADC flota y devuelve basura; si se pone en corto, lee 0. Cinco lecturas seguidas fuera de un rango plausible declaran inválido al sensor, y el controlador debe pasar a un estado seguro (apagar el calefactor). Un controlador que ignora esto y cree que el recinto está a 0 °C pone el calefactor a fondo.
La resolución no es exactitud. El LM35 declara un error de ±0,5 °C a 25 °C, y el recinto no está a la temperatura donde está el sensor. Con 0,015 °C de ruido se puede controlar de forma muy estable en un valor que está 1 °C mal. Si importa el valor absoluto, se calibra con dos puntos y se guarda offset_cc (tema 36).
05 · Paso 3
Control por histéresis
El controlador más simple es el termostato: encender si la temperatura está por debajo de la deseada, apagar si está por encima. Si se hiciera con un solo umbral, el ruido haría conmutar a la salida cientos de veces por segundo cuando la temperatura anda cerca del límite. La solución es la histéresis: dos umbrales, uno para encender y otro para apagar, separados por una banda h.
uint8_t control_onoff(int16_t sp_cc, int16_t t_cc, int16_t h_cc) {
static uint8_t encendido;
if (t_cc < sp_cc - h_cc / 2) encendido = 1; // por debajo de la banda: encender
else if (t_cc > sp_cc + h_cc / 2) encendido = 0; // por encima: apagar
return encendido ? 255 : 0; // dentro de la banda, conserva el estado anterior
}
La banda debe ser mayor que el ruido de la medida (la herramienta 1 del laboratorio calcula la mínima: unos seis desvíos estándar). Con ruido de 0,015 °C bastaría una banda de 0,1 °C; pero otro efecto manda en la práctica: el retardo. El calor tarda en llegar al sensor, y después de apagar el calefactor la temperatura sigue subiendo un rato. La oscilación real es más grande que la banda. En la simulación del laboratorio, el recinto de este proyecto (20 W, constante de tiempo de 15 minutos, 20 s de retardo) con una banda de 1 °C oscila 1,7 °C pico a pico, con un sobrepaso de 0,7 °C. Con una banda de 0,2 °C la oscilación baja a 0,9 °C, pero las maniobras se duplican: de 14 a 27 ciclos por hora.
Ese número de maniobras es lo que decide si el termostato es aceptable. Un relé aguanta 100 000 ciclos: a 14 ciclos por hora duraría menos de un año (tema 38). Un MOSFET con PWM no se gasta, pero cada encendido y apagado calienta, y un calefactor con interruptor mecánico (un termostato de pared) tiene una banda de varios grados justamente por eso. Si el actuador es un relé, se agregan tiempos mínimos de encendido y apagado (por ejemplo, 30 s) para proteger los contactos y la fuente.
El control por histéresis tiene tres virtudes: es simple, no requiere ningún ajuste fino y funciona con cualquier planta que tenga una respuesta monótona. Y tres defectos: la temperatura siempre oscila, no hay forma de reducir esa oscilación sin gastar el actuador, y el actuador trabaja a todo o nada, lo cual es mecánicamente más duro con el calefactor. Para mejorar eso hay que dar al actuador valores intermedios.
06 · Paso 4
Control proporcional e integral
La idea del control proporcional es que la potencia dependa del tamaño del error: lejos de la deseada, a fondo; cerca, poca. u = Kp · e, saturada entre 0 y 100 %. Cuando la temperatura se acerca, la potencia baja sola y no hay sobrepaso. Pero tiene un defecto de fondo: para sostener la temperatura el calefactor tiene que entregar una potencia permanente (aquí 13,3 W, el 67 %, para compensar lo que se pierde por las paredes). Con un controlador puramente proporcional, esa potencia solo existe si hay un error: e = 0,67 / Kp. Con Kp = 0,5 por °C el error permanente sería de 1,3 °C, y con una banda proporcional de 5 °C, de 3,3 °C. Se llama error de régimen permanente, y no se elimina subiendo Kp sin llegar a la inestabilidad.
La solución es el término integral: u = Kp · e + Ki · ∫e dt. La integral suma el error en el tiempo; mientras haya un error, por pequeño que sea, la integral sigue creciendo y empuja la potencia hasta que el error se anula. En régimen permanente la integral guarda justamente los 13,3 W que hacen falta. Es un controlador que aprende cuánta potencia hace falta.
// Kp = 0,5 por °C a plena escala → 255 · 0,5 / 100 = 1,275 cuentas de PWM por centésima de grado = 51/40
// Ki = 0,001 por °C y por segundo → 255 · 0,001 / 100 = 0,00255 cuentas por centésima y por segundo = 51/20000
#define KP_N 51L
#define KP_D 40L
#define KI_N 51L
#define KI_D 20000L
static int32_t integral; // suma de errores, en centésimas de °C · s
uint8_t control_pi(int16_t sp_cc, int16_t t_cc) { // se llama una vez por segundo
int32_t e = (int32_t)sp_cc - t_cc;
int32_t u = e * KP_N / KP_D + integral * KI_N / KI_D;
/* anti-windup: se integra solo si la salida no está saturada, o si el error la saca de la saturación */
if ((u > 0 && u < 255) || (u >= 255 && e < 0) || (u <= 0 && e > 0))
integral += e;
if (u < 0) u = 0;
if (u > 255) u = 255;
return (uint8_t)u;
}
El anti-windup (literalmente, «contra el enrollamiento») es lo que separa un controlador PI de juguete de uno que funciona. Al arrancar, la temperatura está a 20 °C y la deseada es 40: el error es de 20 °C, la salida satura al 100 % y se mantiene así durante 15 minutos. Si la integral siguiera sumando durante la subida, acumularía una enorme cantidad de error que después habría que «desenrollar» pasándose de la temperatura: en la simulación, un PI idéntico sin anti-windup supera la deseada en 6,9 °C; con el anti-windup, el sobrepaso es cero. La regla del código es intuitiva: no se integra mientras la salida está saturada y el error sigue empujando hacia afuera.
Afinar un controlador PI se puede reducir a un método de cuatro pasos, que es el de la verificación (paso 8): medir la respuesta del recinto a un escalón de potencia, extraer tres números —la ganancia K (°C por unidad de potencia), la constante de tiempo τ y el retardo θ—, partir de ganancias moderadas y subirlas hasta ver el límite. Para este recinto, K = 30 °C, τ = 900 s y θ = 20 s; las reglas de la curva de reacción de Ziegler–Nichols dan un Kp máximo de 0,9 · τ / (K · θ) = 1,35 por °C como referencia de «hasta aquí», y se elige un tercio (0,5) con un tiempo integral Ti = Kp / Ki de 500 s, bastante más lento que el que la regla propone (67 s), para no excitar el retardo. El resultado es una subida a ±1 °C en 16,6 minutos, sin sobrepaso y sin oscilación, con un error final de cero. El mayor riesgo de este esquema está en la relación entre el retardo y la constante de tiempo: si el retardo crece (un sensor lejos del calefactor, aire sin circulación), el mismo controlador oscila; el laboratorio lo muestra.
Un PID agrega un término derivativo (Kd · de/dt), que anticipa. En temperatura casi nunca se usa: la derivada amplifica el ruido de la medida y la planta lenta casi no lo necesita. Se prefiere PI, y si la planta tiene mucho retardo, un PI con ganancias bajas.
07 · Paso 5
El actuador y sus protecciones
La potencia se entrega con el PWM de 8 bits del Timer2 sobre el pin OC2B (PD3), en modo de PWM rápida con prescaler 64: 16 MHz / 64 / 256 = 976 Hz (tema 25). Para un calefactor con una constante de tiempo de minutos esa frecuencia es de sobra: el calefactor solo «ve» el promedio. El MOSFET en conmutación casi no disipa, como muestra la tercera herramienta del laboratorio: unos 40 mW a 1,7 A, porque cuando está encendido su resistencia es de 22 mΩ y cuando está apagado no circula corriente.
void pwm_init(void) {
DDRD |= (1 << PD3);
PORTD &= ~(1 << PD3); // apagado
TCCR2A = (1 << WGM21) | (1 << WGM20); // PWM rápida, TOP = 255; OC2B desconectado por ahora
TCCR2B = (1 << CS22); // prescaler 64 → 976 Hz
}
void pwm_set(uint8_t duty) {
if (duty == 0) { // OCR2B = 0 deja un pulso fino en cada ciclo: se desconecta el pin
TCCR2A &= ~(1 << COM2B1);
PORTD &= ~(1 << PD3);
} else {
OCR2B = duty; // con 255 la salida queda fija en alto
TCCR2A |= (1 << COM2B1);
}
}
El detalle del cero no es un adorno: en modo de PWM rápida con salida no invertida, OCR2B = 0 produce una aguja de 1/256 del ciclo, un 0,4 % de potencia, que nunca se apaga del todo. Para un calefactor de 20 W son 80 mW de sobra, y en un controlador que debe apagarse de verdad, la diferencia es la que hay entre «apagado» y «casi apagado». Por eso el cero desconecta el pin y lo fuerza a bajo.
Las protecciones forman una cadena de niveles, de menos a más severos. Todas viven en la tarea de control y se evalúan antes del algoritmo:
#define T_MAX_CC 7000 // 70,00 °C: corte duro por software
static uint8_t falla; // 0 = normal, 1 = sensor, 2 = sobretemperatura
void control_tarea(void) { // cada 1 s
if (!temp_valida) falla = 1;
else if (temp_cc > T_MAX_CC) falla = 2;
uint8_t pot;
if (falla) {
pot = 0; // calefactor apagado y asi se queda hasta pulsar ambos botones
integral = 0;
} else {
pot = control_pi(sp_cc, temp_cc);
}
pwm_set(pot);
potencia = pot; // para la pantalla y el registro
wdt_reset(); // el perro solo se acaricia aquí: si el control se cuelga, reinicia
}
- Sensor inválido: el control no puede saber dónde está la temperatura, así que apaga.
- Sobretemperatura del software: 70 °C, por encima del máximo del recinto. La falla se mantiene (se «traba») hasta que el usuario la reconoce; no se vuelve a encender sola.
- Watchdog de 1 s acariciado solo desde la tarea de control: si el programa se cuelga, el reset pone los pines en entrada y el pull-down de la compuerta apaga el calefactor (paso 1).
- Termostato de corte a 80 °C en el hardware: la protección que funciona aunque todo el resto falle.
Una última precaución con el estado inicial: al encender, el controlador debe empezar con la potencia en cero y la integral en cero, y solo habilitar el calefactor después de tener una lectura válida. Un controlador que arranca con basura en la integral puede encender el calefactor a fondo durante minutos.
08 · Paso 6
La pantalla y los botones
La pantalla LCD 16×2 con controlador HD44780 se maneja en modo de 4 bits: seis pines (RS, E y D4 a D7). Cada carácter son dos escrituras de 4 bits, de unos 40 µs cada una. Un refresco completo de 32 caracteres son 3 ms: no mucho, pero suficiente para que, hecho de golpe, retrase todo lo demás. Se aplica el patrón del tema 42: la tarea escribe unos pocos caracteres por llamada y vuelve.
#define LCD_RS (1 << PB0)
#define LCD_E (1 << PB1)
static void lcd_nibble(uint8_t n, uint8_t rs) {
PORTB = (PORTB & 0xC0) | ((n & 0x0F) << 2) | (rs ? LCD_RS : 0); // D4 a D7 en PB2 a PB5
PORTB |= LCD_E; _delay_us(1); PORTB &= ~LCD_E;
_delay_us(40);
}
static void lcd_byte(uint8_t b, uint8_t rs) { lcd_nibble(b >> 4, rs); lcd_nibble(b, rs); }
void lcd_init(void) { // única parte con esperas largas: solo corre al arrancar
DDRB |= 0x3F;
_delay_ms(50);
lcd_nibble(3, 0); _delay_ms(5);
lcd_nibble(3, 0); _delay_us(150);
lcd_nibble(3, 0);
lcd_nibble(2, 0); // pasa al modo de 4 bits
lcd_byte(0x28, 0); // 2 líneas, 5×8 puntos
lcd_byte(0x0C, 0); // pantalla encendida, sin cursor
lcd_byte(0x01, 0); _delay_ms(2); // borrar
lcd_byte(0x06, 0); // el cursor avanza a la derecha
}
static char buf[2][16]; // lo que debe mostrarse; lo escriben otras tareas
static uint8_t pos;
void pantalla_tarea(void) { // cada 20 ms: 4 caracteres por llamada
for (uint8_t k = 0; k < 4; k++) {
if ((pos & 15) == 0) lcd_byte(0x80 | (pos >= 16 ? 0x40 : 0x00), 0); // cursor al inicio de la línea
lcd_byte((uint8_t)buf[pos >> 4][pos & 15], 1);
pos = (pos + 1) & 31;
}
}
Las otras tareas no escriben en el LCD: arman el texto en buf, y la tarea de pantalla lo recorre (el patrón de «memoria de video» de las pantallas mínimas). Armar el texto requiere convertir centésimas a cifras sin printf (que ocupa 1,5 KB y es lento): una función corta que redondea a décimas y escribe "37.8" a mano. Un refresco completo ocurre cada 8 llamadas, o sea cada 160 ms: los números parpadean sin que se note, y cada llamada dura 0,4 ms.
Los botones usan la técnica del tema 42: una tarea de 10 ms que acumula las lecturas en un registro de desplazamiento y declara «apretado» cuando las últimas cuatro (40 ms) coinciden. Con el botón sostenido, el valor cambia a los 500 ms y después se repite cada 200 ms (autorepetición), lo que permite recorrer 40 °C de rango sin cansar al usuario. El valor de la deseada se guarda en la EEPROM 5 s después del último cambio y solo si cambió (eeprom_update_word): grabar en cada pulsación gastaría la EEPROM por nada, y grabar al soltar el botón perdería el cambio si se corta la luz antes. Esa pequeña espera es el equivalente a un antirrebote para memorias.
09 · Paso 7
El programa completo
Con los módulos, el main se limita a arrancar y a repartir el tiempo. La lista de tareas es la del planificador del tema 42; el orden de la tabla es la prioridad (botones primero, que son lo que el usuario percibe).
static tarea_t tareas[] = {
{ botones_tarea, 10, 0 },
{ pantalla_tarea, 20, 0 },
{ lectura_tarea, 100, 0 },
{ control_tarea, 1000, 0 },
{ registro_tarea, 1000, 0 },
};
int main(void) {
MCUSR = 0;
wdt_disable(); // un reset previo por watchdog lo deja activo
pwm_init(); // 1) lo primero: el calefactor en estado seguro
pwm_set(0);
lcd_init();
adc_init();
uart_init();
config_cargar(); // deseada (por defecto 40,0 °C) y calibración, de la EEPROM
tick_init(); // Timer0 a 1 kHz (tema 41)
sei();
wdt_enable(WDTO_1S); // 2) lo último: supervisión
for (;;) {
planificador_ejecutar();
set_sleep_mode(SLEEP_MODE_IDLE);
sleep_mode(); // el tick de 1 ms despierta la CPU (tema 40)
}
}
Dos decisiones de orden: el PWM se prepara y se fuerza a cero antes que cualquier otra cosa (el estado de los pines al reiniciar es el momento más peligroso), y el watchdog se habilita al final, cuando ya están listas las inicializaciones lentas (el LCD tarda más de 60 ms). La CPU duerme en Idle entre ticks, aunque este dispositivo está enchufado a la red y el consumo no importa: la costumbre ahorra calor y es gratis.
Para Arduino el esquema se traduce sin dificultad: setup() inicializa, loop() llama al planificador, analogReference(INTERNAL) elige la referencia de 1,1 V, analogWrite(3, duty) usa el mismo Timer2 y la biblioteca LiquidCrystal maneja el LCD. Tres precauciones valen en ambos casos: analogWrite(pin, 0) sí apaga el pin (la biblioteca desconecta la salida), analogRead bloquea unos 100 µs por cada llamada, y cambiar de referencia obliga a descartar las primeras lecturas.
10 · Paso 8
Ajustar y verificar
Ajustar un lazo es un procedimiento experimental. En orden:
- Con el calefactor desconectado, se verifican la lectura (contra un termómetro de referencia, a 0 °C con hielo y a 40 °C con agua tibia), el ruido (se registra por UART durante un minuto y se calcula el desvío) y el filtro. Se calibra
offset_cc. - Prueba de las protecciones, antes de dar potencia: se desconecta el sensor (la pantalla debe mostrar la falla y el PWM quedar en 0, medido con el osciloscopio o con un LED en lugar del calefactor), se pone el sensor en corto, y se fuerza la sobretemperatura calentando el sensor con la mano o un secador.
- Respuesta a un escalón en lazo abierto: con el recinto a temperatura ambiente, se fija una potencia fija del 50 % y se registra la temperatura cada 10 s hasta que se estabilice (o durante 30 minutos). Se leen tres datos: la ganancia
K(la subida final dividida por la potencia, en °C por unidad), el retardoθ(el tiempo antes de que la curva se mueva) y la constante de tiempoτ(el tiempo que tarda en recorrer el 63 % de la subida, contado después del retardo). - Lazo cerrado con ganancias bajas: se parte de un tercio de la ganancia de Ziegler–Nichols y un tiempo integral cercano a τ/2. Se aplica un cambio de 5 °C en la deseada y se mira la respuesta. Si hay sobrepaso grande, se baja Ki; si tarda demasiado, se sube Kp; si oscila alrededor del valor, se baja Kp.
- Perturbaciones: se abre la tapa un minuto, se pone un objeto frío adentro, se baja la tensión de la fuente. El controlador PI debe volver a la deseada sin oscilación creciente.
- Falla de alimentación y de reinicio: se corta y se devuelve la energía varias veces, incluso con el recinto caliente. La potencia debe arrancar en cero, el controlador debe esperar la primera lectura válida, y la deseada guardada debe recuperarse.
Una advertencia sobre la planta: el recinto no es un sistema lineal. Con la tapa abierta la pérdida cambia, con una carga adentro la capacidad térmica cambia, y la ganancia en 30 °C no es la de 60. Por eso un buen PI es conservador, y el término integral, que se adapta, es lo que lo hace robusto. Si el rango de trabajo es amplio, se reajusta en el punto más exigente.
11 · Analogía con programación
Simular el lazo con datos
Un lazo cerrado se estudia mejor simulándolo antes de conectar nada, porque la planta tiene minutos de inercia y el experimento real es lento. El modelo es una ecuación diferencial de primer orden con un retardo: C · dT/dt = P · u(t − θ) − (T − Tamb) / R, con la capacidad térmica C (J/K), la resistencia térmica a la ambiente R (K/W), la potencia P y la señal de control u entre 0 y 1. Si venís del desarrollo de software, es un bucle de simulación con un paso de 1 s, donde el «mundo» es una función y el controlador es otra.
Este programa simula el recinto de 20 W (C = 600 J/K, R = 1,5 K/W, retardo de 20 s) durante dos horas con siete controladores, mide el sobrepaso, la oscilación en régimen y el error final, calcula el efecto del sobremuestreo en el ruido de la lectura y los números de la etapa de potencia.
Python en tu navegador. Los bloques son editables: cambiá los valores y presioná «Ejecutar Python». La primera vez tarda unos segundos en cargar.
import math
# 1) el recinto: C dT/dt = P u(t - retardo) - (T - Ta) / R (paso de 1 s)
def simular(modo, sp=40.0, ta=20.0, pot=20.0, r=1.5, c=600.0, retardo=20, duracion=7200, **par):
t, integral, u, prev = ta, 0.0, 0.0, False
cola = [0.0] * retardo
temps, cambios, subida = [], 0, None
for s in range(duracion):
e = sp - t
if modo == "onoff":
if t < sp - par["h"] / 2: u = 1.0
elif t > sp + par["h"] / 2: u = 0.0
if (u > 0.5) != prev:
cambios += 1
prev = u > 0.5
elif modo == "p":
u = min(1.0, max(0.0, e / par["banda"]))
else:
crudo = par["kp"] * e + par["ki"] * integral
u = min(1.0, max(0.0, crudo))
if (not par.get("aw", True)) or 0 < crudo < 1 or (crudo >= 1 and e < 0) or (crudo <= 0 and e > 0):
integral += e # anti-windup: no integra si satura hacia afuera
cola.append(u)
t += (pot * cola.pop(0) - (t - ta) / r) / c
temps.append(t)
if subida is None and t >= sp - 1:
subida = s + 1
cola_t = temps[-1800:]
return dict(sobre=max(0.0, max(temps) - sp), rizado=max(cola_t) - min(cola_t),
error=sum(temps[-600:]) / 600 - sp, subida=subida, ciclos=cambios / 2 / (duracion / 3600))
casos = (
("histeresis de 4 grados", "onoff", dict(h=4)),
("histeresis de 1 grado", "onoff", dict(h=1)),
("histeresis de 0,2 grados", "onoff", dict(h=0.2)),
("proporcional, banda de 2 grados", "p", dict(banda=2)),
("PI (Kp 0,5; Ki 0,001)", "pi", dict(kp=0.5, ki=0.001)),
("el mismo PI sin anti-windup", "pi", dict(kp=0.5, ki=0.001, aw=False)),
)
print("controlador sobrepaso oscilacion error subida ciclos/h")
for nombre, modo, par in casos:
m = simular(modo, **par)
subida = f"{m['subida'] / 60:5.1f} min" if m["subida"] else " nunca"
ciclos = f"{m['ciclos']:6.2f}" if modo == "onoff" else " -"
print(f"{nombre:34s} {m['sobre']:8.2f} {m['rizado']:11.2f} {round(m['error'], 2) + 0.0:8.2f} {subida} {ciclos}")
m = simular("pi", kp=0.5, ki=0.001, retardo=200)
print(f"{'el mismo PI con 200 s de retardo':34s} {m['sobre']:8.2f} {m['rizado']:11.2f} {round(m['error'], 2) + 0.0:8.2f} {m['subida'] / 60:5.1f} min")
# 2) ruido de la lectura: sobremuestreo y filtro exponencial (alfa = 1/4)
lsb = 1.1 / 1024 / 0.010 # grados por cuenta del ADC
filtro = math.sqrt(0.25 / (2 - 0.25))
print()
print(f"un paso del ADC = {lsb:.4f} C; ruido de una conversion = 1,5 cuentas = {1.5 * lsb:.4f} C")
print("muestras ADC (ms) ruido (C) con filtro (C)")
for n in (1, 4, 16, 64):
s = 1.5 / math.sqrt(n) * lsb
print(f"{n:8d} {n * 0.104:10.2f} {s:11.4f} {s * filtro:14.4f}")
# 3) la etapa de potencia: calefactor de 7,2 ohm a 12 V, MOSFET a 976 Hz
V, RH, F, TSW, RTH = 12.0, 7.2, 976, 100e-9, 62
print()
for nombre, rds, duty in (("IRLZ44N, ciclo de 67 %", 0.022, 0.67), ("IRLZ44N, ciclo de 100 %", 0.022, 1.0),
("MOSFET sin nivel logico (0,5 ohm), 67 %", 0.5, 0.67)):
i = V / (RH + rds)
perdida = duty * i * i * rds + 0.5 * V * i * TSW * F
print(f"{nombre:42s} {i:5.2f} A calefactor {i * i * RH * duty:5.1f} W MOSFET {perdida * 1000:6.1f} mW +{perdida * RTH:4.1f} C")
Resultado: el recinto sostiene 40 °C con 13,3 W, el 67 % del calefactor, y llega a 20 + 20 · 1,5 = 50 °C a fondo. Con histéresis de 4 °C el sobrepaso es de 2,18 °C y la oscilación de 4,60 °C con 5,25 ciclos por hora; con 1 °C, 0,72 y 1,67 °C con 14,25 ciclos por hora; con 0,2 °C, 0,33 y 0,89 °C, pero 27 ciclos por hora: cinco veces más maniobras para bajar la oscilación a la mitad. El proporcional de banda 2 °C nunca llega a la deseada: queda 1,25 °C abajo. El PI sube a ±1 °C en 16,6 minutos con sobrepaso, oscilación y error de 0,00 °C; el mismo PI sin anti-windup se pasa 6,89 °C; y con 200 s de retardo en lugar de 20, el mismo PI oscila 5,10 °C pico a pico y sobrepasa 1,41 °C. En la lectura, una conversión tiene un ruido de 0,16 °C (1,5 cuentas de 0,107 °C); con 16 muestras baja a 0,040 °C y con el filtro, a 0,015 °C, a cambio de 1,66 ms de ADC. En la etapa de potencia, el calefactor toma 1,66 A y entrega 13,3 W promedio al 67 % con 41,7 mW de pérdida en el MOSFET (+2,6 °C sobre el ambiente), 19,9 W y 61,7 mW a fondo; un MOSFET que no es de nivel lógico, con 0,5 Ω efectivos, disiparía 814 mW y se calentaría 50 °C.
Los parámetros térmicos (C, R, retardo) y las pérdidas de conmutación son valores de ejemplo; en el recinto real se miden con la prueba de escalón del paso 8.
12 · Exploración interactiva
Lectura, lazo y potencia
La primera herramienta calcula la resolución, el ruido y el retardo de la lectura y la histéresis mínima razonable. La segunda simula el lazo cerrado con los tres controladores. La tercera calcula las pérdidas del MOSFET y el calentamiento.
Banco del controlador
1. Lectura: resolución, ruido y filtro
2. Simulación del lazo cerrado
3. Etapa de potencia: MOSFET y calefactor
Recorrido guiado
- Con los valores iniciales, el PI sube a ±1 °C en 16,6 minutos, sin sobrepaso ni oscilación y con error cero. El recinto sostiene 40 °C con 13,3 W (67 % del calefactor).
- Pasá a «Histéresis» con banda de 1 °C: sobrepaso de 0,72 °C, oscilación de 1,67 °C y unos 14 ciclos por hora; un relé de 100 000 ciclos duraría menos de un año. Con banda de 0,2 °C, 27 ciclos por hora y oscilación de 0,89 °C.
- Pasá a «Proporcional» con banda de 2 °C: la nota explica el error permanente; el sistema se estabiliza 1,25 °C por debajo de la deseada.
- Volvé al PI y desactivá el anti-windup: el sobrepaso sube a unos 6,9 °C. Activalo de nuevo.
- Con el PI, subí el retardo a 200 s: el lazo oscila unos 5 °C pico a pico. Bajá Kp a 0,15 y Ki a 0,0003 y la oscilación desaparece, a costa de una respuesta más lenta.
- En la lectura, 16 conversiones con filtro k = 2 dejan 0,015 °C de ruido, con 0,3 s de atraso y una histéresis mínima de unos 0,1 °C. Con 1 conversión y sin filtro, 0,16 °C de ruido pide una banda de casi 1 °C.
- En la potencia, el calefactor de 7,2 Ω toma 1,66 A; el MOSFET de 22 mΩ pierde 42 mW al 67 % y sube 2,6 °C. Con una tensión de compuerta de 3,3 V la nota advierte que el MOSFET debe ser de nivel lógico para ese valor.
Desafío: ¿qué potencia mínima necesita el calefactor?
Se quiere sostener 55 °C dentro de un recinto de resistencia térmica 1,5 K/W cuando la temperatura exterior baja a 5 °C. La potencia que se pierde es (55 − 5) / 1,5 = 33,3 W. Un calefactor de 20 W no alcanza: el máximo es 5 + 20 · 1,5 = 35 °C. Hay que subir a un calefactor de al menos 33,3 W, y como el controlador necesita margen para subir y para compensar perturbaciones, se pide 50 % más: 50 W. Con 12 V son 4,2 A y una resistencia de 2,9 Ω: la fuente, el fusible, los cables y el MOSFET (con 2,9 Ω, la corriente de 4,2 A produce 0,39 W de pérdida con 22 mΩ, con unos 24 °C de calentamiento sin disipador) se redimensionan. Se diseña desde la planta hacia el controlador, nunca al revés.
13 · Cierre del proyecto
Lo aprendido en el proyecto
- Un lazo cerrado se diseña desde la planta: la potencia debe alcanzar para compensar las pérdidas al setpoint, y la constante de tiempo y el retardo del proceso definen qué controlador y qué ganancias son razonables.
- La lectura necesita resolución y calma: sobremuestreo y filtro bajan el ruido muy por debajo de la banda de control, y la validación de rango detecta un sensor roto antes de que el calefactor lo pague.
- La histéresis evita el rebote en el umbral, pero la oscilación real es mayor que la banda por el retardo, y las maniobras crecen cuando se angosta.
- El proporcional deja un error permanente y el integral lo elimina; el anti-windup evita que la integral acumule durante la saturación y cause sobrepasos.
- El actuador pide cuidado en los bordes: el PWM al 0 % debe desconectar el pin, el MOSFET debe ser de nivel lógico y tener un pull-down de compuerta, y un calefactor necesita un corte térmico que no dependa del software.
- Las protecciones forman una cadena: validación del sensor, límite de temperatura, watchdog acariciado solo desde el control y termostato físico.
- La estructura cooperativa del tema 42 alcanza: cinco tareas con períodos distintos, una pantalla que se escribe a pedazos y utilización de un 4 %.
- Un lazo se ajusta midiendo: escalón en lazo abierto, ganancias moderadas, perturbaciones y reinicios.
Con los dos proyectos termina la parte práctica del curso. El último tema revisa todo el recorrido, propone un método de depuración sistemática y señala por dónde seguir: sensores y actuadores más complejos, comunicación inalámbrica y otras familias de microcontroladores.
14 · Comprensión
Confusiones frecuentes
«Con una histéresis de 0,1 °C la temperatura va a quedar a ±0,05 °C»
Esa es la banda del algoritmo, no la oscilación de la temperatura. El calor sigue llegando un rato después de apagar y la temperatura se pasa; con retardos de decenas de segundos la oscilación real es varias veces la banda. Y una banda angosta multiplica las maniobras.
«Subir Kp hasta que no haya error»
Con control solo proporcional, el error permanente se reduce al subir Kp, pero con un retardo en el lazo hay una ganancia máxima a partir de la cual el sistema oscila. La salida correcta es el término integral, no una ganancia enorme.
«El integral es opcional si el sistema ya funciona»
Un PI sin anti-windup funciona en régimen y falla al arrancar o al cambiar la deseada: la integral acumulada durante la saturación produce un gran sobrepaso. Es un fallo que no aparece mientras todo está tranquilo.
«Más resolución del ADC es más exactitud»
La resolución es el tamaño del paso; la exactitud es la cercanía al valor real. Un LM35 con 0,015 °C de ruido puede estar 1 °C desviado. El sobremuestreo mejora la primera; solo una calibración contra una referencia mejora la segunda.
«Si el PWM está en 0, el calefactor está apagado»
Con OCR2B = 0 en PWM rápida queda una aguja de 1/256 del ciclo. Para apagar de verdad hay que desconectar el pin del temporizador y forzarlo a bajo.
15 · Práctica guiada
Predecí antes de comprobar
1. Resolución y ruido
Con el LM35 (10 mV/°C) y la referencia de 1,1 V, ¿cuántos °C vale una cuenta? Si cada conversión tiene un ruido de 2 cuentas, ¿cuánto ruido queda con 64 muestras promediadas?
Ver solución
1,1 V / 1024 = 1,074 mV por cuenta, o 0,1074 °C. El ruido de una conversión es 2 · 0,1074 = 0,215 °C. Con 64 muestras, el ruido baja en √64 = 8: 0,027 °C. Y el promedio de 64 conversiones equivale a 3 bits más de resolución teórica (cada factor 4 agrega uno).
2. El error permanente
Un recinto necesita 12 W para sostener la temperatura deseada y el calefactor es de 30 W. Con un controlador proporcional de banda 4 °C (100 % de potencia con 4 °C de error), ¿qué error permanente queda?
Ver solución
La fracción de potencia es 12 / 30 = 0,40. El error permanente es 0,40 · 4 °C = 1,6 °C por debajo de la deseada. Con un integral desaparece, porque la integral aporta la potencia de régimen sin necesitar error.
3. Maniobras del relé
Un termostato con relé conmuta 18 ciclos por hora. Si el relé aguanta 100 000 ciclos, ¿cuánto dura? ¿Qué se puede hacer para alargarlo sin cambiar el relé?
Ver solución
100 000 / 18 = 5556 h = 232 días, menos de ocho meses. Se alarga ensanchando la histéresis (menos ciclos por hora, a costa de más oscilación), agregando tiempos mínimos de encendido y apagado, o pasando a un relé de estado sólido o un MOSFET con PWM, que no se gastan por maniobra.
4. El valor de la integral
Un PI con Ki = 0,001 por °C y por segundo tiene un error constante de 0,2 °C durante 100 s. ¿Cuánto sube la salida por efecto del integral, y qué pasa si la salida está saturada al 100 %?
Ver solución
La integral suma 0,2 · 100 = 20 °C·s, y el aporte es 0,001 · 20 = 0,02, un 2 % de potencia. Si la salida está saturada al 100 % y el error empuja a seguir, el anti-windup detiene la integración: la integral queda como estaba, para no acumular un error que el actuador no puede corregir.
5. Pérdidas del MOSFET
Un calefactor de 4 Ω a 12 V se conmuta con un MOSFET de 30 mΩ al 50 % de ciclo. ¿Qué corriente circula y qué potencia pierde el MOSFET por conducción? ¿Y si el MOSFET no es de nivel lógico y su resistencia efectiva es de 1 Ω?
Ver solución
I = 12 / (4 + 0,03) = 2,98 A. Pérdida = 0,5 · 2,98² · 0,03 = 0,133 W: no necesita disipador. Con 1 Ω: I = 12 / 5 = 2,4 A y la pérdida es 0,5 · 2,4² · 1 = 2,88 W, veinte veces más; con 62 K/W sube casi 180 °C: se quema. Por eso el MOSFET debe estar especificado con la tensión de compuerta que realmente se le aplica.
16 · Síntesis
Ideas para recordar
- Un controlador de temperatura es un lazo cerrado: sensor, comparación con la deseada, algoritmo, actuador y planta; el calor tarda en llegar, y ese retardo determina cuánta ganancia soporta el lazo.
- La lectura se mejora con sobremuestreo (el ruido baja con √n) y filtro exponencial (con atraso de una constante de tiempo); se trabaja en enteros con centésimas de grado y se valida el rango para detectar un sensor roto.
- La histéresis evita las conmutaciones por ruido, pero la oscilación real es mayor que la banda por el retardo, y angostar la banda multiplica las maniobras (un relé dura meses a 18 ciclos por hora).
- El control proporcional deja un error permanente (e = potencia de régimen / Kp); el integral lo elimina y aprende la potencia de régimen; el anti-windup impide que se acumule durante la saturación.
- Las ganancias se eligen midiendo la planta (K, τ, θ) y partiendo de valores conservadores; un retardo mayor que el previsto hace oscilar el mismo controlador.
- El PWM de Timer2 a 976 Hz sobre un MOSFET de nivel lógico entrega potencia continua con pérdidas de milivatios; con OCR = 0 hay que desconectar el pin para apagar de verdad, y la compuerta lleva un pull-down.
- Las protecciones forman una cadena: validar el sensor, limitar la temperatura, acariciar el watchdog solo desde el control y, por encima de todo, un corte térmico de hardware que no dependa del programa.
- La pantalla y los botones se resuelven con la estructura cooperativa: escribir pocos caracteres por llamada, antirrebote por registro de desplazamiento y guardar en la EEPROM solo después de una pausa.
¿Listo para el tema 45?
Antes de avanzar, explicá por qué un controlador proporcional deja un error permanente y cómo lo elimina el integral, qué hace el anti-windup y cuándo, y cuántas maniobras por hora puede tener un relé durante un año. Si podés justificar las tres respuestas, entendés cómo se cierra un lazo de control.
En el último tema hacemos el cierre del curso y el puente a sensores y actuadores: una revisión de todo el recorrido, un método de depuración sistemática y los próximos pasos.