One of the features VS Code Digital Twin tooling provides is generating stub code based on the Device Capability Model (DCM) you specified.
Follow the steps to use the generated code with the Azure IoT Device C SDK source to compile a device app.
For more details about setting up your development environment for compiling the C Device SDK. Check the instructions for each platform.
-
Make sure all dependencies are installed before building the SDK. For Ubuntu, you can use apt-get to install the right packages.
sudo apt-get update sudo apt-get install -y git cmake build-essential curl libcurl4-openssl-dev libssl-dev uuid-dev
-
Verify that CMake is at least version 2.8.12 and gcc is at least version 4.4.7.
cmake --version gcc --version
In order to providion a device as an IoT Plug and Play using X.509 certificate, we need :
-
Azure IoT SDK C, Public Preview (As of 6/30/2020)
-
Generate a X.509 certificate and a private key
In this instruction, we will use self-signed certificate using script files from Azure IoT SDK
When the certificate is generated, registration id (or device id) is used for the common name.
The default name isPostmanDevice001
. In you prefer to use a different name, please edit line #3 inprep.sh
REGISTRATION_ID='PostmanDevice001'
-
Modify and build a custom HSM library based on
custom_hsm_example
Change
COMMON_NAME
incustom_hsm_example.c
incustom_hsm
folder.[!IMPORTANT]
The name has to match to what is specified for the certificate above.static const char* COMMON_NAME = "PostmanDevice001";
-
Certificate files
The custom HSM library reads certificate from
cert.pem
and the private key fromkey.pem
.With self-signed certificate, 2 PEM files are generated and copied into
cmake
folder for the executable to read from the PEM files.certificate/certs/new-device.cert.pem
=>cmake/new-device.cert.pem
certificate/private/new-device.key.pem
=>cmake/new-device.key.pem
[!TIP]
For production scenarios, you may hard code certificate or read from a secure storage, or read from SIM card, for example.
This script does :
- Clone Azure IoT SDK C
- Copy scripts to generate certificates to
certificate
folder - Copy generated certificate file and private key file to
cmake
folder - Copy
custom_hsm_example.c
toazure-iot-sdk-c/provisioning_client/samples/custom_hsm_example
You can manually create a new device in your IoT Central application, or use Postman collection to create a new device identity.
Group Enrollment : https://docs.microsoft.com/en-us/azure/iot-central/core/concepts-get-connected#connect-devices-using-x509-certificates
Individual Enrollment : https://docs.microsoft.com/en-us/azure/iot-central/core/concepts-get-connected#individual-enrollment-based-device-connectivity
Create individual enrollment in DPS
Group Enrollment : https://docs.microsoft.com/en-us/azure/iot-dps/tutorial-group-enrollments
Individual Enrollment : https://docs.microsoft.com/en-us/azure/iot-dps/quick-create-simulated-device-x509#create-a-device-enrollment-entry-in-the-portal
As a part of provisioning, IoT Plug and Play enabled device app communicates Model ID. Based on this information, IoT Central can associate the appropriate device template.
Important
With Public Preview, IoT central can associate the device template only if the specified device capability model is published to global repository. If the DCM is not publised to the global repository, you need to create a device template with matching Model ID in your IoT Central application
[!NOTE] (as of 6/30/2020)
It seems the device template must be added to IoT Central app for automatic association to work. Without the device template, their first provisioning succeeds but the device is not assigned to the device template. On the 2nd provisioning, the association is made.
Please ensure you specify the correct Capability Model ID in main.c line #45
static const char *digitalTwinSample_CustomProvisioningData = "{"
"\"__iot:interfaces\":"
"{"
"\"CapabilityModelId\": \"urn:iotcpnptest:postmansample:1\" ,"
"\"CapabilityModel\": \"" DIGITALTWIN_DEVICE_CAPABILITY_MODEL_INLINE_DATA "\""
"}"
"}";
In order for a device to be authenticated using X.509, the certificate must be assigned to the device identity.
You can manually upload the certificate (new-device.cert.pem, in our example), but this requires device template assignment. In other words, you need to know which certificate belongs to which device and which device template the device is supposed to use. In certain scenarios, this prevents zero touch provisioning (e.g. the end user simply turns on the device)
Without device template association, you can not browse to device's page to click Connect
button
Once you are in device's overview page, you can click Connect
button
This is the UI to upload certificate
If the scenario requires automatic device template association, please set certificate using REST API
This requires following REST API calls
-
Create a device template
This step is required only if the DCM is not published to the public repository https://docs.microsoft.com/en-us/rest/api/iotcentral/devicetemplates/set
Example :
-
Device Template Id :
PostmanDeviceTemplate
-
DCM ID : urn:iotcpnptest:postmansample:1
-
Method : PUT
-
Headers
Authorization : API Key from IoT Central
Content-Type : application/json -
Body :
{ "types": [ "DeviceModel" ], "displayName": "IoT Central Postman Sample", "capabilityModel": { "@id": "urn:iotcpnptest:postmansample:1", "@type": [ "CapabilityModel" ], "implements": [ { "@id": "urn:iotcpnptest:DeviceInfo:2", "@type": [ : : ```
-
-
Create a new device identify
https://docs.microsoft.com/en-us/rest/api/iotcentral/devices/set
Example :
-
Device Id (or Registration ID) :
PostmanDevice001
-
Method : PUT
-
URL : https://my-iot-central.azureiotcentral.com/api/preview/devices/PostmanDevice001
-
Headers
Authorization : API Key from IoT Central
Content-Type : application/json -
Body :
{ "displayName": "Postman Test Device", "simulated": false, "approved": true }
-
-
Assign a certificate to the new device identity
https://docs.microsoft.com/en-us/rest/api/iotcentral/devices/setattestation
-
Method : PUT
-
URL : https://my-iot-central.azureiotcentral.com/api/preview/devices/PostmanDevice001/attestation
-
Headers
Authorization : API Key from IoT Central
Content-Type : application/json -
Body :
Send base64 encoded certificate value in the body. This is the contents of
new-device.cert.pem
{ "type": "X509Attestation", "x509": { "clientCertificates": { "primary": { "certificate": "<Base64 Certificate contents in a single line>" } } } }
[!TIP]
Make sure to format base64 encoded certificate value in a single line.
"certificate": "MIIFwzCCA6ugAwIBAgIBAjANBg........"
-
You may import Postman collection and environment variables to make REST API calls to IoT Central.
To use the Postman collection, please configure environment variables
-
Import Postman Collection from these json files
./Postman/DeviceFirstProvision.postman_collection.json
./Postman/IoTCentral.postman_environment.json.json
-
Ensure the environment
IoTCentral
is selected
Variable | Value | Notes |
---|---|---|
IoTCDeviceTemplateId | Device Template ID | |
IoTCAppUrl | IoT Central Application URL | from Administration -> Your application |
IoTCDeviceId | Device Id for the device | This must match to the device id (or common name) used to generate X.509 certificate |
IoTCApiKey | API Key to access IoT Central | from Administration -> API tokens |
Run the app with :
cd cmake
./DeviceFirstWithX509 <Scope ID>
After successful provisioning, the new devices should be assigned to the device template
Example output :
./DeviceFirstWithX509 0ne0010B795
hsm_client_x509_init()
File size 2057
Certificate :
-----BEGIN CERTIFICATE-----
MIIFwzCCA6ugAwIBAgIBAjANBgkqhkiG9w0BAQsFADAqMSgwJgYDVQQDDB9BenVy
: <snip>
PLcibK16lQTKuJoF7ge7Movh/LY0KwIz2sgFAcJrcDy0wapMVn15
-----END CERTIFICATE-----
Private Key :
-----BEGIN RSA PRIVATE KEY-----
MIIJKAIBAAKCAgEAzaVCQKL8kkhZAl76znDXWKigwSJDDlBgkuh9woX4/HJlOEEu
: <snip>
1/99jVUWKshRdvz5VjhnlFMbt5j+Xydr3LgTdZ3KL9j1yQO7i7QAe5/ieOM=
-----END RSA PRIVATE KEY-----
hsm_client_x509_init()
hsm_client_x509_interface()
custom_hsm_create()
custom_hsm_get_common_name() : PostmanDevice001
custom_hsm_get_certificate()
custom_hsm_get_key()
Info: Provisioning callback indicates success. iothubUri=iotc-97bbc0fc-d946-41b6-896a-ad9a740cd3b0.azure-devices.net, deviceId=PostmanDevice001
Info: DPS successfully registered. Continuing on to creation of IoTHub device client handle.
custom_hsm_destroy()
hsm_client_x509_interface()
custom_hsm_create()
custom_hsm_get_certificate()
custom_hsm_get_key()
Info: Successfully created DigitalTwin device with connectionString=<****>, deviceHandle=<0x562fe0e36f90>
Info: DigitalTwin Interface : Changing interface state on interface DeviceInfo from DT_INTERFACE_STATE_INVALID to DT_INTERFACE_STATE_CREATED
Info: DEVICEINFO_INTERFACE: Created DIGITALTWIN_INTERFACE_CLIENT_HANDLE successfully for interfaceId=<urn:azureiot:DeviceManagement:DeviceInformation:1>, interfaceInstanceName=<DeviceInfo>, handle=<0x562fe0e31370>
Info: DigitalTwin Interface : Changing interface state on interface Battery from DT_INTERFACE_STATE_INVALID to DT_INTERFACE_STATE_CREATED
Info: BATTERY_INTERFACE: Created DIGITALTWIN_INTERFACE_CLIENT_HANDLE successfully for interfaceId=<urn:seeedkk:Interface:Battery:1>, interfaceInstanceName=<Battery>, handle=<0x562fe0e31ff0>
Info: DigitalTwin Interface : Changing interface state on interface PushButton from DT_INTERFACE_STATE_INVALID to DT_INTERFACE_STATE_CREATED
Info: PUSHBUTTON_INTERFACE: Created DIGITALTWIN_INTERFACE_CLIENT_HANDLE successfully for interfaceId=<urn:seeedkk:Interface:PushButton2:1>, interfaceInstanceName=<PushButton>, handle=<0x562fe0e0ad50>
Info: DigitalTwin Interface : Changing interface state on interface DeviceInfo from DT_INTERFACE_STATE_CREATED to DT_INTERFACE_STATE_BOUND_TO_CLIENT_HANDLE
Info: DigitalTwin Interface : Changing interface state on interface Battery from DT_INTERFACE_STATE_CREATED to DT_INTERFACE_STATE_BOUND_TO_CLIENT_HANDLE
Info: DigitalTwin Interface : Changing interface state on interface PushButton from DT_INTERFACE_STATE_CREATED to DT_INTERFACE_STATE_BOUND_TO_CLIENT_HANDLE
Info: Successfully queued registration message
custom_hsm_get_certificate()
custom_hsm_get_key()
Info: DigitalTwin Client Core: Processing register DigitalTwin interfaces callback. confirmationResult=IOTHUB_CLIENT_CONFIRMATION_OK
Info: DigitalTwin Client Core: Interfaces successfully registered. Register for device method and twin callbacks if needed
Info: Sending reported state for sdkInfo={"$iotin:urn_azureiot_Client_SDKInformation": { "language":{ "value":"C"},"version":{ "value":"0.9.0"},"vendor":{ "value":"Microsoft"}}}
Info: DigitalTwin Interface : Changing interface state on interface DeviceInfo from DT_INTERFACE_STATE_BOUND_TO_CLIENT_HANDLE to DT_INTERFACE_STATE_REGISTERED
Info: DeviceInfoInterface_InterfaceRegisteredCallback with status=<DIGITALTWIN_CLIENT_RESULT_INVALID>, userContext=<0x562fe0498478>
Info: DEVICEINFO_INTERFACE: Interface successfully registered.
Info: DigitalTwin Interface : Changing interface state on interface Battery from DT_INTERFACE_STATE_BOUND_TO_CLIENT_HANDLE to DT_INTERFACE_STATE_REGISTERED
Info: BatteryInterface_InterfaceRegisteredCallback with status=<DIGITALTWIN_CLIENT_RESULT_INVALID>, userContext=<0x562fe0498480>
Info: BATTERY_INTERFACE: Interface successfully registered.
Info: DigitalTwin Interface : Changing interface state on interface PushButton from DT_INTERFACE_STATE_BOUND_TO_CLIENT_HANDLE to DT_INTERFACE_STATE_REGISTERED
Info: PushButtonInterface_InterfaceRegisteredCallback with status=<DIGITALTWIN_CLIENT_RESULT_INVALID>, userContext=<0x562fe0498490>
Info: PUSHBUTTON_INTERFACE: Interface successfully registered.
Info: Interface registration callback invoked, interfaces have been successfully registered
Info: DigitalTwin interfaces successfully registered
Info: DEVICEINFO_INTERFACE: Queued async report read only property for manufacturer
Info: DEVICEINFO_INTERFACE: Queued async report read only property for model
Info: DEVICEINFO_INTERFACE: Queued async report read only property for swVersion
Info: DEVICEINFO_INTERFACE: Queued async report read only property for osName
Info: DEVICEINFO_INTERFACE: Queued async report read only property for processorArchitecture
Info: DEVICEINFO_INTERFACE: Queued async report read only property for processorManufacturer
Info: DEVICEINFO_INTERFACE: Queued async report read only property for totalStorage
Info: DEVICEINFO_INTERFACE: Queued async report read only property for totalMemory
Info: DEVICE_INFO: Queuing of all properties to be reported has succeeded
Info: PnP enabled, running...