Scenario: Create Digital Twins for Indirectly Connected Devices Using a Gateway

Create digital twin models, adapters, a gateway, direct, and indirect device digital twin instances so that you can route telemetry and commands through a shared gateway.

Use this scenario to create one reusable gateway, one directly connected device, and several indirectly connected devices.

To illustrate this scenario, the examples model a small building heating, ventilation, and air conditioning system (HVAC) including:
  • A gateway named Bosch Gateway 1 authenticates to the IoT domain and collects or routes data for devices such as HVAC units, boilers, and dehumidifiers.
  • The directly connected HVAC instance shows a device that sends its own telemetry.
  • The indirectly connected HVAC, boiler, and dehumidifier instances show devices whose telemetry and commands move through the gateway instead.

For more concepts, see Indirectly Connected Devices FAQ.

Review the diagram to understand how the gateway, directly connected devices, and indirectly connected devices interact with the 3 types of digital twin instances.

digital-twin-instances

Step 1: Create Digital Twin Models

Create a digital twin model for the gateway and one digital twin model for each device type in the scenario. These devices emit structured data.

Use the snippets below to save these files: gateway-model.json, hvac-model.json, and boiler-model.json.

  • Use the Console when you want to upload a saved model file or paste the specification directly.

    1. On the IoT domains list page, open the domain you want to work with. If you need help finding the IoT domains list page, see Listing IoT Domains or Create a new IoT Domain.
    2. Select the Digital twin models tab, and then select Create.
    3. Enter a name and an optional description. Avoid entering confidential information.
    4. Choose Upload the specification or Paste specification, and then use one of the example files from this scenario.
    5. Optional: Add tags.
    6. Select Create.

    Use one of the following files when you upload or paste the specification.

    gateway-model.json
    {
      "@context": [
        "dtmi:dtdl:context;3"
      ],
      "@id": "dtmi:com:oracle:example:gateway;1",
      "@type": "Interface",
      "displayName": "Gateway",
      "description": "A digital twin model for Gateway",
      "contents": [
        {
          "@type": "Telemetry",
          "name": "cpuUtilization",
          "schema": "integer"
        },
        {
          "@type": "Telemetry",
          "name": "memoryUtilization",
          "schema": "integer"
        },
        {
          "@type": "Telemetry",
          "name": "storageUtilization",
          "schema": "integer"
        },
        {
          "@type": "Property",
          "name": "firmwareVersion",
          "schema": "string"
        }
      ]
    }
    hvac-model.json
    {
      "@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"
        }
      ]
    }
    boiler-model.json
    {
      "@context": [
        "dtmi:dtdl:context;3"
      ],
      "@id": "dtmi:com:oracle:example:boiler;1",
      "@type": "Interface",
      "displayName": "Boiler",
      "description": "A digital twin model for Boiler",
      "contents": [
        {
          "@type": "Telemetry",
          "name": "temperature",
          "schema": "integer"
        },
        {
          "@type": "Telemetry",
          "name": "pressure",
          "schema": "integer"
        }
      ]
    }
  • Use the oci iot digital-twin-model create command to create each digital twin model in the IoT domain.

    Create the gateway model

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

    Create the HVAC model

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

    Create the boiler model

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

    For this example, save these snippets to these model files, gateway-model.json, hvac-model.json, and boiler-model.json.

    gateway-model.json
    {
      "@context": [
        "dtmi:dtdl:context;3"
      ],
      "@id": "dtmi:com:oracle:example:gateway;1",
      "@type": "Interface",
      "displayName": "Gateway",
      "description": "A digital twin model for Gateway",
      "contents": [
        {
          "@type": "Telemetry",
          "name": "cpuUtilization",
          "schema": "integer"
        },
        {
          "@type": "Telemetry",
          "name": "memoryUtilization",
          "schema": "integer"
        },
        {
          "@type": "Telemetry",
          "name": "storageUtilization",
          "schema": "integer"
        },
        {
          "@type": "Property",
          "name": "firmwareVersion",
          "schema": "string"
        }
      ]
    }
    hvac-model.json
    {
      "@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"
        }
      ]
    }
    boiler-model.json
    {
      "@context": [
        "dtmi:dtdl:context;3"
      ],
      "@id": "dtmi:com:oracle:example:boiler;1",
      "@type": "Interface",
      "displayName": "Boiler",
      "description": "A digital twin model for Boiler",
      "contents": [
        {
          "@type": "Telemetry",
          "name": "temperature",
          "schema": "integer"
        },
        {
          "@type": "Telemetry",
          "name": "pressure",
          "schema": "integer"
        }
      ]
    }
  • Run the CreateDigitalTwinModel operation to create a digital twin model in the IoT domain.

    Use the same DTDL payload shown in the example digital twin model files when you call the API.

Step 2: Create Digital Twin Adapters

Create one adapter for the gateway and one adapter for each structured device type that needs payload mapping.

Use the snippets below to save these files: gateway-envelope.json, gateway-routes.json, hvac-envelope.json, hvac-routes.json, boiler-envelope.json, and boiler-routes.json.

Resolve the target in the inbound envelope

Gateway adapters can identify the target digital twin instance when all forwarded device messages follow the same topic or payload convention. The gateway authenticates as itself, the service checks that the resolved target is an indirectly connected digital twin instance associated with that gateway, and then the service delegates the payload to the target device's adapter when structured mapping is required.

The gateway resolves target and contentRoot once for the whole message before routes run. If target resolves to an indirectly connected device external key, the payload is delegated to that device. If target is empty or null, the message is treated as gateway data.

  • Use a digital twin adapter when the incoming payload must be mapped to the target digital twin model. A digital twin adapter is not required when the device sends unstructured data, you can pass data through unchanged.

    1. On the IoT domains list page, open the domain you want to work with.
    2. Select the Digital twin adapters tab, and then select Create.
    3. Enter a Name and an optional Description. Avoid entering confidential information.
    4. Select the digital twin model that matches the payload you are mapping.
    5. Turn on Specify inbound envelope and routes, and then upload or paste the envelope JSON and routes JSON for the adapter.
    6. Optional: Add tags.
    7. Select Create.

    Create the gateway adapter with these gateway files.

    gateway-envelope.json
    {
      "referenceEndpoint": "/data",
      "referencePayload": {
        "dataFormat": "JSON",
        "data": {
          "time": 1773768299143534,
          "cpuUtil": 0,
          "memUtil": 0,
          "diskUtil": 0,
          "firmware": "Oracle Linux 9.1"
        }
      },
      "envelopeMapping": {
        "timeObserved": "$.time",
        "target": "${if ([\"boilers\", \"hvacs\", \"dehumidifiers\"] | contains([endpoint(1)])) then endpoint(2) else null end}",
        "contentRoot": "$"
      }
    }
    gateway-routes.json
    [
      {
        "condition": "*",
        "payloadMapping": {
          "$.cpuUtilization": "$.cpuUtil",
          "$.memoryUtilization": "$.memUtil",
          "$.storageUtilization": "$.diskUtil",
          "$.firmwareVersion": "$.firmware"
        }
      }
    ]

    Create the HVAC and boiler adapters with the matching device files.

    hvac-envelope.json
    {
      "referenceEndpoint": "/telemetry",
      "referencePayload": {
        "dataFormat": "JSON",
        "data": {
          "time": 1773768299143534,
          "temperature": 0,
          "humidity": 0
        }
      },
      "envelopeMapping": {
        "timeObserved": "$.time"
      }
    }
    hvac-routes.json
    [
      {
        "condition": "*",
        "payloadMapping": {
          "$.temperature": "$.temperature",
          "$.humidity": "$.humidity"
        }
      }
    ]
    boiler-envelope.json
    {
      "referenceEndpoint": "/telemetry",
      "referencePayload": {
        "dataFormat": "JSON",
        "data": {
          "timestamp": 1773768299143534,
          "temperature": 0,
          "pressure": 0
        }
      },
      "envelopeMapping": {
        "timeObserved": "$.timestamp"
      }
    }
    boiler-routes.json
    [
      {
        "condition": "*",
        "payloadMapping": {
          "$.temperature": "$.temperature",
          "$.pressure": "$.pressure"
        }
      }
    ]
  • Use the oci iot digital-twin-adapter create command to create each adapter in the IoT domain.

    Create the gateway adapter

    Use this command to create a digital twin adapter. This example shows how to associate the digital twin model using the DTMI and how to define the envelope and routes using .json files.

    oci iot digital-twin-adapter create \
      --iot-domain-id <iot-domain-OCID> \
      --digital-twin-model-spec-uri "dtmi:com:oracle:example:gateway;1" \
      --inbound-envelope file://gateway-envelope.json \
      --inbound-routes file://gateway-routes.json
    gateway-envelope.json
    {
      "referenceEndpoint": "/data",
      "referencePayload": {
        "dataFormat": "JSON",
        "data": {
          "time": 1773768299143534,
          "cpuUtil": 0,
          "memUtil": 0,
          "diskUtil": 0,
          "firmware": "Oracle Linux 9.1"
        }
      },
      "envelopeMapping": {
        "timeObserved": "$.time",
        "target": "${if ([\"boilers\", \"hvacs\", \"dehumidifiers\"] | contains([endpoint(1)])) then endpoint(2) else null end}",
        "contentRoot": "$"
      }
    }
    gateway-routes.json
    [
      {
        "condition": "*",
        "payloadMapping": {
          "$.cpuUtilization": "$.cpuUtil",
          "$.memoryUtilization": "$.memUtil",
          "$.storageUtilization": "$.diskUtil",
          "$.firmwareVersion": "$.firmware"
        }
      }
    ]

    Create the HVAC adapter

    Use this command to create a digital twin adapter. This example shows how to associate the digital twin model using the DTMI and how to define the envelope and routes using .json files.

    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://hvac-envelope.json \
      --inbound-routes file://hvac-routes.json
    hvac-envelope.json
    {
      "referenceEndpoint": "/telemetry",
      "referencePayload": {
        "dataFormat": "JSON",
        "data": {
          "time": 1773768299143534,
          "temperature": 0,
          "humidity": 0
        }
      },
      "envelopeMapping": {
        "timeObserved": "$.time"
      }
    }
    hvac-routes.json
    [
      {
        "condition": "*",
        "payloadMapping": {
          "$.temperature": "$.temperature",
          "$.humidity": "$.humidity"
        }
      }
    ]

    Create the boiler adapter

    Use this command to create a digital twin adapter. This example shows how to associate the digital twin model using the DTMI and how to define the envelope and routes using .json files.

    oci iot digital-twin-adapter create \
      --iot-domain-id <iot-domain-OCID> \
      --digital-twin-model-spec-uri "dtmi:com:oracle:example:boiler;1" \
      --inbound-envelope file://boiler-envelope.json \
      --inbound-routes file://boiler-routes.json
    boiler-envelope.json
    {
      "referenceEndpoint": "/telemetry",
      "referencePayload": {
        "dataFormat": "JSON",
        "data": {
          "timestamp": 1773768299143534,
          "temperature": 0,
          "pressure": 0
        }
      },
      "envelopeMapping": {
        "timeObserved": "$.timestamp"
      }
    }
    boiler-routes.json
    [
      {
        "condition": "*",
        "payloadMapping": {
          "$.temperature": "$.temperature",
          "$.pressure": "$.pressure"
        }
      }
    ]

    For more information about referencing files from the CLI, see Using a JSON File for Complex Input.

  • Run the CreateDigitalTwinAdapter operation to create a digital twin adapter in the IoT domain.

    Use the same inbound envelope and inbound routes payloads shown in the example files.

