Cómo armé un Pit Wall con AWS IoT Core (y por qué este patrón sirve para cualquier industria)
Fijate lo que no puede hacer este certificado: no puede publicar en sim/pit-wall-sim-02/telemetry (el topic de otro simulador), y no puede suscribirse a nada que no sea su propio topic de comandos. Si mañana alguien clona el certificado de un simulador, el radio de daño queda contenido a ese único dispositivo — nunca escala al resto de la flota.
Esto es literalmente lo mismo que harías con un sensor de humedad en una planta agrícola, o con la tablet de un camión de reparto: un certificado por dispositivo, un permiso mínimo por dispositivo, sin excepciones.
IoT Rules: el corazón del procesamiento
Una vez que el dato entra por MQTT, ¿quién lo agarra? Ahí entran las IoT Rules: consultas SQL que corren sobre cada mensaje que pasa por un topic, y que disparan una o más acciones.
DATA_TOPICS = ("telemetry", "event", "lap", "session", "status") def _crear_regla(self, topic: str): regla = iot.CfnTopicRule( self, f"Rule{topic}", topic_rule_payload=iot.CfnTopicRule.TopicRulePayloadProperty( sql=f"SELECT * FROM '{self.topic_prefix}/+/{topic}'", actions=[ iot.CfnTopicRule.ActionProperty( lambda_=iot.CfnTopicRule.LambdaActionProperty( function_arn=self.ingest_function.function_arn ) ), iot.CfnTopicRule.ActionProperty( lambda_=iot.CfnTopicRule.LambdaActionProperty( function_arn=self.broadcast_function.function_arn ) ), ], ), )
Lo interesante acá no es el SQL (SELECT * FROM 'sim/+/telemetry', con el + como wildcard de un nivel — cualquier simulador, ese topic puntual). Lo interesante es que una sola regla dispara dos acciones en paralelo, sobre el mismo mensaje:
- Una Lambda de ingesta, que persiste el dato (más sobre esto abajo).
- Una Lambda de broadcast, que reenvía el mismo mensaje por WebSocket a quien esté mirando ese simulador en vivo.
No hay una segunda suscripción al mismo topic, ni una cola intermedia, ni el ingesta reenviándole el dato al broadcast. Es la misma regla, la misma invocación de IoT Core, dos acciones independientes. Si mañana el broadcast se cae, la ingesta sigue funcionando — y viceversa.
Este es el patrón que más me sirvió entender de todo el proyecto: una IoT Rule no es “un trigger”, es un punto de fan-out. Podés colgar tantas acciones como necesites (otra Lambda, un tópico SNS, un stream de Kinesis, otra regla) sin tocar nada del lado del dispositivo ni de las reglas existentes.
El desafío real: reconstruir algo coherente a partir de pedacitos
Acá viene la parte menos obvia. MQTT tiene un límite de tamaño de payload, y la telemetría de un simulador genera muestras varias veces por segundo — no entra todo en un solo mensaje. El agente en la PC del simulador arma lotes (batches) de muestras y los publica de a poco, con un número de secuencia cada uno.
Del lado de AWS, eso significa que la Lambda de ingesta nunca recibe “una vuelta completa” — recibe fragmentos, que van a staging en DynamoDB con un TTL (si una sesión nunca cierra una vuelta, ese staging se autodestruye solo). Cuando llega el mensaje que dice “esta vuelta se cerró”, ahí sí, la Lambda junta todos los lotes que tiene en staging para esa sesión y reconstruye la traza completa:
class CoberturaIncompletaError(ReconstructionError): """La cobertura de lotes en staging no alcanza — todavía. Las IoT Rules invocan la Lambda una vez por mensaje, sin ninguna garantía de orden entre invocaciones concurrentes: el mensaje de "vuelta cerrada" puede llegar antes de que termine de guardarse su último lote de telemetría. Acá se reintenta unos segundos dentro de la misma invocación, en vez de descartar la vuelta. """
Este tipo de problema — datos que llegan en desorden, sin garantía de que el último pedacito ya esté cuando lo necesitás — es el problema clásico de cualquier pipeline de IoT en tiempo real. No es específico de telemetría de autos: es lo mismo que te pasa reconstruyendo el recorrido de un camión a partir de puntos GPS sueltos, o el estado de una máquina a partir de eventos de sensores que no llegan en orden.
Guardado: una tabla, un bucket, y ya
Del lado del guardado no hay demasiada magia, pero vale la pena nombrarlo porque es donde termina el dato una vez procesado:
- DynamoDB, en una tabla única (single-table design): sesiones, vueltas, recomendaciones, todo con
PK/SKpensados para que cada consulta sea unaQuerybarata sobre un prefijo, nunca unScande toda la tabla. - S3, para lo pesado: la telemetría reconstruida, los archivos de audio, cualquier blob que no tenga sentido meter en DynamoDB.
Nada de esto es exclusivo de IoT — es el mismo par de servicios que usarías para cualquier backend serverless. Lo importante es que para cuando el dato llega acá, ya pasó por todo el trabajo pesado de identidad, transporte y reconstrucción, así que esta capa queda simple.
De IoT a “en vivo”: el otro lado del fan-out
Contaba arriba que la misma IoT Rule dispara dos Lambdas en paralelo. La segunda (broadcast) es la que arma la parte “en vivo” del dashboard: mantiene un índice chico de qué conexión de WebSocket está mirando qué simulador, y cuando llega un mensaje nuevo, lo reenvía tal cual a esas conexiones.
Fijate que este Lambda no procesa nada — no interpreta, no clasifica, no llama a ningún otro servicio. Solo reenvía. Esa separación entre “el camino que persiste” y “el camino que muestra en vivo” es la que permite que uno pueda evolucionar sin romper al otro: si un día quiero agregar un análisis con IA arriba de la ingesta (spoiler: lo hice, es tema de otro post), el WebSocket en vivo ni se entera.
Generalizando el patrón
Volvamos a la idea del principio: esto no es sobre autos. La misma arquitectura, con los mismos cuatro bloques (identidad por dispositivo → IoT Rules → procesamiento → guardado/consumo), sirve para:
| Industria | El “dispositivo” | El dato | Qué reemplaza al pit wall |
|---|---|---|---|
| Agro | Sensor de humedad/temperatura en el campo | Lecturas periódicas | Un dashboard de riego automatizado |
| Logística | Tablet o GPS de un camión de reparto | Posición, velocidad, paradas | Seguimiento de flota en vivo |
| Industria | PLC o sensor de una línea de producción | Vibración, temperatura, ciclos | Mantenimiento predictivo |
| Salud | Wearable de un paciente | Frecuencia cardíaca, pasos | Alertas en tiempo real al equipo médico |
En todos los casos cambia el dominio, pero no cambia la pregunta que tenés que responder: ¿cómo identifico a cada dispositivo sin que uno pueda hacerse pasar por otro, y cómo proceso lo que manda sin acoplar mi lógica de negocio a los detalles del transporte? Esa pregunta la responde AWS IoT Core, la resuelvas con autos, camiones o sensores de humedad.
Próximos pasos
Terminé este proyecto sabiendo mucho más de IoT que de lo que sabía de simracing cuando arranqué, que ya es decir bastante. Todo lo de acá es la base: una vez que tenés el dato capturado, identificado y procesado, podés poner arriba lo que quieras. En mi caso fue un ingeniero de carrera que te habla por voz mientras manejás, generado con Amazon Bedrock y Amazon Polly — de eso hablo en el próximo post.
Una última cosa antes de cerrar:
Todo el material de esta charla — slides, código, diagramas — está en github.com/alvarongg/charlas-pub. Voy a estar subiendo contenido nuevo seguido: más posts como este, ejemplos de código ejecutable y arquitecturas de referencia.
Si le tirás una ⭐ al repo GitHub te avisa automáticamente cuando haya novedades. Es la forma más fácil de no perderte nada — sin newsletter, sin formularios, sin nada raro.
Abrazo grande. Hasta la próxima 🚀
Cualquier pregunta sobre IoT Core, certificados, o el diseño de la tabla, la dejo abierta en los comentarios. ¡Nos vemos en la pista! 🏎️
Fuente: Artículo original