Tutorial: Create and test a device capability model using Visual Studio Code

This tutorial shows you how, as a device developer, to use Visual Studio Code to create a device capability model. You can use the model to generate skeleton code to run on a device that connects to an Azure IoT Hub instance in the cloud.

The section in this tutorial that describes how to build the generated skeleton code assumes you're using Windows.

In this tutorial, you learn how to:

  • Create a device capability model
  • Generate skeleton device code from the model
  • Implement the stubs in the generated code
  • Run the code to test the interactions with an IoT hub

Prerequisites

To work with the device capability model in this tutorial, you need:

  • Visual Studio Code: VS Code is available for multiple platforms

  • Azure IoT Tools for VS Code extension pack. Use the following steps to install the extension pack in VS Code:

    1. In VS Code, select the Extensions tab.
    2. Search for Azure IoT Tools.
    3. Select Install.

To build the generated C code on Windows in this tutorial, you need:

To test your device code in this tutorial, you need:

Use Azure Cloud Shell

Azure hosts Azure Cloud Shell, an interactive shell environment that you can use through your browser. You can use either Bash or PowerShell with Cloud Shell to work with Azure services. You can use the Cloud Shell preinstalled commands to run the code in this article without having to install anything on your local environment.

To start Azure Cloud Shell:

Option Example/Link
Select Try It in the upper-right corner of a code block. Selecting Try It doesn't automatically copy the code to Cloud Shell. Example of Try It for Azure Cloud Shell
Go to https://shell.azure.com, or select the Launch Cloud Shell button to open Cloud Shell in your browser. Launch Cloud Shell in a new window
Select the Cloud Shell button on the top-right menu bar in the Azure portal. Cloud Shell button in the Azure portal

To run the code in this article in Azure Cloud Shell:

  1. Start Cloud Shell.

  2. Select the Copy button on a code block to copy the code.

  3. Paste the code into the Cloud Shell session by selecting Ctrl+Shift+V on Windows and Linux or by selecting Cmd+Shift+V on macOS.

  4. Select Enter to run the code.

Model your device

You use the digital twin definition language to create a device capability model. A model typically consists of multiple interface definition files and a single model file. The Azure IoT Tools for VS Code includes tools to help you create and edit these JSON files.

Create the interface file

To create an interface file that defines the capabilities of your IoT device in VS Code:

  1. Create a folder called devicemodel.

  2. Launch VS Code and use Ctrl+Shift+P to open the command palette.

  3. Enter Plug and Play and then select the IoT Plug & Play: Create Interface command.

  4. Browse to and select the devicemodel folder you created.

  5. Then enter EnvironmentalSensor as the name of the interface and press Enter. VS Code creates a sample interface file called EnvironmentalSensor.interface.json.

  6. Replace the contents of this file with the following JSON and replace {your name} in the @id field with a unique value. Use only the characters a-z, A-Z, 0-9, and underscore. For more information, see Digital Twin identifier format. The interface ID must be unique to save the interface in the repository:

    {
      "@id": "urn:{your name}:EnvironmentalSensor:1",
      "@type": "Interface",
      "displayName": "Environmental Sensor",
      "description": "Provides functionality to report temperature, humidity. Provides telemetry, commands and read-write properties",
      "comment": "Requires temperature and humidity sensors.",
      "contents": [
        {
          "@type": "Property",
          "displayName": "Device State",
          "description": "The state of the device. Two states online/offline are available.",
          "name": "state",
          "schema": "boolean"
        },
        {
          "@type": "Property",
          "displayName": "Customer Name",
          "description": "The name of the customer currently operating the device.",
          "name": "name",
          "schema": "string",
          "writable": true
        },
        {
          "@type": "Property",
          "displayName": "Brightness Level",
          "description": "The brightness level for the light on the device. Can be specified as 1 (high), 2 (medium), 3 (low)",
          "name": "brightness",
          "writable": true,
          "schema": "long"
        },
        {
          "@type": [
            "Telemetry",
            "SemanticType/Temperature"
          ],
          "description": "Current temperature on the device",
          "displayName": "Temperature",
          "name": "temp",
          "schema": "double",
          "unit": "Units/Temperature/fahrenheit"
        },
        {
          "@type": [
            "Telemetry",
            "SemanticType/Humidity"
          ],
          "description": "Current humidity on the device",
          "displayName": "Humidity",
          "name": "humid",
          "schema": "double",
          "unit": "Units/Humidity/percent"
        },
        {
          "@type": "Telemetry",
          "name": "magnetometer",
          "displayName": "Magnetometer",
          "comment": "This shows a complex telemetry that contains a magnetometer reading.",
          "schema": {
            "@type": "Object",
            "fields": [
              {
                "name": "x",
                "schema": "integer"
              },
              {
                "name": "y",
                "schema": "integer"
              },
              {
                "name": "z",
                "schema": "integer"
              }
            ]
          }
        },
        {
          "@type": "Command",
          "name": "turnon",
          "response": {
            "name": "turnon",
            "schema": "string"
          },
          "comment": "This Commands will turn-on the LED light on the device.",
          "commandType": "synchronous"
        },
        {
          "@type": "Command",
          "name": "turnoff",
          "comment": "This Commands will turn-off the LED light on the device.",
          "response": {
            "name": "turnoff",
            "schema": "string"
          },
          "commandType": "synchronous"
        }
      ],
      "@context": "http://azureiot.com/v1/contexts/IoTModel.json"
    }
    

    This interface defines device properties such as Customer Name, telemetry types such as Temperature, and commands such as turnon.

  7. Add a command capability called blink at the end of this interface file. Be sure to add a comma before you add the command. Try typing the definition to see how intellisense, autocomplete, and validation can help you edit an interface definition:

    {
      "@type": "Command",
      "description": "This command will begin blinking the LED for given time interval.",
      "name": "blink",
      "request": {
        "name": "blinkRequest",
        "schema": {
          "@type": "Object",
          "fields": [
            {
              "name": "interval",
              "schema": "long"
            }
          ]
        }
      },
      "response": {
        "name": "blinkResponse",
        "schema": "string"
      },
      "commandType": "synchronous"
    }
    
  8. Save the file.

Create the model file

The model file specifies the interfaces that your IoT Plug and Play device implements. There are typically at least two interfaces in a model - one or more that define the specific capabilities of your device, and a standard interface that all IoT Plug and Play devices must implement.

To create a model file that specifies the interfaces your IoT Plug and Play device implements in VS Code:

  1. Use Ctrl+Shift+P to open the command palette.

  2. Enter Plug and Play and then select the IoT Plug & Play: Create Capability Model command. Then enter SensorboxModel as the name of the model. VS Code creates a sample interface file called SensorboxModel.capabilitymodel.json.

  3. Replace the contents of this file with the following JSON and replace {your name} in the @id field and in the EnvironmentalSensor interface with the same value you used in the EnvironmentalSensor.interface.json file. The interface ID must be unique to save the interface in the repository:

    {
      "@id": "urn:{your name}:SensorboxModel:1",
      "@type": "CapabilityModel",
      "displayName": "Environmental Sensorbox Model",
      "implements": [
        {
          "schema": "urn:{your name}:EnvironmentalSensor:1",
          "name": "environmentalSensor"
        },
        {
          "schema": "urn:azureiot:DeviceManagement:DeviceInformation:1",
          "name": "deviceinfo"
        }
      ],
      "@context": "http://azureiot.com/v1/contexts/CapabilityModel.json"
    }
    

    The model defines a device that implements your EnvironmentalSensor interface and the standard DeviceInformation interface.

  4. Save the file.

Download the DeviceInformation interface

Before you can generate skeleton code from the model, you must create a local copy of the DeviceInformation from the public model repository. The public model repository already contains the DeviceInformation interface.