Step 3: Create the Gateway and Device Instances

Create the gateway first so you can reuse it when you create the indirectly connected devices.

Authentication ID options:

  • For testing digital twin instances, if you use secret OCID as the authentication ID, then use the device username as the external key and the plain-text secret contents as the device password. For instructions, when you Create a Secret see Step 7 and for secrets that are already created, see Getting a Secret's Contents.
  • For production digital twin instances use mTLS certificate OCID instead of a vault secret as the authentication ID. When you use a mTLS certificate, then you need to use the common name from the certificate details as the external key: --external-key <common-name-from-certificate-details>

  • Use the Console to create a gateway and associate multiple devices to it.

    In the Console, there are two ways to create a gateway for the IoT domain. You can create a gateway from the Digital twin instance tab, by selecting the gateway type when you create a digital twin instance. Or you can select the Gateway tab to create the gateway.

    1. On the IoT domains list page, open the IoT domain you want to work with.
    2. Select the Gateways tab and then select Create to create a Bosch Gateway Instance so that it's available for indirect devices.
      • Enter a Name for the gateway and a Description.
      • Enter the external key gateway1.
      • Select the adapter that uses the gateway-envelope.json and gateway-routes.json created in Step 2.
      • Select the authentication ID or paste the authentication ID's OCID and then select Create.
    3. On the IoT domain page, select the Digital twin instances tab, select Create and then select directly connected or indirectly connected as the type to create the digital twin instances for these devices, using the model and adapters created in Step 1 and Step 2 for:
      • directly connected LG HVAC instance with the external key hvac1.
      • indirectly connected HVAC instance with the external key hvac2
      • indirectly connected boiler instance with the external key boiler1.
      • indirectly connected dehumidifier instance with the external key dehumidifier1. The dehumidifier does not use a digital twin adapter or digital twin model since it sends unstructured raw telemetry that does not need structured payload mapping. It can also receive raw commands through the gateway.
      Note

      If you do not enter an external key, it will be generated.
    4. For the directly connected and the gateway instance, select an authentication ID.
    5. For an indirectly connected instance, select one or more existing gateways instead of adding device authentication credentials. Select more than one gateway when a wireless device might roam between gateways.
    6. Optional: Add tags.
    7. Review the digital twin instance summary, and then select Create.
    After you create the digital twin instances use the Gateway details page to view which devices depend on and are associated to the gateway.
    • Find the Gateway details on the IoT domain page, by selecting the Gateway tab to view the gateway list, select a gateway to go to the Gateway details page.
    • On the Digital twin instance tab, you can search by type to view the different digital twin instance types for an IoT domain.
    • View the directly and indirectly connected devices from the Digital twin instance details page.

    For a complete list of settings, see Creating a Digital Twin Instance, Listing Digital Twin Instances, and Getting a Digital Twin's Instance Details.

  • Use the oci iot digital-twin-instance create command to create the gateway and the directly connected and indirectly connected device instances.

    Create the directly connected HVAC instance

    Use this command to create a digital twin instance that's associated to a specific IoT domain and that's directly connected, using an external key hvac1.

    oci iot digital-twin-instance create \
      --iot-domain-id <iot-domain-OCID> \
      --connectivity-type DIRECT \
      --display-name "LG HVAC 1" \
      --auth-id <certificate-or-secret-OCID> \
      --digital-twin-adapter-id <hvac-adapter-OCID> \
      --external-key hvac1

    Create the gateway instance

    Use this command to create a digital twin instance that's associated to a specific IoT domain, use the required --connectivity-type parameter with the GATEWAY value to create a digital twin instance that's a gateway. The external key is gateway1.

    oci iot digital-twin-instance create \
      --iot-domain-id <iot-domain-OCID> \
      --connectivity-type GATEWAY \
      --display-name "Bosch Gateway 1" \
      --auth-id <certificate-or-secret-OCID> \
      --digital-twin-adapter-id <gateway-adapter-OCID> \
      --external-key gateway1

    Create the indirectly connected HVAC instance

    Use this command and the required parameter --connectivity-type INDIRECT to create a digital twin that's an indirectly connected device. This scenario shows one gateway association per device. When your deployment supports roaming, associate the indirectly connected device with more than one gateway so it can move between them without changing the digital twin instance.
    Note

    When you create an indirectly connected device using the required --gateways parameter, it's a complex type and must use a JSON array. For example: --gateways '["<gateway-instance-OCID>"]' or for roaming use --gateways '["<gateway-instance-1-OCID>","<gateway-instance-2-OCID>"]'. Alternatively, you can use a .json file.
    oci iot digital-twin-instance create \
      --iot-domain-id <iot-domain-OCID> \
      --connectivity-type INDIRECT \
      --display-name "LG HVAC 2" \
      --gateways '["<gateway-instance-OCID>"]' \
      --digital-twin-adapter-id <hvac-adapter-OCID> \
      --external-key hvac2

    Create the indirectly connected boiler instance

    Use this command and the required parameter --connectivity-type INDIRECT to create a digital twin that's an indirectly connected device.

    oci iot digital-twin-instance create \
      --iot-domain-id <iot-domain-OCID> \
      --connectivity-type INDIRECT \
      --display-name "GE Boiler 1" \
      --gateways '["<gateway-instance-OCID>"]' \
      --digital-twin-adapter-id <boiler-adapter-OCID> \
      --external-key boiler1

    Create the indirectly connected dehumidifier instance

    Use this command and the required parameter --connectivity-type INDIRECT to create a digital twin that's an indirectly connected device.

    oci iot digital-twin-instance create \
      --iot-domain-id <iot-domain-OCID> \
      --connectivity-type INDIRECT \
      --display-name Danby Dehumidifier 1 \
      --gateways '["<gateway-instance-OCID>"]' \
      --external-key dehumidifier1
  • Run the CreateDigitalTwinInstance operation to create a gateway instance, a directly connected instance, and an indirectly connected instance.

    When you create an indirectly connected instance, make sure that the request includes the gateway association.

