DTDL, el Digital Twin Definition Language, es una forma basada en JSON de describir de qué está hecho un gemelo digital: la telemetría que emite, las propiedades que guarda, los comandos que acepta y cómo se relaciona con otros gemelos. En este artículo aprenderás cómo se estructuran los modelos DTDL, leerás un ejemplo completo y verás cómo mapear ese modelo a topics MQTT para que los dispositivos y las aplicaciones del gemelo compartan un contrato único y claro.
Para la visión general de dónde encajan los modelos, consulta nuestra guía de tecnología de gemelo digital.
Qué es DTDL y por qué existe
Cuando diez equipos construyen gemelos por su cuenta, cada uno inventa su propia forma de decir "esta bomba tiene un caudal en litros por minuto y un comando de arranque". DTDL estandariza esa descripción. Es un lenguaje abierto publicado por Microsoft, basado en JSON-LD y usado por Azure Digital Twins e IoT Plug and Play, pero nada en el propio lenguaje te ata a una nube concreta. Un archivo DTDL es simplemente un documento que describe un tipo de activo; puedes usarlo para generar código, validar payloads, construir interfaces o documentar contratos de topics.
El lenguaje ha pasado por varias versiones; DTDL v3 es la actual, y la v2 todavía aparece mucho en proyectos existentes. La especificación oficial está en el repositorio de DTDL en GitHub.
Los bloques de un modelo DTDL
Todo en DTDL se define dentro de una Interface. Una interfaz tiene un identificador y una lista de contenidos, cada uno de los cuales es uno de estos tipos de elemento:
| Elemento | Qué describe | Ejemplo |
|---|---|---|
| Telemetry | Un flujo de mediciones, no almacenado como estado | Temperatura cada segundo |
| Property | Un valor de estado, opcionalmente escribible | Versión de firmware, consigna |
| Command | Una operación que se le puede pedir al gemelo | Arrancar, parar, reiniciar contador |
| Component | Reutilización de otra interfaz dentro de esta | Una bomba que contiene una interfaz de motor |
| Relationship | Un enlace con otro gemelo | Bomba isPartOf estación |
Cada modelo y cada elemento tienen un DTMI (Digital Twin Model Identifier), como dtmi:com:example:Pump;1. El número final es la versión. Cuando cambias un modelo de forma incompatible, incrementas la versión en lugar de editarlo en el sitio.
Telemetría frente a propiedad
Esta es la distinción en la que más se equivocan los equipos. La telemetría es un flujo de eventos: te importa cada muestra y la plataforma del gemelo no guarda ningún "valor actual". Las propiedades son estado: el gemelo siempre tiene un valor actual, y una propiedad escribible puede fijarse desde el lado de la nube (un valor deseado) mientras el dispositivo informa de su valor real.
Regla práctica: si un nuevo visor necesita verlo nada más cargar, es una propiedad; si solo importa como serie a lo largo del tiempo, es telemetría.
Un ejemplo completo de DTDL
Esta es una interfaz DTDL v3 para una bomba centrífuga:
{
"@context": "dtmi:dtdl:context;3",
"@id": "dtmi:com:example:Pump;1",
"@type": "Interface",
"displayName": "Centrifugal Pump",
"contents": [
{
"@type": ["Telemetry", "VolumeFlowRate"],
"name": "flow",
"schema": "double",
"unit": "litrePerSecond"
},
{
"@type": ["Telemetry", "Pressure"],
"name": "dischargePressure",
"schema": "double",
"unit": "bar"
},
{
"@type": "Property",
"name": "running",
"schema": "boolean"
},
{
"@type": "Property",
"name": "speedSetpoint",
"schema": "double",
"writable": true
},
{
"@type": "Property",
"name": "firmwareVersion",
"schema": "string"
},
{
"@type": "Command",
"name": "resetRunHours"
},
{
"@type": "Relationship",
"name": "isPartOf",
"target": "dtmi:com:example:PumpingStation;1"
}
]
}
Los tipos semánticos como VolumeFlowRate y las unidades como litrePerSecond proceden de la extensión de tipos cuantitativos de DTDL. Permiten que las herramientas muestren las unidades correctamente sin adivinar. Consulta la especificación para ver la lista exacta de tipos semánticos y unidades válidos en la versión que uses.
Mapear DTDL a topics MQTT
DTDL describe qué contiene un gemelo; no dice cómo viajan los datos. MQTT es un transporte natural, y un mapeo limpio convierte tu modelo DTDL en un contrato de topics. Una convención práctica:
| Elemento DTDL | Topic MQTT | Retenido | QoS |
|---|---|---|---|
Telemetry flow |
<asset>/telemetry (JSON con todos los campos de telemetría) |
No | 0 |
| Property (solo lectura) | <asset>/properties/reported |
Sí | 1 |
| Valor deseado de una propiedad escribible | <asset>/properties/desired |
Sí | 1 |
Command resetRunHours |
<asset>/commands/resetRunHours |
No | 1 |
| Respuesta al comando | <asset>/commands/resetRunHours/response |
No | 1 |
| Relationship | Se guarda en el modelo o en el registro, no viaja por la red | n/a | n/a |
Siendo <asset> una ruta ISA-95 como acme/site1/water/station3/pump2, un visor puede suscribirse a acme/site1/water/# y saber, gracias al modelo, cómo interpretar cada mensaje.
¿Por qué combinar la telemetría en un mensaje por activo? Publicar un topic por cada campo de telemetría multiplica el número de mensajes sin aportar información, y la mayoría de los consumidores quieren todos los campos con la misma marca de tiempo. Las propiedades se benefician de los retained messages para que los visores del gemelo obtengan el estado actual al instante; el tutorial gemelo digital con retained messages MQTT lo muestra en la práctica.
Validar payloads contra un modelo DTDL
Una vez que tienes un modelo, úsalo. Una pequeña comprobación en Python puede rechazar la telemetría que no coincida con la interfaz antes de que contamine tu histórico:
import json
import paho.mqtt.client as mqtt
with open("pump.json", encoding="utf-8") as f:
model = json.load(f)
TYPE_MAP = {"double": (int, float), "boolean": bool, "string": str, "integer": int}
telemetry = {
c["name"]: c["schema"]
for c in model["contents"]
if "Telemetry" in (c["@type"] if isinstance(c["@type"], list) else [c["@type"]])
}
def on_connect(client, userdata, flags, reason_code, properties):
client.subscribe("connectmq/demo/d41f/+/+/+/+/telemetry")
def on_message(client, userdata, msg):
data = json.loads(msg.payload)
for name, schema in telemetry.items():
if name not in data:
print(f"{msg.topic}: missing {name}")
elif not isinstance(data[name], TYPE_MAP[schema]):
print(f"{msg.topic}: {name} should be {schema}")
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="dtdl-validator-d41f")
client.on_connect = on_connect
client.on_message = on_message
client.connect("broker.connectmq.com", 1883)
client.loop_forever()
Pruébalo publicando un mensaje de ejemplo en el servidor MQTT público gratuito, reservado para pruebas y prototipos:
mosquitto_pub -h broker.connectmq.com -p 1883 -i dtdl-pub-d41f \
-t "connectmq/demo/d41f/acme/site1/station3/pump2/telemetry" \
-m '{"flow": 12.4, "dischargePressure": "4.1"}'
El validador señala que dischargePressure es una cadena en lugar de un double, el tipo de error que, de otro modo, aparece semanas después en forma de gráfico roto.
Checklist de modelado DTDL
- Una interfaz por tipo de activo, no por instancia.
- Telemetría para flujos, propiedades para estado, comandos para acciones.
- Usa componentes para partes reutilizables como motores o sensores.
- Incluye las unidades mediante tipos semánticos siempre que sea posible.
- Versiona los DTMI y nunca rompas una versión publicada.
- Documenta el mapeo a MQTT junto al modelo, en el mismo repositorio.
- Valida los payloads en la ingesta, no solo en la interfaz.
DTDL comparado con otros modelos de gemelo
DTDL no es la única opción. La Asset Administration Shell, del mundo Industria 4.0, se centra en submodelos estandarizados para activos industriales y es habitual en la fabricación europea; consulta Asset Administration Shell explicada. Eclipse Ditto usa sus propios "things" JSON con atributos y features. Muchos equipos usan también un JSON Schema simple por tipo de activo. Lo que más importa es tener un modelo explícito y un mapeo documentado al transporte, que es justo el papel que DTDL desempeña bien. Para ver cómo se sitúa la capa de modelo junto a la mensajería, el estado y la visualización, consulta arquitectura de gemelo digital.
Ejecuta tu gemelo basado en DTDL en ConnectMQ
DTDL define el contrato; sigues necesitando un transporte fiable. ConnectMQ es un broker MQTT gestionado compatible con MQTT 3.1.1 y 5.0, con retained messages para las propiedades, TLS en el 8883 para los dispositivos y WSS en el 8084 para los gemelos en el navegador, como un gemelo digital 3D de una planta o un edificio. La validación de modelos, la generación de código y los grafos de gemelos se ejecutan en tus propias herramientas, conectadas a ConnectMQ como clientes MQTT normales. Crea un broker con el plan Serverless gratuito en app.connectmq.com, sin tarjeta de crédito, y publica en minutos tu primera telemetría con forma DTDL.
Preguntas frecuentes
¿DTDL es solo para Azure?
No. DTDL es un lenguaje abierto basado en JSON-LD. Azure Digital Twins lo usa de forma nativa, pero puedes usar modelos DTDL con cualquier transporte, incluido MQTT en cualquier broker.
¿Qué diferencia hay entre una propiedad y la telemetría en DTDL?
Una propiedad es estado con un valor actual, opcionalmente escribible; la telemetría es un flujo de mediciones en el que importa cada muestra, pero no se guarda ningún valor actual.
¿Cómo represento los comandos DTDL sobre MQTT?
Publica el comando en un topic de comandos no retenido con QoS 1 y un correlation ID, y haz que el dispositivo publique el resultado en un topic de respuesta. Las propiedades response topic y correlation data de MQTT 5 lo soportan directamente.
¿Qué versión de DTDL debería usar?
Usa DTDL v3 para proyectos nuevos, salvo que una herramienta de la que dependas solo soporte la v2.



