Un ESP32 que publica la temperatura cada minuto genera datos, pero esos mensajes MQTT desaparecen si nadie los está escuchando. Para guardarlos, o para recibir un aviso cuando algo se sale de lo normal, no hace falta un servidor: AWS IoT Core tiene un motor de reglas que procesa cada mensaje y lo envía a otros servicios.
En esta parte creamos dos reglas: una que guarda las lecturas de un sensor en DynamoDB y otra que envía un correo cuando la temperatura pasa de un límite.
Cómo funciona una regla
Una regla tiene dos partes:
- Una consulta SQL que dice qué mensajes le interesan y qué campos extrae.
- Una o varias acciones: a dónde envía el resultado (DynamoDB, SNS, Lambda, S3, otro tópico...).
SELECT temperatura, humedad, topic(2) AS dispositivoId, timestamp() AS ts
FROM 'casa-demo/+/telemetria'
WHERE temperatura > 30
FROMes un filtro de tópico MQTT, con+y#como comodines.topic(2)devuelve el segundo segmento del tópico: concasa-demo/sensor-patio/telemetriadevuelvesensor-patio.timestamp()devuelve la hora de llegada en milisegundos.WHEREfiltra: si no se cumple, la regla no hace nada.
El dispositivo: publicar la telemetría
Siguiendo la convención de tópicos de la parte 13, el ESP32 publica en un tópico con su nombre:
void publicarTelemetria(float temperatura, float humedad) {
JsonDocument doc;
doc["temperatura"] = temperatura;
doc["humedad"] = humedad;
char buffer[128];
size_t n = serializeJson(doc, buffer);
String topico = String("casa-demo/") + THING_NAME + "/telemetria";
client.publish(topico.c_str(), buffer, n);
}
Recuerda permitir ese tópico en la política del dispositivo (iot:Publish sobre topic/casa-demo/${iot:Connection.Thing.ThingName}/telemetria).
Regla 1: guardar las lecturas en DynamoDB
Con la tabla de lecturas de la parte 7 (clave de partición dispositivoId, de ordenación ts):
SELECT temperatura, humedad,
topic(2) AS dispositivoId,
parse_time("yyyy-MM-dd'T'HH:mm:ss'Z'", timestamp()) AS ts,
floor(timestamp() / 1000) + 2592000 AS expira
FROM 'casa-demo/+/telemetria'
La acción DynamoDBv2 escribe cada fila del resultado como un item, con un atributo por columna. El campo expira (ahora + 30 días, en segundos) es el atributo de TTL: DynamoDB borrará la lectura sola al mes.
En la consola: AWS IoT Core → Message routing → Rules → Create rule, pega la consulta, elige la acción DynamoDBv2, la tabla y un rol. La consola puede crear el rol con el permiso exacto (dynamodb:PutItem sobre esa tabla).
Con la CLI, la regla es un JSON:
{
"sql": "SELECT temperatura, humedad, topic(2) AS dispositivoId, parse_time(\"yyyy-MM-dd'T'HH:mm:ss'Z'\", timestamp()) AS ts, floor(timestamp() / 1000) + 2592000 AS expira FROM 'casa-demo/+/telemetria'",
"awsIotSqlVersion": "2016-03-23",
"actions": [
{ "dynamoDBv2": { "roleArn": "arn:aws:iam::CUENTA:role/regla-lecturas", "putItem": { "tableName": "lecturas" } } }
],
"errorAction": {
"cloudwatchLogs": { "roleArn": "arn:aws:iam::CUENTA:role/regla-lecturas", "logGroupName": "/iot/reglas/errores" }
}
}
aws iot create-topic-rule --rule-name guardar_lecturas --topic-rule-payload file://regla.json
Los nombres de reglas solo admiten letras, números y guion bajo.
Regla 2: una alerta por correo con SNS
Primero, un tema de SNS con tu correo suscrito:
aws sns create-topic --name alertas-casa
aws sns subscribe --topic-arn arn:aws:sns:REGION:CUENTA:alertas-casa \
--protocol email --notification-endpoint tu@correo.com
# Confirma la suscripción desde el enlace que llega al correo
Después, la regla:
SELECT concat('Temperatura alta en ', topic(2), ': ', cast(temperatura AS String), ' °C') AS mensaje
FROM 'casa-demo/+/telemetria'
WHERE temperatura > 30
Con la acción SNS y el formato de mensaje RAW, llega un correo cada vez que un sensor publica más de 30 °C.
Cuidado: si el sensor publica cada minuto y la temperatura sigue alta, llega un correo por minuto. Para un aviso único hay que guardar estado (por ejemplo, con una Lambda que recuerde la última alerta, o con AWS IoT Events, que modela estados como "normal" y "alarma").
La acción de error
Si una acción falla (la tabla no existe, el rol no tiene permiso), el mensaje se pierde en silencio. La acción de error (errorAction) envía el detalle del fallo a otro destino, como CloudWatch Logs o un tema de SNS. Configúrala siempre: es la única forma de enterarte.
Probar sin el ESP32
Desde AWS IoT Core → Test → MQTT test client, publica en casa-demo/sensor-patio/telemetria:
{ "temperatura": 31.5, "humedad": 40 }
Deberías ver el item nuevo en la tabla de DynamoDB y recibir el correo. Si no pasa nada, revisa el grupo de registros de la acción de error, o activa los registros de AWS IoT en CloudWatch.
Errores comunes
- La regla no se dispara. El
FROMno coincide con el tópico (mayúsculas, un segmento de más) o elWHEREno se cumple. Prueba primero sinWHERE. AccessDenieden los registros. El rol de la regla no tiene permiso sobre el destino, o su relación de confianza no permite aiot.amazonaws.comasumirlo.- Los números llegan como texto. Si el dispositivo envía
"temperatura": "31.5"(entre comillas), la comparación> 30falla. Envía números, o usacast(). - Mensajes que no son JSON. El SQL solo puede leer campos de un JSON válido.
¿Cuánto cuesta?
Se cobra por regla disparada y por acción ejecutada, por millón, más el costo del servicio de destino (escrituras de DynamoDB, mensajes de SNS). Una casa con unos pocos sensores que publican cada minuto genera unas decenas de miles de mensajes al mes: fracciones de centavo en reglas. Los correos de SNS tienen su propia capa gratuita. La estimación completa está en la parte 22.
Preguntas frecuentes
¿Reglas o una Lambda suscrita a todo?
Las reglas no necesitan código ni servidores, escalan solas y cuestan menos. Usa una Lambda como acción solo cuando necesites lógica que el SQL no permite.
¿Puede una regla escribir en el Device Shadow?
Sí, con la acción de republicar en el tópico $aws/things/<thing>/shadow/update. Es útil, por ejemplo, para que un sensor cambie el estado deseado de otro dispositivo.
¿Las reglas leen los mensajes del Shadow?
Sí: puedes usar los tópicos reservados del Shadow en el FROM, por ejemplo $aws/things/+/shadow/update/accepted, para guardar un historial de cambios de estado.