Step 4: Send Telemetry Data

These examples show three data paths. A directly connected device, such as hvac1, authenticates with its own external key and sends data to its own digital twin instance. The gateway, gateway1, also authenticates with its own external key when it sends its own CPU, memory, storage, and firmware telemetry.

Indirectly connected devices do not authenticate to the IoT domain; the gateway authenticates as gateway1 and the endpoint path, such as hvacs/hvac2, boilers/boiler1, or dehumidifiers/dehumidifier1, identifies the target device behind the gateway.

    1. Install and configure MQTTX.

    2. Use the <digital-twin-instance-external-key> as the device username. If you need to find the digital instance external key, you can Getting a Digital Twin's Instance Details.
    3. Connect to <domain-short-id>.device.iot.<region>.oci.oraclecloud.com on port 8883, enable SSL/TLS, and use a clean session.

    Send telemetry from the directly connected HVAC instance:

    mqttx pub \
      -h '<domain-short-id>.device.iot.<region>.oci.oraclecloud.com' \
      -p 8883 \
      -l mqtts \
      -t 'data' \
      -m '{
        "temperature": 70,
        "humidity": 60
      }' \
      -u hvac1 \
      -P '<secret-contents>'

    Send telemetry from the gateway instance:

    mqttx pub \
      -h '<domain-short-id>.device.iot.<region>.oci.oraclecloud.com' \
      -p 8883 \
      -l mqtts \
      -t 'data' \
      -m '{
        "cpuUtil": 30,
        "memUtil": 25,
        "diskUtil": 20,
        "firmware": "Oracle Linux 9.1"
      }' \
      -u gateway1 \
      -P '<secret-contents>'

    Send telemetry for the indirectly connected HVAC instance through the gateway:

    mqttx pub \
      -h '<domain-short-id>.device.iot.<region>.oci.oraclecloud.com' \
      -p 8883 \
      -l mqtts \
      -t 'hvacs/hvac2' \
      -m '{
        "temperature": 75,
        "humidity": 65
      }' \
      -u gateway1 \
      -P '<secret-contents>'

    Send telemetry for the indirectly connected boiler instance through the gateway:

    mqttx pub \
      -h '<domain-short-id>.device.iot.<region>.oci.oraclecloud.com' \
      -p 8883 \
      -l mqtts \
      -t 'boilers/boiler1' \
      -m '{
        "temperature": 80,
        "pressure": 90
      }' \
      -u gateway1 \
      -P '<secret-contents>'

    Send telemetry for the indirectly connected dehumidifier instance through the gateway:

    mqttx pub \
      -h '<domain-short-id>.device.iot.<region>.oci.oraclecloud.com' \
      -p 8883 \
      -l mqtts \
      -t 'dehumidifiers/dehumidifier1' \
      -m 'humidity=50,fanSpeed=medium' \
      -u gateway1 \
      -P '<secret-contents>'
  • Use curl to send telemetry over HTTPS. These examples use HTTP basic authentication with the digital twin instance external key and the plain-text secret contents.

    1. Use the digital twin instance external key as the username. For example for the directly connected telemetry, use the instance external key, such as hvac1.
    2. Use the plain-text secret contents as the password.
    3. Send the request to https://<domain-short-id>.device.iot.<region>.oci.oraclecloud.com/<endpoint-path>.
    4. For indirectly connected devices, authenticate as the gateway instance and use the endpoint path to identify the target device, such as hvacs/hvac2 or boilers/boiler1.

    Send telemetry from the directly connected HVAC instance:

    curl -i -u "hvac1:<secret-contents>" \
      -H "Content-Type: application/json" \
      -X POST \
      "https://<domain-short-id>.device.iot.<region>.oci.oraclecloud.com/data" \
      -d '{
        "temperature": 70,
        "humidity": 60
      }'

    Send telemetry from the gateway instance:

    curl -i -u "gateway1:<secret-contents>" \
      -H "Content-Type: application/json" \
      -X POST \
      "https://<domain-short-id>.device.iot.<region>.oci.oraclecloud.com/data" \
      -d '{
      "cpuUtil": 30,
      "memUtil": 25,
      "diskUtil": 20,
      "firmware": "Oracle Linux 9.1"
    }'

    Send telemetry for the indirectly connected HVAC instance through the gateway:

    curl -i -u "gateway1:<secret-contents>" \
      -H "Content-Type: application/json" \
      -X POST \
      "https://<domain-short-id>.device.iot.<region>.oci.oraclecloud.com/hvacs/hvac2" \
      -d '{
        "temperature": 75,
        "humidity": 65
      }'

    Send telemetry for the indirectly connected boiler instance through the gateway:

    curl -i -u "gateway1:<secret-contents>" \
      -H "Content-Type: application/json" \
      -X POST \
      "https://<domain-short-id>.device.iot.<region>.oci.oraclecloud.com/boilers/boiler1" \
      -d '{
        "temperature": 80,
        "pressure": 90
      }'

    Send telemetry for the indirectly connected dehumidifier instance through the gateway:

    curl -i -u "gateway1:<secret-contents>" \
      -H "Content-Type: text/plain" \
      -X POST \
      "https://<domain-short-id>.device.iot.<region>.oci.oraclecloud.com/dehumidifiers/dehumidifier1" \
      -d 'humidity=50,fanSpeed=medium'

Optional Step 5: Get a Digital Twin's Instance Content

To confirm the digital twin instance's data is captured use the Console, the CLI, or the API to view the digital twin's instance content, that's the latest snapshot data.

For an unstructured data digital twin instance, it typically does not have an associated digital twin model so you can not use the get contents for a digital twin instance to view it's data.

    1. On the Domains list page, select the domain that you want to work with. If you need help finding the IoT domains list page or an IoT domain, see Listing IoT Domains.
    2. Select the Digital twin instances tab.
    3. Select the digital twin instance name, the details page opens.
    4. Select the Data tab, to view the latest snapshot data for this instance. For more information, see IoT Domain Database Schema Reference.
  • Use the oci iot digital-twin-instance get-content command and the required parameter to get a digital twin's instance content:

    Verify the directly connected HVAC instance:

    oci iot digital-twin-instance get-content \
      --digital-twin-instance-id <lg-hvac-1-OCID>

    Verify the gateway instance:

    oci iot digital-twin-instance get-content \
      --digital-twin-instance-id <bosch-gateway-1-OCID>

    Verify the indirectly connected HVAC instance:

    oci iot digital-twin-instance get-content \
      --digital-twin-instance-id <lg-hvac-2-OCID>

    Verify the indirectly connected boiler instance:

    oci iot digital-twin-instance get-content \
      --digital-twin-instance-id <ge-boiler-1-OCID>
  • Run the GetDigitalTwinInstanceContent operation to get content from a digital twin instance.