To download the DeviceInformation interface from the public model repository using VS Code:

  1. Use Ctrl+Shift+P to open the command palette.

  2. Enter Plug and Play, select the Open Model Repository command, and then select Open Public Model Repository.

  3. Select Interfaces, then select the device information interface with ID urn:azureiot:DeviceManagement:DeviceInformation:1, and then select Download.

You now have the three files that make up your device capability model:

  • urn_azureiot_DeviceManagement_DeviceInformation_1.interface.json
  • EnvironmentalSensor.interface.json
  • SensorboxModel.capabilitymodel.json

Publish the model

For the Azure IoT explorer tool to read your device capability model, you need to publish it in your company repository. To publish from VS Code, you need the connection string for the company repository:

  1. Navigate to the Azure Certified for IoT portal.

  2. Use your Microsoft work account to sign in to the portal.

  3. Select Company repository and then Connection strings.

  4. Copy the connection string.

To open your company repository in VS Code:

  1. Use Ctrl+Shift+P to open the command palette.

  2. Enter Plug and Play and then select the IoT Plug & Play: Open Model Repository command.

  3. Select Open Organizational Model Repository and paste in your connection string.

  4. Press Enter to open your company repository.

To publish your device capability model and interfaces to your company repository:

  1. Use Ctrl+Shift+P to open the command palette.

  2. Enter Plug and Play and then select the IoT Plug & Play: Submit files to Model Repository command.

  3. Select the EnvironmentalSensor.interface.json and SensorboxModel.capabilitymodel.json files and select OK.

Your files are now stored in your company repository.

Generate code

You can use the Azure IoT Tools for VS Code to generate skeleton C code from your model. To generate the skeleton code in VS Code:

  1. Use Ctrl+Shift+P to open the command palette.

  2. Enter Plug and Play and then select the IoT Plug & Play: Generate Device Code Stub command.

  3. Select your SensorboxModel.capabilitymodel.json capability model file.

  4. Enter sensorbox_app as the project name.

  5. Choose ANSI C as the language.

  6. Choose Via IoT Hub device connection string as the way to connect.

  7. Choose CMake Project on Windows as project template.

  8. Choose Via Vcpkg as way to include the device SDK.

VS Code generates the skeleton C code and saves the files in the sensorbox_app folder in the modelcode folder. VS Code opens a new window that contains the generated code files.

Update the generated code

Before you can build and run the code, you need to implement the stubbed properties, telemetry, and commands.

To provide implementations for the stubbed code in VS Code:

  1. Open the SensorboxModel_impl.c file.

  2. Add code to implement the stubbed functions.

  3. Save your changes.

Build the code

Before you run the code to test your IoT Plug and Play device with an Azure IoT hub, you need to compile the code.

Follow the instructions in the Readme.md file in the sensorbox_app folder to build and run the code on Windows. The following section includes instructions for retrieving a device connection string to use when you run the device code.

Test the code

When you run the code, it connects to IoT Hub and starts sending sample telemetry and property values. The device also responds to commands sent from IoT Hub. To verify this behavior:

  1. To create an IoT hub:

    az group create --name environmentalsensorresources --location centralus
    az iot hub create --name {your iot hub name} \
      --resource-group environmentalsensorresources --sku F1
    
  2. Add a device to your IoT hub and retrieve its connection string:

    az iot hub device-identity create --hub-name {your iot hub name} --device-id MyPnPDevice
    az iot hub device-identity show-connection-string --hub-name {your iot hub name} --device-id MyPnPDevice --output table
    

    Make a note of the connection string.

  3. At a command prompt, navigate to the azure-iot-sdk-c folder where you built the SDK and samples. Then navigate to the cmake\sensorbox_app\Release folder.

  4. Run the following command:

    sensorbox_app.exe {your device connection string}
    
  5. Use the Azure IoT explorer tool to interact with the IoT Plug and Play device connected to your IoT hub. For more information, see Install and use Azure IoT explorer.

Next steps

Now that you've built an IoT Plug and Play ready for certification, learn how to: