We recently announced IoT Plug and Play (in preview) as part of a broader effort to reduce the time needed to integrate IoT devices into IoT solutions and IoT Central. What Windows Plug and Play did for connecting hardware devices to Windows, IoT Plug and Play aims to do the same for IoT devices connecting to IoT solutions.
There is some excellent Plug and Play documentation available at https://aka.ms/iotpnpdocs/. Take some time to read through this documentation and familiarize yourself with Plug and Play concepts, including DTDL concepts.
What this blog post attempts to do is take the Plug and Play documentation a step further and apply it to a real world device. In this case, a Raspberry Pi 3 Model B+ with a Sense HAT installed. We will configure the Sense HAT LED and sensors for Plug and Play and develop the underlying code to read from these sensors and control the LED display.
This blog is targeted at device builders (interested in device certification and getting their device listed on our Device Catalog) and solution developers. This blog assumes you have a solid understanding of Raspberry Pi + Linux, Azure IoT services such as IoT Hub and the Azure IoT Device SDKs, as well as developing in Python and C. The steps in this blog are based on the Tutorial: Create and test a device capability model using Visual Studio Code.
Reminder, Plug and Play is in Preview meaning no SLA and support is limited to certain regions (Central US, North Europe, or Japan East), SDKs and for devices only (no IoT Edge support yet).
The Sense HAT device has a LED display and number of sensors built into it including: temperature, humidity and pressure. It also has a well documented Python API. But the biggest reason I like to use it is to avoid fiddling with breadboards, resistors and jumper cables .
Once you have the Sense Hat installed on the Raspberry Pi along with Raspbian 9 (stretch), the next step is to install the Sense HAT Python API.
sudo apt-get update
sudo apt-get install sense-hat
sudo reboot
Note: I haven't found a decently documented C-based API for the Sense HAT. If you know of one, please send me a link to it!
Let's also ensure we have Python 3 installed.
pi@raspberrypi: python3 --version
Python 3.5.3
Now test that the Sense HAT is working with the following Python commands.
pi@raspberrypi:/ $ python3
Python 3.5.3 (default, Sep 27 2018, 17:25:39)
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from sense_hat import SenseHat
>>> sense = SenseHat()
>>> sense.show_message("Hello world!")
If all is successful, a "Hello world!" message should scroll across the LED display.
If you are familiar with DTDL (which is based on JSON-LD) you'll know we need to first develop an Interface for the Sense HAT and then create a Model file. Together these form a Device Capability Model (DCM).
A Interface describes the capabilities that are implemented by a device. Interfaces are reusable and can be used across different capability models.
In our case, the Interface we will develop will describe the Sense HAT's:
In DTDL, the three (3) sensors are considered a Telemetry data type since they emit data from the sensor. The LED display will be a Command data type, as our IoT solution will send a command to the Sense HAT to display a message on the LED.
Follow the "Create the interface file" instructions, substituting the following:
SenseHat as the name of the interface and press Enter. VS Code creates a sample interface file called SenseHat.interface.json.
Replace the contents of the sample SenseHat.interface.json file with the following JSON:
{
"@id": "urn:sense_hat:SenseHat:1",
"@type": "Interface",
"displayName": "Sense Hat Interface",
"contents": [
{
"@type": "Telemetry",
"comment": "Sense Hat Temperature",
"name": "temperature",
"schema": "double",
"unit": "Units/Temperature/celsius"
},
{
"@type": "Telemetry",
"comment": "Sense Hat Relative Humidity",
"name": "humidity",
"schema": "double",
"unit": "Units/Humidity/percent"
},
{
"@type": "Telemetry",
"comment": "Sense Hat Pressure",
"name": "pressure",
"schema": "double",
"unit": "Units/Pressure/kiloPascal"
},
{
"@type": "Command",
"description": "This command will display a message on the Sense Hat LED display.",
"name": "show_message",
"commandType": "synchronous",
"request": {
"name": "message",
"schema": "string"
}
}
],
"@context": "http://azureiot.com/v1/contexts/IoTModel.json"
}
As you can see we've modeled the 3 sensors and the LED display in the Interface using DTDL. For each data type there are a number of DTDL properties which can be set. In the above example I have configured a few, but there are more.
IntelliSense is available when editing the DTDL using VS Code. Try changing any of the "unit" values and you'll see what I mean.
Next, we will want to create a Model file. The model file specifies the interfaces that your 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.
Follow the "Create the model file" instructions, substituting the following:
{
"@id": "urn:sense_hat:SenseHatModel:1",
"@type": "CapabilityModel",
"displayName": "Sense Hat Capability Model",
"implements": [
{
"schema": "urn:sense_hat:SenseHat:1",
"name": "sensehat"
},
{
"schema": "urn:azureiot:DeviceManagement:DeviceInformation:1",
"name": "deviceInfo"
}
],
"@context": "http://azureiot.com/v1/contexts/IoTModel.json"
}
Notice the DeviceInformation interface (known as a standard interface) was automatically added and is required (e.g. for Device Certification).
Lastly download the DeviceInformation interface from the public model repository using the instructions.
As part of the release of Plug and Play, Microsoft is hosting an Azure Certified for IoT repository for you to host your model. Using the model repository is optional; you can store the model on the local device (not covered in this Blog post).
There are two sides to the repository:
Follow the "Publish your model" instructions to publish the following two files to your Company repository:
By default, the model will be stored in the Company repository. You can optionally publish it to the Public repository.
Using VS Code you can generate stub code for your IoT device. However, there are some limitations with this at the moment:
These limitations pose a problem for us, as you will see later (recall, the Sense HAT only ships with a Python API!).
Follow the "Generate code" instructions to generate the stub C code, substituting the following:
Once you have finished generating the stub code you will find the following files in a new sensehat_app folder:
CMakeLists.txt
main.c
pnp_device.c
pnp_device.h
Readme.md
SenseHatModel_impl.c
SenseHatModel_impl.h
\utilities
The primary file you need to concern yourself with is SenseHatModel_impl.c. This is the file where we will add our implementation code.
Now let's add our implementation code to SenseHatModel_impl.c.
Recall that I mentioned earlier that the Sense HAT API is Python-based. To work around this, I am using popen to run the Python 3 process and pass in one of several Python scripts I developed for interacting with the Sense HAT hardware. Popen returns pointer to an open stream, which contains the return value from the Python script. I admit it's a bit of a hack, but it works.
Now it's time to configure your Raspberry Pi.
pi@raspberrypi: cd /home/pi
pi@raspberrypi: git clone https://github.com/khilscher/SenseHATPnP.git
The last command will copy the Sense HAT Python scripts from GitHub to the /home/pi/SenseHatPnP/python_scripts/ folder.
Note: The Python script paths are hard coded in the SenseHatModel_impl.c file. It expects them to be saved under /home/pi/SenseHATPnP/python_scripts/. If you use another location, please update SenseHatModel_impl.c accordingly.
The final step is to build your code on the Raspberry Pi device.
Follow the Ubuntu instructions in the Readme.md file in the sensehat_app folder, substituting the following:
c:\my_working_folder> scp -r sensehat_app pi@<RPi IP address>:/home/pi/azure-iot-sdk-c
Run the remaining build steps contained in the Readme.md. Ignore warnings but fix any errors encountered during the build process.
Running your code involves executing the sensehat_app.
pi@raspberrypi: cd /home/pi/azure-iot-sdk-c/cmake/sensehat_app/
pi@raspberrypi: ./sensehat_app "[IoTHub device connection string]"
After executing the above commands the sensehat_app should be running and sending telemetry to Azure IoT Hub. During startup, watch how it logs the registration of its PnP interfaces with IoT Hub.
...
Info: DEVICEINFO_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: Send telemetry data to IoT Hub
32.26278305053711
Info: SENSEHAT_INTERFACE:: DigitalTwin_InterfaceClient_SendTelemetryAsync successfully sent temperature
32.638771057128906
Info: SENSEHAT_INTERFACE:: DigitalTwin_InterfaceClient_SendTelemetryAsync successfully sent humidity
Info: DigitalTwin Client Core: Processing telemetry callback. confirmationResult=IOTHUB_CLIENT_CONFIRMATION_OK, userContextCallback=0x66c418
Info: DigitalTwin Interface: Invoking telemetry confirmation callback for interface=sensehat, reportedStatus=DIGITALTWIN_CLIENT_RESULT_INVALID, userContextCallback=0xacb4c
Info: DigitalTwin successfully delivered telemetry message for Sensehat::temperature
Info: SENSEHAT_INTERFACE: DigitalTwin successfully delivered telemetry message for <temperature>
Info: DigitalTwin Interface: Invoking telemetry confirmation returned
880.73828125
Info: SENSEHAT_INTERFACE:: DigitalTwin_InterfaceClient_SendTelemetryAsync successfully sent pressure
Info: DigitalTwin Client Core: Processing telemetry callback. confirmationResult=IOTHUB_CLIENT_CONFIRMATION_OK, userContextCallback=0x6538e8
Info: DigitalTwin Interface: Invoking telemetry confirmation callback for interface=sensehat, reportedStatus=DIGITALTWIN_CLIENT_RESULT_INVALID, userContextCallback=0xacb58
Info: DigitalTwin successfully delivered telemetry message for Sensehat::humidity
Info: SENSEHAT_INTERFACE: DigitalTwin successfully delivered telemetry message for <humidity>
Info: DigitalTwin Interface: Invoking telemetry confirmation returned
Info: SENSEHAT_INTERFACE: Queuing of all telemetries to be sent has succeeded
Info: DigitalTwin Client Core: Processing telemetry callback. confirmationResult=IOTHUB_CLIENT_CONFIRMATION_OK, userContextCallback=0x66c418
Info: DigitalTwin Interface: Invoking telemetry confirmation callback for interface=sensehat, reportedStatus=DIGITALTWIN_CLIENT_RESULT_INVALID, userContextCallback=0xacb64
Info: DigitalTwin successfully delivered telemetry message for Sensehat::pressure
Info: SENSEHAT_INTERFACE: DigitalTwin successfully delivered telemetry message for <pressure>
...
Using Device Explorer, you will see messages arriving at your IoT Hub that resemble:
Receiving events...
10/3/2019 10:11:34 AM> Device: [SenseHat], Data:[{"modelInformation":{"capabilityModelId":"urn:sense_hat:SenseHatModel:1","interfaces":{"urn_azureiot_ModelDiscovery_ModelInformation":"urn:azureiot:ModelDiscovery:ModelInformation:1","urn_azureiot_Client_SDKInformation":"urn:azureiot:Client:SDKInformation:1","sensehat":"urn:sense_hat:SenseHat:1","deviceInfo":"urn:azureiot:DeviceManagement:DeviceInformation:1"}}}]Properties:
'iothub-message-schema': 'modelInformation'
SYSTEM>iothub-interface-id=urn:azureiot:ModelDiscovery:ModelInformation:1
SYSTEM>iothub-interface-name=urn_azureiot_ModelDiscovery_ModelInformation
SYSTEM>iothub-connection-device-id=SenseHat
SYSTEM>iothub-connection-auth-method={"scope":"device","type":"sas","issuer":"iothub","acceptingIpFilterRule":null}
SYSTEM>iothub-connection-auth-generation-id=637055717398532553
SYSTEM>iothub-enqueuedtime=10/3/2019 4:11:34 PM
SYSTEM>iothub-message-source=Telemetry
SYSTEM>x-opt-sequence-number=467
SYSTEM>x-opt-offset=222592
SYSTEM>x-opt-enqueued-time=10/3/2019 4:11:34 PM
SYSTEM>EnqueuedTimeUtc=10/3/2019 4:11:34 PM
SYSTEM>SequenceNumber=467
SYSTEM>Offset=222592
SYSTEM>content-type=application/json
10/3/2019 10:11:46 AM> Device: [SenseHat], Data:[{ "temperature": 32.170456 }]Properties:
'iothub-message-schema': 'temperature'
SYSTEM>iothub-interface-name=sensehat
SYSTEM>iothub-connection-device-id=SenseHat
SYSTEM>iothub-connection-auth-method={"scope":"device","type":"sas","issuer":"iothub","acceptingIpFilterRule":null}
SYSTEM>iothub-connection-auth-generation-id=637055717398532553
SYSTEM>iothub-enqueuedtime=10/3/2019 4:11:46 PM
SYSTEM>iothub-message-source=Telemetry
SYSTEM>x-opt-sequence-number=468
SYSTEM>x-opt-offset=223520
SYSTEM>x-opt-enqueued-time=10/3/2019 4:11:46 PM
SYSTEM>EnqueuedTimeUtc=10/3/2019 4:11:46 PM
SYSTEM>SequenceNumber=468
SYSTEM>Offset=223520
SYSTEM>content-type=application/json
The above shows that Plug and Play information is passed between the device and IoT Hub using message properties.
IoT explorer is a new tool that allows you to test Plug and Play interfaces as well as view the telemetry being received by IoT Hub from the device. Think of it as Device Explorer with Plug and Play support.
Notice how the device information is now available from the IoT Hub device twin!
Under Digital Twin, expand urn:sense_hat:SenseHat:1 and select Commands. Send a message to the LED on the Sense HAT.
To view the telemetry from the Sense HAT, select Telemetry and click Start.
Congratulations! You have successfully Plug and Play-enabled your Sense HAT device. But wait, that's not all!
IoT Central has preview support for Plug and Play as well! In my next blog post I will show you how to connect the Sense HAT to IoT Central using Plug and Play.
Hopefully this blog post has provided you the details needed to get your device working with Plug and Play and helps you avoid some of the pitfalls I ran into. I would also love to hear your feedback on what you like (and don't like) about Plug and Play.
Cheers,
Kevin
Twitter: @khilscher
LinkedIn: https://www.linkedin.com/in/kevinhilscher
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.