Step 6: Invoke Commands

For indirectly connected devices, use an at-least-once delivery policy and expect retries or duplicate delivery.

When a response is required, configure a response endpoint so the gateway can publish the reply on behalf of the target device.

  • Use the Console when you want to send a raw command from the digital twin instance details page.

    1. Open the IoT domain and select the Digital twin instances tab.
    2. Select the instance that you want to control.
    3. From the Actions menu, select Send raw command.
    4. Enter the request endpoint, the request duration, and the command payload.
    5. Optional: Configure a response endpoint and response duration, and then select Send raw command. Use the values from these files: gateway-command.json, boiler-command.json
    gateway-command.json
    {
      "force": true
    }
    boiler-command.json
    {
      "hardReset": false
    }
  • Use the oci iot digital-twin-instance invoke-raw-json-command command to invoke the example actions in this scenario. For more information, see Sending a Raw Command from a Digital Twin Instance.

    Reboot the gateway instance

    oci iot digital-twin-instance invoke-raw-json-command \
      --digital-twin-instance-id <bosch-gateway-1-OCID> \
      --request-endpoint "actions/reboot" \
      --request-data '{"force": true}'

    Reset the indirectly connected boiler instance

    Specify the --request-endpoint, in this example that's boilers/boiler1/actions/reset.

    Specify the --response-endpoint, in this example that's boilers/boiler1/actions/response.

    If you expect a response, then you must include the --response-duration parameter. If not, the command transitions to a COMPLETED state without receiving the response. As a result, the command's response is not captured in the RAW_COMMAND_DATA database table.

    oci iot digital-twin-instance invoke-raw-json-command \
      --digital-twin-instance-id <ge-boiler-1-OCID> \
      --request-endpoint "boilers/boiler1/actions/reset" \
      --request-data '{"hardReset": false}' \
      --response-endpoint "boilers/boiler1/actions/response" \
      --response-duration PT10M

    Publish the boiler response

    Use the response endpoint to publish the boiler response to this endpoint: boilers/boiler1/actions/response in this example gateway1 is the external key and the device username.

    mqttx pub \
      -h '<domain-short-id>.device.iot.<region>.oci.oraclecloud.com' \
      -p 8883 \
      -l mqtts \
      -t "boilers/boiler1/actions/response" \
      -m '{
        "status": "OK"
      }' \
      -u gateway1 \
      -P '<secret-contents>'

    Turn off the indirectly connected dehumidifier fan

    Use this command to invoke a raw JSON command on the device that shuts off the fan on the dehumidifier.

    oci iot digital-twin-instance invoke-raw-json-command \
      --digital-twin-instance-id <danby-dehumidifier-1-OCID> \
      --request-endpoint "dehumidifiers/dehumidifier1/actions/fanOff" \
      --request-data '{"fanOff": true}'
  • Run the InvokeRawCommand operation to invoke a raw command on a device.

    Use a response endpoint when the command should return an application-level acknowledgement.

