01 · Punto de partida
Diseñar desde la falla
Cada tema anterior asumió componentes que funcionan. Pero un sensor en un ambiente húmedo, un relé que conmuta decenas de veces por hora o un cable que se mueve cada vez que alguien pasa tienen una probabilidad real de fallar. El método de la ingeniería de seguridad es invertir la pregunta: en vez de «¿qué hace mi programa?», «¿qué es lo peor que puede pasar y cómo lo evito?».
Para el horno de los temas anteriores, lo peor es un sobrecalentamiento (que daña, quema o provoca un incendio) y lo que lo causa es casi cualquier falla que deje la resistencia encendida. Un análisis mínimo de modos de falla (una tabla estilo FMEA) para ese horno es:
| Falla | Efecto | Cómo se detecta | Respuesta |
|---|---|---|---|
| Sensor desconectado (−127 °C, 1023 cuentas) | El controlador ve frío y enciende a pleno | Rango físico | Estado seguro, calefactor apagado |
| Sensor que lee «frío» (congelado, deriva) | Ídem: la temperatura real sube sin control | Discrepancia, congelamiento, respuesta esperada | Estado seguro |
| Sensor que lee «caliente» | El horno no calienta: falla segura (pero para el proceso) | Rango, discrepancia | Alarma, parada |
| Calefactor o relé pegado en «encendido» | Calienta sin parar, sin que el programa pueda impedirlo | Temperatura que sube con la salida apagada | Protección independiente que corta la alimentación |
| Relé abierto o resistencia cortada | No calienta (segura) | La temperatura no responde a la salida | Alarma de fallo del actuador |
| Programa colgado con la salida encendida | Se queda calentando | Perro guardián | Reinicio con las salidas apagadas |
| Caída de tensión (brownout) | Comportamiento impredecible, memoria corrupta | Detector de baja tensión | Reinicio limpio y arranque seguro |
Dos ideas organizan el tema. La primera: el estado seguro, aquel en el que un sistema no puede hacer daño (calefactor apagado, válvula cerrada, motor frenado), y la regla de diseñar para que cualquier falla detectada lleve a él. La segunda: ninguna protección debe depender de lo que protege: un límite de temperatura que usa el mismo sensor, el mismo microcontrolador y el mismo relé que el control no protege de la falla de ninguno de los tres.
02 · Entradas dudosas
Sensores defectuosos
Un sensor falla de pocas maneras características. Reconocerlas dice qué hay que mirar:
| Falla | Cómo se ve | Detector |
|---|---|---|
| Circuito abierto o en corto | Valores extremos: 0 o 1023 cuentas, −127 °C, 85 °C de arranque, NaN | Rango físico (mín. y máx. plausibles) |
| Pico (EMI, contacto intermitente) | Salto imposible de una muestra a la siguiente | Tasa de cambio máxima |
| Congelado (stuck-at) | El valor no cambia más, ni siquiera el último bit de ruido | Congelamiento: valor idéntico durante una ventana larga |
| Deriva (envejecimiento, contaminación) | Se aparta lentamente de la realidad, sin saltos | Discrepancia con un segundo sensor o con un modelo |
| Ruido excesivo | La varianza crece de golpe | Tasa o desviación típica sobre una ventana corta |
| Dato viejo | La comunicación se cortó pero la variable conserva el último valor | Edad de la lectura (marca de tiempo) |
El último es el más traicionero: un programa que lee un sensor I2C o un DHT con una función que, al fallar, no escribe la variable deja el último valor bueno como si fuera actual. La defensa es la Lectura del tema 40, con valor, estado y marca de tiempo, y rechazar todo dato cuya edad supere un límite.
Una señal simulada de 40 ± 0,5 °C con un sensor de 12 bits (resolución de 0,0625 °C) y fallas inyectadas a los 300 s da estos tiempos de detección con un límite de tasa de 0,5 K por muestra, una ventana de congelamiento de 30 muestras y un segundo sensor que discrepa a partir de 0,5 K:
| Falla | Rango | Tasa | Congelado | Discrepancia |
|---|---|---|---|---|
| Sensor congelado | – | – | 28 | 176 |
| Pico de +15 °C durante 3 s | – | 0 | – | 0 |
| Deriva de 0,02 K/s | – | – | – | 25 |
| Desconectado (−127) | 0 | 0 | 29 | 0 |
| Ráfaga de ruido | – | 1 | – | 1 |
Ningún detector los atrapa a todos: la deriva solo la ve la discrepancia con un segundo sensor, y el sensor congelado, el detector de congelamiento (28 s) mucho antes que la discrepancia (176 s, cuando la temperatura real se aleja 0,5 K del valor retenido). Y cada detector tiene su costo de falsos positivos: con un límite de tasa de 0,05 K por muestra (menor que el ruido y la cuantización), el detector se dispara 391 veces en 600 s sin ninguna falla; con 0,5 K, ninguna. Los umbrales se eligen entre la variación normal más grande y la falla más chica que importa.
La política de reacción importa tanto como la detección. Una sola muestra mala (un pico) no debería parar un horno; se descarta y se conserva el último valor bueno. Una falla que persiste más de un tiempo corto (unos segundos a unos minutos, según la inercia del proceso) lleva al estado seguro. Y una falla detectada no se «cura» sola: se vuelve a la operación normal solo tras un período de lecturas buenas o un reconocimiento manual.
03 · Más de uno
Redundancia y votación
La discrepancia con un segundo sensor es el detector más potente, y de ahí la redundancia. Hay tres esquemas básicos:
- Dos sensores comparados: si coinciden (dentro de una tolerancia), se promedian; si discrepan, se detecta la falla pero no se sabe cuál miente, y se pasa al estado seguro. Es lo mínimo para detectar una deriva.
- Tres sensores con votación 2 de 3 (2oo3): se toma la mediana; la falla de uno se detecta, se identifica y el sistema sigue funcionando. Mantiene el servicio y la seguridad.
- Redundancia disímil: dos sensores de tecnologías distintas (un NTC y un DS18B20) fallan por causas distintas.
El enemigo de la redundancia es la falla de causa común: lo que puede dañar a todos a la vez, como la misma fuente de alimentación, el mismo cable, un rayo, el mismo error de software o la misma contaminación. Con un modelo simple e ilustrativo (probabilidad anual de falla de un sensor p = 5 %, cobertura diagnóstica del 60 % para las verificaciones de la sección anterior y un 10 % de causa común), el resultado por año es:
| Configuración | Dato malo sin detectar | Pérdida de servicio (parada) |
|---|---|---|
| 1 sensor | 2,00 % | 3,00 % |
| 2 sensores, comparar | 0,28 % | 9,00 % |
| 3 sensores, votar 2 de 3 | 0,44 % | 0,36 % |
Los números enseñan un compromiso. Duplicar y comparar reduce siete veces el riesgo de un dato malo, pero triplica las paradas, porque cualquiera de los dos sensores que falle detiene el sistema. La votación 2 de 3 consigue casi la misma seguridad con muchas menos paradas. Y la causa común fija un piso: con ella en 10 %, los 0,28 % y 0,44 % están dominados por ese término, y agregar un cuarto sensor idéntico no los baja. Por eso conviene variar la tecnología, la alimentación y el cableado entre los sensores.
Lo que cuesta: más sensores, más cables y más código; solo se justifica donde la falla es grave. Y una verificación que no se sustituye con redundancia: cada sensor sigue necesitando sus propios detectores (la redundancia detecta la discrepancia, pero no distingue cuál de dos sensores miente).
04 · Capas
Límites y capas de protección
Ningún mecanismo es infalible; por eso la seguridad se arma en capas independientes, de modo que la falla de una no deje al sistema sin protección:
- Control normal: el PID mantiene 40 °C.
- Límites y alarmas por software: si la temperatura medida supera 55 °C, se apaga el calefactor y se enclava el modo seguro.
- Protección independiente (hardware): un termostato bimetálico de seguridad en serie con la alimentación de la resistencia, que abre a 55 o 60 °C sin ningún software; un fusible térmico de un solo uso por encima.
- Contención física: aislamiento, materiales que no arden, una carcasa que contenga el incendio.
Con el horno de los temas 42 y 43 (100 W a pleno, 65 °C de temperatura final), la simulación de tres fallas con distintas combinaciones de capas da:
| Falla | Protecciones | T máxima | Primera protección |
|---|---|---|---|
| Calefactor pegado | Ninguna | 65,0 °C | — |
| Calefactor pegado | Software y chequeos | 65,0 °C | Límite por software a los 932 s: no puede abrir un relé pegado |
| Calefactor pegado | Software, chequeos y termostato de 55 °C | 55,0 °C | El termostato independiente corta la alimentación |
| Sensor que lee frío | Solo límite por software | 65,0 °C | Ninguna: el límite usa el mismo sensor que miente |
| Sensor que lee frío | Software y chequeos | 46,1 °C | Chequeo de respuesta a los 295 s |
| Sensor desconectado | Ninguna | 65,0 °C | — |
| Sensor desconectado | Chequeo de rango | 40,5 °C | De inmediato (0 s) |
Cada fila confirma una regla. Un límite por software con el mismo sensor no ve la falla de ese sensor (el sensor que lee frío mantiene el horno calentando hasta 65 °C). Un límite por software que actúa por el mismo relé no puede abrir un relé pegado (el calefactor sigue a 65 °C); solo la protección independiente en serie corta. Y cada capa se elige para atrapar lo que las otras no ven: el rango atrapa la desconexión al instante, la verificación de respuesta atrapa al sensor mentiroso en 295 s, el termostato atrapa al actuador pegado.
Reglas para los límites:
- Escalonados: operación normal < alarma < disparo por software < protección independiente < daño. En el horno, 40 °C < 50 °C < 55 °C < 60 °C.
- Con tiempo para reaccionar: con la salida a pleno, el horno llega de 40 a 55 °C en 916 s y a 60 °C en 1609 s; en un proceso rápido (un motor, un láser) las protecciones deben ser rápidas, y por eso de hardware.
- Enclavados: un disparo de seguridad se mantiene hasta que una persona lo reconoce; una protección que se reinicia sola cuando baja la temperatura hace oscilar un calefactor defectuoso.
- Probados periódicamente: una protección que nunca se prueba puede estar rota sin que nadie lo sepa.
05 · Salidas
Actuadores y estado seguro
Los actuadores también fallan, y de dos maneras distintas: fallan abiertos (no actúan; normalmente seguro) o fallan cerrados (actúan sin parar; normalmente peligroso). El diseño busca que la falla caiga del lado seguro:
| Actuador | Estado seguro | Cómo se logra |
|---|---|---|
| Calefactor | Apagado | Relé normalmente abierto: sin corriente de bobina, abre |
| Válvula de gas o de agua | Cerrada | Electroválvula normalmente cerrada (tema 29): el resorte la cierra |
| Motor con carga que cae | Frenado | Freno electromagnético que se aplica sin corriente |
| Ventilador de refrigeración | Encendido | El estado seguro depende de la función: aquí, lo seguro es enfriar |
| Bomba de riego | Apagada, con tiempo máximo | Temporizador de máxima duración de marcha |
- Tiempo máximo de marcha: un actuador que puede causar daño por permanecer activo (una bomba que inunda, un calefactor) tiene un límite de tiempo continuo en el driver (tema 40) y un tiempo de enfriamiento obligatorio.
- Verificación de respuesta: si se manda calentar y la temperatura no sube, algo falla (actuador, sensor o fuente). Con el horno a pleno se esperan 1,5 K en 60 s; si tras 300 s encendido no subió al menos 0,5 K, se declara la falla. Es la misma comprobación que atrapó al sensor mentiroso en la tabla anterior.
- El relé de estado sólido (SSR) falla en cortocircuito con frecuencia (el triac queda conduciendo): no se debe confiar solo en él para cortar; se coloca en serie un contactor electromecánico o un termostato de seguridad.
- Contactos duplicados: dos relés en serie, cada uno comandado por una vía distinta, para que un relé pegado no deje la carga encendida.
- Enclavamientos: el calefactor no se enciende si el ventilador no gira; la bomba no arranca sin agua; el motor no invierte sentido sin parar.
06 · El programa también falla
El perro guardián
Un programa se puede colgar por un error, una interferencia, una pila de llamadas corrupta o un dispositivo que no responde. El perro guardián (watchdog) es un temporizador de hardware que reinicia el microcontrolador si el programa no lo «alimenta» a tiempo. Lo normal es que el lazo principal lo reinicie en cada vuelta; si el lazo se detiene, el temporizador vence y fuerza un reinicio.
En un ATmega328P (Arduino Uno) los tiempos disponibles son 15, 30, 60, 120, 250, 500 ms y 1, 2, 4 y 8 s (valores nominales, con un oscilador interno poco preciso). Se activa con wdt_enable(WDTO_250MS) y se alimenta con wdt_reset(). El ESP32 tiene un perro guardián de tareas (por defecto unos 5 s) y el RP2040 uno configurable con watchdog_enable(). También existen circuitos externos (supervisores como el TPL5010 o el MAX6369) que no dependen del oscilador del microcontrolador.
La elección del tiempo tiene dos cotas:
- Mínimo: mayor que el lazo más lento con margen, o habrá reinicios falsos. Una regla es 4 veces el tiempo máximo del lazo (se mide, tema 41): un lazo de 28 ms pide al menos 112 ms, y se elige 120 ms; uno de 150 ms, 600 ms (se elige 1 s).
- Máximo: menor que el tiempo en que el sistema puede hacer daño sin control, incluido el tiempo de arranque. Con 250 ms de timeout y 600 ms de arranque, el sistema estará sin control 850 ms: de sobra para un horno (600 s) o una válvula (5 s), pero no alcanza para un motor con carga que debe frenar en 100 ms: ahí la protección debe ser de hardware (un límite de corriente del driver, un freno).
Cómo se usa bien, y los errores más comunes:
- Un solo punto de alimentación, condicionado a que todo esté bien. Cada tarea marca una bandera al completarse y el lazo principal alimenta al perro solo si todas las banderas están levantadas, y luego las baja. Así un fallo de cualquier tarea provoca el reinicio.
- No alimentarlo desde una interrupción ni desde un temporizador: la interrupción sigue funcionando aunque el lazo principal esté colgado, y el perro nunca vence.
- No alimentarlo al principio del lazo «por si acaso» ni dentro de esperas largas: oculta el bloqueo que debería detectar.
- Reaccionar al reinicio: leer la causa (en un AVR, el registro
MCUSR:WDRFes el perro guardián,BORFla baja tensión), registrarla y contar los reinicios seguidos. Con tres seguidos, no seguir intentando: pasar al modo falla con las salidas apagadas, para no oscilar entre colgarse y reiniciar. - Perro guardián con ventana: algunos obligan a alimentarlo ni demasiado pronto ni demasiado tarde, y detectan un lazo que corre demasiado rápido.
- Compatibilidad con el cargador de arranque: algunas placas con un cargador antiguo quedan atrapadas en bucles de reinicio si el perro guardián se activa sin desactivarse al arrancar; conviene probar con la placa concreta.
07 · Encender y reiniciar
Energía, reinicios y arranque seguro
Un reinicio es el momento más peligroso: los pines de un microcontrolador pasan a alta impedancia (flotantes) hasta que el programa los configura, y lo que está conectado a ellos recibe niveles indefinidos. La defensa es de hardware y de software:
- Resistencias de reposo: un pull-down (10 kΩ a masa) en cada salida de activación alta (o un pull-up si el módulo es de activación baja) hace que durante el reinicio el actuador esté apagado. Los módulos de relé de activación baja suelen traer su propia resistencia de pull-up, pero hay que comprobarlo.
- Reposo antes que salida:
digitalWrite(pin, apagado); pinMode(pin, OUTPUT);(tema 40), nunca al revés. - Autoprueba al arrancar (POST): comprobar que los sensores responden y están dentro de rango, que la configuración guardada es válida (CRC) y que las salidas están apagadas, antes de habilitar nada. Si falla, no arrancar: ir al modo falla.
- Detección de baja tensión (brown-out): con la tensión por debajo de lo que el microcontrolador necesita (a 16 MHz, cerca de 4,5 V), el procesador ejecuta mal y una escritura en la EEPROM puede corromper datos. El ATmega328P trae un detector programable (por ejemplo, 4,3 V) que mantiene el reinicio hasta que la tensión sube; con una batería o una fuente débil, un supervisor externo (MAX809 y similares) lo mejora.
- Interferencia: los relés, motores y solenoides inyectan picos que reinician o perturban al microcontrolador: diodos de rebote (tema 27), redes RC en contactos de relé, capacitores de desacople, cableado de potencia separado del de señal y, si hace falta, alimentaciones separadas.
La causa del reinicio se lee al arrancar: encendido (PORF), pin de reset (EXTRF), baja tensión (BORF) o perro guardián (WDRF). Un equipo que se reinicia muchas veces por baja tensión tiene un problema de alimentación que el software no puede resolver.
08 · Máquina de modos
Modos de funcionamiento
Con las piezas anteriores, el programa se organiza en una máquina de modos (tema 41) con comportamiento definido en cada uno y reglas explícitas de transición:
| Modo | Qué hace | Salidas | Cómo se sale |
|---|---|---|---|
| INICIO | Autoprueba: sensores, configuración, salidas | Apagadas | Pasa a NORMAL si todo está bien; a FALLA si no en unos 10 s |
| NORMAL | Control con datos válidos | Controladas | Un dato malo → DEGRADADO |
| DEGRADADO | Sigue con el último valor bueno o con el sensor de respaldo, con alarma | Controladas con límites más estrictos | Dato bueno → NORMAL; tiempo agotado (p. ej. 60 s) → SEGURO |
| SEGURO | Estado seguro enclavado, con aviso (LED y zumbador) | Apagadas | Solo un reconocimiento manual, tras revisar la causa |
| FALLA | Falla grave (autoprueba, reinicios repetidos): no intenta operar | Apagadas | Reinicio manual de la alimentación |
Dos decisiones de diseño. El tiempo en DEGRADADO debe ser mucho menor que la inercia del proceso (60 s frente a los 1000 s del horno): el último valor bueno sigue siendo una buena estimación; con un proceso rápido, el modo degradado casi no existe. Y los modos SEGURO y FALLA están enclavados: no vuelven a NORMAL solos, para que una falla intermitente no deje al equipo conmutando entre ambos. Se informa siempre al usuario (un LED que parpadea con un patrón, tema 35; un zumbador, tema 39) y se registra la causa.
09 · Programación
Código defensivo
Programar a la defensiva es asumir que las entradas son dudosas, los tiempos de espera son finitos y la memoria puede estar corrupta. Reglas prácticas:
- Validar toda entrada (rango, tasa, edad) antes de usarla, y tratar cualquier estado distinto de
OKcomo falla. - Ninguna espera sin tiempo límite (tema 41): lecturas, comunicaciones, bucles
while. Unwhile (!listo)sin límite es un cuelgue esperando ocurrir. - Un
defaultseguro en cadaswitchsobre un estado, que lleve al modo seguro: una variable corrupta no puede dejar al programa en un estado sin salida. - Sin memoria dinámica en un microcontrolador pequeño (fragmentación, falta de memoria impredecible): objetos estáticos.
- Cuidado con los límites aritméticos: desbordes de contadores y de
millis()(tema 41), divisiones por cero, comparaciones de decimales con==. - Configuración con suma de verificación (CRC) y valores por defecto seguros: si el CRC no coincide, no se usa el dato.
- Registro de fallas en la EEPROM, con cuidado por el desgaste: cada celda soporta unas 100 000 escrituras. Un registro por minuto en la misma celda la agota en 69 días; repartido de forma rotativa en 256 celdas, dura 48,7 años. Se escribe solo lo que cambia (
EEPROM.update).
#include <avr/wdt.h>
#include <EEPROM.h>
// ---------- 1. Validación de la lectura: un decorador de SensorTemperatura (tema 40) ----------
class SensorValidado : public SensorTemperatura {
public:
SensorValidado(SensorTemperatura &s, float minimo, float maximo, float saltoMax, uint16_t ventanaCongelado)
: s_(s), min_(minimo), max_(maximo), saltoMax_(saltoMax), ventana_(ventanaCongelado) {}
bool begin() override { return s_.begin(); }
void iniciar() override { s_.iniciar(); }
bool lista() override { return s_.lista(); }
Lectura leer() override {
Lectura l = s_.leer();
if (l.estado != Estado::OK) return l; // el driver ya la marcó
if (isnan(l.valor) || l.valor < min_ || l.valor > max_) { // rango físico: -127, 85 de arranque, 1023...
l.estado = Estado::FUERA_DE_RANGO; return l;
}
if (hay_ && fabsf(l.valor - ultimo_) > saltoMax_ && rechazos_ < 5) { // salto imposible para el proceso
rechazos_++; l.estado = Estado::FUERA_DE_RANGO; return l; // tras 5 seguidos se acepta: puede ser real
}
rechazos_ = 0;
iguales_ = (hay_ && l.valor == ultimo_) ? iguales_ + 1 : 0; // valor exactamente igual durante muchas muestras
if (iguales_ >= ventana_) { l.estado = Estado::SIN_RESPUESTA; return l; } // sensor congelado
ultimo_ = l.valor; hay_ = true;
return l;
}
private:
SensorTemperatura &s_;
float min_, max_, saltoMax_, ultimo_ = 0;
uint16_t ventana_, iguales_ = 0, rechazos_ = 0;
bool hay_ = false;
};
inline bool fresca(const Lectura &l, uint32_t edadMaxMs) { return (uint32_t)(millis() - l.tMs) <= edadMaxMs; }
// ---------- 2. Dos sensores: se comparan y, si discrepan, no se confía en ninguno ----------
class SensorRedundante : public SensorTemperatura {
public:
SensorRedundante(SensorTemperatura &a, SensorTemperatura &b, float difMax) : a_(a), b_(b), dif_(difMax) {}
bool begin() override { return a_.begin() & b_.begin(); }
void iniciar() override { a_.iniciar(); b_.iniciar(); }
bool lista() override { return a_.lista() && b_.lista(); }
Lectura leer() override {
Lectura a = a_.leer(), b = b_.leer();
bool okA = a.estado == Estado::OK, okB = b.estado == Estado::OK;
degradado_ = okA != okB;
if (okA && okB) {
if (fabsf(a.valor - b.valor) > dif_) return { NAN, Estado::FUERA_DE_RANGO, millis() }; // discrepan: no se sabe cuál miente
return { (a.valor + b.valor) / 2, Estado::OK, a.tMs };
}
if (okA || okB) return okA ? a : b; // uno falló: se sigue con el otro, degradado
return { NAN, Estado::SIN_RESPUESTA, millis() };
}
bool degradado() const { return degradado_; }
private:
SensorTemperatura &a_, &b_; float dif_; bool degradado_ = false;
};
// ---------- 3. Modos de funcionamiento con tiempos y enclavamiento ----------
enum class Modo : uint8_t { INICIO, NORMAL, DEGRADADO, SEGURO, FALLA };
Modo modo = Modo::INICIO;
uint32_t desdeMs = 0;
float ultimoBueno = NAN;
void entrar(Modo nuevo) { modo = nuevo; desdeMs = millis(); if (nuevo >= Modo::SEGURO) calefactor.apagar(); }
float temperaturaParaControl(const Lectura &l) { // qué dato ve el controlador en cada modo
bool buena = l.estado == Estado::OK && fresca(l, 5000);
switch (modo) {
case Modo::INICIO:
if (buena && !isnan(l.valor)) { ultimoBueno = l.valor; entrar(Modo::NORMAL); }
else if ((uint32_t)(millis() - desdeMs) > 10000) entrar(Modo::FALLA); // la autoprueba no pasó
break;
case Modo::NORMAL:
if (buena) ultimoBueno = l.valor; else entrar(Modo::DEGRADADO);
break;
case Modo::DEGRADADO: // se mantiene el último valor bueno un rato breve
if (buena) { ultimoBueno = l.valor; entrar(Modo::NORMAL); }
else if ((uint32_t)(millis() - desdeMs) > 60000) entrar(Modo::SEGURO); // 60 s << constante de tiempo del horno
break;
case Modo::SEGURO: case Modo::FALLA:
break; // enclavados: solo un reinicio manual los saca
}
return (modo == Modo::NORMAL || modo == Modo::DEGRADADO) ? ultimoBueno : NAN;
}
// ---------- 4. Comprobación de respuesta: ¿el calefactor encendido calienta? ----------
bool respuestaPlausible(bool encendido, float medida) {
static uint32_t inicio = 0; static float t0 = 0; static bool contando = false;
if (!encendido) { contando = false; return true; }
if (!contando) { contando = true; inicio = millis(); t0 = medida; return true; }
if ((uint32_t)(millis() - inicio) < 300000UL) return true; // 5 minutos con la salida a pleno
return (medida - t0) >= 0.5f; // debió subir al menos 0,5 °C
}
// ---------- 5. Perro guardián: se reinicia solo si TODAS las tareas dan señales de vida ----------
const uint8_t T_SENSOR = 1, T_CONTROL = 2, T_PANTALLA = 4, TODAS = 7;
volatile uint8_t vivas = 0;
void setup() {
uint8_t causa = MCUSR; MCUSR = 0; wdt_disable(); // por qué se reinició (WDRF = perro guardián)
digitalWrite(PIN_CALEFACTOR, LOW); pinMode(PIN_CALEFACTOR, OUTPUT); // reposo ANTES de ser salida (y pull-down externo)
if (causa & _BV(WDRF)) { /* contar reinicios seguidos en la EEPROM; con 3, entrar(Modo::FALLA) */ }
wdt_enable(WDTO_250MS); // >= 4 x el lazo más lento (28 ms) y << tiempo de daño
}
void loop() {
// ... cada tarea hace su trabajo y marca: vivas |= T_SENSOR; etc.
if (vivas == TODAS) { wdt_reset(); vivas = 0; } // un solo punto donde se alimenta al perro
}
// ---------- 6. Registro de fallas en la EEPROM: rotativo y con suma de verificación ----------
struct Registro { uint32_t t; uint8_t causa; uint8_t modo; uint8_t crc; };
const int N_REGISTROS = 32;
uint8_t crc8(const uint8_t *d, uint8_t n) { uint8_t c = 0; while (n--) { c ^= *d++; for (uint8_t i = 0; i < 8; i++) c = (c & 0x80) ? (c << 1) ^ 0x07 : c << 1; } return c; }
void registrar(uint32_t indice, uint8_t causa) { // el índice crece siempre: las escrituras se reparten
Registro r = { millis(), causa, (uint8_t)modo, 0 };
r.crc = crc8((uint8_t *)&r, sizeof(r) - 1);
EEPROM.put((indice % N_REGISTROS) * sizeof(Registro), r); // put() solo escribe los bytes que cambian
}
Los decoradores del tema 40 permiten apilar esta validación sobre cualquier sensor sin tocar la aplicación: SensorValidado envuelve al driver, SensorRedundante combina dos, y el controlador del tema 43 recibe una lectura con estado, nunca un número sin contexto.
10 · Verificación
Pruebas con fallas inyectadas
Una protección que nunca se vio actuar no está probada. La técnica es inyectar fallas a propósito, primero en simulación y después en el montaje real con precauciones:
| Falla inyectada | Cómo | Resultado esperado |
|---|---|---|
| Sensor desconectado | Desenchufar el sensor en marcha | Salida apagada en segundos; modo SEGURO con aviso |
| Sensor congelado o mentiroso | Sustituirlo por una resistencia fija, o forzar la lectura en el programa | Chequeo de respuesta o de discrepancia; salida apagada |
| Pico en la señal | Golpear o acercar un motor al cable | Se descarta la muestra; no hay disparo si dura poco |
| Programa colgado | Un bucle infinito de prueba en el lazo | Reinicio por perro guardián en el tiempo configurado; salidas apagadas |
| Reinicios repetidos | Provocar tres reinicios seguidos | Modo FALLA, salidas apagadas, sin intentar operar |
| Caída de tensión | Bajar la alimentación con una fuente regulable | Reinicio limpio por baja tensión; ninguna salida errática |
| Actuador pegado (en una prueba segura) | Con la carga a baja potencia o una resistencia de prueba | La protección independiente corta; se registra |
| Datos de configuración corruptos | Alterar un byte de la EEPROM | El CRC falla; se usan los valores por defecto seguros |
- Escribí el análisis de fallas antes que el código y verificá que cada fila tenga un detector y una respuesta.
- Probá cada protección una por una, deshabilitando las demás, para ver qué atrapa cada una.
- Medí los tiempos de reacción (detección, reinicio, corte) y compará con el tiempo que el proceso tarda en dañarse.
- Hacé las pruebas peligrosas con carga reducida y con un extintor y un corte de emergencia a mano; nunca con tensión de red sin experiencia ni aislamiento.
- Repetí las pruebas tras cada cambio de programa o de montaje.
11 · Analogía con programación
Fallas inyectadas, protecciones y confiabilidad
Simulamos las fallas y las defensas del tema: los cuatro detectores sobre cinco fallas de sensor, el horno con tres fallas y distintas combinaciones de protecciones, la elección del tiempo del perro guardián, el modelo de redundancia y el desgaste de la EEPROM.
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
M32 = 2 ** 32
class Ruido:
"""Ruido de desviación típica 1, reproducible (suma de seis uniformes)."""
def __init__(self, semilla):
self.x = semilla
def dato(self):
s = 0.0
for _ in range(6):
self.x = (self.x * 1664525 + 1013904223) % M32
s += self.x / M32
return (s - 3) / 0.7071
# ---- 1. Detectores de sensor defectuoso sobre una temperatura de 40 ± 0,5 °C ----
LSB = 0.0625 # resolución de un DS18B20 de 12 bits
N, T_FALLA = 600, 300
def cuantizar(x):
return round(x / LSB) * LSB
def señales(falla):
ruido_a, ruido_b = Ruido(12345), Ruido(54321)
a, b = [], []
for t in range(N):
real = 40 + 0.5 * math.sin(2 * math.pi * t / 600)
sigma = 1.0 if (falla == "ruido" and t >= T_FALLA) else 0.05
lectura = real + sigma * ruido_a.dato()
if falla == "pegado" and t >= T_FALLA:
lectura = a[T_FALLA - 1]
elif falla == "pico" and T_FALLA <= t < T_FALLA + 3:
lectura += 15
elif falla == "deriva" and t >= T_FALLA:
lectura += 0.02 * (t - T_FALLA)
elif falla == "desconectado" and t >= T_FALLA:
lectura = -127.0
a.append(cuantizar(lectura) if lectura != -127.0 else lectura)
b.append(cuantizar(real + 0.05 * ruido_b.dato()))
return a, b
def detectar(a, b, limite_tasa=0.5, ventana=30, redundante=True):
marcas = {"rango": [], "tasa": [], "congelado": [], "discrepancia": []}
for t in range(N):
if a[t] < -40 or a[t] > 125:
marcas["rango"].append(t)
if t > 0 and abs(a[t] - a[t - 1]) > limite_tasa:
marcas["tasa"].append(t)
if t >= ventana and max(a[t - ventana + 1:t + 1]) == min(a[t - ventana + 1:t + 1]):
marcas["congelado"].append(t)
if redundante and abs(a[t] - b[t]) > 0.5:
marcas["discrepancia"].append(t)
return marcas
print("falla rango tasa congelado discrepancia (segundos hasta detectarla; – = no la detecta)")
for falla in ("ninguna", "pegado", "pico", "deriva", "desconectado", "ruido"):
a, b = señales(falla)
marcas = detectar(a, b)
celdas = []
for nombre in ("rango", "tasa", "congelado", "discrepancia"):
despues = [t - T_FALLA for t in marcas[nombre] if t >= T_FALLA]
antes = [t for t in marcas[nombre] if t < T_FALLA]
texto = "–" if not despues else str(despues[0])
if antes:
texto += f" (+{len(antes)} falsos)"
celdas.append(texto)
print(f"{falla:13s} " + " ".join(f"{c:>9s}" for c in celdas))
print()
for limite in (0.05, 0.5):
a, b = señales("ninguna")
falsos = len(detectar(a, b, limite_tasa=limite)["tasa"])
print(f"límite de tasa de {limite} K por muestra: {falsos} falsos positivos en {N} s sin falla")
# ---- 2. Capas de protección del horno ----
P_MAX, CAP, RES, T_AMB = 100.0, 2000.0, 0.5, 15.0
def horno(falla, sw=False, chequeo=False, termostato=None, segundos=7200, t_falla=600):
temp, orden, medida_prev = 40.0, False, 40.0
seguro_sw = seguro_chk = False
hw_cerrado = True
seguidos, medida_ini, t_max, primera = 0, 40.0, 40.0, None
for t in range(segundos):
if falla == "frio" and t >= t_falla:
medida = 20.0
elif falla == "desconectado" and t >= t_falla:
medida = -127.0
else:
medida = temp
if chequeo and not seguro_chk:
if medida < -40 or medida > 125:
seguro_chk, primera = True, primera if primera is not None else (t, "chequeo del sensor")
if orden:
if seguidos == 0:
medida_ini = medida
seguidos += 1
if seguidos >= 300 and medida - medida_ini < 0.5:
seguro_chk, primera = True, primera if primera is not None else (t, "chequeo de respuesta")
else:
seguidos = 0
if sw and not seguro_sw and medida > 55:
seguro_sw, primera = True, primera if primera is not None else (t, "límite por software")
if medida < 39.5:
orden = True
elif medida > 40.5:
orden = False
if seguro_sw or seguro_chk:
orden = False
pegado = falla == "pegado" and t >= t_falla
if termostato is not None:
if hw_cerrado and temp >= termostato:
hw_cerrado, primera = False, primera if primera is not None else (t, "termostato independiente")
elif not hw_cerrado and temp < termostato - 10:
hw_cerrado = True
potencia = (1.0 if (orden or pegado) else 0.0) * (1.0 if hw_cerrado else 0.0)
temp += (P_MAX * potencia - (temp - T_AMB) / RES) / CAP
t_max = max(t_max, temp)
return t_max, primera
print()
print("falla protecciones T máx primera protección")
casos = (("calefactor pegado", "pegado", dict(), "ninguna"),
("calefactor pegado", "pegado", dict(sw=True, chequeo=True), "software y chequeos"),
("calefactor pegado", "pegado", dict(sw=True, chequeo=True, termostato=55), "software, chequeos y termostato de 55 °C"),
("sensor lee frío", "frio", dict(), "ninguna"),
("sensor lee frío", "frio", dict(sw=True), "solo límite por software"),
("sensor lee frío", "frio", dict(sw=True, chequeo=True), "software y chequeos"),
("sensor desconectado", "desconectado", dict(), "ninguna"),
("sensor desconectado", "desconectado", dict(chequeo=True), "chequeo de rango"))
for nombre, falla, opciones, proteccion in casos:
t_max, primera = horno(falla, **opciones)
cuando = f"a los {primera[0] - 600} s de la falla: {primera[1]}" if primera else "ninguna actuó"
print(f"{nombre:19s} {proteccion:40s} {t_max:6.1f} °C {cuando}")
tau, t_ss = RES * CAP, T_AMB + P_MAX * RES
print(f"con la salida a pleno el horno tiende a {t_ss:.0f} °C; desde 40 °C llega a 55 °C en {tau * math.log((t_ss - 40) / (t_ss - 55)):.0f} s y a 60 °C en {tau * math.log((t_ss - 40) / (t_ss - 60)):.0f} s")
# ---- 3. Perro guardián ----
print()
TIEMPOS = [15, 30, 60, 120, 250, 500, 1000, 2000, 4000, 8000]
for lazo in (5, 28, 150, 700):
minimo = 4 * lazo
elegido = next((t for t in TIEMPOS if t >= minimo), None)
print(f"lazo más lento de {lazo:4d} ms -> timeout mínimo {minimo:5d} ms -> se elige {elegido} ms")
for proceso, seguro in (("motor con carga (100 ms)", 100), ("válvula (5 s)", 5000), ("horno (600 s)", 600_000)):
sin_control = 250 + 600
print(f"{proceso:26s}: sin control durante {sin_control} ms (timeout de 250 ms + 600 ms de arranque): {'alcanza' if sin_control < seguro else 'NO alcanza'}")
# ---- 4. Redundancia (modelo ilustrativo) ----
print()
print("configuración dato malo sin detectar pérdida de servicio (p = 5 %/año, cobertura 60 %, causa común 10 %)")
def modelo(p, d, beta):
indep = ((1 - beta) * p) ** 2
return {
"1 sensor": (p * (1 - d), p * d),
"2 sensores, comparar": (beta * p * (1 - d) + indep * (1 - d), 2 * (1 - beta) * p),
"3 sensores, votar 2 de 3": (beta * p * (1 - d) + 3 * indep * (1 - d), 3 * indep * d),
}
for nombre, (malo, parada) in modelo(0.05, 0.60, 0.10).items():
print(f"{nombre:26s} {malo * 100:8.2f} % {parada * 100:6.2f} %")
# ---- 5. Desgaste de la EEPROM ----
print()
for celdas in (1, 256):
minutos = 100_000 * celdas
print(f"un registro por minuto en {celdas:3d} celda(s): {minutos / 1440:9.0f} días ({minutos / 1440 / 365:5.1f} años) hasta 100 000 escrituras por celda")
Resultado: los detectores atrapan el sensor congelado en 28 s (congelamiento) o 176 s (discrepancia), el pico y el ruido en 0 a 1 s con la tasa, la desconexión en 0 s con el rango y la deriva solo con el segundo sensor, en 25 s; con un límite de tasa de 0,05 K por muestra hay 391 falsos positivos en 600 s, con 0,5 K ninguno. Con el calefactor pegado, el límite por software y los chequeos no impiden los 65,0 °C; el termostato independiente de 55 °C lo deja en 55,0 °C. Con el sensor que lee frío, el límite por software no ve nada (65,0 °C); el chequeo de respuesta lo corta a los 295 s con 46,1 °C. La desconexión se atrapa de inmediato (40,5 °C). A pleno, el horno llega a 55 °C en 916 s y a 60 °C en 1609 s. Un lazo de 28 ms pide un perro guardián de 120 ms, y 850 ms sin control no alcanzan para un motor de 100 ms. Con p = 5 %, cobertura del 60 % y causa común del 10 %, el dato malo sin detectar es de 2,00 % con un sensor, 0,28 % con dos comparados y 0,44 % con votación 2 de 3, con pérdidas de servicio de 3,00 %, 9,00 % y 0,36 %. Un registro por minuto agota una celda de la EEPROM en 69 días y repartido en 256 celdas, en 48,7 años.
12 · Exploración interactiva
Detectores, protecciones, perro guardián y redundancia
El primer instrumento aplica cuatro detectores a una señal con una falla inyectada. El segundo simula el horno con una falla y capas de protección que se activan una por una. El tercero ayuda a elegir el tiempo del perro guardián. El cuarto compara las configuraciones de sensores con el modelo ilustrativo de la sección 3. Los dos primeros usan el mismo modelo del programa de Python.
Instrumento 1: detectores de sensor defectuoso
Instrumento 2: capas de protección del horno
Instrumento 3: tiempo del perro guardián
Instrumento 4: redundancia de sensores
Recorrido guiado
- Con «sensor congelado» a los 300 s, límite de tasa de 0,5 y ventana de 30: el detector de congelamiento lo atrapa a los 28 s y la discrepancia a los 176 s. Con una ventana de 120 muestras, el congelamiento tarda 118 s: la ventana es una decisión.
- Elegí «deriva»: con el segundo sensor se detecta a los 25 s. Desmarcá la casilla: ningún detector la ve y el estado lo avisa. Es la falla que la redundancia o un modelo deben atrapar.
- Con «pico» o «ráfaga de ruido», la tasa los detecta de inmediato. Bajá el límite a 0,05: aparecen cientos de falsos positivos antes de la falla.
- En las protecciones, con el relé pegado, el límite por software y los chequeos y sin termostato: 65,0 °C, daño. El límite por software no puede abrir un relé pegado. Activá el termostato de 55 °C: queda en 55,0 °C.
- Elegí «el sensor lee siempre 20 °C» con solo el límite por software: 65,0 °C. Agregá los chequeos: se detecta a los 295 s, con 46,1 °C. Elegí «sensor desconectado» con los chequeos: 40,5 °C de inmediato.
- En el perro guardián, con el lazo de 28 ms, timeout de 250 ms y un horno: correcto. Cambiá el proceso a «motor con carga»: 850 ms sin control son más que los 100 ms: no protege a tiempo.
- Con un bloqueo ocasional de 300 ms y un timeout de 250 ms, el programa se reinicia sin tener ninguna falla real: un falso reinicio. Subí el timeout a 500 ms.
- En la redundancia, con 5 %, 60 % y 10 %: 2,00 % de datos malos con un sensor, 0,28 % con dos y 0,44 % con tres; paradas de 3,00 %, 9,00 % y 0,36 %. Subí la causa común al 30 %: la redundancia casi no mejora el dato malo.
Desafío: un riego automático que no inunde
Un sistema de riego (tema 45 lo retoma) abre una electroválvula cuando la humedad del suelo baja del umbral. ¿Qué fallas hay y cómo se las protege? Sensor de humedad desconectado o en corto: lee «seco» y la válvula quedaría abierta; se valida el rango y, ante un dato inválido, se cierra (válvula normalmente cerrada). Sensor que lee seco por deriva: la humedad real sube pero la lectura no responde: se verifica que, tras un riego, el sensor cambie (respuesta esperada); si no, falla. Válvula que se queda abierta: un tiempo máximo de marcha (por ejemplo, 10 minutos) y un tiempo de espera mínimo entre riegos; y un segundo sensor (un caudalímetro o un sensor de nivel en el recipiente) que mida lo regado. Programa colgado con la válvula abierta: perro guardián con la válvula normalmente cerrada, que cierra al perder la corriente. Corte de luz: la válvula cierra sola. Protección independiente: un temporizador mecánico o un sensor de nivel que corta el agua de forma independiente del microcontrolador, si lo regado puede dañar algo. Se prueban todas las fallas una por una, con agua en un recipiente.
13 · Método de trabajo
Cómo diseñar para la falla, para cualquier proyecto
- Definí qué es peligroso y cuál es el estado seguro de cada actuador; elegí componentes que fallen hacia él.
- Hacé la tabla de fallas: falla, efecto, detección, respuesta; empezá por las que dejan una salida activa.
- Validá cada sensor (rango, tasa, congelamiento, edad), con umbrales entre lo normal y lo peligroso, y duplicá donde la falla sea grave.
- Armá capas independientes: control, límite por software, protección de hardware en serie con la carga, contención física.
- Verificá la respuesta de los actuadores, con tiempos máximos de marcha y contactos que no dependan de un solo elemento.
- Usá un perro guardián con tiempo medido y alimentación condicionada a todas las tareas; sin reinicios en bucle.
- Hacé el arranque seguro: pull-down, reposo antes que salida, autoprueba, baja tensión, causa del reinicio registrada.
- Organizá los modos (inicio, normal, degradado, seguro, falla), probalos con fallas inyectadas y documentá el resultado.
Si una sola falla puede dejar una salida activa sin que nada la detenga, falta una capa; si una protección depende de lo que protege, no es independiente; si el sistema reinicia sin parar, falta el modo falla; si nunca viste actuar una protección, no la probaste.
14 · Comprensión
Confusiones frecuentes
«Con un límite de temperatura en el programa ya estoy protegido»
Ese límite usa el mismo sensor, el mismo microcontrolador y el mismo relé que el control: no ve un sensor que miente ni puede abrir un relé pegado. Hace falta una protección independiente en serie con la carga.
«El perro guardián soluciona los cuelgues»
Reinicia el programa, no lo repara: si la causa persiste, el equipo se reinicia en bucle. Hay que registrar la causa, contar los reinicios y pasar a un modo seguro tras unos pocos.
«Con dos sensores ya sé cuál es el bueno»
Con dos, una discrepancia se detecta pero no se atribuye. Para identificar el defectuoso y seguir funcionando hacen falta tres (votación 2 de 3) o un detector adicional.
«Un sensor que devuelve un valor constante está estable»
Un sensor real tiene ruido; si el valor es idéntico durante decenas de muestras, probablemente esté congelado, sobre todo si el proceso cambia.
«Un relé de estado sólido corta siempre»
Los SSR suelen fallar en cortocircuito, quedando encendidos. Para cortar con seguridad se agrega un contactor electromecánico o un termostato en serie.
«Si el programa funciona en las pruebas, es robusto»
Las pruebas sin fallas solo muestran el caso feliz. La robustez se verifica inyectando fallas: sensor desconectado, programa colgado, baja tensión, memoria corrupta.
15 · Práctica guiada
Predecí antes de comprobar
1. Detectores
Un sensor de temperatura de un horno puede dar entre −40 y 125 °C, el proceso cambia como máximo 0,1 K por segundo y se lee cada 1 s. ¿Qué rango y qué límite de tasa usás? ¿Qué lecturas descartarías: 130 °C, 40,3 °C seguido de 41,5 °C, −127 °C?
Ver solución
Rango de −40 a 125 °C y un límite de tasa algo mayor que 0,1 K/muestra con margen para el ruido (por ejemplo 0,5 K). Se descartan: 130 °C (fuera de rango), 41,5 °C tras 40,3 °C (salto de 1,2 K, mayor que 0,5) y −127 °C (fuera de rango).
2. Ventana de congelamiento
Un sensor de 12 bits (0,0625 °C) en un proceso que cambia 0,002 K/s ¿puede dar un valor idéntico durante 30 muestras sin estar averiado?
Ver solución
En 30 s el proceso cambia 0,06 K, casi un escalón de resolución, y con ruido lo normal es ver al menos un cambio; pero puede darse sin avería en procesos muy lentos y estables. La ventana debe ser mayor que el tiempo que tarda el proceso en cambiar varios escalones (aquí, 0,0625 / 0,002 ≈ 31 s por escalón: conviene una ventana de varios minutos).
3. Votación
Tres sensores dan 40,2; 40,4 y 52,0 °C. ¿Qué valor usa una votación 2 de 3 por mediana y cuál es el sensor defectuoso?
Ver solución
La mediana es 40,4 °C; el sensor de 52,0 °C discrepa más de la tolerancia y se marca como defectuoso, y el sistema sigue (modo degradado con aviso).
4. Perro guardián
El lazo más lento dura 60 ms y el arranque tras un reinicio, 400 ms. ¿Qué timeout de los disponibles (15, 30, 60, 120, 250, 500 ms…) elegís y cuánto tiempo queda el sistema sin control ante un cuelgue?
Ver solución
4 × 60 = 240 ms mínimos → 250 ms. Sin control: 250 + 400 = 650 ms.
5. Desgaste
Se registra un evento cada 10 s en la misma celda de EEPROM (100 000 escrituras de vida). ¿Cuánto dura? ¿Y repartido en 64 celdas?
Ver solución
100 000 × 10 s = 1 000 000 s ≈ 11,6 días; con 64 celdas, 64 veces más: ~740 días (2 años). Mejor aún, escribir solo si el dato cambia.
6. Diagnóstico
Un termostato con un relé de estado sólido sigue calentando aunque el programa le ordene apagar; el límite por software, a 55 °C, se dispara varias veces sin efecto. ¿Qué pasa y qué agregás?
Ver solución
El SSR está en cortocircuito (falla típica) y no se puede abrir por software. Se agrega una protección independiente en serie con la carga: un termostato de seguridad o un contactor electromecánico que no dependa del microcontrolador, y se detecta la falla porque la temperatura sube con la salida apagada.
16 · Síntesis
Ideas para recordar
- Se diseña desde la falla: ¿qué es lo peor que puede pasar? Cada falla debe llevar a un estado seguro (calefactor apagado, válvula cerrada, motor frenado), y se comienza por las que dejan una salida activa.
- Los sensores fallan abiertos, en corto, congelados, con picos, con deriva o con dato viejo: se detectan con rango, tasa, congelamiento, discrepancia y edad; ningún detector atrapa todo y los umbrales van entre lo normal y lo peligroso.
- La redundancia (2 comparados, 3 con votación 2 de 3, mejor si son disímiles) detecta la deriva; la causa común fija su límite.
- Las protecciones son capas independientes: un límite por software con el mismo sensor o el mismo relé no ve ni puede cortar la falla de ninguno; la capa final es de hardware, en serie con la carga, y enclavada.
- Los actuadores fallan hacia lo seguro por diseño (normalmente abierto o cerrado, freno por resorte), con tiempo máximo de marcha y verificación de respuesta; un SSR suele fallar en corto.
- El perro guardián (≥ 4 veces el lazo, < tiempo de daño menos el de arranque) se alimenta en un solo punto, condicionado a todas las tareas, nunca desde una interrupción; los reinicios repetidos llevan al modo falla.
- El arranque seguro: pull-down, reposo antes de ser salida, autoprueba, detección de baja tensión y causa del reinicio registrada.
- Una máquina de modos (inicio, normal, degradado, seguro, falla) con estados enclavados, código defensivo (validación, tiempos límite, CRC) y pruebas con fallas inyectadas: una protección que nunca se vio actuar no está probada.
¿Listo para el tema 45?
Antes de avanzar, explicá por qué un límite de temperatura por software con el mismo sensor no protege de un sensor que lee frío, elegí el timeout del perro guardián para un lazo máximo de 40 ms y decí qué ventaja tiene votar 2 de 3 sobre comparar dos sensores. Si podés justificar las tres respuestas, ya sabés diseñar para la falla.
En el próximo tema usamos todo lo del curso en un proyecto completo: el control climático de un invernadero, con temperatura, humedad, luz, ventilador, relé y riego automático.