Introduction
I was on an Azure Sphere technical call with a customer a while ago, and one of the engineers made an interesting statement regarding their IoT plans. The comment was that they really did not understand what data they wanted to collect or what they would do with it. He said that it would be helpful to quickly deploy sensors across the production floor on different equipment to collect data and see if they could identify any patterns or conditions that they would want to monitor long term. This got me to thinking about Azure Sphere and how something like this could be accomplished.
My idea was simple, create a high level application that managed the connection to an IoTHub to send data. The high level application would interact with one or two real-time applications running on the MT3620 M4 cores. The real-time applications would all implement a basic set of commands/responses that would allow the high level application to collect data from the real-time applications without having knowledge of what sensors the real-time applications interacted with. This way I could create library of real-time applications that read different sensors and the user could swap out real-time applications depending on what sensor(s) they needed at the time.
But how could the high level application know how to create telemetry messages if it does not have knowledge about the sensors connected to the real-time applications? What if each real-time application constructed the telemetry JSON message? After all, the real-time application knows the sensor(s) that it reads and knows what types of data it collects. With this loose plan I got to work!
Common Interface
For my idea to work I needed to define a common set of commands/responses between the high level and real-time applications. Each real-time application uses these same enumerations, but may have additional commands as well. For example each application can also just return the sensor data for implementations where the high level application needs to read the sensor data and make decisions based on the data. I also had a little fun and created a feature where the real-time application could send unsolicited telemetry messages based on an interval (in seconds) pushed down from the high level application.
IC_GENERIC_UNKNOWN
- no-op command
IC_GENERIC_HEARTBEAT
- Test command, RTApp will echo the command back to the high level application
IC_GENERIC_READ_SENSOR_RESPOND_WITH_TELEMETRY
- Instructs the RTApp to read its sensor(s) and return valid JSON telemetry. If there is a hardware error, the JSON will include an error message.
IC_GENERIC_SAMPLE_RATE
- When set to an integer > 0 instructs the RTApp to periodically (at the rate requested in Seconds) read its sensor(s) and return the telemetry JSON automatically without the high level application making a request. Set the period to zero to disable the auto mode.
High Level Application
For the high level application to connect to the real-time applications it needs to know the real-time applications Component ID/GUID (defined in the app_manifest.json) file. I generated a new GUID and modified the last few characters to indicate if the real-time application was on the device as the first real-time application or the second. These GUIDs also need to be included in the app_manifest.json and launch.json files.
- Real-time app1 Component ID: f6768b9a-e086-4f5a-8219-5ffe9684b001
- Real-time app2 Component ID: f6768b9a-e086-4f5a-8219-5ffe9684b002
When the high level application starts up it tries to connect to the two real time applications.
High Level Application Features
The high level application connects to an Azure IoTHub and implements three different Device Twins. Using the device twins I have cloud based control over how often each real time application sends up telemetry JSON.
- telemetryTimerAllApps
- Requests each real time application to read their sensors and return telemetry JSON at the interval requested
- Takes an integer >= 0
- 0 == Don't request any telemetry
- integer > 0 requests telemetry every X seconds
- The timer default is to request telemetry data ever 15 seconds
- rtApp1AutoTelemetryTimer
- Requests real time application #1 to automatically read its sensors and return telemetry JSON at the interval requested
- Takes an integer >= 0
- 0 == Stop the auto telemetry function
- Integer > 0 requests telemetry every X seconds
- The default is 0, off
- rtApp2AutoTelemetryTimer
- Requests real time application #2 to automatically read its sensors and return telemetry JSON at the interval requested
- Takes an integer >= 0
- 0 == Stop the auto telemetry function
- Integer > 0 requests telemetry every X seconds
- The default is 0, off
Real-time Applications
I had already been working on a collection of Azure RTOS real-time applications and they already implement a command/response interface. I modified my applications to make sure that the base command enumerations matched my generic enumerations. Then I did lots of builds and tests. For each real-time application I created builds for both Avnet Starter Kits (Rev1 and Rev2) and when required, separate builds to allow users to move sensors to different CLICK sockets on the boards. I also generated complete sets of binaries that used both the App1 GUID and the App2 GUID.
My AzureRTOS Github repo is here. Checkout the binaries folders to see all the pre-built binaries. Note the naming convention for the files ApplicationName-<Starter Kit Rev1 | Starter Kit Rev2 | (missing if app supports both kits)>-<ClickBoardSocket | (missing if both CLICK sites are supported)>-<App1 | App2>-<application Version Number>.imagepackage
Let's see it work!
- Pull the DevX Examples Repo to get the high level application
- git clone --recurse-submodules https://github.com/Azure-Sphere-DevX/AzureSphereDevX.Examples.git <target directory>
- Pull the AzureRTOS Example Repo to get the real-time applications and pre-built binaries
- git clone --recurse-submodules https://github.com/Avnet/avnet-azure-sphere-AzureRTOS.git <target directory>
- Make sure your device is in development mode
- azsphere device enable-development
- Sideload two real-time applications
- Open a PowerShell window
- Sideload App1
- Change to the directory */binaries/RTApp-1-Images
- azsphere device sideload deploy -p .\AvnetAlsPt19_RTApp-App1-V1.imagepackage
- Sideload App2
- Change to the directory */binaries/RTApp-2-Images
- azsphere device sideload deploy -p .\AvnetLPS22HH-RTApp-App2-V1.imagepackage
- Open Visual Studio 2019 or Visual Studio Code
- Open a folder and navigate to the DevX Example repo you pulled, open the intercore_generic_example folder
- Open the app_manifest.json file and add your DPS Scope ID, IoTHub Hostname, and Azure Sphere Tenant GUID
- If you don't have the IoTHub and DPS setup you can also disable the IoTHub connection by . . .
- Open main.c and comment out line #151: dx_azureConnect(&dx_config, NETWORK_INTERFACE, IOT_PLUG_AND_PLAY_MODEL_ID);
- Open app_manifest.json and replace "REPLACE_WITH_YOUR_AZURE_SPHERE_TENANT_ID" with "00000000-0000-0000-0000-000000000000"
- Open main.c and comment out line #151: dx_azureConnect(&dx_config, NETWORK_INTERFACE, IOT_PLUG_AND_PLAY_MODEL_ID);
- If you don't have the IoTHub and DPS setup you can also disable the IoTHub connection by . . .
- Build and run the high level application (F5)
Video Demo of the solution in action