01 · Nuevo bloque
De observar a actuar
En el tema 36 el panel mostraba información y permitía reconocer una alarma en una simulación. Ahora comenzamos el bloque de control remoto y seguridad: el usuario puede solicitar un cambio en una salida o en los parámetros del dispositivo.
Una orden remota es una solicitud que el equipo evalúa, no un permiso para escribir cualquier pin desde Internet. El dispositivo conserva su lógica local, sus modos y sus límites. La nube expresa qué se pide; el firmware decide si puede aceptarlo en las condiciones actuales.
Observar
«El ventilador está apagado». Es información con una fuente y una antigüedad; recibirla no cambia por sí solo la salida.
Solicitar una acción
«Encender el ventilador durante diez segundos». Requiere un destinatario, permisos, condiciones de aplicación y una forma de saber qué ocurrió.
El caso guía es una salida auxiliar representada por un LED. El simulador agrega una realimentación ideal o fallida para distinguir salida eléctrica y efecto observado. Los tiempos son didácticos: no constituyen un controlador completo de refrigeración, bomba o motor.
En el tema 38 profundizamos el protocolo de comandos, estados, idempotencia y confirmaciones. Aquí construimos la base de operación: quién puede mandar, cuándo corresponde actuar y qué límites mantiene el dispositivo.
02 · Del clic al proceso
El recorrido de una orden
Un botón del panel no debería publicar directamente una instrucción ilimitada. Un servicio recibe la solicitud, comprueba identidad y alcance, registra la intención y la entrega por el canal del dispositivo. El firmware vuelve a validar lo que depende de su estado local.
- Solicitar: el usuario elige una operación y el panel indica que está pendiente.
- Autorizar: el servicio comprueba que ese usuario puede actuar sobre ese dispositivo y operación.
- Entregar: el canal intenta llevar la orden, con su identidad y vigencia.
- Admitir: el equipo revisa esquema, destinatario, caducidad, modo y condiciones locales.
- Aplicar y observar: se modifica la salida y se verifica la realimentación disponible.
- Informar: el servicio y el panel reciben un resultado o mantienen la incertidumbre si no llega.
El servicio puede aceptar registrar una solicitud aunque el equipo esté desconectado. Eso no significa que sea apropiado ejecutarla horas después. La validez de una orden y la política de cola se definen antes de permitir ese comportamiento.
Permisos con alcance
Identificar a un usuario no implica autorizar todas las acciones. Puede tener permiso de lectura, de encender una salida por un tiempo limitado o de cambiar ciertos parámetros. El servicio verifica dispositivo, sitio y operación; el firmware conserva sus propias restricciones aunque el emisor esté autorizado.
Para reconstruir lo ocurrido conviene registrar quién solicitó, qué pidió, cuándo, con qué identidad, cómo fue entregado y qué resultado se obtuvo. Una etiqueta «error» sin etapa ni causa no permite saber si fallaron permisos, red, admisión o ejecución.
03 · Operaciones con significado
Encender, apagar y configurar
El contrato debería nombrar acciones del producto, como set_output o set_threshold. Exponer «escribir GPIO 4» desde una interfaz pública acopla al usuario con la placa y permite esquivar restricciones que pertenecen al equipo.
| Operación | Qué pide | Qué comprobar |
|---|---|---|
| Encender | Llevar una salida a ON durante un plazo acotado | Modo, interbloqueos, duración y efecto observado |
| Apagar | Llevar una salida a OFF | Permisos, modo y estado resultante |
| Cambiar parámetro | Aplicar un umbral, período o consigna | Tipo, unidad, rango, coherencia y versión aplicada |
| Cambiar modo | Modificar quién tiene autoridad para decidir | Permiso específico y transición local definida |
Pedir un valor explícito
set_output(true) expresa el estado solicitado. toggle expresa «invertir lo que haya», de modo que repetirlo puede deshacer la primera acción. Las operaciones explícitas facilitan razonar sobre reintentos; aun así, necesitan identidad y una política de ejecución, que desarrollamos en el tema siguiente.
Repetir «encender diez segundos» también puede tener efectos diferentes si cada recepción reinicia el temporizador. El dispositivo debe distinguir un reintento de la misma orden de una nueva solicitud que pretende extender el plazo. Cambiar el nombre de la operación no resuelve ese detalle.
Parámetro solicitado y aplicado
Para una regla de temperatura alta, no alcanza con comprobar que el nuevo umbral sea un número. Debe quedar por encima del rearme y dentro del rango admitido. El ejemplo parte de alta = 8,0 °C y rearme = 7,5 °C; acepta altas mayores que 7,5 y de hasta 10,0 °C.
La configuración se valida completa y se aplica como una unidad entre evaluaciones, como vimos en el tema 32. El dispositivo informa la versión que realmente está usando. El panel no presenta la versión solicitada como aplicada solo porque el servidor guardó el pedido.
Guardar una configuración aceptada en memoria no volátil es otra etapa. Si el producto promete conservarla al reiniciar, comprueba esa escritura y define cómo se recupera una versión válida. El simulador mantiene el cambio en memoria y no modela cortes de energía.
04 · Quién tiene el mando
Modos y prioridades locales
Una misma salida puede recibir propuestas del control automático, de un operador local y de la nube. Si cada callback escribe el pin por su cuenta, el resultado depende de quién corrió último. Una única parte del firmware debe resolver la autoridad y aplicar la salida.
| Modo | Quién decide la salida | Órdenes remotas del ejemplo |
|---|---|---|
| Automático | Controlador local según sensores y reglas | Se rechazan cambios directos de salida |
| Remoto | Órdenes admitidas, sujetas a restricciones locales | Se evalúan salida y parámetro |
| Local | Interfaz o selector del equipo | Se rechazan las operaciones del canal remoto |
En este modelo, el canal de órdenes solo admite las operaciones mostradas en modo remoto. Es una política explícita del ejercicio: un producto podría permitir cambiar ciertos parámetros en automático sin habilitar el control directo de salidas.
Los bloqueos limitan la activación
Un interbloqueo puede impedir encender porque falta una condición local. El ejemplo tiene un bloqueo booleano que inhibe ON, pero permite OFF cuando el modo remoto está habilitado. No se modifica ese bloqueo al recibir una orden: se informa el rechazo y su causa.
En un sistema real puede ser una tapa abierta, falta de nivel, temperatura fuera de rango o un diagnóstico de sensor. Las condiciones que deben cumplirse para activar y las que obligan a desactivar se diseñan por separado; no se deducen del permiso del usuario.
Una intervención local puede terminar la acción
El simulador permite que un selector local tome el mando en t = 8 s y apague la salida. Si la orden llegó antes, la acción puede haber sido aplicada y luego interrumpida. Si llega después, se rechaza por modo. Ambas situaciones deben explicarse sin reescribir la historia de la solicitud.
La pérdida de Internet tampoco define por sí sola qué modo elegir. Un termostato puede seguir automático; una salida temporizada puede terminar su plazo; otra aplicación puede requerir desactivación inmediata. La política se establece según el proceso, no como una consecuencia accidental de la biblioteca de red.
05 · La orden como mensaje
Qué debe decir una orden
Un contrato pequeño pero explícito puede incluir versión, identidad, destinatario, operación, parámetros y fecha límite de admisión:
{
"v": 1,
"id": "cmd-1",
"device": "camara07",
"op": "set_output",
"args": {"on": true, "duration_ms": 10000},
"issued_at_ms": 1767225600000,
"expires_at_ms": 1767225615000
}La orden se crea en T0, puede admitirse antes de T0 + 15 s y solicita un encendido de diez segundos desde su aplicación. La fecha límite no dice cuánto debe durar la salida; la duración tampoco impide admitir una orden vieja si no se revisa su vigencia.
En producción, id debe ser único dentro del alcance de identificación elegido y conservarse en los reintentos. cmd-1 es un nombre de una ejecución aislada del laboratorio, no una estrategia de generación de identidades para toda una flota.
Validaciones antes de aplicar
- Origen y destinatario: emisor permitido y equipo correcto, según el canal autenticado y las reglas del servicio.
- Esquema: versión conocida, campos obligatorios, tipos y límites de tamaño.
- Operación: nombre permitido y parámetros con unidad y rango definidos.
- Vigencia: que no haya vencido al admitirla y que el reloj permita comprobarlo.
- Estado local: modo, interbloqueos y recursos disponibles.
- Identidad: reconocer si se trata de una orden nueva o una entrega repetida.
Un reloj Unix sin sincronizar no permite verificar una fecha de caducidad absoluta de forma confiable. El producto necesita una política para ese estado, por ejemplo no admitir nuevas activaciones remotas hasta recuperar una referencia válida. No basta con comparar dos números que usan orígenes distintos.
La duración de una acción ya admitida se mide con un reloj monótono local. Un ajuste de hora por red no debería extender un encendido que debía durar diez segundos. Después de un reinicio, no se restaura ciegamente un temporizador con marcas del arranque anterior.
Cancelar la espera no cancela la ejecución
Si el servicio deja de esperar un resultado, la orden puede seguir en tránsito o ya estar ejecutándose. Para cancelarla hace falta una operación y un resultado propios, y puede existir una carrera con la ejecución. Cerrar el diálogo del navegador no borra por sí solo una acción del dispositivo.
06 · Dos formas de transportar la intención
HTTP y MQTT para controlar
Ambos transportes pueden llevar órdenes; la elección no elimina validaciones ni límites. El tema 30 comparó su modelo de comunicación. Ahora los ubicamos en el recorrido de control.
HTTP: una API de solicitudes
POST /api/dispositivos/camara07/ordenes
Content-Type: application/json
{"id":"cmd-1","op":"set_output","args":{"on":true,"duration_ms":10000}}
HTTP/1.1 202 Accepted
Content-Type: application/json
{"id":"cmd-1","status":"pending","status_url":"/api/ordenes/cmd-1"}La respuesta 202 Accepted indica aceptación para procesamiento, no ejecución completada, según RFC 9110, sección 15.3.3. La API del ejemplo devuelve una referencia para consultar el resultado. El panel conserva «pendiente» y muestra la respuesta de aplicación cuando llega.
El dispositivo puede consultar órdenes pendientes desde el servidor o mantener otro canal por el que se le entregan. Un equipo detrás de NAT no queda accesible desde Internet solo por tener una IP privada. El panel puede hablar con la API mientras el equipo inicia su propia conexión de salida.
MQTT: órdenes, resultados y estado en canales separados
sitio/camara07/cmd/salida → solicitud al dispositivo
sitio/camara07/result → resultado con la identidad de la orden
sitio/camara07/state/salida → estado reportado y su tiempo de observaciónEl broker controla qué identidad puede publicar o suscribirse a esos canales. Un permiso de lectura de telemetría no debería incluir publicación de órdenes. El dispositivo también verifica esquema y condiciones locales; el tópico no reemplaza esos controles.
Para una orden puntual de encendido no se usa un mensaje retenido como si fuera un botón: un nuevo suscriptor podría recibirlo después. Un estado deseado persistente es otro modelo y necesita reconciliación, versión y vigencia; lo desarrollamos con comandos y gemelo digital en los temas siguientes.
MQTT 5 define caducidad del mensaje, útil para limitar su permanencia en el transporte. El firmware aún necesita revisar la vigencia de la acción al admitirla. Ni un PUBACK ni la entrega con QoS prueban que el actuador produjo su efecto. Estas propiedades pertenecen al transporte descrito por la especificación MQTT 5 de OASIS.
Las bibliotecas y versiones del cliente pueden ofrecer funciones distintas. El contrato de aplicación no debe depender de una propiedad de MQTT 5 si el firmware solo implementa MQTT 3.1.1; en ese caso se define el vencimiento en la carga y la política correspondiente en los extremos.
07 · Tres tiempos distintos
Caducidad, duración y pérdida de red
El control remoto tiene varios temporizadores con propósitos diferentes. En el simulador se fijan así:
| Tiempo | Regla | Qué no significa |
|---|---|---|
| Espera del servicio: 6 s | Sin resultado antes de 6 s, se declara espera agotada | No demuestra que la salida siga apagada |
| Caducidad de la orden: configurable | Se admite solo si llegada < TTL desde su creación | No limita por sí sola cuánto dura una ejecución admitida |
| Duración solicitada de ON | Se mide desde que el dispositivo activa la salida | No renueva la vigencia de una orden que llegó vieja |
| Máximo local: 10 s | Se rechazan duraciones mayores; el equipo apaga al terminar el plazo aceptado | No necesita que la nube mande una segunda orden |
Con demora de entrega de 3 s y duración de 10 s, el equipo activa en t = 3 y apaga en t = 13 s. La realimentación normal se comprueba en t = 4. Si se corta la red en t = 8, el plazo local sigue corriendo y la salida se apaga igualmente a los 13 s.
Con demora de 12 s y caducidad de 5 s, la orden llega vencida y no activa. Con caducidad de 15 s, la misma demora todavía permite admitirla; el servicio ya agotó su espera de 6 s y recibe después un resultado tardío. Timeout y vencimiento no son sinónimos.
Que un encendido no dependa de un apagado futuro por Internet
Una acción acotada puede terminar localmente aunque no llegue otra comunicación. El temporizador se ejecuta en la misma lógica que resuelve modos y bloqueos, sin esperar al servidor. Un apagado remoto necesita que funcionen su canal y su admisión; para detener algo sin esa dependencia se necesita un mecanismo local apropiado al equipo.
Si la aplicación permite operación continua, debe definir cómo se mantiene esa autorización, qué pasa al perderla y qué límites siguen activos. No se obtiene continuidad simplemente eliminando todos los temporizadores.
Arranque e interrupciones
El estado inicial se define explícitamente. El ejemplo arranca con salida apagada y requiere una orden nueva para activar. Restaurar «último ON» desde flash podría reactivar sin una intención vigente. El hardware y el firmware deben ser coherentes sobre los niveles iniciales y las etapas de habilitación.
Un temporizador en el bucle también presupone que el bucle sigue ejecutándose. Su peor demora depende del período de revisión y de los bloqueos del programa. Si el requisito exige un límite más estricto, se diseña y verifica otro mecanismo; una resta con millis() no corrige por sí sola un firmware detenido.
08 · Qué significa «hecho»
Salida aplicada y efecto observado
Escribir un nivel lógico en un pin comprueba una decisión de software. No demuestra que una válvula se movió ni que un motor giró. La realimentación puede provenir de un contacto auxiliar, un sensor de posición, corriente, velocidad o alguna medida adecuada al proceso.
Salida aplicada
El controlador estableció ON en su salida. Se puede reportar como una etapa de ejecución, indicando qué representa.
Efecto observado
La entrada de realimentación confirmó el resultado esperado. Si esa información no existe, el sistema conserva ese límite de conocimiento.
En el simulador, la realimentación normal sigue instantáneamente la salida y se revisa un segundo después. La variante de falla permanece en OFF. Si pasan dos segundos sin confirmación, el controlador apaga su salida y declara fallo. Es una política pequeña para ver el recorrido, no un modelo físico de todo actuador.
Con entrega en t = 3 s y realimentación fallida, la salida se activa en t = 3, no se confirma y se desactiva en t = 5 s. El servicio recibe un fallo si el canal de respuesta funciona. No se presenta la sola escritura del pin como un éxito físico.
Resultado de ejecución y estado actual
«Encendido confirmado en t = 4» es un resultado histórico de una solicitud. La salida puede apagarse a los 13 s por su límite local o antes por intervención local. La pantalla necesita también el estado actual reportado con su antigüedad, como en el tema 36.
En el laboratorio se ve el estado interno del dispositivo porque todo ocurre en la misma simulación. En un panel real, perder la respuesta no habilita a mostrar ese estado como conocido: se necesita un reporte recibido. Esta separación permite observar aquí qué ocurrió realmente y qué pudo saber el servicio.
La salida requiere una interfaz eléctrica adecuada
El fragmento C++ usa un LED como indicador. Un actuador real necesita la etapa de manejo, alimentación y protección que corresponda, como estudiamos en el tema 11. El nombre «encender» no convierte un GPIO en una salida de potencia ni verifica su circuito.
09 · Un único dueño de la salida
Un núcleo de control en C++
El siguiente núcleo para Arduino/ESP32 conserva modo, interbloqueo y duración local. Se llama después de autenticar, validar destinatario y caducidad, y reconocer la identidad de la orden. No incluye el cliente HTTP/MQTT ni el parser.
#include <Arduino.h>
#include <stdint.h>
enum class Modo { Automatico, Remoto, Local };
class ControlSalida {
public:
explicit ControlSalida(uint8_t pin) : pin_(pin) {}
void comenzar() {
pinMode(pin_, OUTPUT);
apagar();
}
void cambiarModo(Modo nuevo) {
if (nuevo != modo_) apagar(); // política de transición del ejemplo
modo_ = nuevo;
}
// Solo para una orden NUEVA ya admitida por el protocolo de aplicación.
bool solicitar(bool on, uint32_t duracionMs, uint32_t ahora,
bool habilitacionLocal) {
if (modo_ != Modo::Remoto) return false;
if (!on) {
apagar();
return true;
}
if (!habilitacionLocal || duracionMs == 0 || duracionMs > MAX_ON_MS) {
return false;
}
inicio_ = ahora;
duracion_ = duracionMs;
activa_ = true;
digitalWrite(pin_, HIGH);
return true; // salida aplicada; no confirma un actuador físico
}
void revisar(uint32_t ahora, bool habilitacionLocal) {
if (!activa_) return;
if (!habilitacionLocal || uint32_t(ahora - inicio_) >= duracion_) {
apagar();
}
}
bool salidaActiva() const { return activa_; }
private:
void apagar() {
digitalWrite(pin_, LOW);
activa_ = false;
}
static constexpr uint32_t MAX_ON_MS = 10000;
uint8_t pin_;
Modo modo_ = Modo::Local;
bool activa_ = false;
uint32_t inicio_ = 0, duracion_ = 0;
};
// Elegir un GPIO de salida permitido por la placa y un LED con su circuito.
ControlSalida indicador(4);
void setup() {
indicador.comenzar();
// El modo inicial es local. Su habilitación remota se define por el producto.
}
void loop() {
const uint32_t ahora = uint32_t(millis());
const bool habilitacionLocal = true; // reemplazar por interbloqueos reales
indicador.revisar(ahora, habilitacionLocal);
// Procesar red y órdenes sin bloquear; aplicar solo solicitudes nuevas.
}El GPIO 4 es una elección de ejemplo, no una afirmación de que el LED integrado de cualquier placa use ese pin. Revisá las restricciones de tu variante. La documentación GPIO de Arduino-ESP32 distingue configurar la salida con pinMode y escribir su nivel con digitalWrite.
El ejemplo arranca en local y no acepta encendidos remotos hasta que el producto habilite ese modo. Todos los cambios de pin de esta salida pasan por el mismo controlador. El bucle revisa el límite aunque no haya red; una tarea de realimentación puede apagar y reportar una falla con una política adicional.
La resta de contadores de 32 bits tolera el retorno a cero de millis() para estos intervalos cortos, siempre que el controlador se revise regularmente y no se pierda un ciclo completo del contador. Arduino documenta su contador de tiempo en la referencia de millis(). No se compara con una fecha Unix ni se usa para reconstruir un plazo después de reiniciar.
Una salida apagada por software presupone que la etapa eléctrica responde y que el firmware se ejecuta. Los estados durante el arranque y el comportamiento ante una falla del hardware se verifican en el conjunto real; no se garantizan únicamente con el valor inicial de una variable.
10 · Un recorrido reproducible
Simular el recorrido en Python
El modelo usa un reloj común en segundos y recorre t = 0 a 40. El servicio registra una orden en t = 0, deja de esperar a los 6 s y el dispositivo aplica sus propias validaciones. La red puede estar disponible, volver a los 20 s o cortarse entre los 8 y los 20 s.
La respuesta se intenta una sola vez: si se pierde, el servicio conserva la incertidumbre. No hay reintentos de orden ni de resultado en este modelo; así podemos estudiar primero los límites y la diferencia entre ejecución y conocimiento. Esos reintentos se diseñan en el tema siguiente.
Python en el navegador. El bloque es independiente y no controla equipos reales. Podés editarlo y ejecutarlo con Pyodide o copiarlo a un archivo Python 3 local.
def simular(operacion="on", modo="remoto", permiso=True, bloqueo=False,
red="estable", demora=3, ttl=15, duracion=10, umbral=90,
realimentacion="normal", perderRespuesta=False, prioridadLocal=False):
modo_actual, salida, fisico, hasta = modo, False, False, None
entregada, verificar, respuesta, respuesta_intentada = False, None, None, False
nube = "Pendiente" if permiso else "Rechazada por el servicio"
resultado_en, dispositivo, cfg, alto = None, "Sin orden recibida", 1, 80
filas = []
for t in range(41):
online = (red == "estable" or
(t >= 20 if red == "reconexion" else t < 8 or t >= 20))
acciones = []
if t == 0:
acciones.append("Servicio registra la solicitud cmd-1" if permiso else
"Servicio rechaza: emisor sin permiso")
if prioridadLocal and t == 8:
modo_actual, salida, hasta = "local", False, None
dispositivo = "Apagado por intervención local"
acciones.append("Selector local toma el mando y apaga")
if verificar is not None:
respuesta, verificar = "Interrumpida por intervención local", None
if salida and hasta is not None and t >= hasta:
salida, hasta, dispositivo = False, None, "Apagado por límite local"
acciones.append("Vence el tiempo de encendido: apagar sin consultar a la nube")
if verificar is not None:
respuesta, verificar = "Interrumpida por límite local", None
if permiso and not entregada and online and t >= demora:
entregada = True
acciones.append("La orden llega al dispositivo")
if t >= ttl:
respuesta = "Rechazada: orden vencida"
elif modo_actual != "remoto":
respuesta = "Rechazada: modo no remoto"
elif operacion == "on" and bloqueo:
respuesta = "Rechazada: bloqueo local"
elif operacion == "on" and not 1 <= duracion <= 10:
respuesta = "Rechazada: duración fuera de rango"
elif operacion == "config" and not 75 < umbral <= 100:
respuesta = "Rechazada: umbral fuera de rango"
elif operacion == "config":
alto, cfg, respuesta = umbral, cfg + 1, "Confirmado: parámetro aplicado"
elif operacion == "off":
salida, hasta, respuesta = False, None, "Confirmado: apagado"
else:
salida, hasta, verificar = True, t + duracion, t
dispositivo = "Salida activada · verificando"
acciones.append(f"Activar salida hasta t = {hasta} s como máximo")
if respuesta is not None:
dispositivo = respuesta
fisico = salida if realimentacion == "normal" else False
if verificar is not None:
if t >= verificar + 1 and fisico:
dispositivo, respuesta, verificar = "Encendido verificado", "Confirmado: encendido", None
acciones.append("La realimentación confirma encendido")
elif t >= verificar + 2:
salida, fisico, hasta, verificar = False, False, None, None
dispositivo, respuesta = "Falla de realimentación · salida apagada", "Fallo: sin realimentación"
acciones.append("No llegó realimentación: apagar y declarar fallo")
if respuesta is not None and not respuesta_intentada:
respuesta_intentada = True
acciones.append(f"Resultado local: {respuesta}")
if online and not perderRespuesta:
nube = f"Respuesta tardía · {respuesta}" if t >= 6 else respuesta
resultado_en = t
acciones.append("El servicio recibe el resultado")
else:
acciones.append("La respuesta no llega al servicio")
if t >= 6 and nube == "Pendiente":
nube = "Tiempo de espera agotado"
acciones.append("El servicio deja de esperar: el resultado sigue siendo incierto")
filas.append(dict(t=t, online=online, modo=modo_actual, salida=salida,
fisico=fisico, hasta=hasta, entregada=entregada, nube=nube,
resultadoEn=resultado_en, dispositivo=dispositivo,
cfg=cfg, alto=alto, acciones=acciones))
return filas
base = simular()
for f in base:
if f["acciones"]:
print(f't={f["t"]:2} s | salida={f["salida"]} | {". ".join(f["acciones"])}')
print("Resultado histórico:", base[-1]["nube"])
print("Estado final de salida:", base[-1]["salida"])
assert not base[2]["salida"] and base[3]["salida"]
assert base[4]["nube"] == "Confirmado: encendido"
assert base[12]["salida"] and not base[13]["salida"]
assert not any(f["salida"] for f in simular(permiso=False))
assert "modo no remoto" in simular(modo="local")[3]["nube"]
assert "bloqueo local" in simular(bloqueo=True)[3]["nube"]
assert "orden vencida" in simular(demora=12, ttl=5)[12]["nube"]
assert simular(perderRespuesta=True)[6]["nube"] == "Tiempo de espera agotado"
assert not simular(realimentacion="falla")[5]["salida"]
assert not simular(red="corte")[13]["salida"]
assert simular(prioridadLocal=True)[8]["modo"] == "local"
assert simular(operacion="config", umbral=90)[3]["alto"] == 90
assert simular(operacion="config", umbral=70)[3]["cfg"] == 1
assert not any(f["salida"] for f in simular(duracion=20))
assert "orden vencida" in simular(demora=3, ttl=3)[3]["nube"]
print("\n15 comprobaciones superadas.")
Resultado inicial: salida ON en t = 3 s, realimentación y resultado en t = 4 s, salida OFF en t = 13 s por límite local. El resultado histórico continúa indicando que aquel encendido fue confirmado. Las quince comprobaciones terminan sin errores.
Alcance del modelo
Se simula una solicitud aislada, con esquema y tipos ya validados y reloj común confiable. Los modos automático y local no ejecutan aquí otro proceso: mantienen apagado y rechazan estas órdenes remotas. La falla física modelada es falta de encendido; no representa todas las averías posibles ni una garantía de que un actuador real podrá apagarse.
Los registros muestran lo que sabe el simulador tanto del dispositivo como del servicio. En un despliegue, la interfaz solo conoce el estado que efectivamente recibió. No debe rellenar ese conocimiento mirando la acción solicitada ni la animación de un botón.
11 · Exploración interactiva
Laboratorio de control remoto
«Preparar simulación» registra una nueva ejecución aislada de cmd-1 en t = 0. Avanzá segundo a segundo o completá los cuarenta segundos. Los controles cambian únicamente el modelo de esta página: no se envían órdenes a una red ni a un dispositivo real.
Solicitud y ejecución de una salida auxiliar
Preparando configuración.
Estado interno del dispositivo simulado
Sin orden recibida
Resultado de la solicitud visto por el servicio
Pendiente
Sin resultado recibido
Preparando recorrido.
| t (s) | Qué ocurre | Salida | Realimentación | Servicio |
|---|
Recorrido guiado
- Con los valores iniciales, avanzá a t = 3 s: la salida está activada y la solicitud sigue pendiente. En t = 4 se verifica y llega «Confirmado: encendido». En t = 13 se apaga por el plazo local.
- Ejecutá hasta 40 s. La salida queda apagada, pero el resultado histórico sigue siendo «encendido confirmado». Eso describe la ejecución de aquella orden, no un reporte continuo del estado actual.
- Marcá Perder la respuesta y prepará otra simulación. La salida se activa y luego se apaga, mientras el servicio agota su espera a los 6 s. No puede concluir que la ejecución no ocurrió.
- Elegí demora de 12 s y caducidad de 5 s. La orden se rechaza al llegar y nunca activa. Con caducidad de 15 s sí se admite a los 12, y el resultado llega tarde a los 13.
- Elegí red que vuelve a los 20 s. Con caducidad de 15 s se rechaza. Con caducidad de 60 s y demora de 3 s, se aplica en t = 20, se confirma en 21 y se apaga en 30.
- Volvé a la configuración inicial y elegí realimentación fallida. La salida se activa en t = 3 y se apaga en 5 por falta de confirmación; el servicio recibe el fallo si su respuesta no se pierde.
- Probá modo local, usuario sin permiso y bloqueo de activación. Los tres impiden el encendido por motivos diferentes. Una duración solicitada de 20 s también se rechaza, sin reducirla silenciosamente a diez.
- Con red que cae en t = 8 y demora de 3 s, el límite local sigue apagando en t = 13. Si marcás además la intervención local, se apaga a los 8 y el modo cambia a local.
- Elegí Cambiar umbral en modo remoto. 9,0 °C se aplica como versión 2; 7,0 °C y 12,0 °C se rechazan y mantienen versión 1 con alta de 8,0 °C. No se activa la salida para configurar.
Cuando la red está caída, el modelo conserva la orden hasta poder intentar entregarla. La fecha límite se vuelve a comprobar en el dispositivo. En producción conviene que el servicio y el transporte también descarten lo vencido; la última validación local evita depender solo de ese filtro previo.
Cada ejecución aislada empieza con salida apagada. Por eso la operación OFF confirma que permanece en ese estado, sin necesitar un cambio previo. El núcleo de control también permite apagar una salida que ya estaba encendida; una secuencia de varias órdenes requiere además el protocolo de identidades y orden que estudiamos a continuación.
Desafío: ¿por qué una respuesta tardía todavía puede ser importante?
Porque aclara una ejecución que quedó incierta al vencer la espera del servicio. Debe asociarse a la misma identidad y conservar la hora del hecho. No debe hacer que una interfaz interprete una confirmación antigua de encendido como un estado actual reciente.
12 · Diseñar antes de publicar un botón
Método y confusiones frecuentes
- Nombrá operaciones del producto: destinatario, unidad, rango y resultado esperado.
- Asigná autoridad: permisos del servicio y prioridad entre modos y condiciones locales.
- Separá tiempos: vigencia de admisión, espera del solicitante, duración y verificación de efecto.
- Concentrá la salida: un controlador resuelve el estado; los callbacks no compiten por el pin.
- Definí el estado sin red y al arrancar: debe cumplirse sin esperar una nueva orden remota.
- Decidí qué podés confirmar: recepción, salida aplicada o efecto observado.
- Registrá identidad y causa: se deben poder distinguir rechazos, fallas y respuestas tardías.
- Probá fallas: orden vencida, respuesta perdida, interbloqueo, cambio local y realimentación ausente.
- Revisá el circuito y el firmware juntos: tiempos, niveles de arranque y comportamiento ante fallas deben coincidir con el requisito.
«El servidor respondió 202, así que el equipo ya encendió»
La aceptación para procesamiento no confirma ejecución. La interfaz mantiene pendiente hasta tener el resultado previsto y distingue ese resultado del estado actual reportado.
«Si se agotó la espera, puedo asegurar que no pasó nada»
La orden pudo ejecutarse y perder su respuesta, o todavía estar en tránsito. Se conserva la incertidumbre y se consulta o reconcilia por la misma identidad; no se deduce el estado físico del timeout.
«QoS alto confirma que el actuador se movió»
QoS describe entrega de mensajes en el intercambio MQTT. El firmware y la realimentación definen qué efecto se aplicó y qué evidencia existe. Son confirmaciones de niveles diferentes.
«Dejo un ON retenido para que el equipo lo ejecute cuando vuelva»
Una orden puntual puede volverse inapropiada con el tiempo. Necesita vigencia y condiciones de admisión. Un estado deseado persistente es otro contrato y debe diseñarse con versión y reconciliación.
«La nube autorizada puede saltear un bloqueo local»
En este diseño no. El permiso habilita a solicitar una operación; el equipo verifica si es admisible ahora. Cambiar o anular un bloqueo sería otra operación y otra política, no un efecto implícito de encender.
«Repito ON hasta ver que responde»
Si cada recepción reinicia el plazo, los reintentos pueden extender la activación. La aplicación distingue una entrega repetida de una orden nueva y conserva su identidad. El próximo tema implementa esa disciplina.
«Cambiar un parámetro siempre es menos importante que activar una salida»
Un parámetro puede cambiar cuándo el controlador actúa o qué condición acepta. Se validan unidad, rango, coherencia y versión, además de los permisos. Una configuración solicitada no se presenta como aplicada sin evidencia.
13 · Práctica guiada
Predecí antes de comprobar
1. Tres plazos
La orden se crea en t = 0, vence a los 15 s y llega en t = 3. Pide ON durante 10 s; el servicio espera 6 s. ¿Hasta cuándo puede quedar activada por esa orden y qué plazo gobierna ese apagado?
Ver solución
Hasta t = 13 s, salvo una condición local que la detenga antes. Lo gobierna la duración local desde la aplicación. La caducidad de admisión y la espera del servicio tienen otros propósitos.
2. Llegar exactamente al vencimiento
Se admite solo antes de t = 5 s. La orden llega exactamente a los 5 s. ¿Se acepta según el contrato del tema?
Ver solución
No. La condición es llegada < vencimiento; la igualdad ya está vencida. La comparación exacta forma parte del contrato y debe probarse.
3. Se perdió la respuesta
El equipo encendió y verificó la salida, pero el resultado no llegó. ¿Qué puede afirmar el servicio al agotar su espera?
Ver solución
Que no obtuvo la confirmación dentro de su plazo. No puede concluir que la acción falló ni que la salida sigue encendida. Necesita consultar un estado recibido o resolver la solicitud por su identidad.
4. Cambio a modo local
La salida encendió en t = 3 con plazo hasta t = 13. El selector local toma el mando en t = 8 y la política indica apagar. ¿Qué ocurre con el resultado recibido en t = 4?
Ver solución
La salida se apaga a los 8 s. La confirmación de t = 4 sigue siendo un hecho histórico válido; el estado actual y la causa de interrupción se reportan aparte. No se borra ni se interpreta como continuidad del encendido.
5. GPIO y realimentación
Se escribió ON en t = 3, pero la realimentación sigue OFF. El límite de verificación es de 2 s. ¿Qué hace el modelo a los 5 s?
Ver solución
Apaga la salida y declara fallo de realimentación. No presenta la escritura de GPIO como un efecto físico confirmado. El comportamiento ante otras fallas del actuador necesita su propio diseño.
6. Un umbral incoherente
El rearme es 7,5 °C y el rango admite alta hasta 10,0 °C. Se piden 7,0; 9,0 y 12,0 °C. ¿Cuál se aplica?
Ver solución
Solo 9,0 °C. 7,0 no queda por encima del rearme; 12,0 supera el máximo. En los rechazos permanece la configuración válida previa y se informa la causa.
7. La red vuelve tarde
La orden se creó en t = 0, vence a los 15 s y el equipo vuelve a conectarse en t = 20. ¿Puede usar que «acaba de recibirla» como comienzo de vigencia?
Ver solución
No. La vigencia está ligada a la creación y al vencimiento del contrato. Recibirla ahora no la vuelve nueva. Se rechaza sin activar, incluso si el transporte la conservó durante el corte.
8. Repetir una solicitud
Un encendido de diez segundos se aplicó en t = 3. Una entrega repetida de esa misma orden llega en t = 7. ¿Qué problema aparece si el núcleo reinicia su plazo sin distinguir identidad?
Ver solución
El apagado se desplaza de t = 13 a t = 17 s. Un reintento cambió el efecto de la solicitud original. El protocolo debe reconocerlo y devolver su resultado sin volver a ejecutar ni extender el plazo.
14 · Síntesis
Ideas para recordar
- El control remoto expresa solicitudes con identidad y límites. El dispositivo conserva la admisión según modo, bloqueos y condiciones actuales.
- Autorizar en el servicio, entregar por la red, aplicar una salida y observar el efecto son etapas distintas.
- Operaciones explícitas como encender, apagar o aplicar un parámetro facilitan el contrato; reintentar todavía exige reconocer la identidad.
- Caducidad, espera del servicio y duración de ejecución son relojes diferentes. Un timeout no cancela ni demuestra ausencia de ejecución.
- El límite de encendido se cumple localmente y no depende de que llegue otra orden de apagado.
- Una confirmación de ejecución es histórica. El panel necesita además estado actual reportado y antigüedad.
- En el caso base, la salida enciende a los 3 s, se verifica a los 4 y apaga a los 13, incluso si la red se corta antes.
- Los parámetros remotos se validan y versionan; la configuración aplicada no se deduce de la solicitada.
¿Listo para el tema 38?
Antes de avanzar, elegí una operación de tu proyecto y escribí su destinatario, permisos, modo permitido, vigencia, límites locales y evidencia de resultado. Probá qué diría el panel si la acción se ejecuta y se pierde la respuesta.
En el próximo tema, Comandos, estados y confirmaciones, formalizamos ese intercambio: estado deseado frente a estado real, identidades, reintentos e idempotencia para que una entrega repetida no vuelva a ejecutar una acción.