Demo application for using the AT&T IoT Starter Kit Powered by AWS.
Dependencies: SDFileSystem
Fork of ATT_AWS_IoT_demo by
IoT Starter Kit Powered by AWS Demo
This program demonstrates the AT&T IoT Starter Kit sending data directly into AWS IoT. It's explained and used in the Getting Started with the IoT Starter Kit Powered by AWS on starterkit.att.com.
What's required
- AT&T IoT LTE Add-on (also known as the Cellular Shield)
- NXP K64F - for programming
- microSD card - used to store your AWS security credentials
- AWS account
- Python, locally installed
If you don't already have an IoT Starter Kit, you can purchase a kit here. The IoT Starter Kit Powered by AWS includes the LTE cellular shield, K64F, and a microSD card.
Diff: PythonGUI/ATT_AWS_IoT_Demo_GUI.py
- Revision:
- 17:46780b2de3e4
- Child:
- 19:488dad1e168e
diff -r 02008a2a2569 -r 46780b2de3e4 PythonGUI/ATT_AWS_IoT_Demo_GUI.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PythonGUI/ATT_AWS_IoT_Demo_GUI.py Fri Dec 02 22:49:17 2016 +0000 @@ -0,0 +1,341 @@ +''' +/* + * Copyright 2010-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + ''' + +from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTShadowClient +import sys +import os.path +import logging +import time +import json +import getopt +import Tkinter +import tkFont +import time +from threading import Timer +from Tkinter import * + +####################################################################################################################### +# +# Set up our Tkinter based GUI +# +####################################################################################################################### +accepted = ["0", "1", "2", "4", "7"] +colorDict = {0 : 'off', 1 : 'red', 2 : 'green', 4 : "blue", 7 : "white"} + +window = Tkinter.Tk() +window.wm_title("AT&T AWS IoT Demo") + +topframe = Frame(window) +topframe.pack() +bottomframe = Frame(window) +bottomframe.pack( side = BOTTOM ) + +statusLabel = StringVar() +statusString = StringVar() + +# Control callbacks +def setStatus(statusStr): + if(statusString == statusStr): + statusString.set("unknown") + else: + statusString.set(statusStr) + +def offButtonCallBack(): + sendLEDRequest("0") + +def redButtonCallBack(): + sendLEDRequest("1") + +def greenButtonCallBack(): + sendLEDRequest("2") + +def blueButtonCallBack(): + sendLEDRequest("4") + +def whiteButtonCallBack(): + sendLEDRequest("7") + +# Create our Labels +StaticLabel = Label(topframe, textvariable=statusLabel, relief=FLAT) +statusLabel.set("AWS Reported LED Status: ") + +StatusLabel = Label(topframe, textvariable=statusString, relief=FLAT) +statusString.set("unknown (needs to sync)") + +# Create our Buttons +O = Tkinter.Button(bottomframe, text ="Off", fg="gray", bg="black", command = offButtonCallBack) +R = Tkinter.Button(bottomframe, text ="R", fg="gray", bg="red", command = redButtonCallBack) +G = Tkinter.Button(bottomframe, text ="G", fg="gray", bg="green", command = greenButtonCallBack) +B = Tkinter.Button(bottomframe, text ="B", fg="gray", bg="blue", command = blueButtonCallBack) +W = Tkinter.Button(bottomframe, text ="W", fg="gray", bg="white", command = whiteButtonCallBack) + +# Set font +font = tkFont.Font(family='Times', size=24, weight='bold') +O['font'] = font +R['font'] = font +G['font'] = font +B['font'] = font +W['font'] = font + +# Arrange our controls +StaticLabel.pack(side = LEFT) +StatusLabel.pack(side = LEFT) +O.pack(side = LEFT) +R.pack(side = LEFT) +G.pack(side = LEFT) +B.pack(side = LEFT) +W.pack(side = LEFT) + +####################################################################################################################### +# +# These functions are for AWS +# +####################################################################################################################### +# Shadow JSON schema: +# +# Name: Bot +# { +# "state": { +# "desired":{ +# "ledColor":<INT VALUE> +# } +# } +#} + +def sendLEDRequest(color_input): + if (color_input in accepted): + JSONPayload = '{"state":{"desired":{"ledColor":' + str(color_input) + '}}}' + Bot.shadowUpdate(JSONPayload, customShadowCallback_Update, 5) + else: + print("WARN: Color input invalid - " + color_input) + +# Custom Shadow callback +def customShadowCallback_Delta(payload, responseStatus, token): + # payload is a JSON string ready to be parsed using json.loads(...) + # in both Py2.x and Py3.x + print(responseStatus) + payloadDict = json.loads(payload) + print("++++++++DELTA++++++++++") + print("ledColor: " + str(payloadDict["state"]["ledColor"])) + print("version: " + str(payloadDict["version"])) + print("+++++++++++++++++++++++\n\n") + + # Send payload to our status + setStatus(colorDict[payloadDict["state"]["ledColor"]]) + +# Custom Shadow callback +def customShadowCallback_Update(payload, responseStatus, token): + # payload is a JSON string ready to be parsed using json.loads(...) + # in both Py2.x and Py3.x + if responseStatus == "timeout": + print("Update request " + token + " time out!") + if responseStatus == "accepted": + payloadDict = json.loads(payload) + print("~~~~~~~~~~~~~~~~~~~~~~~") + print("Update request with token: " + token + " accepted!") + print("ledColor: " + str(payloadDict["state"]["desired"]["ledColor"])) + print("~~~~~~~~~~~~~~~~~~~~~~~\n\n") + if responseStatus == "rejected": + print("Update request " + token + " rejected!") + +def customShadowCallback_Delete(payload, responseStatus, token): + if responseStatus == "timeout": + print("Delete request " + token + " time out!") + if responseStatus == "accepted": + print("~~~~~~~~~~~~~~~~~~~~~~~") + print("Delete request with token: " + token + " accepted!") + print("~~~~~~~~~~~~~~~~~~~~~~~\n\n") + if responseStatus == "rejected": + print("Delete request " + token + " rejected!") + +# Custom Shadow callback +def customShadowCallback_Get(payload, responseStatus, token): + print(responseStatus) + payloadDict = json.loads(payload) + reportedColor = payloadDict["state"]["reported"]["ledColor"] + print("++++++++GET++++++++++") + print("ledColor: " + str(reportedColor) + " (" + colorDict[reportedColor] + ")") + print("version: " + str(payloadDict["version"])) + print("+++++++++++++++++++++\n\n") + + # Send payload to our status + setStatus(colorDict[reportedColor]) + +getTimerAlive = TRUE +def customShadowTimer_Get(): + while (getTimerAlive): + Bot.shadowGet(customShadowCallback_Get, 5) + time.sleep(3) + +####################################################################################################################### +# +# Vars +# +####################################################################################################################### +# Usage +usageInfo = """Usage: + +Use certificate based mutual authentication: +python basicShadowDeltaListener.py -e <endpoint> -r <rootCAFilePath> -c <certFilePath> -k <privateKeyFilePath> + +Use MQTT over WebSocket: +python basicShadowDeltaListener.py -e <endpoint> -r <rootCAFilePath> -w + +Type "python basicShadowDeltaListener.py -h" for available options. + + +""" +# Help info +helpInfo = """-e, --endpoint + Your AWS IoT custom endpoint +-r, --rootCA + Root CA file path +-c, --cert + Certificate file path +-k, --key + Private key file path +-w, --websocket + Use MQTT over WebSocket +-h, --help + Help information + + +""" + +####################################################################################################################### +# +# AWS IoT Config Parameters. The user needs to enter these (they should match the parameters in aws_iot_config.h) +# +####################################################################################################################### +useWebsocket = False +# TODO Move params to aws_iot_config.txt +AWS_IOT_MQTT_HOST = "a3sptpklzib7sa.iot.us-west-2.amazonaws.com" +AWS_IOT_MQTT_PORT = 8883 +AWS_IOT_MQTT_CLIENT_ID = "EesFRDM" +AWS_IOT_MY_THING_NAME = "EesFRDM" +AWS_IOT_ROOT_CA_FILENAME = "C:/Temp/certs/rootCA-certificate.crt" +AWS_IOT_PRIVATE_KEY_FILENAME = "C:/Temp/certs/private.pem.key" +AWS_IOT_CERTIFICATE_FILENAME = "C:/Temp/certs/certificate.pem.crt" + +####################################################################################################################### +# +# Arg Parser +# +####################################################################################################################### +''' +try: + opts, args = getopt.getopt(sys.argv[1:], "hwe:k:c:r:", ["help", "endpoint=", "key=","cert=","rootCA=", "websocket"]) + if len(opts) == 0: + raise getopt.GetoptError("No input parameters!") + for opt, arg in opts: + if opt in ("-h", "--help"): + print(helpInfo) + exit(0) + if opt in ("-e", "--endpoint"): + AWS_IOT_MQTT_HOST = arg + if opt in ("-r", "--rootCA"): + AWS_IOT_ROOT_CA_FILENAME = arg + if opt in ("-c", "--cert"): + AWS_IOT_CERTIFICATE_FILENAME = arg + if opt in ("-k", "--key"): + AWS_IOT_PRIVATE_KEY_FILENAME = arg + if opt in ("-w", "--websocket"): + useWebsocket = True +except getopt.GetoptError: + print(usageInfo) + #exit(1) + +# Missing configuration notification +missingConfiguration = False +if not AWS_IOT_MQTT_HOST: + print("Missing '-e' or '--endpoint'") + missingConfiguration = True +if not AWS_IOT_ROOT_CA_FILENAME: + print("Missing '-r' or '--rootCA'") + missingConfiguration = True +if not useWebsocket: + if not AWS_IOT_CERTIFICATE_FILENAME: + print("Missing '-c' or '--cert'") + missingConfiguration = True + if not AWS_IOT_PRIVATE_KEY_FILENAME: + print("Missing '-k' or '--key'") + missingConfiguration = True +if missingConfiguration: + exit(2) +''' + +# Configure logging +logger = logging.getLogger("AWSIoTPythonSDK.core") +logger.setLevel(logging.DEBUG) +streamHandler = logging.StreamHandler() +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +streamHandler.setFormatter(formatter) +logger.addHandler(streamHandler) + +####################################################################################################################### +# +# Main Code +# +####################################################################################################################### +# Makes sure files exist +if not os.path.exists(AWS_IOT_ROOT_CA_FILENAME): + print "ERROR: Root CA file not found, " + AWS_IOT_ROOT_CA_FILENAME + exit(1) +if not os.path.exists(AWS_IOT_PRIVATE_KEY_FILENAME): + print "ERROR: Private Key file not found, " + AWS_IOT_PRIVATE_KEY_FILENAME + exit(1) +if not os.path.exists(AWS_IOT_CERTIFICATE_FILENAME): + print "ERROR: AWS IOT cert file not found, " + AWS_IOT_CERTIFICATE_FILENAME + exit(1) + +# Init AWSIoTMQTTShadowClient +myAWSIoTMQTTShadowClient = None +if useWebsocket: + myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient("basicShadowDeltaListener", useWebsocket=True) + myAWSIoTMQTTShadowClient.configureEndpoint(AWS_IOT_MQTT_HOST, 443) + myAWSIoTMQTTShadowClient.configureCredentials(AWS_IOT_ROOT_CA_FILENAME) +else: + myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient("basicShadowDeltaListener") + myAWSIoTMQTTShadowClient.configureEndpoint(AWS_IOT_MQTT_HOST, AWS_IOT_MQTT_PORT) + myAWSIoTMQTTShadowClient.configureCredentials(AWS_IOT_ROOT_CA_FILENAME, AWS_IOT_PRIVATE_KEY_FILENAME, AWS_IOT_CERTIFICATE_FILENAME) + +# AWSIoTMQTTShadowClient configuration +myAWSIoTMQTTShadowClient.configureAutoReconnectBackoffTime(1, 32, 20) +myAWSIoTMQTTShadowClient.configureConnectDisconnectTimeout(10) # 10 sec +myAWSIoTMQTTShadowClient.configureMQTTOperationTimeout(5) # 5 sec + +# Connect to AWS IoT +myAWSIoTMQTTShadowClient.connect() + +# Create a deviceShadow with persistent subscription +Bot = myAWSIoTMQTTShadowClient.createShadowHandlerWithName(AWS_IOT_MY_THING_NAME, True) + +# Listen on deltas +Bot.shadowRegisterDeltaCallback(customShadowCallback_Delta) + +# Delete shadow JSON doc +#Bot.shadowDelete(customShadowCallback_Delete, 5) + +getTimer = Timer(3.0, customShadowTimer_Get, ()) +getTimer.start() + +# GUI loop (loops until closed) +window.mainloop() + +# Kill our timer after the GUI closes +getTimerAlive = FALSE +getTimer.cancel()