Choose the Right Connectivity Type for your Digital Twin Instance

How to decide which connectivity type to use for a digital twin instance:

Directly Connected

Use when
The device connects to the IoT domain on its own.
Required parameter
Authentication ID, for testing use a secret and for production use mTLS certificate.
Required for structured data
A digital twin adapter matching the digital twin model can be provided or inferred from the adapter, and the model must contain at least one telemetry, property, or command.
For unstructured data
Do not use a digital twin adapter or digital twin model.
Optional input
External key, if you do not provide an external, the service generates one. Use for the device username.
Do not use
Gateway association with a directly connected device.

Gateway

Use when
A device forwards data and commands for itself and for other devices.
Required parameters
Authentication ID and a gateway adapter. Gateway instances do not support unstructured telemetry, so the adapter is required.
Digital Twin Model
The gateway's digital twin model can be associated or inferred from the digital twin adapter, and the model must contain at least one telemetry, property, or command. You can create a digital twin instance with an adapter without specifying the model, a model is associated through the adapter.
For unstructured data, you typically do not use either an adapter or a model.
Optional input
External key, if you do not provide one, the service generates one.
Recommended
Before you change or delete it, review the gateway details page to see how many devices depend on the gateway.
Do not
Associate a gateway instance to another gateway instance.

Indirectly Connected

Use when
Another device, typically a gateway, sends telemetry data and receives commands on the device's behalf.
Required
One or more gateway associations is required. Use more than one gateway when a wireless device might roam between gateways. Confirm with an administrator if this works in your environment.
For structured data
For structured data a digital twin adapter is required. The corresponding digital twin model can be provided or inferred from the adapter, by specifying the DTMI or the digital twin model OCID, the model must contain at least one telemetry, property, or command.
For unstructured data
For unstructured data do not use a digital twin adapter or digital twin model. You can not get contents for unstructured data since it does not contain a digital twin model.
Optional input
External key, if you do not provide an external key, the service generates one.
Authentication ID
Do not use an authentication ID for an indirectly connected digital twin instance.

Not Connected

