diff --git a/.gitignore b/.gitignore index 0d111c13d..7749768d0 100644 --- a/.gitignore +++ b/.gitignore @@ -9,5 +9,6 @@ env.sh labs/.DS_Store oci-iot-platform-getting-started/VALIDATION-RESULT.md +oci-iot-gateway-config/VALIDATION-RESULT.md .vscode apps-ai-experience-agentic-ai/README.md diff --git a/oci-iot-gateway-config/appendix-water-pump-assets/appendix-water-pump-assets.md b/oci-iot-gateway-config/appendix-water-pump-assets/appendix-water-pump-assets.md new file mode 100644 index 000000000..7eacf299c --- /dev/null +++ b/oci-iot-gateway-config/appendix-water-pump-assets/appendix-water-pump-assets.md @@ -0,0 +1,287 @@ +# Appendix A: Create Required Factory, Production Line, and WaterPump Assets + +## Introduction + +Use this appendix only when the IoT domain you are using does not contain the models and adapters created in [Get Started with OCI Internet of Things Platform](https://livelabs.oracle.com/ords/r/dbpm/livelabs/view-workshop?wid=4515). If those assets are available, retain their OCIDs and reuse them rather than creating duplicate models or adapters. + +Continue with this appendix only if your OCI IoT domain lacks those models and adapters. The tasks create the Factory, ProductionLine, ElectricMotor, and WaterPump models in that order, followed by the default and Flat PSI WaterPump adapters. Factory and ProductionLine keep the WaterPump model consistent with Getting Started. ProductionLine supplies the target for the WaterPump `installedOn` relationship, while WaterPump uses ElectricMotor as a component. Installing the complete model set also supports future labs. + +Estimated Time: 55 minutes + +### Objectives + +In this appendix, you will: + +- Create the Factory, ProductionLine, ElectricMotor, and WaterPump models. +- Create the default WaterPump adapter. +- Create the Flat PSI WaterPump adapter. +- Record and verify the resulting OCIDs. + +### Prerequisites + +- An active IoT domain and OCI CLI profile. +- A writable working directory. +- Permission to create digital twin models and adapters in the IoT domain. + +## Task 1: Create the Factory, ProductionLine, ElectricMotor, and WaterPump models + +1. Set your working directory and IoT domain OCID. The following commands save the model specifications as JSON files in the working directory. + + ```bash + export WORKSHOP_DIR="$PWD" + export IOT_DOMAIN_OCID='' + ``` + +2. Save the Factory model specification as `$WORKSHOP_DIR/factory-model.json`. This model defines the `contains` relationship and has no telemetry, property, or command content. + + ```bash + cat > "$WORKSHOP_DIR/factory-model.json" <<'EOF' + { + "@context": "dtmi:dtdl:context;3", + "@id": "dtmi:com:oracle:beverage:Factory;1", + "@type": "Interface", + "displayName": "Factory", + "description": "A beverage production factory.", + "contents": [ + { + "@type": "Relationship", + "name": "contains" + } + ] + } + EOF + ``` + +3. Save the ProductionLine model specification as `$WORKSHOP_DIR/production-line-model.json`. This model has no DTDL content and provides the target type for the WaterPump `installedOn` relationship. + + ```bash + cat > "$WORKSHOP_DIR/production-line-model.json" <<'EOF' + { + "@context": "dtmi:dtdl:context;3", + "@id": "dtmi:com:oracle:beverage:ProductionLine;1", + "@type": "Interface", + "displayName": "Production Line", + "description": "A beverage factory production line." + } + EOF + ``` + +4. Save the ElectricMotor model specification as `$WORKSHOP_DIR/electric-motor-model.json`. + + ```bash + cat > "$WORKSHOP_DIR/electric-motor-model.json" <<'EOF' + { + "@context": [ + "dtmi:dtdl:context;3", + "dtmi:dtdl:extension:historization;1" + ], + "@id": "dtmi:com:oracle:iot:example:ElectricMotor;1", + "@type": "Interface", + "displayName": "Electric Motor", + "contents": [ + { + "@type": ["Telemetry", "Historized"], + "name": "motorTemperature", + "schema": "double" + }, + { + "@type": ["Telemetry", "Historized"], + "name": "vibrationLevel", + "schema": "double" + }, + { + "@type": "Telemetry", + "name": "powerConsumption", + "schema": "double" + } + ] + } + EOF + ``` + +5. Save the WaterPump model specification as `$WORKSHOP_DIR/water-pump-model.json`. Its `motor` component references ElectricMotor and its `installedOn` relationship targets ProductionLine. This definition is identical to the WaterPump model in the Getting Started workshop. + + ```bash + cat > "$WORKSHOP_DIR/water-pump-model.json" <<'EOF' + { + "@context": [ + "dtmi:dtdl:context;3", + "dtmi:dtdl:extension:quantitativeTypes;1", + "dtmi:com:oracle:dtdl:extension:validation;1" + ], + "@id": "dtmi:com:oracle:iot:example:WaterPump;1", + "@type": "Interface", + "displayName": "Water Pump", + "contents": [ + { + "@type": "Component", + "name": "motor", + "schema": "dtmi:com:oracle:iot:example:ElectricMotor;1" + }, + { + "@type": ["Telemetry", "Validated"], + "name": "flowRate", + "schema": "double", + "minimum": 0, + "maximum": 1000 + }, + { + "@type": ["Telemetry", "Pressure"], + "name": "dischargePressure", + "schema": "double", + "unit": "bar" + }, + { + "@type": "Relationship", + "name": "installedOn", + "target": "dtmi:com:oracle:beverage:ProductionLine;1" + } + ] + } + EOF + ``` + +6. Create the models in the same order as their files: Factory, ProductionLine, ElectricMotor, then WaterPump. This order creates the referenced ProductionLine and ElectricMotor DTMIs before the WaterPump model. + + ```bash + export FACTORY_MODEL_ID=$(oci iot digital-twin-model create \ + --iot-domain-id "$IOT_DOMAIN_OCID" \ + --display-name "Factory Model" \ + --spec "file://$WORKSHOP_DIR/factory-model.json" \ + --wait-for-state ACTIVE \ + --query 'data.id' --raw-output) + + export PRODUCTION_LINE_MODEL_ID=$(oci iot digital-twin-model create \ + --iot-domain-id "$IOT_DOMAIN_OCID" \ + --display-name "Production Line Model" \ + --spec "file://$WORKSHOP_DIR/production-line-model.json" \ + --wait-for-state ACTIVE \ + --query 'data.id' --raw-output) + + export ELECTRIC_MOTOR_MODEL_ID=$(oci iot digital-twin-model create \ + --iot-domain-id "$IOT_DOMAIN_OCID" \ + --display-name "Electric Motor Model" \ + --spec "file://$WORKSHOP_DIR/electric-motor-model.json" \ + --wait-for-state ACTIVE \ + --query 'data.id' --raw-output) + + export WATER_PUMP_MODEL_ID=$(oci iot digital-twin-model create \ + --iot-domain-id "$IOT_DOMAIN_OCID" \ + --display-name "Water Pump Model" \ + --spec "file://$WORKSHOP_DIR/water-pump-model.json" \ + --wait-for-state ACTIVE \ + --query 'data.id' --raw-output) + ``` + +7. Verify the four stored specifications. + + ```bash + oci iot digital-twin-model get-spec --digital-twin-model-id "$FACTORY_MODEL_ID" + oci iot digital-twin-model get-spec --digital-twin-model-id "$PRODUCTION_LINE_MODEL_ID" + oci iot digital-twin-model get-spec --digital-twin-model-id "$ELECTRIC_MOTOR_MODEL_ID" + oci iot digital-twin-model get-spec --digital-twin-model-id "$WATER_PUMP_MODEL_ID" + ``` + +## Task 2: Create the default WaterPump adapter + +1. Create the default adapter. This command matches the default adapter in the Getting Started workshop. It requires no JSON files because the source payload already matches the WaterPump model shape and uses bar for pressure. If you completed that workshop, retain its adapter OCID rather than creating a second adapter. + + ```bash + export DEFAULT_WATER_PUMP_ADAPTER_ID=$(oci iot digital-twin-adapter create \ + --iot-domain-id "$IOT_DOMAIN_OCID" \ + --digital-twin-model-id "$WATER_PUMP_MODEL_ID" \ + --display-name "Water Pump Default Adapter" \ + --description "Default adapter for model-shaped water pump telemetry." \ + --wait-for-state ACTIVE \ + --query 'data.id' --raw-output) + ``` + +## Task 3: Create the Flat PSI WaterPump adapter + +1. Save the Flat PSI adapter inbound envelope as `$WORKSHOP_DIR/flat-psi-water-pump-envelope.json`. This definition is identical to the Flat PSI adapter in the Getting Started workshop. If that adapter already exists, use its OCID in Lab 3 rather than creating another one. + + ```bash + cat > "$WORKSHOP_DIR/flat-psi-water-pump-envelope.json" <<'EOF' + { + "referenceEndpoint": "/waterpump/flat-psi", + "referencePayload": { + "dataFormat": "JSON", + "data": { + "motorTemperature": 68.4, + "vibrationLevel": 1.7, + "powerConsumption": 12.6, + "flowRate": 247.5, + "dischPressPsi": 62.37 + } + } + } + EOF + ``` + +2. Save the Flat PSI adapter routes as `$WORKSHOP_DIR/flat-psi-water-pump-routes.json`. The route builds the nested motor component and converts PSI to bar. + + ```bash + cat > "$WORKSHOP_DIR/flat-psi-water-pump-routes.json" <<'EOF' + [ + { + "condition": "*", + "description": "Build the motor component from flat telemetry and convert PSI pressure to bar.", + "payloadMapping": { + "$.motor.motorTemperature": "$.motorTemperature", + "$.motor.vibrationLevel": "$.vibrationLevel", + "$.motor.powerConsumption": "$.powerConsumption", + "$.flowRate": "$.flowRate", + "$.dischargePressure": "${(.dischPressPsi * 0.0689475729)}" + }, + "referencePayload": { + "dataFormat": "JSON", + "data": { + "motorTemperature": 68.4, + "vibrationLevel": 1.7, + "powerConsumption": 12.6, + "flowRate": 247.5, + "dischPressPsi": 62.37 + } + } + } + ] + EOF + ``` + +3. Create the Flat PSI adapter. + + ```bash + export FLAT_PSI_WATER_PUMP_ADAPTER_ID=$(oci iot digital-twin-adapter create \ + --iot-domain-id "$IOT_DOMAIN_OCID" \ + --digital-twin-model-id "$WATER_PUMP_MODEL_ID" \ + --display-name "Water Pump Flat PSI Adapter" \ + --description "Maps flat pump telemetry to WaterPump and converts PSI pressure to bar." \ + --inbound-envelope "file://$WORKSHOP_DIR/flat-psi-water-pump-envelope.json" \ + --inbound-routes "file://$WORKSHOP_DIR/flat-psi-water-pump-routes.json" \ + --wait-for-state ACTIVE \ + --query 'data.id' --raw-output) + ``` + +## Task 4: Verify the WaterPump adapters + +1. List active adapters for the WaterPump model. Retain the exported adapter IDs for Lab 3. + + ```bash + oci iot digital-twin-adapter list \ + --iot-domain-id "$IOT_DOMAIN_OCID" \ + --digital-twin-model-id "$WATER_PUMP_MODEL_ID" \ + --lifecycle-state ACTIVE \ + --all --output table + ``` + +## Documentation References + +- [Creating digital twin models](https://docs.oracle.com/en-us/iaas/Content/internet-of-things/create-digital-twin-model.htm) +- [Creating digital twin adapters](https://docs.oracle.com/en-us/iaas/Content/internet-of-things/create-digital-twin-adapter.htm) +- [Digital twin model overview](https://docs.oracle.com/en-us/iaas/Content/internet-of-things/digital-twin-models.htm) +- [DTDL v3 specification](https://azure.github.io/opendigitaltwins-dtdl/DTDL/v3/DTDL.v3.html) + +## Acknowledgements + +* **Author** - Pete St. Pierre, Director, Product Management +* **Last Updated By/Date** - Pete St. Pierre, September 2026 diff --git a/oci-iot-gateway-config/appendix-water-pump-assets/files/electric-motor-model.json b/oci-iot-gateway-config/appendix-water-pump-assets/files/electric-motor-model.json new file mode 100644 index 000000000..58f53cbef --- /dev/null +++ b/oci-iot-gateway-config/appendix-water-pump-assets/files/electric-motor-model.json @@ -0,0 +1,14 @@ +{ + "@context": [ + "dtmi:dtdl:context;3", + "dtmi:dtdl:extension:historization;1" + ], + "@id": "dtmi:com:oracle:iot:example:ElectricMotor;1", + "@type": "Interface", + "displayName": "Electric Motor", + "contents": [ + {"@type": ["Telemetry", "Historized"], "name": "motorTemperature", "schema": "double"}, + {"@type": ["Telemetry", "Historized"], "name": "vibrationLevel", "schema": "double"}, + {"@type": "Telemetry", "name": "powerConsumption", "schema": "double"} + ] +} diff --git a/oci-iot-gateway-config/appendix-water-pump-assets/files/factory-model.json b/oci-iot-gateway-config/appendix-water-pump-assets/files/factory-model.json new file mode 100644 index 000000000..5a9e0edc0 --- /dev/null +++ b/oci-iot-gateway-config/appendix-water-pump-assets/files/factory-model.json @@ -0,0 +1,13 @@ +{ + "@context": "dtmi:dtdl:context;3", + "@id": "dtmi:com:oracle:beverage:Factory;1", + "@type": "Interface", + "displayName": "Factory", + "description": "A beverage production factory.", + "contents": [ + { + "@type": "Relationship", + "name": "contains" + } + ] +} diff --git a/oci-iot-gateway-config/appendix-water-pump-assets/files/flat-psi-water-pump-envelope.json b/oci-iot-gateway-config/appendix-water-pump-assets/files/flat-psi-water-pump-envelope.json new file mode 100644 index 000000000..927f2f967 --- /dev/null +++ b/oci-iot-gateway-config/appendix-water-pump-assets/files/flat-psi-water-pump-envelope.json @@ -0,0 +1,13 @@ +{ + "referenceEndpoint": "/waterpump/flat-psi", + "referencePayload": { + "dataFormat": "JSON", + "data": { + "motorTemperature": 68.4, + "vibrationLevel": 1.7, + "powerConsumption": 12.6, + "flowRate": 247.5, + "dischPressPsi": 62.37 + } + } +} diff --git a/oci-iot-gateway-config/appendix-water-pump-assets/files/flat-psi-water-pump-routes.json b/oci-iot-gateway-config/appendix-water-pump-assets/files/flat-psi-water-pump-routes.json new file mode 100644 index 000000000..08d318c22 --- /dev/null +++ b/oci-iot-gateway-config/appendix-water-pump-assets/files/flat-psi-water-pump-routes.json @@ -0,0 +1,23 @@ +[ + { + "condition": "*", + "description": "Build the motor component from flat telemetry and convert PSI pressure to bar.", + "payloadMapping": { + "$.motor.motorTemperature": "$.motorTemperature", + "$.motor.vibrationLevel": "$.vibrationLevel", + "$.motor.powerConsumption": "$.powerConsumption", + "$.flowRate": "$.flowRate", + "$.dischargePressure": "${(.dischPressPsi * 0.0689475729)}" + }, + "referencePayload": { + "dataFormat": "JSON", + "data": { + "motorTemperature": 68.4, + "vibrationLevel": 1.7, + "powerConsumption": 12.6, + "flowRate": 247.5, + "dischPressPsi": 62.37 + } + } + } +] diff --git a/oci-iot-gateway-config/appendix-water-pump-assets/files/production-line-model.json b/oci-iot-gateway-config/appendix-water-pump-assets/files/production-line-model.json new file mode 100644 index 000000000..80d2c0b4b --- /dev/null +++ b/oci-iot-gateway-config/appendix-water-pump-assets/files/production-line-model.json @@ -0,0 +1,7 @@ +{ + "@context": "dtmi:dtdl:context;3", + "@id": "dtmi:com:oracle:beverage:ProductionLine;1", + "@type": "Interface", + "displayName": "Production Line", + "description": "A beverage factory production line." +} diff --git a/oci-iot-gateway-config/appendix-water-pump-assets/files/water-pump-model.json b/oci-iot-gateway-config/appendix-water-pump-assets/files/water-pump-model.json new file mode 100644 index 000000000..d9c668d27 --- /dev/null +++ b/oci-iot-gateway-config/appendix-water-pump-assets/files/water-pump-model.json @@ -0,0 +1,16 @@ +{ + "@context": [ + "dtmi:dtdl:context;3", + "dtmi:dtdl:extension:quantitativeTypes;1", + "dtmi:com:oracle:dtdl:extension:validation;1" + ], + "@id": "dtmi:com:oracle:iot:example:WaterPump;1", + "@type": "Interface", + "displayName": "Water Pump", + "contents": [ + {"@type": "Component", "name": "motor", "schema": "dtmi:com:oracle:iot:example:ElectricMotor;1"}, + {"@type": ["Telemetry", "Validated"], "name": "flowRate", "schema": "double", "minimum": 0, "maximum": 1000}, + {"@type": ["Telemetry", "Pressure"], "name": "dischargePressure", "schema": "double", "unit": "bar"}, + {"@type": "Relationship", "name": "installedOn", "target": "dtmi:com:oracle:beverage:ProductionLine;1"} + ] +} diff --git a/oci-iot-gateway-config/connect-indirect-water-pumps/connect-indirect-water-pumps.md b/oci-iot-gateway-config/connect-indirect-water-pumps/connect-indirect-water-pumps.md new file mode 100644 index 000000000..168cd239a --- /dev/null +++ b/oci-iot-gateway-config/connect-indirect-water-pumps/connect-indirect-water-pumps.md @@ -0,0 +1,213 @@ +# Lab 3: Connect and Monitor Indirect Water Pumps + +## Introduction + +Create two indirect water-pump twins and publish telemetry through Gateway 1. Water Pump 3 sends telemetry that matches the WaterPump model. Water Pump 4 sends flat telemetry with pressure in PSI. Each target twin uses its adapter to produce the same canonical WaterPump state. + +Estimated Time: 65 minutes + +### Objectives + +In this lab, you will: + +- Create indirect twins that share a gateway association. +- Reuse the default and Flat PSI WaterPump adapters. +- Publish two source payload shapes through one MQTTs connection. +- Verify normalized values and the gateway association. + +### Prerequisites + +- Complete [Lab 2: Create the Gateway and Routing Adapter](?lab=create-gateway-and-routing). +- Have the ElectricMotor and WaterPump models and the default and Flat PSI WaterPump adapters in the IoT domain. +- Retain `IOT_DOMAIN_OCID`, `GATEWAY_INSTANCE_ID`, `GATEWAY_EXTERNAL_KEY`, `GATEWAY_SECRET_VALUE`, and `IOT_DEVICE_HOST` from Lab 2. + +If the WaterPump models or adapters are missing, create them with [Appendix A: Create Required Factory, Production Line, and WaterPump Assets](?lab=appendix-water-pump-assets). + +## Task 1: Locate the WaterPump assets + +1. List active WaterPump adapters. Identify **Water Pump Default Adapter** and **Water Pump Flat PSI Adapter**. They match the adapters in the Getting Started workshop. Set their existing OCIDs. Do not create adapters with duplicate display names. + + ```bash + oci iot digital-twin-adapter list \ + --iot-domain-id "$IOT_DOMAIN_OCID" \ + --lifecycle-state ACTIVE \ + --all --output table + + export WATER_PUMP_MODEL_ID='' + export DEFAULT_WATER_PUMP_ADAPTER_ID='' + export FLAT_PSI_WATER_PUMP_ADAPTER_ID='' + ``` + +2. Set stable external keys. The gateway routing adapter uses each key to identify an indirect device. These keys are not MQTT user names because the pumps do not authenticate to OCI. + + ```bash + export PUMP_3_EXTERNAL_KEY='water-pump-3' + export PUMP_4_EXTERNAL_KEY='water-pump-4' + ``` + +## Task 2: Create the indirect pump twins + +1. Create Water Pump 3 with the default adapter. Do not set `--auth-id`. Its `--gateways` array associates it with Gateway 1. + + ```bash + export PUMP_3_INSTANCE_ID=$(oci iot digital-twin-instance create \ + --iot-domain-id "$IOT_DOMAIN_OCID" \ + --digital-twin-model-id "$WATER_PUMP_MODEL_ID" \ + --digital-twin-adapter-id "$DEFAULT_WATER_PUMP_ADAPTER_ID" \ + --connectivity-type INDIRECT \ + --gateways "[\"$GATEWAY_INSTANCE_ID\"]" \ + --external-key "$PUMP_3_EXTERNAL_KEY" \ + --display-name "Water Pump 3" \ + --wait-for-state ACTIVE \ + --query 'data.id' --raw-output) + ``` + +2. Create Water Pump 4 with the Flat PSI adapter. It uses the same WaterPump model and gateway association as Water Pump 3. + + ```bash + export PUMP_4_INSTANCE_ID=$(oci iot digital-twin-instance create \ + --iot-domain-id "$IOT_DOMAIN_OCID" \ + --digital-twin-model-id "$WATER_PUMP_MODEL_ID" \ + --digital-twin-adapter-id "$FLAT_PSI_WATER_PUMP_ADAPTER_ID" \ + --connectivity-type INDIRECT \ + --gateways "[\"$GATEWAY_INSTANCE_ID\"]" \ + --external-key "$PUMP_4_EXTERNAL_KEY" \ + --display-name "Water Pump 4" \ + --wait-for-state ACTIVE \ + --query 'data.id' --raw-output) + ``` + +3. Confirm the connectivity and gateway association. Both indirect pumps have a null `auth-id`. + + ```bash + oci iot digital-twin-instance get \ + --digital-twin-instance-id "$PUMP_3_INSTANCE_ID" \ + --query 'data.{name:"display-name",type:"connectivity-type",auth:"auth-id",gateways:gateways,key:"external-key"}' + + oci iot digital-twin-instance get \ + --digital-twin-instance-id "$PUMP_4_INSTANCE_ID" \ + --query 'data.{name:"display-name",type:"connectivity-type",auth:"auth-id",gateways:gateways,key:"external-key"}' + ``` + +4. **Optional:** List devices associated with Gateway 1. This read-only pipeline lists indirect twins, then uses `jq` to filter each `gateways` array for `$GATEWAY_INSTANCE_ID`. OCI IoT has no server-side filter for a specific gateway, so `jq` completes that filter locally. This informational step is not required for the rest of the lab. + + ```bash + oci iot digital-twin-instance list \ + --iot-domain-id "$IOT_DOMAIN_OCID" \ + --connectivity-type INDIRECT \ + --all \ + --output json | + jq --arg gateway "$GATEWAY_INSTANCE_ID" ' + .data.items[] + | select((.gateways // []) | index($gateway) != null) + | { + id, + name: ."display-name", + externalKey: ."external-key", + gateways + }' + ``` + +## Task 3: Connect Gateway 1 and publish telemetry + +1. Configure MQTTX for Gateway 1. Use `mqtts://$IOT_DEVICE_HOST`, port `8883`, TLS, and a clean session. Use `$GATEWAY_EXTERNAL_KEY` as the user name and `$GATEWAY_SECRET_VALUE` as the password. + +2. Publish gateway status telemetry to the `data` topic. The empty target keeps this message with Gateway 1. + + ```bash + mqttx pub \ + -h "$IOT_DEVICE_HOST" -p 8883 -l mqtts \ + -t data \ + -m '{"time":"2026-09-02T18:00:00.000000Z","connectedDeviceCount":2,"cpuUtil":30,"memUtil":25,"firmware":"Oracle Linux 9.1"}' \ + -u "$GATEWAY_EXTERNAL_KEY" -P "$GATEWAY_SECRET_VALUE" + ``` + +3. Publish model-shaped telemetry for Water Pump 3. The `water-pumps/water-pump-3` path resolves the target external key. OCI IoT sends the payload to Pump 3's default adapter. + + ```bash + mqttx pub \ + -h "$IOT_DEVICE_HOST" -p 8883 -l mqtts \ + -t "water-pumps/$PUMP_3_EXTERNAL_KEY" \ + -m '{"time":"2026-09-02T18:01:00.000000Z","motor":{"motorTemperature":68.4,"vibrationLevel":1.7,"powerConsumption":12.6},"flowRate":247.5,"dischargePressure":4.3}' \ + -u "$GATEWAY_EXTERNAL_KEY" -P "$GATEWAY_SECRET_VALUE" + ``` + +4. Publish flat PSI telemetry for Water Pump 4. The gateway resolves Pump 4 from the path and supplies `timeObserved`. The target adapter inherits that timestamp and receives the original endpoint. Its wildcard route matches the endpoint, maps flat pump fields, converts PSI to bar, and builds the nested motor component. No gateway-specific Flat PSI adapter is required. + + ```bash + mqttx pub \ + -h "$IOT_DEVICE_HOST" -p 8883 -l mqtts \ + -t "water-pumps/$PUMP_4_EXTERNAL_KEY" \ + -m '{"time":"2026-09-02T18:02:00.000000Z","motorTemperature":68.4,"vibrationLevel":1.7,"powerConsumption":12.6,"flowRate":247.5,"dischPressPsi":62.37}' \ + -u "$GATEWAY_EXTERNAL_KEY" -P "$GATEWAY_SECRET_VALUE" + ``` + +## Task 4: Verify normalized state and gateway association + +1. Retrieve the latest content and metadata for Gateway 1 and both pumps. + + ```bash + oci iot digital-twin-instance get-content \ + --digital-twin-instance-id "$GATEWAY_INSTANCE_ID" \ + --should-include-metadata true + + oci iot digital-twin-instance get-content \ + --digital-twin-instance-id "$PUMP_3_INSTANCE_ID" \ + --should-include-metadata true + + oci iot digital-twin-instance get-content \ + --digital-twin-instance-id "$PUMP_4_INSTANCE_ID" \ + --should-include-metadata true + ``` + + The first response shows Gateway 1 status. The second shows Water Pump 3 values in the model-shaped payload. The third shows Water Pump 4 values after the Flat PSI adapter normalizes its payload. For Water Pump 4, look for the canonical `dischargePressure`, `flowRate`, and nested `motor` fields. The metadata records the observation time for each field and `timeLastHeard` for the pump. Your `etag` value will differ. + + The following is an example Water Pump 4 response: + + ```json + { + "data": { + "_metadata": { + "dischargePressure": { + "timeObserved": "2026-09-02T18:02:00.000000+00:00" + }, + "flowRate": { + "timeObserved": "2026-09-02T18:02:00.000000+00:00" + }, + "motor": { + "motorTemperature": { + "timeObserved": "2026-09-02T18:02:00.000000+00:00" + }, + "powerConsumption": { + "timeObserved": "2026-09-02T18:02:00.000000+00:00" + }, + "vibrationLevel": { + "timeObserved": "2026-09-02T18:02:00.000000+00:00" + } + }, + "timeLastHeard": "2026-09-02T18:02:00.000000+00:00" + }, + "dischargePressure": 4.300260121773, + "flowRate": 247.5, + "motor": { + "motorTemperature": 68.4, + "powerConsumption": 12.6, + "vibrationLevel": 1.7 + } + }, + "etag": "06a4a140a4c7d6d8cc66bc5cb5bc591dc89c98feb78fbdd947ac37e56d4c04d0" + } + ``` + +2. Confirm that both pumps expose the canonical WaterPump paths. For Water Pump 4, `62.37` PSI is approximately `4.30` bar. Check recent `timeLastHeard` metadata for the gateway and both pumps. + +## Learn More + +- [Scenario: Create digital twins for indirectly connected devices using a gateway](https://docs.oracle.com/en-us/iaas/Content/internet-of-things/gateway-instance.htm) +- [Route indirect device data using target and contentRoot](https://docs.oracle.com/en-us/iaas/Content/internet-of-things/gateway-target-content-root.htm) +- [IoT domain database schema reference](https://docs.oracle.com/en-us/iaas/Content/internet-of-things/iot-domain-database-schema.htm) + +## Acknowledgements + +* **Author** - Pete St. Pierre, Director, Product Management +* **Last Updated By/Date** - Pete St. Pierre, September 2026 diff --git a/oci-iot-gateway-config/create-gateway-and-routing/create-gateway-and-routing.md b/oci-iot-gateway-config/create-gateway-and-routing/create-gateway-and-routing.md new file mode 100644 index 000000000..6f20be639 --- /dev/null +++ b/oci-iot-gateway-config/create-gateway-and-routing/create-gateway-and-routing.md @@ -0,0 +1,243 @@ +# Lab 2: Create the Gateway and Routing Adapter + +## Introduction + +Create Gateway 1, an authenticated twin that publishes its status and forwards pump telemetry. Its adapter maps data sent to `/data` as gateway status. It derives a pump external key from paths such as `/water-pumps/water-pump-3`. When it resolves a target, OCI IoT sends the message to that pump's adapter. + +If you completed the Getting Started workshop, reuse `WORKSHOP_COMPARTMENT_OCID`, `IOT_DOMAIN_OCID`, `VAULT_OCID`, and `VAULT_MASTER_KEY_OCID`. In a separate environment, retrieve the compartment and IoT domain OCIDs from their Console details pages. Retrieve the Vault OCID from the Vault details page and the key OCID from the master encryption key details page. Create a Vault and master encryption key first if they do not exist. + +The Vault and master encryption key encrypt Gateway 1's authentication secret. In this lab, choose `gateway-1` as the gateway external key and choose a strong plain-text secret. Task 4 creates the Vault secret and Gateway 1 twin. + +Estimated Time: 45 minutes + +### Objectives + +In this lab, you will: + +- Create the GatewayStatus model. +- Create a gateway adapter with a target mapping in its inbound envelope. +- Create a dedicated secret and Gateway 1 twin. +- Verify that Gateway 1 is active and ready for indirect devices. + +### Prerequisites + +- Complete the workshop introduction and Lab 1. +- Complete [Get Started with OCI Internet of Things Platform](https://livelabs.oracle.com/ords/r/dbpm/livelabs/view-workshop?wid=4515), or ensure that both of the following prerequisites are met: + - Have an active IoT domain, an OCI CLI profile, a Vault, and a master encryption key. + - Have the ElectricMotor and WaterPump models and the default and Flat PSI WaterPump adapters available in the IoT domain. + +If the WaterPump models or adapters are missing, use [Appendix A: Create Required Factory, Production Line, and WaterPump Assets](?lab=appendix-water-pump-assets) to create them. + +## Task 1: Set gateway variables + +1. Set the environment variables. If you completed Getting Started, use the saved OCIDs. Otherwise, copy the compartment, IoT domain, Vault, and key OCIDs from the OCI Console. `GATEWAY_EXTERNAL_KEY` identifies the gateway when it connects to OCI IoT. Choose a strong `GATEWAY_SECRET_VALUE`. Task 4 stores it in the Vault as Gateway 1's credential. + + ```bash + export WORKSHOP_DIR="$PWD" + export WORKSHOP_COMPARTMENT_OCID='' + export IOT_DOMAIN_OCID='' + export VAULT_OCID='' + export VAULT_MASTER_KEY_OCID='' + export GATEWAY_EXTERNAL_KEY='gateway-1' + export GATEWAY_SECRET_VALUE='' + ``` + +2. Retrieve the device host. Lab 3 connects Gateway 1 to this host through MQTTs on port `8883`. + + ```bash + export IOT_DEVICE_HOST=$(oci iot domain get \ + --iot-domain-id "$IOT_DOMAIN_OCID" \ + --query 'data."device-host"' \ + --raw-output) + ``` + +## Task 2: Create the GatewayStatus model + +1. Save the GatewayStatus model as `$WORKSHOP_DIR/gateway-status-model.json`. It describes gateway data, not WaterPump data. + + ```bash + cat > "$WORKSHOP_DIR/gateway-status-model.json" <<'EOF' + { + "@context": ["dtmi:dtdl:context;3"], + "@id": "dtmi:com:oracle:iot:example:GatewayStatus;1", + "@type": "Interface", + "displayName": "Gateway Status", + "description": "Reports the essential operating state of an OCI IoT gateway.", + "contents": [ + { + "@type": "Telemetry", + "name": "connectedDeviceCount", + "displayName": "Connected Device Count", + "description": "Reports the number of indirectly connected devices currently communicating through the gateway.", + "schema": "integer" + }, + { + "@type": "Telemetry", + "name": "cpuUtilization", + "displayName": "CPU Utilization", + "description": "Reports the percentage of gateway CPU capacity currently in use.", + "schema": "integer" + }, + { + "@type": "Telemetry", + "name": "memoryUtilization", + "displayName": "Memory Utilization", + "description": "Reports the percentage of gateway memory currently in use.", + "schema": "integer" + }, + { + "@type": "Telemetry", + "name": "firmwareVersion", + "displayName": "Firmware Version", + "description": "Reports the firmware version running on the gateway.", + "schema": "string" + } + ] + } + EOF + ``` + +2. Create the model and retain its OCID. + + ```bash + export GATEWAY_STATUS_MODEL_ID=$(oci iot digital-twin-model create \ + --iot-domain-id "$IOT_DOMAIN_OCID" \ + --display-name "Gateway Status Model" \ + --spec "file://$WORKSHOP_DIR/gateway-status-model.json" \ + --wait-for-state ACTIVE \ + --query 'data.id' --raw-output) + ``` + +3. Verify the stored model specification. + + ```bash + oci iot digital-twin-model get-spec \ + --digital-twin-model-id "$GATEWAY_STATUS_MODEL_ID" + ``` + +## Task 3: Create the gateway routing adapter + +1. Save the inbound envelope as `$WORKSHOP_DIR/gateway-routing-envelope.json`. It resolves `target` before it evaluates routes. An empty target keeps the message with Gateway 1. A value such as `water-pump-3` sends the payload to the indirect twin with that external key. + + ```bash + cat > "$WORKSHOP_DIR/gateway-routing-envelope.json" <<'EOF' + { + "referenceEndpoint": "/data", + "referencePayload": { + "dataFormat": "JSON", + "data": { + "time": "2026-09-02T18:00:00.000000Z", + "cpuUtil": 0, + "memUtil": 0, + "connectedDeviceCount": 0, + "firmware": "Oracle Linux 9.1" + } + }, + "envelopeMapping": { + "timeObserved": "$.time", + "target": "${if endpoint(1) == \"water-pumps\" then endpoint(2) else null end}", + "contentRoot": "$" + } + } + EOF + ``` + +2. Save the inbound routes as `$WORKSHOP_DIR/gateway-routing-routes.json`. The routes map gateway status telemetry. OCI IoT sends indirect-device telemetry to the target twin's adapter instead. + + ```bash + cat > "$WORKSHOP_DIR/gateway-routing-routes.json" <<'EOF' + [ + { + "condition": "*", + "payloadMapping": { + "$.cpuUtilization": "$.cpuUtil", + "$.memoryUtilization": "$.memUtil", + "$.connectedDeviceCount": "$.connectedDeviceCount", + "$.firmwareVersion": "$.firmware" + }, + "referencePayload": { + "dataFormat": "JSON", + "data": { + "time": "2026-09-02T18:00:00.000000Z", + "cpuUtil": 0, + "memUtil": 0, + "connectedDeviceCount": 0, + "firmware": "Oracle Linux 9.1" + } + } + } + ] + EOF + ``` + +3. Create the adapter and retain its OCID. + + ```bash + export GATEWAY_ROUTING_ADAPTER_ID=$(oci iot digital-twin-adapter create \ + --iot-domain-id "$IOT_DOMAIN_OCID" \ + --digital-twin-model-id "$GATEWAY_STATUS_MODEL_ID" \ + --display-name "Gateway 1 Routing Adapter" \ + --description "Routes forwarded water-pump telemetry by endpoint target." \ + --inbound-envelope "file://$WORKSHOP_DIR/gateway-routing-envelope.json" \ + --inbound-routes "file://$WORKSHOP_DIR/gateway-routing-routes.json" \ + --wait-for-state ACTIVE \ + --query 'data.id' --raw-output) + ``` + +4. Verify that the adapter is active. + + ```bash + oci iot digital-twin-adapter get \ + --digital-twin-adapter-id "$GATEWAY_ROUTING_ADAPTER_ID" \ + --query 'data.{name:"display-name",state:"lifecycle-state",model:"digital-twin-model-id"}' + ``` + +## Task 4: Create Gateway 1 + +1. Create a Vault secret for Gateway 1. In this lab, the external key is the MQTT user name and the plain-text value is the password. + + ```bash + export GATEWAY_SECRET_OCID=$(oci vault secret create-base64 \ + --compartment-id "$WORKSHOP_COMPARTMENT_OCID" \ + --vault-id "$VAULT_OCID" \ + --key-id "$VAULT_MASTER_KEY_OCID" \ + --secret-name gateway-1-auth \ + --secret-content-content "$(printf %s "$GATEWAY_SECRET_VALUE" | base64)" \ + --wait-for-state ACTIVE \ + --query 'data.id' --raw-output) + ``` + +2. Create the gateway twin. A gateway needs an authentication ID and a gateway adapter. + + ```bash + export GATEWAY_INSTANCE_ID=$(oci iot digital-twin-instance create \ + --iot-domain-id "$IOT_DOMAIN_OCID" \ + --digital-twin-model-id "$GATEWAY_STATUS_MODEL_ID" \ + --digital-twin-adapter-id "$GATEWAY_ROUTING_ADAPTER_ID" \ + --connectivity-type GATEWAY \ + --auth-id "$GATEWAY_SECRET_OCID" \ + --external-key "$GATEWAY_EXTERNAL_KEY" \ + --display-name "Gateway 1" \ + --wait-for-state ACTIVE \ + --query 'data.id' --raw-output) + ``` + +3. Confirm that Gateway 1 is active and has an authentication ID. + + ```bash + oci iot digital-twin-instance get \ + --digital-twin-instance-id "$GATEWAY_INSTANCE_ID" \ + --query 'data.{name:"display-name",type:"connectivity-type",auth:"auth-id",adapter:"digital-twin-adapter-id",key:"external-key",state:"lifecycle-state"}' + ``` + + You may now **proceed to the next lab**. + +## Learn More + +- [Creating a digital twin adapter](https://docs.oracle.com/en-us/iaas/Content/internet-of-things/create-digital-twin-adapter.htm) +- [Gateway and indirect-device scenario](https://docs.oracle.com/en-us/iaas/Content/internet-of-things/gateway-instance.htm) + +## Acknowledgements + +* **Author** - Pete St. Pierre, Director, Product Management +* **Last Updated By/Date** - Pete St. Pierre, September 2026 diff --git a/oci-iot-gateway-config/create-gateway-and-routing/files/gateway-routing-envelope.json b/oci-iot-gateway-config/create-gateway-and-routing/files/gateway-routing-envelope.json new file mode 100644 index 000000000..550298fdd --- /dev/null +++ b/oci-iot-gateway-config/create-gateway-and-routing/files/gateway-routing-envelope.json @@ -0,0 +1,18 @@ +{ + "referenceEndpoint": "/data", + "referencePayload": { + "dataFormat": "JSON", + "data": { + "time": "2026-09-02T18:00:00.000000Z", + "cpuUtil": 0, + "memUtil": 0, + "connectedDeviceCount": 0, + "firmware": "Oracle Linux 9.1" + } + }, + "envelopeMapping": { + "timeObserved": "$.time", + "target": "${if endpoint(1) == \"water-pumps\" then endpoint(2) else null end}", + "contentRoot": "$" + } +} diff --git a/oci-iot-gateway-config/create-gateway-and-routing/files/gateway-routing-routes.json b/oci-iot-gateway-config/create-gateway-and-routing/files/gateway-routing-routes.json new file mode 100644 index 000000000..7dc8452a9 --- /dev/null +++ b/oci-iot-gateway-config/create-gateway-and-routing/files/gateway-routing-routes.json @@ -0,0 +1,21 @@ +[ + { + "condition": "*", + "payloadMapping": { + "$.cpuUtilization": "$.cpuUtil", + "$.memoryUtilization": "$.memUtil", + "$.connectedDeviceCount": "$.connectedDeviceCount", + "$.firmwareVersion": "$.firmware" + }, + "referencePayload": { + "dataFormat": "JSON", + "data": { + "time": "2026-09-02T18:00:00.000000Z", + "cpuUtil": 0, + "memUtil": 0, + "connectedDeviceCount": 0, + "firmware": "Oracle Linux 9.1" + } + } + } +] diff --git a/oci-iot-gateway-config/create-gateway-and-routing/files/gateway-status-model.json b/oci-iot-gateway-config/create-gateway-and-routing/files/gateway-status-model.json new file mode 100644 index 000000000..f09945415 --- /dev/null +++ b/oci-iot-gateway-config/create-gateway-and-routing/files/gateway-status-model.json @@ -0,0 +1,39 @@ +{ + "@context": [ + "dtmi:dtdl:context;3" + ], + "@id": "dtmi:com:oracle:iot:example:GatewayStatus;1", + "@type": "Interface", + "displayName": "Gateway Status", + "description": "Reports the essential operating state of an OCI IoT gateway.", + "contents": [ + { + "@type": "Telemetry", + "name": "connectedDeviceCount", + "displayName": "Connected Device Count", + "description": "Reports the number of indirectly connected devices currently communicating through the gateway.", + "schema": "integer" + }, + { + "@type": "Telemetry", + "name": "cpuUtilization", + "displayName": "CPU Utilization", + "description": "Reports the percentage of gateway CPU capacity currently in use.", + "schema": "integer" + }, + { + "@type": "Telemetry", + "name": "memoryUtilization", + "displayName": "Memory Utilization", + "description": "Reports the percentage of gateway memory currently in use.", + "schema": "integer" + }, + { + "@type": "Telemetry", + "name": "firmwareVersion", + "displayName": "Firmware Version", + "description": "Reports the firmware version running on the gateway.", + "schema": "string" + } + ] +} diff --git a/oci-iot-gateway-config/introduction/introduction.md b/oci-iot-gateway-config/introduction/introduction.md new file mode 100644 index 000000000..abb5d3cbe --- /dev/null +++ b/oci-iot-gateway-config/introduction/introduction.md @@ -0,0 +1,43 @@ +# Working with Gateways in OCI Internet of Things (IoT) Platform + +## Introduction + +This workshop extends a water-pump digital twin scenario to devices that cannot connect directly to OCI. You create one authenticated gateway and use it to forward telemetry for two indirectly connected water pumps. The gateway publishes its own health data and identifies each pump from the endpoint path. OCI IoT delegates a forwarded payload to the target pump's adapter for normalization. + +Estimated Workshop Time: 2 hours with existing assets; approximately 2 hours and 55 minutes if you complete the full appendix. + +### Objectives + +In this workshop, you will: + +- Create a gateway model, routing adapter, and authenticated gateway twin. +- Create two indirectly connected WaterPump twins that use different adapters. +- Publish telemetry through one gateway MQTTs connection. +- Verify normalized state and the dependency of indirect devices on the gateway. + +### Prerequisites + +- An OCI tenancy, compartment, IoT domain group, and IoT domain. +- OCI CLI and MQTTX installed locally or in Cloud Shell. +- A Vault and master encryption key that the IoT domain can read for gateway credentials. +- The ElectricMotor and WaterPump models and the default and Flat PSI WaterPump adapters. + +If your IoT domain does not already contain the WaterPump assets, use [Appendix A: Create Required Factory, Production Line, and WaterPump Assets](?lab=appendix-water-pump-assets). The appendix makes this workshop self-contained; it does not require the [earlier getting-started workshop](https://livelabs.oracle.com/ords/r/dbpm/livelabs/view-workshop?wid=4515). + +## Workshop Flow + +1. **Lab 1** compares direct and indirect connectivity and explains how gateway routing delegates a payload to its target device. +2. **Lab 2** creates Gateway 1 and its routing configuration. +3. **Lab 3** creates two indirect pumps, publishes both payload shapes through Gateway 1, and verifies the results. +4. **Appendix A** supplies the WaterPump model and adapter assets when they are not already available. + +## Learn More + +- [Get Started with OCI Internet of Things Platform](https://livelabs.oracle.com/ords/r/dbpm/livelabs/view-workshop?wid=4515) +- [OCI IoT gateway scenario](https://docs.oracle.com/en-us/iaas/Content/internet-of-things/gateway-instance.htm) +- [OCI IoT Platform overview](https://docs.oracle.com/en-us/iaas/Content/internet-of-things/overview.htm) + +## Acknowledgements + +* **Author** - Pete St. Pierre, Director, Product Management +* **Last Updated By/Date** - Pete St. Pierre, September 2026 diff --git a/oci-iot-gateway-config/understand-gateways-and-indirect-devices/images/direct-and-indirect-topology.svg b/oci-iot-gateway-config/understand-gateways-and-indirect-devices/images/direct-and-indirect-topology.svg new file mode 100644 index 000000000..a80dd2830 --- /dev/null +++ b/oci-iot-gateway-config/understand-gateways-and-indirect-devices/images/direct-and-indirect-topology.svg @@ -0,0 +1,105 @@ + + Direct and gateway-based indirect device telemetry paths + A charcoal and Oracle-red diagram comparing a direct water pump connection to an indirect pump connection through Gateway 1. + + + Directly connected device + Indirectly connected device + + + + + + + + + + + + Pump: its own credentials + + + + + + + + + + OCI IoT Platform + + + + + + + + + + + + device adapter + + + + + + + + direct twin + + + + + + + + + + Pump: no domain credentials + + + + + + + + + + + Gateway 1 + gateway credentials + + + + + + + + + OCI IoT Platform + + + + + + + + + + gateway target → device adapter + + + + + + Indirect + twin + diff --git a/oci-iot-gateway-config/understand-gateways-and-indirect-devices/images/gateway-routing-flow.png b/oci-iot-gateway-config/understand-gateways-and-indirect-devices/images/gateway-routing-flow.png new file mode 100644 index 000000000..c740fe306 Binary files /dev/null and b/oci-iot-gateway-config/understand-gateways-and-indirect-devices/images/gateway-routing-flow.png differ diff --git a/oci-iot-gateway-config/understand-gateways-and-indirect-devices/images/gateway-routing-target-selection.svg b/oci-iot-gateway-config/understand-gateways-and-indirect-devices/images/gateway-routing-target-selection.svg new file mode 100644 index 000000000..5f05fed1e --- /dev/null +++ b/oci-iot-gateway-config/understand-gateways-and-indirect-devices/images/gateway-routing-target-selection.svg @@ -0,0 +1,60 @@ + + Gateway routing selects an associated indirect device + One message enters the gateway routing adapter. The adapter extracts water-pump-4 from the endpoint and sends the payload to Water Pump 4 from the associated digital twin instances. + + + Gateway routing: one message selects one indirect device + The gateway authenticates. Its adapter extracts the target, then delegates the payload to that device's adapter. + + + + Gateway message + Topic + + water-pumps/ + water-pump-4 + Pump telemetry + + + + + Gateway routing adapter + + Extract target from endpoint + + target = water-pump-4 + + Delegate payload to the + selected device adapter + + Associated indirect digital twins + Match the target to an external key. + + + + + Water Pump 3water-pump-3 + + + + + + Water Pump 4water-pump-4 + + + + + + Water Pump 5water-pump-5 + + + + The selected twin's own adapter normalizes the forwarded payload. + diff --git a/oci-iot-gateway-config/understand-gateways-and-indirect-devices/images/gateway-understanding-badge.svg b/oci-iot-gateway-config/understand-gateways-and-indirect-devices/images/gateway-understanding-badge.svg new file mode 100644 index 000000000..01f78382e --- /dev/null +++ b/oci-iot-gateway-config/understand-gateways-and-indirect-devices/images/gateway-understanding-badge.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/oci-iot-gateway-config/understand-gateways-and-indirect-devices/understand-gateways-and-indirect-devices.md b/oci-iot-gateway-config/understand-gateways-and-indirect-devices/understand-gateways-and-indirect-devices.md new file mode 100644 index 000000000..b1b9b9c1b --- /dev/null +++ b/oci-iot-gateway-config/understand-gateways-and-indirect-devices/understand-gateways-and-indirect-devices.md @@ -0,0 +1,88 @@ +# Lab 1: Understand Gateways and Indirectly Connected Devices + +## Introduction + +Before you configure Gateway 1, compare OCI IoT connectivity patterns. A directly connected twin authenticates with the IoT domain and sends its own telemetry. An indirectly connected twin relies on an authenticated gateway to forward telemetry. + +Estimated Time: 10 minutes + +```quiz-config +badge: images/gateway-understanding-badge.svg +``` + +### Objectives + +In this lab, you will: + +- Distinguish direct connectivity from gateway-based indirect connectivity. +- Identify gateway, target-device, and adapter roles in the indirect path. +- Check your understanding before you configure the gateway. + +### Prerequisites + +- Complete the workshop introduction. + +## Task 1: Compare direct and indirect connectivity + +1. Review the topology. Both paths update a digital twin in OCI IoT Platform. Authentication and routing occur in different places. + + ![Direct and indirect OCI IoT Platform connectivity topology](images/direct-and-indirect-topology.svg) + +2. In the **direct** path, the device connects with its own authentication ID and external key. When required, its adapter maps telemetry before OCI IoT updates the twin. + +3. In the **indirect** path, the gateway faces the IoT domain. It authenticates to OCI IoT Platform and forwards data for one or more indirect devices. An indirect device has a gateway association instead of its own authentication ID. + + A gateway needs an authentication ID, just as a directly connected device does. Use a Vault secret or an mTLS certificate. For production deployments, use certificates. + +4. The gateway adapter resolves the message target before it evaluates routes. When the target matches an associated indirect-device external key, OCI IoT sends the payload to that device's adapter. An empty or null target leaves the message with the gateway. Lab 2 creates the gateway model and routing adapter before Lab 3 creates the pumps. + + ![Gateway routing sends each pump payload to its matching indirect twin adapter, while gateway health data remains with the gateway](images/gateway-routing-flow.png) + +5. In this workshop, the segment after `water-pumps/` identifies the target pump. Gateway health telemetry uses `data` and stays with Gateway 1. After routing, each pump can use an adapter that matches its payload shape. + +## Task 2: Check your understanding + +1. Answer each question, then review its explanation. + + ```quiz + Q: Which component authenticates to OCI IoT Platform for telemetry from an indirectly connected water pump? + - The indirectly connected pump, using its own authentication ID + * The gateway that is associated with the pump + - The WaterPump model + - The pump's digital twin adapter + > An indirect device has one or more gateway associations instead of its own authentication ID. The gateway authenticates and forwards its telemetry. + + Q: Which authentication mechanisms can an OCI IoT gateway use? + * A Vault secret or an mTLS certificate; certificates are recommended for production. + - Only a Vault secret because certificates are for directly connected devices. + - Only an mTLS certificate because gateways cannot use secrets. + - Credentials from each indirectly connected device. + > A gateway can use a Vault secret or an mTLS certificate for its authentication ID. Use certificates for production deployments. + + Q: What does the gateway adapter target determine for a forwarded message? + - Which gateway certificate OCI uses for the MQTT connection + - Which WaterPump model definition is deleted after processing + * Which associated indirect digital twin instance should receive the payload for its adapter to process + - Whether the device must change to direct connectivity + > The gateway resolves a target before it evaluates routes. When the target matches an associated indirect digital twin instance's external key, OCI IoT delegates the payload to that instance's adapter. + + Q: Why can the two indirect pumps in this workshop use different adapters? + - A gateway can authenticate only one indirect device at a time + - Each adapter creates a separate IoT domain + * After the gateway routes a message to the correct indirectly connected pump digital twin, the adapter defined for that digital twin instance can normalize its particular payload shape + - An indirectly connected device cannot use the same model as another device + > Gateway routing selects the target indirectly connected digital twin instance. The adapter defined for that instance maps its payload to the shared WaterPump model, so the pumps can use different source formats. + ``` + + You may now **proceed to the next lab**. + +## Learn More + +- [Gateway and indirect-device scenario](https://docs.oracle.com/en-us/iaas/Content/internet-of-things/gateway-instance.htm) +- [Creating a digital twin instance](https://docs.oracle.com/en-us/iaas/Content/internet-of-things/create-digital-twin-instance.htm) +- [Digital twin adapters](https://docs.oracle.com/en-us/iaas/Content/internet-of-things/digital-twin-adapters.htm) + +## Acknowledgements + +* **Author** - Pete St. Pierre, Director, Product Management +* **Last Updated By/Date** - Pete St. Pierre, September 2026 diff --git a/oci-iot-gateway-config/workshops/tenancy/index.html b/oci-iot-gateway-config/workshops/tenancy/index.html new file mode 100644 index 000000000..264d910f9 --- /dev/null +++ b/oci-iot-gateway-config/workshops/tenancy/index.html @@ -0,0 +1,59 @@ + + + + + + + + + Oracle LiveLabs + + + + + + + + + + + + +
+
+
+
+
+
+
+
+ + + + + diff --git a/oci-iot-gateway-config/workshops/tenancy/manifest.json b/oci-iot-gateway-config/workshops/tenancy/manifest.json new file mode 100644 index 000000000..c6291f2ea --- /dev/null +++ b/oci-iot-gateway-config/workshops/tenancy/manifest.json @@ -0,0 +1,41 @@ +{ + "workshoptitle": "Working with Gateways in OCI Internet of Things (IoT) Platform", + "help": "Pete.St.Pierre@oracle.com", + "tutorials": [ + { + "title": "Introduction", + "description": "", + "type": "livelabs", + "filename": "../../introduction/introduction.md" + }, + { + "title": "Lab 1: Understand Gateways and Indirectly Connected Devices", + "description": "", + "type": "livelabs", + "filename": "../../understand-gateways-and-indirect-devices/understand-gateways-and-indirect-devices.md" + }, + { + "title": "Lab 2: Create the Gateway and Routing Adapter", + "description": "", + "type": "livelabs", + "filename": "../../create-gateway-and-routing/create-gateway-and-routing.md" + }, + { + "title": "Lab 3: Connect and Monitor Indirect Water Pumps", + "description": "", + "type": "livelabs", + "filename": "../../connect-indirect-water-pumps/connect-indirect-water-pumps.md" + }, + { + "title": "Appendix A: Create Required Factory, Production Line, and WaterPump Assets", + "description": "", + "type": "livelabs", + "filename": "../../appendix-water-pump-assets/appendix-water-pump-assets.md" + }, + { + "title": "Need Help?", + "description": "Template to link to Need Help lab at the end of workshop.", + "filename": "https://raw.githubusercontent.com/oracle-livelabs/common/main/labs/need-help/need-help-freetier.md" + } + ] +}