IoT Plug and Play - A deep dive using a Raspberry Pi and Sense HAT
Published Oct 15 2019 11:12 AM 5,732 Views
Microsoft

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).

 

Prerequisites

  • Install the software prerequisites on your PC that are listed here
  • Create an IoT Hub in either Central US, North Europe, or Japan East.
  • Add an IoT Device to your IoT Hub.Make a note of the connection string.
  • Setup your Azure Cloud Shell as described here
  • Raspberry Pi 3 Model B+ with Raspbian GNU/Linux 9 (stretch) installed
  • Sense HAT installed on the Raspberry Pi device

Sense HATSense HAT

 

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 :smile:.

 

Getting Started

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.

 

Model your device

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).

 

Creating an Interface

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:

  • temperature sensor
  • humidity sensor
  • pressure sensor
  • LED display

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.

 

Creating a Model file

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:

 

  • Enter SenseHatModel as the name of the model. VS Code creates a sample interface file called SenseHatModel.capabilitymodel.json.
  • Replace the contents of the sample SenseHatModel.capabilitymodel.json file (filename must end with .capabilitymodel.json) with the following JSON:

 

{
  "@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.

 

Publish the model

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:

 

  • Public repository - where publicly published Models and Interfaces are stored.
  • Company repository - where private Models and Interfaces are stored (e.g. prior to publishing publicly or when you have feature/device that is specific to your organization).

Follow the "Publish your model" instructions to publish the following two files to your Company repository:

 

  • SenseHat.interface.json
  • SenseHatModel.capabilitymodel.json

By default, the model will be stored in the Company repository. You can optionally publish it to the Public repository.

 

Generate stub code

Using VS Code you can generate stub code for your IoT device. However, there are some limitations with this at the moment:

 

  • You will need to use Azure IoT Device SDKs that are Plug and Play-aware. Currently, only the C and Node Device SDKs are Plug and Play-aware. The other SDKs (e.g. Python) will be updated to be Plug and Play-aware in the future.
  • VS Code only generates stub code in C (sorry Node :cry:).

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:

 

  • SenseHatModel.capabilitymodel.json as the model file
  • sensehat_app as the app name

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.

 

Update the stub code

Now let's add our implementation code to SenseHatModel_impl.c.

 

  1. Using VS Code, open the sensehat_app\SenseHatModel_impl.c file.
  2. Paste in the implementation code from https://github.com/khilscher/SenseHATPnP/blob/master/sensehat_app/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.

 

Configure the Raspberry Pi

Now it's time to configure your Raspberry Pi.

 

  1. SSH into your Raspberry Pi. Note: I am logged in as the user "pi" and I will save everything to the pi user home directory.
  2. Run the following commands:

 

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.

 

Build the code

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:

 

  1. The Readme.md references a azure-iot-sdk-c-pnp folder. This is incorrect. It is azure-iot-sdk-c.
  2. Open a cmd prompt in your working folder on your PC.
  3. Use scp to copy the sensehat_app folder from your PC to the azure-iot-sdk-c folder on you Pi device.

 

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 the code

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

 

Plug and Play testing with IoT explorer

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.

 

  1. Download and install IoT explorer on you PC.
  2. Launch IoT explorer and connect to your IoT Hub using the connection string.
  3. Select Settings in the top right corner.
  4. Under "We'll look for your model definition in the following locations (in order)" click New and add the connection string to your Company model repository from the Azure Certified for IoT repository If you've published your device model to the Public repository, you can skip this step.
  5. Select Save and close Settings.
  6. Select your Plug and Play device and select Device Twin.

IoTExplorer1.jpg

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.

IoTExplorer2.jpg

 

To view the telemetry from the Sense HAT, select Telemetry and click Start.

 

IoTExplorer3.jpg

 

Congratulations! You have successfully Plug and Play-enabled your Sense HAT device. But wait, that's not all!

 

IoT Central

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.

 

Conclusion

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

 

References

Version history
Last update:
‎Oct 15 2019 08:12 PM
Updated by: