Scenario: Sending Structured Data in a Default Format using HTTPs

This scenario explains how to create digital twin resources, send structured telemetry in a default or custom format over HTTPS or MQTTS, and view the normalized data.

This scenario uses a digital twin model, digital twin adapter, and digital twin instance to send structured telemetry.

Default format: Use this format for a device you can program to send the property names and data structure expected by the IoT Platform. Configure the physical device to send telemetry matching the digital twin model.

Custom format: Use this format when the device's output cannot be changed. Define a custom digital twin adapter mapping to transform the incoming data into model properties recognized by the IoT Platform.

Step 1: Create an IoT Domain Group and an IoT Domain

Use an existing IoT domain group and IoT domain or create them for this scenario. All resources must be in the same region. See Creating an IoT Domain Group and Creating an IoT Domain for Console, CLI, and API instructions.

Step 2: Create a Digital Twin Model

Save the following DTDL v3 specification as digital-twin-model.json. Both adapter choices in the next step use this HVAC model.

{
  "@context": [
    "dtmi:dtdl:context;3"
  ],
  "@id": "dtmi:com:oracle:example:hvac;1",
  "@type": "Interface",
  "displayName": "HVAC",
  "description": "A digital twin model for HVAC",
  "contents": [
    {
      "@type": "Telemetry",
      "name": "temperature",
      "schema": "integer"
    },
    {
      "@type": "Telemetry",
      "name": "humidity",
      "schema": "integer"
    },
    {
      "@type": "Property",
      "name": "power",
      "schema": "boolean"
    },
    {
      "@type": "Property",
      "name": "batteryLevel",
      "schema": "integer"
    }
  ]
}
  • Use the Console procedure in Creating a Digital Twin Model to create the model. Upload or paste the contents of digital-twin-model.json.

  • Use the oci iot digital-twin-model create command:

    oci iot digital-twin-model create \
      --iot-domain-id <iot-domain-OCID> \
      --spec file://digital-twin-model.json

    Save the returned digital twin model OCID for the following steps.

  • Run the CreateDigitalTwinModel operation. Set the IoT domain ID and use the same DTDL specification as the request's model specification.

Step 3: Create a Digital Twin Adapter

Choose the adapter format that matches the device payload:

  • Default format: Use when the payload fields already match the digital twin model. Do not specify an inbound envelope or inbound routes.
  • Custom format: Use when the device payload fields or envelope must be transformed. Specify both an inbound envelope and inbound routes.

Custom format mapping files

If you choose the custom format, save this inbound envelope as inbound-envelope.json. It maps $.time to timeObserved.

{
  "reference-endpoint": "telemetry/health",
  "reference-payload": {
    "data-format": "JSON",
    "data": {
      "time": "<timestamp>",
      "data": {
        "temp": 0,
        "hum": 0,
        "power": false,
        "batteryLevel": 0
      }
    }
  },
  "envelope-mapping": {
    "timeObserved": "$.time"
  }
}

Save these inbound routes as inbound-routes.json:

[
  {
    "condition": "${endpoint(2) == \"heartbeat\"}",
    "description": "heartbeat",
    "payload-mapping": {
      "$.humidity": "${.hum-1}",
      "$.temperature": "$.temp"
    },
    "reference-payload": {
      "data": {
        "hum": 62,
        "temp": 75
      },
      "data-format": "JSON"
    }
  },
  {
    "condition": "${endpoint(2) == \"health\"}",
    "description": "health",
    "payload-mapping": {
      "$.batteryLevel": "$.batteryPercentage",
      "$.power": "$.on"
    },
    "reference-payload": {
      "data": {
        "batteryPercentage": 60,
        "on": false
      },
      "data-format": "JSON"
    }
  }
]
  • Use the Console procedure in Creating a Digital Twin Adapter.

    • Default format: Select the HVAC model and leave the custom inbound envelope and inbound routes empty.
    • Custom format: Select the HVAC model and supply both inbound-envelope.json and inbound-routes.json.
  • Default format:

    oci iot digital-twin-adapter create \
      --iot-domain-id <iot-domain-OCID> \
      --digital-twin-model-spec-uri 'dtmi:com:oracle:example:hvac;1'

    Custom format:

    oci iot digital-twin-adapter create \
      --iot-domain-id <iot-domain-OCID> \
      --digital-twin-model-spec-uri 'dtmi:com:oracle:example:hvac;1' \
      --inbound-envelope file://inbound-envelope.json \
      --inbound-routes file://inbound-routes.json

    Save the returned digital twin adapter OCID.

  • Run the CreateDigitalTwinAdapter operation.

    • Default format: Set the IoT domain and model specification URI and omit the inbound envelope and inbound routes.
    • Custom format: Set the same resource values and include both mappings from the shared JSON examples.

Step 4: Create a Digital Twin Instance with a Digital Twin Adapter

Create a directly connected instance with the default or custom adapter from the previous step. For testing, use a vault secret as the authentication ID for the digital twin instance. For production, use an mTLS certificate.

  • Use the Console procedure in Creating a Digital Twin Instance. Select Directly connected, select the adapter created in Step 3, and select or paste the authentication OCID.

  • Use the oci iot digital-twin-instance create command:

    oci iot digital-twin-instance create \
      --iot-domain-id <iot-domain-OCID> \
      --display-name <display-name> \
      --auth-id <secret-or-certificate-OCID> \
      --digital-twin-adapter-id <digital-twin-adapter-OCID>

    Save the returned instance OCID and external key.

  • Run the CreateDigitalTwinInstance operation. Set the IoT domain ID, selected adapter ID, and secret or certificate authentication ID.

Step 5: Send Telemetry Data

Authenticate with the digital twin instance external key as the device user name. When testing with a vault secret, use its plain-text contents as the device password.

Note

Use the payload that matches the adapter choice from Step 3. A default payload uses the model property names. A custom payload uses the device field names defined in the adapter mapping.
  • Default format:

    curl -i -X POST \
      -u "<digital-twin-instance-external-key>:<secret-contents>" \
      -H "Content-Type: application/json" \
      "https://<domain-short-id>.device.iot.<region>.oci.oraclecloud.com/telemetry" \
      -d '{
        "temperature": 72,
        "humidity": 60,
        "power": true,
        "batteryLevel": 95
      }'

    Custom format:

    curl -i -X POST \
      -u "<digital-twin-instance-external-key>:<secret-contents>" \
      -H "Content-Type: application/json" \
      "https://<domain-short-id>.device.iot.<region>.oci.oraclecloud.com/telemetry/heartbeat" \
      -d '{
        "time": "2025-09-05T18:36:55.213861Z",
        "data": {
          "temp": 70,
          "hum": 55
        }
      }'
  • Use MQTTX or another MQTT client to connect and publish structured telemetry:

    1. Install and open MQTTX. See MQTTX documentation.
    2. In MQTTX, select + New Connection.
    3. For Username, enter the digital twin instance external key. For Password, enter the device credential.
    4. For Host, select the mqtts:// protocol and enter <domain-short-id>.device.iot.<region>.oci.oraclecloud.com. For Port, enter 8883.
    5. Enable SSL/TLS.
    6. Select Connect.
    7. Publish with QoS 1 using the payload that matches the adapter format:

      Default format: Publish to /telemetry.

      {
        "temperature": 72,
        "humidity": 60,
        "power": true,
        "batteryLevel": 95
      }

      Custom format: Publish to /telemetry/heartbeat.

      {
        "time": "2025-09-05T18:36:55.213861Z",
        "data": {
          "temp": 70,
          "hum": 55
        }
      }

Step 6: View Telemetry Data

Confirm the platform received and normalized the telemetry. If the expected values are absent, review rejected data for a mapping or validation error.

  • The Internet of Things Data API is separate from the OCI REST API. Before you call the Data API, configure access to your data in ORDS and obtain the OAuth access token for the data host.

    Use the Internet of Things Data API to get raw, rejected, or historized data. This example lists raw telemetry from the IoT domain group data host and filters it by the digital twin instance OCID:

    curl \
      -H "Authorization: Bearer <token>" \
      -X GET \
      "https://<domain-group-short-id>.data.iot.<region>.oci.oraclecloud.com/ords/<domain-short-id>/20250531/rawData?q={\"$and\":[{\"digital_twin_instance_id\":\"<digital-twin-instance-OCID>\"}]}"
  • Use APEX or a database connection to query normalized and rejected data:

    select *
    from <domain-short-id>__IOT.HISTORIZED_DATA
    where digital_twin_instance_id = '<digital-twin-instance-OCID>';
    
    select *
    from <domain-short-id>__IOT.REJECTED_DATA
    where digital_twin_instance_id = '<digital-twin-instance-OCID>';

    To view your IoT data in APEX, you must set up APEX IoT data access and to view your IoT data in the database, you must set up your direct connection to the database.