Intro
Securely and easily managing an IOT software solution on multiple gateways across the world can be a challenge. However, for gateways running Wind River Helix Device Cloud* there is a clear path to follow that diminishes the challenge. The Wind River Helix Device Cloud allows for complete device lifecycle management, from deploying, to monitoring, to updating, to decommissioning. Wind River Helix Device Cloud has telemetry capabilities as well, allowing it to receive and store data in the cloud, as well as act on data using triggers and alarms. This article will explore a proof of concept that leverages the capabilities of Helix Device Cloud (HDC) and uses the UP Squared* board as the vending machine gateway. HDC will allow the stock of the vending machine to be monitored and set up of automated triggers to restock it automatically as well as view the stock data in a dashboard.
To learn more about the Helix Device Cloud:
https://www.helixdevicecloud.com
To learn more about the UP Squared Board:
https://software.intel.com/en-us/iot/hardware/up-squared-grove-dev-kit
Helix Device Cloud*
- Attributes are for string data that is typically static, like OS version or MAC id.
- Properties are for integer values that will change over time such as temperature.
- Alarms are for events and alerts, like signaling the temperature is too high.
- Methods define a behavior to invoke on the device or client application, like rebooting the device or sending more stock to the vending machine. Methods are implemented in code on the device level but can be triggered from the cloud.
Set-up
sudo add-apt-repository ppa:mraa/mraa sudo apt-get update sudo apt-get install libmraa1 libmraa-dev mraa-tools python-mraa python3-mraa sudo apt-get install libupm-dev libupm-java python-upm python3-upm node-upm upm-examples
Code 1: Commands to Install MRAA and UPM on Ubuntu
In the Helix Device Cloud, the Thing Definition needs to be added for the POC Vending Thing. Figure 4 shows the full overview of the thing, including alarms and the states to configure. There is a chocobar_out_of_stock alarm for alerting that all the chocolate bars have been sold. Figure 5 shows the telemetry properties, the data being collected is telemetry_motion, telemetry_temp, and telemetry_stock_chocobars. Figure 6 shows the method, method_restock, which needs to be configured so that the chocolate bars can be restocked. The fields labeled ‘Key’ are what will be used in the code in the client application. Note that Thing Definition can also be exported and imported as json files, so alternatively import thing_defs.json as shown in Code 2 and update it with your org id instead of manually inputting everything.
Figure 4: POC Vending Thing Definitions and Alarms View
Figure 5: POC Vending Thing Definitions Properties Figure 6: POC Vending Thing Definitions Methods
Figure 6: POC Vending Thing Definitions Methods
[{"ownerOrgId":"yourOrgIdHere","key":"poc_vending_thing","name":"POC Vending Thing","version":13,"autoDefProps":true,"autoDefAttrs":true,"properties":{"telemetry_motion":{"name":"auto:telemetry_motion","calcAggregates":false},"telemetry_stock_chocobars":{"name":"auto:telemetry_stock_chocobars","calcAggregates":false},"telemetry_temp":{"name":"auto:telemetry_temp","calcAggregates":false}},"alarms":{"chocobar_out_of_stock":{"name":"Chocolate Bars Out of Stock","states":[{"name":"Out of Stock","color":"#E61717"},{"name":"Stock Available","color":"#3BBD1B"}]},"high_temp":{"name":"High Temperature Alarm","states":[{"name":"Melted","color":"#EB0505"},{"name":"Normal","color":"#46E61E"}]}},"methods":{"method_restock":{"name":"Restock Chocolate Bars","description":"Send more stock to the vending machine","notificationVariables":{"num_chocobars_sent":{"name":"num_chocobars_sent","type":"int","uiType":"text"}}}}}]
Code 2: thing_def.json for Importing the Thing Definition
Next, configure the Application definition and link it to the POC Vending Thing Definition as per Figure 7. An Application token will automatically be generated after creation, see Figure 8. The token will be used on the device so the client application will know which Application and Thing Definition it will be using.
Figure 7: POC Vending Application
Figure 8: POC Vending Application View
Vending Machine Telemetry
The data collected from the vending machine is where the real value comes into play. The gateway application will collect motion, temperature, and inventory data, and send it to the Helix Device Cloud. The application is a python script ‘iot-poc-vending.py’ that will be turned into a service. Then in HDC, a variety of triggers and alarms can be set up to handle the values coming in. For example, if inventory runs out, a trigger can be set up to send more inventory out to the machine automatically. The Grove sensors will supply the data to upload. To interface with sensors through the Grove shield, add the line below in the code. This will tie into MRAA and GROVEPI. GROVEPI will allow the sensors to talk to the gateway, and MRAA handles the IO pin communications. Note that root access is required to access the shield by default, so when running the python script, it must be run as sudo.
Import upm import mraa # Interface with Grove Sensors mraa.addSubplatform(mraa.GROVEPI, "0 ")
Code 3: Line to Have MRAA use GROVEPI
- Temperature sensor: A0
- Button sensor: D8
- Motion sensor: D7
- Blue motion indicator LED: D2
- Red out of stock indicator LED: D4
- Green purchase indicator LED: D3
temperature_sensor = grove.GroveTemp(512 + 0) button_sensor = grove.GroveButton(512 + 8) motion_sensor = upmMotion.BISS0001(512 + 7) blue_motion_led = grove.GroveLed(512 + 2) red_stock_led = grove.GroveLed(512 + 4) green_stock_led = grove.GroveLed(512 + 3)
Code 4: Grove Sensor Initialization Code
The program’s loop will gather the sensor data, handle items being purchased, publish alarms as needed, and then send data to HDC every 10 seconds.
counter = 0 while running and client.is_alive(): counter += 1 #purchase flow green_stock_led.off() customer_purchase = button_sensor.value() if (stock_chocobars > 0): red_stock_led.off() if (customer_purchase): client.info("Customer purchasing item") green_stock_led.on() stock_chocobars -= 1 else: red_stock_led.on() client.alarm_publish("chocobar_out_of_stock", 0) current_motion= motion_sensor.value() if(current_motion): motion +=1 blue_motion_led.on() else: blue_motion_led.off() celsius = temperature_sensor.value() fahrenheit = celsius * 9.0/5.0 + 32.0; if (fahrenheit >= 90): client.alarm_publish("high_temp", 0) if counter >= TELEMINTERVAL: send_telemetry() # Reset counter after sending telemetry counter = 0 sleep(1)
Code 5: The Main Loop of the Program
To send telemetry to HDC, it is one line of code using telemetry_publish. If the property is not registered in HDC already, publish will fail on the first try and then it will auto register the property (if enabled in the Thing Definition, refer back to Figure 4).
# temperature telemetry to send client.info("Publishing Property: %s to %s", fahrenheit, "telemetry_temp") ts = datetime.datetime.utcnow() status = client.telemetry_publish("telemetry_temp", fahrenheit, cloud_response, timestamp=ts) # Log response from cloud if cloud_response: if status == iot.STATUS_SUCCESS: client.log(iot.LOGINFO, "Telemetry Publish - SUCCESS") else: client.log(iot.LOGERROR, "Telemetry Publish - FAIL")
Code 6: Code Needed for each Telemetry Metric
Properties can be seen in the thing view in the Helix Device Cloud in graph form.
Figure 9: Thing View in HDC
Methods
Methods can be called from the cloud directly, or called automatically with Triggers, which will be discussed in the next section.
In addition to configuring the method in the cloud, the function to call must be registered in the client application so HDC knows which function to execute when it is called.
client.action_register_callback("method_restock", method_restock)
Code 7: Register the Callback Function for the Method
Next, add the function for what to do when the method call is received from the gateway. Here it is mimicking restocking the chocolate bars, so the sent number will be added to the current stock value.
def method_restock(client, params): """ Restocks Chocolate Bars """ global stock_chocobars message = params.get("num_chocobars_sent") stock_chocobars += message p = {} p['chocobars'] = "RESTOCKED" msgstr = "Chocolate Bars Restocked" client.info(msgstr) client.event_publish(msgstr) return (iot.STATUS_SUCCESS, "", p)
Code 8: Code to Handle Method Received from HDC
Triggers
Now that the data and methods have been set up in the cloud and on the device, the triggers feature can be leveraged. This will help monitor the vending machine when data is received for conditions that require attention.
The vending machine needs to send out an email alert if the temperature gets too high, as the chocolate inside might melt. To create a new rule, go to the Developer -> Triggers and click on New Trigger.
Name the trigger ‘POC Vending High Temperature Alarm’. Right click on the trigger event and choose Event type of alarm.change, add the Thing key, Alarm Key of high_temp, Alarm State of 0 for melted as configured back in the Thing Definition, and 0 as the Time in condition (see Figure 10). From the Trigger actions menu on the side, go into Networking and drag out email.send node. Alternatively, http, mqtt, or sms messages could be sent instead. Configure the email node with the message, subject, and to email (see Figure 11). Then, at the bottom of the Trigger actions, expand End and drag out a Success and Failure node. Lastly click on the triangles at the bottom of the nodes and drag it to appropriate node (see Figure 12 as reference). Now that the Trigger has been created, view it and click on Start to activate it.
Figure 10: Config of Initial Trigger Event
Figure 11: Email Configuration for the Trigger
Figure 12: Node Flow of the High Temperature Alarm Trigger
The Trigger to auto restock the chocolate bars is very similar except with an added method.exec node from the Method actions and an alarm.publish from Alarm. Select the POC Vending Thing as the Thing Definition. This is how it knows which methods are available. Then select method_restock as the method. Add the Thing Key to execute the method on, the Ack Timeout, and the method input (which is num_chocobars_sent). Also add the alarm.publish to change the alarm state of chocobar_out_of_stock to 1 to indicate they are back in stock now. See Figure 13, 14, and 15 for reference.
Figure 13: Config of method.exec for Auto Restocking
Figure 14: Config of alarm.publish to Change Alarm State back to Chocobars in Stock
Figure 15: Node Flow of the Method Restock Trigger
Figure 16: Triggers for Restocking and High Temperature are Started
Deployment
There are 4 files needed to run the client application on the device: iot-vending-connect.cfg, iot-poc-vending.py, device_id, and HDC_VendingMachine.service. The service file will turn the code into a service running continuously on the gateway, even after reboot. The device_id file should already be on the device in the device-cloud folder. The iot-vending-connect.cfg will link the application up to cloud and contains the HDC host name and the Application Token; see the getting started guide and Code 9 for more information. Make sure device_id and iot-vending-connect.cfg are in the same directory and update the config_dir in iot-poc-vending.py to their location.
{ "cloud": { "host": "yourHostName", "port": 8883, "token": "yourAppToken" }, "qos_level": 1, "validate_cloud_cert": true }
Code 9: iot-vending-connect.cfg file
#!/usr/bin/python from __future__ import print_function import argparse import errno import random import signal import sys import os import math import mraa import time, sys, signal, atexit from upm import pyupm_grove as grove from upm import pyupm_biss0001 as upmMotion # Interface with Grove sheild mraa.addSubplatform(mraa.GROVEPI, "0") import datetime import time from time import sleep head, tail = os.path.split(os.path.dirname(os.path.realpath(__file__))) sys.path.insert(0, head) import device_cloud as iot #from device_cloud import osal B=3975 temperature_sensor = grove.GroveTemp(512 + 0) button_sensor = grove.GroveButton(512 + 8) motion_sensor = upmMotion.BISS0001(512 + 7) blue_motion_led = grove.GroveLed(512 + 2) red_stock_led = grove.GroveLed(512 + 4) green_stock_led = grove.GroveLed(512 + 3) running = True # Return status once the cloud responds cloud_response = False # Second intervals between telemetry TELEMINTERVAL = 10 def sighandler(signum, frame): """ Signal handler for exiting app """ global running if signum == signal.SIGINT: print("Received SIGINT, stopping application...") running = False def method_restock(client, params): """ Restocks Chocolate Bars """ global stock_chocobars message = params.get("num_chocobars_sent") stock_chocobars += message p = {} p['chocobars'] = "RESTOCKED" msgstr = "Chocolate Bars Restocked" client.info(msgstr) client.event_publish(msgstr) return (iot.STATUS_SUCCESS, "", p) def send_telemetry(): global motion, stock_chocobars, temperature # temperature telemetry to send client.info("Publishing Property: %s to %s", fahrenheit, "telemetry_temp") ts = datetime.datetime.utcnow() status = client.telemetry_publish("telemetry_temp", fahrenheit, cloud_response, timestamp=ts) # Log response from cloud if cloud_response: if status == iot.STATUS_SUCCESS: client.log(iot.LOGINFO, "Telemetry Publish - SUCCESS") else: client.log(iot.LOGERROR, "Telemetry Publish - FAIL") # motion telemetry to send client.info("Publishing Property: %s to %s", motion, "telemetry_motion") ts = datetime.datetime.utcnow() status = client.telemetry_publish("telemetry_motion", motion, cloud_response, timestamp=ts) motion = 0 # Log response from cloud if cloud_response: if status == iot.STATUS_SUCCESS: client.log(iot.LOGINFO, "Telemetry Publish - SUCCESS") else: client.log(iot.LOGERROR, "Telemetry Publish - FAIL") # chocolate bar stock telemetry to send client.info("Publishing Property: %s to %s", stock_chocobars, "telemetry_stock_chocobars") ts = datetime.datetime.utcnow() status = client.telemetry_publish("telemetry_stock_chocobars", stock_chocobars, cloud_response, timestamp=ts) # Log response from cloud if cloud_response: if status == iot.STATUS_SUCCESS: client.log(iot.LOGINFO, "Telemetry Publish - SUCCESS") else: client.log(iot.LOGERROR, "Telemetry Publish - FAIL") if __name__ == "__main__": global motion, stock_chocobars, temperature stock_chocobars = 2 temperature = 0 motion = 0 signal.signal(signal.SIGINT, sighandler) # Initialize client app_id = "iot-poc-vending" client = iot.Client(app_id) # Use the .cfg file inside the directory config_file = "iot-vending-connect.cfg" client.config.config_file = config_file # Look in this directory config_dir = "/home/upsquared/device_cloud/demo/" client.config.config_dir = config_dir # Finish configuration and initialize client client.initialize() # Set action callbacks client.action_register_callback("method_restock", method_restock) # Connect to Cloud if client.connect(timeout=10) != iot.STATUS_SUCCESS: client.error("Failed") sys.exit(1) counter = 0 while running and client.is_alive(): counter += 1 #purchase flow green_stock_led.off() customer_purchase = button_sensor.value() if (stock_chocobars > 0): red_stock_led.off() if (customer_purchase): client.info("Customer purchasing item") green_stock_led.on() stock_chocobars -= 1 else: red_stock_led.on() client.alarm_publish("chocobar_out_of_stock", 0) current_motion= motion_sensor.value() if(current_motion): motion +=1 blue_motion_led.on() else: blue_motion_led.off() celsius = temperature_sensor.value() fahrenheit = celsius * 9.0/5.0 + 32.0; if (fahrenheit >= 90): client.alarm_publish("high_temp", 0) if counter >= TELEMINTERVAL: send_telemetry() # Reset counter after sending telemetry counter = 0 sleep(1) client.disconnect(wait_for_replies=True)
Code 10: Full Code for iot-poc-vending.py
The HDC_VendingMachine.service file is shown below. The service should start after the network.target starts, and then start the python code. Place this file in /lib/systemd/system/ with sudo cp.
[Unit] Description=HDC POC After=network.target [Service] ExecStart=/home/upsquared/device_cloud/demo/iot-poc-vending.py Restart=always User=root StandardOutput=journal StandardError=journal KillMode=process KillSignal=SIGINT [Install] WantedBy=multi-user.target
Code 11: HDC_Vendingmachine.service File
Make the python file and the service file executable, and then enable and start the service. Look in /var/log/syslog for any errors on startup, and also for the info logs from the application.
chmod +x /home/upsquared/device_cloud/demo/iot-poc-vending.py chmod +x /lib/systemmd/system/HDC_VendingMachine.service
Code 12: Change iot-poc-vending.py to Executable
sudo systemctl enable HDC_VendingMachine.service sudo systemctl start HDC_VendingMachine.service
Code 13: Enable and Start the Service
Dashboard
Now that the client app is configured in the cloud, and running on the device, a dashboard can be setup to see the data, alarms, and other important information at a glance. There is some customization available with layout and colors, however it is designed more for testing not as an industrial grade solution.
When creating the dashboard, only Name, Thing definition, and Date/Time are required.
Figure 17: POC Vending Telemetry Dashboard Setup
Next, the layout can be designed as wanted with different dashboard widget types. The property graph widget is used for all three of the telemetry properties: temperature, stock, and motion. The current alarm state widget is used for the Temperature alarm and the Alarm History for the out of stock alarm.
Figure 18: Dashboard Layout Design
Each widget needs to be configured with the Thing Key and property or alarm to display.
Figure 19: Property Graph Widget for Temperature
Figure 20: Current Alarm State widget for Temperature Alarm
The final dashboard with the data and alarms looks like Figure 21 below.
Figure 21: Dashboard View
Summary
Our vending machine code has now been successfully deployed on Helix Device Cloud. Temperature and stock data is being monitored with automated triggers. Motion data can be referenced as time goes on to monitor foot traffic around the vending machine. Any future updates to the program and overall gateway health can be deployed and monitored using HDC.
To purchase HDC visit https://www.windriver.com/company/contact/index.html or email sales@windriver.com
About the Author
Whitney Foster is a software engineer at Intel in the Software and Services Group working on scale enabling projects for Internet of Things.
Notices
No license (express or implied, by estoppel or otherwise) to any intellectual property rights is granted by this document.
Intel disclaims all express and implied warranties, including without limitation, the implied warranties of merchantability, fitness for a particular purpose, and non-infringement, as well as any warranty arising from course of performance, course of dealing, or usage in trade.
This document contains information on products, services and/or processes in development. All information provided here is subject to change without notice. Contact your Intel representative to obtain the latest forecast, schedule, specifications and roadmaps.
The products and services described may contain defects or errors known as errata which may cause deviations from published specifications. Current characterized errata are available on request.
Copies of documents which have an order number and are referenced in this document may be obtained by calling 1-800-548-4725 or by visiting www.intel.com/design/literature.htm.
Intel, the Intel logo, and Intel RealSense are trademarks of Intel Corporation in the U.S. and/or other countries.
*Other names and brands may be claimed as the property of others
© 2018 Intel Corporation.