Use when
Testing a digital twin instance that does not authenticate, send, or receive device data so that you can test digital twin relationships to test your IoT configuration without the complexity of data or authentication.
Required
For structured data, a digital twin model is required and must not contain telemetry, properties, or commands.
Optional
Digital twin relationships for testing. For more information, see Digital Twin Relationships.
Do not use
Authentication ID, external key, digital twin adapter, gateway association, or device data on digital twin instances that are not connected.

FAQ

This FAQ describes the gateway user stories described in this scenario.

Can I configure a digital twin instance to be indirectly connected so that another device sends data on its behalf?
Yes. Create the digital twin instance for the device with INDIRECT connectivity type and associate it with one or more gateway digital twin instances instead of giving the device its own authentication ID.
How can I tell which devices are connected through a specific gateway and understand the impact of losing that gateway?
Use the gateway details page to review the associated device count and the list of dependent digital twin instances. This gives you the fastest view of the gateway's blast radius before maintenance, migration, or delete operations.
How do I know how many of my digital twin instances are directly connected, indirectly connected, or gateways?
On the Digital twin instance's list page or the Gateway list page you can find the list of gateways.
Review the digital twin instance connectivity type for each digital twin and use a domain-level gateway and topology views to gain insights on your environment. This scenario shows one direct device, one gateway, and several indirect devices so you can compare the three patterns.
How can I configure an adapter for my gateway so that incoming telemetry is routed to the correct target device?
Use a digital twin adapter for the gateway that includes a target mapping in the envelope. See Step 2: Create Digital Twin Adapters.
When should I put target in the inbound routes instead of in inbound envelope?
No. Put target in the inbound envelope.
What happens if target is empty or null?
The message is treated as gateway data instead of being delegated to an indirectly connected device.
Can one gateway adapter handle both gateway telemetry and indirectly connected device telemetry?
Yes. That is the main purpose of gateway routing. Forwarded device messages go to a resolved target, while unmatched or empty-target data stays with the gateway.
Can I reuse one gateway across directly connected and indirectly connected devices without duplicating my definitions?
Yes. Treat the gateway as a reusable asset, create the matching adapters once, and then associate the gateway to each indirectly connected device that should use that adapter route condition.
Can I send unstructured telemetry from indirectly connected devices?
Yes. If you do not need structured payload mapping for the target device, you can send the raw payload through the gateway and skip the device-specific adapter.
Can I invoke unstructured commands on indirectly connected devices and on the gateway itself?
Yes. Use raw command endpoints for both patterns. Add a response endpoint to the unstructured command when you need the gateway to publish an application-level acknowledgement for the target device.
--request-endpoint, for example boilers/boiler1/actions/reset
--response-endpoint, for example boilers/boiler1/actions/response
Can I associate a device with multiple gateways so that it can roam?
Yes. An indirectly connected device can be associated with one or more gateways. This lets a wireless device keep the same digital twin instance while it moves between available gateways.
In the CLI, you can configure multi-gateway roaming by using an array for the digital twin model's gateway association.
Use the --gateways option with a JSON array of gateway digital twin instance OCIDs:
oci iot digital-twin-instance create \
  --iot-domain-id <iot-domain-OCID> \
  --connectivity-type INDIRECT \
  --display-name "Roaming Wireless Device 1" \
  --gateways '["<gateway-instance-1-OCID>","<gateway-instance-2-OCID>"]' \
  --external-key roaming-device1
If you prefer to avoid shell quoting issues, put the gateway list in a roaming-device.json input file and use --from-json:
oci iot digital-twin-instance create \
  --from-json file://roaming-device.json
roaming-device.json
{
  "iotDomainId": "<iot-domain-OCID>",
  "connectivityType": "INDIRECT",
  "displayName": "Roaming Wireless Device 1",
  "gateways": [
    "<gateway-instance-1-OCID>",
    "<gateway-instance-2-OCID>"
  ],
  "externalKey": "roaming-device1"
}
Before you depend on multi-gateway roaming in production, confirm the behavior in your testing environment and validate the handoff pattern that your gateways use.

Troubleshooting

Command stays in ACCEPTED state

In Step 6: Invoke Commands, if you do not include the --request-data parameter the command stays in the ACCEPTED state and results in an error: Invalid Command Invocation id [unique-id]: [Previous command invocation is not finished yet]