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.
PythonGUI/ATT_AWS_IoT_Demo_GUI.py@20:ee34856ae510, 2016-12-07 (annotated)
- Committer:
- ampembeng
- Date:
- Wed Dec 07 20:37:20 2016 +0000
- Revision:
- 20:ee34856ae510
- Parent:
- 19:488dad1e168e
- Child:
- 25:91d771247ac8
The demo now reads the FRDM-K64F temperature and humidity sensors and adds that data to the reported AWS IoT device Shadow. Both the firmware and Python GUI are updated.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
ampembeng | 17:46780b2de3e4 | 1 | ''' |
ampembeng | 17:46780b2de3e4 | 2 | /* |
ampembeng | 17:46780b2de3e4 | 3 | * Copyright 2010-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. |
ampembeng | 17:46780b2de3e4 | 4 | * |
ampembeng | 17:46780b2de3e4 | 5 | * Licensed under the Apache License, Version 2.0 (the "License"). |
ampembeng | 17:46780b2de3e4 | 6 | * You may not use this file except in compliance with the License. |
ampembeng | 17:46780b2de3e4 | 7 | * A copy of the License is located at |
ampembeng | 17:46780b2de3e4 | 8 | * |
ampembeng | 17:46780b2de3e4 | 9 | * http://aws.amazon.com/apache2.0 |
ampembeng | 17:46780b2de3e4 | 10 | * |
ampembeng | 17:46780b2de3e4 | 11 | * or in the "license" file accompanying this file. This file is distributed |
ampembeng | 17:46780b2de3e4 | 12 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either |
ampembeng | 17:46780b2de3e4 | 13 | * express or implied. See the License for the specific language governing |
ampembeng | 17:46780b2de3e4 | 14 | * permissions and limitations under the License. |
ampembeng | 17:46780b2de3e4 | 15 | */ |
ampembeng | 17:46780b2de3e4 | 16 | ''' |
ampembeng | 17:46780b2de3e4 | 17 | |
ampembeng | 17:46780b2de3e4 | 18 | from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTShadowClient |
ampembeng | 17:46780b2de3e4 | 19 | import sys |
ampembeng | 17:46780b2de3e4 | 20 | import os.path |
ampembeng | 17:46780b2de3e4 | 21 | import logging |
ampembeng | 17:46780b2de3e4 | 22 | import time |
ampembeng | 17:46780b2de3e4 | 23 | import json |
ampembeng | 19:488dad1e168e | 24 | import re |
ampembeng | 17:46780b2de3e4 | 25 | import getopt |
ampembeng | 17:46780b2de3e4 | 26 | import Tkinter |
ampembeng | 17:46780b2de3e4 | 27 | import tkFont |
ampembeng | 17:46780b2de3e4 | 28 | import time |
ampembeng | 17:46780b2de3e4 | 29 | from threading import Timer |
ampembeng | 17:46780b2de3e4 | 30 | from Tkinter import * |
ampembeng | 17:46780b2de3e4 | 31 | |
ampembeng | 17:46780b2de3e4 | 32 | ####################################################################################################################### |
ampembeng | 17:46780b2de3e4 | 33 | # |
ampembeng | 17:46780b2de3e4 | 34 | # Set up our Tkinter based GUI |
ampembeng | 17:46780b2de3e4 | 35 | # |
ampembeng | 17:46780b2de3e4 | 36 | ####################################################################################################################### |
ampembeng | 17:46780b2de3e4 | 37 | accepted = ["0", "1", "2", "4", "7"] |
ampembeng | 17:46780b2de3e4 | 38 | colorDict = {0 : 'off', 1 : 'red', 2 : 'green', 4 : "blue", 7 : "white"} |
ampembeng | 17:46780b2de3e4 | 39 | |
ampembeng | 17:46780b2de3e4 | 40 | window = Tkinter.Tk() |
ampembeng | 17:46780b2de3e4 | 41 | window.wm_title("AT&T AWS IoT Demo") |
ampembeng | 17:46780b2de3e4 | 42 | |
ampembeng | 20:ee34856ae510 | 43 | # Setup frames |
ampembeng | 17:46780b2de3e4 | 44 | topframe = Frame(window) |
ampembeng | 20:ee34856ae510 | 45 | topframe.pack(fill=X, side = TOP) |
ampembeng | 20:ee34856ae510 | 46 | frameR1 = Frame(window) |
ampembeng | 20:ee34856ae510 | 47 | frameR1.pack(fill=X, side = TOP) |
ampembeng | 20:ee34856ae510 | 48 | frameR2 = Frame(window) |
ampembeng | 20:ee34856ae510 | 49 | frameR2.pack(fill=X, side = TOP) |
ampembeng | 20:ee34856ae510 | 50 | frameR3 = Frame(window) |
ampembeng | 20:ee34856ae510 | 51 | frameR3.pack(fill=X, side = TOP) |
ampembeng | 17:46780b2de3e4 | 52 | bottomframe = Frame(window) |
ampembeng | 20:ee34856ae510 | 53 | bottomframe.pack(fill=X, side = BOTTOM) |
ampembeng | 17:46780b2de3e4 | 54 | |
ampembeng | 20:ee34856ae510 | 55 | # Label vars (controls shown text) |
ampembeng | 20:ee34856ae510 | 56 | ledStaticLabelTxt = StringVar() |
ampembeng | 20:ee34856ae510 | 57 | ledStatusLabelTxt = StringVar() |
ampembeng | 20:ee34856ae510 | 58 | tempStaticLabelTxt = StringVar() |
ampembeng | 20:ee34856ae510 | 59 | tempStatusLabelTxt = StringVar() |
ampembeng | 20:ee34856ae510 | 60 | humidStaticLabelTxt = StringVar() |
ampembeng | 20:ee34856ae510 | 61 | humidStatusLabelTxt = StringVar() |
ampembeng | 20:ee34856ae510 | 62 | versionStaticLabelTxt = StringVar() |
ampembeng | 20:ee34856ae510 | 63 | versionStatusLabelTxt = StringVar() |
ampembeng | 20:ee34856ae510 | 64 | versionLastValue = -1 |
ampembeng | 17:46780b2de3e4 | 65 | |
ampembeng | 17:46780b2de3e4 | 66 | # Control callbacks |
ampembeng | 20:ee34856ae510 | 67 | def setStatus(ledStatus, tempStatus, humidStatus, version): |
ampembeng | 20:ee34856ae510 | 68 | global versionLastValue |
ampembeng | 20:ee34856ae510 | 69 | |
ampembeng | 20:ee34856ae510 | 70 | ledStatusLabelTxt.set(ledStatus) |
ampembeng | 20:ee34856ae510 | 71 | tempStatusLabelTxt.set(str(tempStatus) + " (F)") |
ampembeng | 20:ee34856ae510 | 72 | humidStatusLabelTxt.set(str(humidStatus) + " %") |
ampembeng | 20:ee34856ae510 | 73 | |
ampembeng | 20:ee34856ae510 | 74 | isStale = "" |
ampembeng | 20:ee34856ae510 | 75 | if (versionLastValue == version): |
ampembeng | 20:ee34856ae510 | 76 | isStale = " (stale)" |
ampembeng | 17:46780b2de3e4 | 77 | else: |
ampembeng | 20:ee34856ae510 | 78 | versionLastValue = version |
ampembeng | 20:ee34856ae510 | 79 | versionStatusLabelTxt.set(str(version) + isStale) |
ampembeng | 17:46780b2de3e4 | 80 | |
ampembeng | 17:46780b2de3e4 | 81 | def offButtonCallBack(): |
ampembeng | 17:46780b2de3e4 | 82 | sendLEDRequest("0") |
ampembeng | 17:46780b2de3e4 | 83 | |
ampembeng | 17:46780b2de3e4 | 84 | def redButtonCallBack(): |
ampembeng | 17:46780b2de3e4 | 85 | sendLEDRequest("1") |
ampembeng | 17:46780b2de3e4 | 86 | |
ampembeng | 17:46780b2de3e4 | 87 | def greenButtonCallBack(): |
ampembeng | 17:46780b2de3e4 | 88 | sendLEDRequest("2") |
ampembeng | 17:46780b2de3e4 | 89 | |
ampembeng | 17:46780b2de3e4 | 90 | def blueButtonCallBack(): |
ampembeng | 17:46780b2de3e4 | 91 | sendLEDRequest("4") |
ampembeng | 17:46780b2de3e4 | 92 | |
ampembeng | 17:46780b2de3e4 | 93 | def whiteButtonCallBack(): |
ampembeng | 17:46780b2de3e4 | 94 | sendLEDRequest("7") |
ampembeng | 17:46780b2de3e4 | 95 | |
ampembeng | 17:46780b2de3e4 | 96 | # Create our Labels |
ampembeng | 20:ee34856ae510 | 97 | ledStaticLabel = Label(topframe, textvariable=ledStaticLabelTxt, anchor=W, relief=FLAT) |
ampembeng | 20:ee34856ae510 | 98 | ledStaticLabelTxt.set("Reported LED Status: ") |
ampembeng | 20:ee34856ae510 | 99 | ledStatusLabel = Label(topframe, textvariable=ledStatusLabelTxt, anchor=W, relief=FLAT) |
ampembeng | 20:ee34856ae510 | 100 | ledStatusLabelTxt.set("unknown (needs to sync)") |
ampembeng | 20:ee34856ae510 | 101 | |
ampembeng | 20:ee34856ae510 | 102 | tempStaticLabel = Label(frameR1, textvariable=tempStaticLabelTxt, anchor=W, relief=FLAT) |
ampembeng | 20:ee34856ae510 | 103 | tempStaticLabelTxt.set("Reported Temperature: ") |
ampembeng | 20:ee34856ae510 | 104 | tempStatusLabel = Label(frameR1, textvariable=tempStatusLabelTxt, anchor=W, relief=FLAT) |
ampembeng | 20:ee34856ae510 | 105 | tempStatusLabelTxt.set("unknown (needs to sync)") |
ampembeng | 17:46780b2de3e4 | 106 | |
ampembeng | 20:ee34856ae510 | 107 | humidStaticLabel = Label(frameR2, textvariable=humidStaticLabelTxt, anchor=W, relief=FLAT) |
ampembeng | 20:ee34856ae510 | 108 | humidStaticLabelTxt.set("Reported Humidity: ") |
ampembeng | 20:ee34856ae510 | 109 | humidStatusLabel = Label(frameR2, textvariable=humidStatusLabelTxt, anchor=W, relief=FLAT) |
ampembeng | 20:ee34856ae510 | 110 | humidStatusLabelTxt.set("unknown (needs to sync)") |
ampembeng | 20:ee34856ae510 | 111 | |
ampembeng | 20:ee34856ae510 | 112 | versionStaticLabel = Label(frameR3, textvariable=versionStaticLabelTxt, anchor=W, relief=FLAT) |
ampembeng | 20:ee34856ae510 | 113 | versionStaticLabelTxt.set("IoT Shadow Version: ") |
ampembeng | 20:ee34856ae510 | 114 | versionStatusLabel = Label(frameR3, textvariable=versionStatusLabelTxt, anchor=W, relief=FLAT) |
ampembeng | 20:ee34856ae510 | 115 | versionStatusLabelTxt.set("unknown (needs to sync)") |
ampembeng | 17:46780b2de3e4 | 116 | |
ampembeng | 17:46780b2de3e4 | 117 | # Create our Buttons |
ampembeng | 17:46780b2de3e4 | 118 | O = Tkinter.Button(bottomframe, text ="Off", fg="gray", bg="black", command = offButtonCallBack) |
ampembeng | 17:46780b2de3e4 | 119 | R = Tkinter.Button(bottomframe, text ="R", fg="gray", bg="red", command = redButtonCallBack) |
ampembeng | 17:46780b2de3e4 | 120 | G = Tkinter.Button(bottomframe, text ="G", fg="gray", bg="green", command = greenButtonCallBack) |
ampembeng | 17:46780b2de3e4 | 121 | B = Tkinter.Button(bottomframe, text ="B", fg="gray", bg="blue", command = blueButtonCallBack) |
ampembeng | 17:46780b2de3e4 | 122 | W = Tkinter.Button(bottomframe, text ="W", fg="gray", bg="white", command = whiteButtonCallBack) |
ampembeng | 17:46780b2de3e4 | 123 | |
ampembeng | 17:46780b2de3e4 | 124 | # Set font |
ampembeng | 17:46780b2de3e4 | 125 | font = tkFont.Font(family='Times', size=24, weight='bold') |
ampembeng | 17:46780b2de3e4 | 126 | O['font'] = font |
ampembeng | 17:46780b2de3e4 | 127 | R['font'] = font |
ampembeng | 17:46780b2de3e4 | 128 | G['font'] = font |
ampembeng | 17:46780b2de3e4 | 129 | B['font'] = font |
ampembeng | 17:46780b2de3e4 | 130 | W['font'] = font |
ampembeng | 17:46780b2de3e4 | 131 | |
ampembeng | 17:46780b2de3e4 | 132 | # Arrange our controls |
ampembeng | 20:ee34856ae510 | 133 | ledStaticLabel.pack(side = LEFT) |
ampembeng | 20:ee34856ae510 | 134 | ledStatusLabel.pack(side = LEFT) |
ampembeng | 20:ee34856ae510 | 135 | tempStaticLabel.pack(side = LEFT) |
ampembeng | 20:ee34856ae510 | 136 | tempStatusLabel.pack(side = LEFT) |
ampembeng | 20:ee34856ae510 | 137 | humidStaticLabel.pack(side = LEFT) |
ampembeng | 20:ee34856ae510 | 138 | humidStatusLabel.pack(side = LEFT) |
ampembeng | 20:ee34856ae510 | 139 | versionStaticLabel.pack(side = LEFT) |
ampembeng | 20:ee34856ae510 | 140 | versionStatusLabel.pack(side = LEFT) |
ampembeng | 17:46780b2de3e4 | 141 | O.pack(side = LEFT) |
ampembeng | 17:46780b2de3e4 | 142 | R.pack(side = LEFT) |
ampembeng | 17:46780b2de3e4 | 143 | G.pack(side = LEFT) |
ampembeng | 17:46780b2de3e4 | 144 | B.pack(side = LEFT) |
ampembeng | 17:46780b2de3e4 | 145 | W.pack(side = LEFT) |
ampembeng | 17:46780b2de3e4 | 146 | |
ampembeng | 17:46780b2de3e4 | 147 | ####################################################################################################################### |
ampembeng | 17:46780b2de3e4 | 148 | # |
ampembeng | 17:46780b2de3e4 | 149 | # These functions are for AWS |
ampembeng | 17:46780b2de3e4 | 150 | # |
ampembeng | 17:46780b2de3e4 | 151 | ####################################################################################################################### |
ampembeng | 17:46780b2de3e4 | 152 | # Shadow JSON schema: |
ampembeng | 20:ee34856ae510 | 153 | ''' |
ampembeng | 20:ee34856ae510 | 154 | Name: AIT (AWS IoT Thing) |
ampembeng | 20:ee34856ae510 | 155 | { |
ampembeng | 20:ee34856ae510 | 156 | "state": { |
ampembeng | 20:ee34856ae510 | 157 | "desired": { |
ampembeng | 20:ee34856ae510 | 158 | "ledColor": <UINT8> |
ampembeng | 20:ee34856ae510 | 159 | }, |
ampembeng | 20:ee34856ae510 | 160 | "reported": { |
ampembeng | 20:ee34856ae510 | 161 | "ledColor": <UINT8>, |
ampembeng | 20:ee34856ae510 | 162 | "temperature": <FLOAT>, |
ampembeng | 20:ee34856ae510 | 163 | "humidity": <INT16> |
ampembeng | 20:ee34856ae510 | 164 | } |
ampembeng | 20:ee34856ae510 | 165 | } |
ampembeng | 20:ee34856ae510 | 166 | } |
ampembeng | 20:ee34856ae510 | 167 | ''' |
ampembeng | 17:46780b2de3e4 | 168 | |
ampembeng | 17:46780b2de3e4 | 169 | def sendLEDRequest(color_input): |
ampembeng | 17:46780b2de3e4 | 170 | if (color_input in accepted): |
ampembeng | 17:46780b2de3e4 | 171 | JSONPayload = '{"state":{"desired":{"ledColor":' + str(color_input) + '}}}' |
ampembeng | 20:ee34856ae510 | 172 | AIT.shadowUpdate(JSONPayload, customShadowCallback_Update, 5) |
ampembeng | 17:46780b2de3e4 | 173 | else: |
ampembeng | 17:46780b2de3e4 | 174 | print("WARN: Color input invalid - " + color_input) |
ampembeng | 17:46780b2de3e4 | 175 | |
ampembeng | 17:46780b2de3e4 | 176 | # Custom Shadow callback |
ampembeng | 17:46780b2de3e4 | 177 | def customShadowCallback_Delta(payload, responseStatus, token): |
ampembeng | 17:46780b2de3e4 | 178 | # payload is a JSON string ready to be parsed using json.loads(...) |
ampembeng | 17:46780b2de3e4 | 179 | # in both Py2.x and Py3.x |
ampembeng | 17:46780b2de3e4 | 180 | print(responseStatus) |
ampembeng | 17:46780b2de3e4 | 181 | payloadDict = json.loads(payload) |
ampembeng | 17:46780b2de3e4 | 182 | print("++++++++DELTA++++++++++") |
ampembeng | 17:46780b2de3e4 | 183 | print("ledColor: " + str(payloadDict["state"]["ledColor"])) |
ampembeng | 17:46780b2de3e4 | 184 | print("version: " + str(payloadDict["version"])) |
ampembeng | 17:46780b2de3e4 | 185 | print("+++++++++++++++++++++++\n\n") |
ampembeng | 17:46780b2de3e4 | 186 | |
ampembeng | 17:46780b2de3e4 | 187 | # Custom Shadow callback |
ampembeng | 17:46780b2de3e4 | 188 | def customShadowCallback_Update(payload, responseStatus, token): |
ampembeng | 17:46780b2de3e4 | 189 | # payload is a JSON string ready to be parsed using json.loads(...) |
ampembeng | 17:46780b2de3e4 | 190 | # in both Py2.x and Py3.x |
ampembeng | 17:46780b2de3e4 | 191 | if responseStatus == "timeout": |
ampembeng | 17:46780b2de3e4 | 192 | print("Update request " + token + " time out!") |
ampembeng | 17:46780b2de3e4 | 193 | if responseStatus == "accepted": |
ampembeng | 17:46780b2de3e4 | 194 | payloadDict = json.loads(payload) |
ampembeng | 17:46780b2de3e4 | 195 | print("~~~~~~~~~~~~~~~~~~~~~~~") |
ampembeng | 17:46780b2de3e4 | 196 | print("Update request with token: " + token + " accepted!") |
ampembeng | 17:46780b2de3e4 | 197 | print("ledColor: " + str(payloadDict["state"]["desired"]["ledColor"])) |
ampembeng | 17:46780b2de3e4 | 198 | print("~~~~~~~~~~~~~~~~~~~~~~~\n\n") |
ampembeng | 17:46780b2de3e4 | 199 | if responseStatus == "rejected": |
ampembeng | 17:46780b2de3e4 | 200 | print("Update request " + token + " rejected!") |
ampembeng | 17:46780b2de3e4 | 201 | |
ampembeng | 17:46780b2de3e4 | 202 | def customShadowCallback_Delete(payload, responseStatus, token): |
ampembeng | 17:46780b2de3e4 | 203 | if responseStatus == "timeout": |
ampembeng | 17:46780b2de3e4 | 204 | print("Delete request " + token + " time out!") |
ampembeng | 17:46780b2de3e4 | 205 | if responseStatus == "accepted": |
ampembeng | 17:46780b2de3e4 | 206 | print("~~~~~~~~~~~~~~~~~~~~~~~") |
ampembeng | 17:46780b2de3e4 | 207 | print("Delete request with token: " + token + " accepted!") |
ampembeng | 17:46780b2de3e4 | 208 | print("~~~~~~~~~~~~~~~~~~~~~~~\n\n") |
ampembeng | 17:46780b2de3e4 | 209 | if responseStatus == "rejected": |
ampembeng | 17:46780b2de3e4 | 210 | print("Delete request " + token + " rejected!") |
ampembeng | 17:46780b2de3e4 | 211 | |
ampembeng | 17:46780b2de3e4 | 212 | # Custom Shadow callback |
ampembeng | 17:46780b2de3e4 | 213 | def customShadowCallback_Get(payload, responseStatus, token): |
ampembeng | 17:46780b2de3e4 | 214 | print(responseStatus) |
ampembeng | 17:46780b2de3e4 | 215 | payloadDict = json.loads(payload) |
ampembeng | 17:46780b2de3e4 | 216 | reportedColor = payloadDict["state"]["reported"]["ledColor"] |
ampembeng | 20:ee34856ae510 | 217 | reportedTemp = payloadDict["state"]["reported"]["temperature"] |
ampembeng | 20:ee34856ae510 | 218 | reportedHumid = payloadDict["state"]["reported"]["humidity"] |
ampembeng | 20:ee34856ae510 | 219 | reportedVersion = payloadDict["version"] |
ampembeng | 17:46780b2de3e4 | 220 | print("++++++++GET++++++++++") |
ampembeng | 20:ee34856ae510 | 221 | print("ledColor : " + str(reportedColor) + " (" + colorDict[reportedColor] + ")") |
ampembeng | 20:ee34856ae510 | 222 | print("temperature: " + str(reportedTemp)) |
ampembeng | 20:ee34856ae510 | 223 | print("humidity : " + str(reportedHumid)) |
ampembeng | 20:ee34856ae510 | 224 | print("version: " + str(reportedVersion)) |
ampembeng | 17:46780b2de3e4 | 225 | print("+++++++++++++++++++++\n\n") |
ampembeng | 17:46780b2de3e4 | 226 | |
ampembeng | 17:46780b2de3e4 | 227 | # Send payload to our status |
ampembeng | 20:ee34856ae510 | 228 | setStatus(colorDict[reportedColor], reportedTemp, reportedHumid, reportedVersion) |
ampembeng | 17:46780b2de3e4 | 229 | |
ampembeng | 17:46780b2de3e4 | 230 | getTimerAlive = TRUE |
ampembeng | 17:46780b2de3e4 | 231 | def customShadowTimer_Get(): |
ampembeng | 17:46780b2de3e4 | 232 | while (getTimerAlive): |
ampembeng | 20:ee34856ae510 | 233 | AIT.shadowGet(customShadowCallback_Get, 5) |
ampembeng | 17:46780b2de3e4 | 234 | time.sleep(3) |
ampembeng | 17:46780b2de3e4 | 235 | |
ampembeng | 17:46780b2de3e4 | 236 | ####################################################################################################################### |
ampembeng | 17:46780b2de3e4 | 237 | # |
ampembeng | 17:46780b2de3e4 | 238 | # Vars |
ampembeng | 17:46780b2de3e4 | 239 | # |
ampembeng | 17:46780b2de3e4 | 240 | ####################################################################################################################### |
ampembeng | 17:46780b2de3e4 | 241 | # Usage |
ampembeng | 17:46780b2de3e4 | 242 | usageInfo = """Usage: |
ampembeng | 17:46780b2de3e4 | 243 | |
ampembeng | 17:46780b2de3e4 | 244 | Use certificate based mutual authentication: |
ampembeng | 17:46780b2de3e4 | 245 | python basicShadowDeltaListener.py -e <endpoint> -r <rootCAFilePath> -c <certFilePath> -k <privateKeyFilePath> |
ampembeng | 17:46780b2de3e4 | 246 | |
ampembeng | 17:46780b2de3e4 | 247 | Use MQTT over WebSocket: |
ampembeng | 17:46780b2de3e4 | 248 | python basicShadowDeltaListener.py -e <endpoint> -r <rootCAFilePath> -w |
ampembeng | 17:46780b2de3e4 | 249 | |
ampembeng | 17:46780b2de3e4 | 250 | Type "python basicShadowDeltaListener.py -h" for available options. |
ampembeng | 17:46780b2de3e4 | 251 | |
ampembeng | 17:46780b2de3e4 | 252 | |
ampembeng | 17:46780b2de3e4 | 253 | """ |
ampembeng | 17:46780b2de3e4 | 254 | # Help info |
ampembeng | 17:46780b2de3e4 | 255 | helpInfo = """-e, --endpoint |
ampembeng | 17:46780b2de3e4 | 256 | Your AWS IoT custom endpoint |
ampembeng | 17:46780b2de3e4 | 257 | -r, --rootCA |
ampembeng | 17:46780b2de3e4 | 258 | Root CA file path |
ampembeng | 17:46780b2de3e4 | 259 | -c, --cert |
ampembeng | 17:46780b2de3e4 | 260 | Certificate file path |
ampembeng | 17:46780b2de3e4 | 261 | -k, --key |
ampembeng | 17:46780b2de3e4 | 262 | Private key file path |
ampembeng | 17:46780b2de3e4 | 263 | -w, --websocket |
ampembeng | 17:46780b2de3e4 | 264 | Use MQTT over WebSocket |
ampembeng | 17:46780b2de3e4 | 265 | -h, --help |
ampembeng | 17:46780b2de3e4 | 266 | Help information |
ampembeng | 17:46780b2de3e4 | 267 | |
ampembeng | 17:46780b2de3e4 | 268 | |
ampembeng | 17:46780b2de3e4 | 269 | """ |
ampembeng | 17:46780b2de3e4 | 270 | |
ampembeng | 17:46780b2de3e4 | 271 | ####################################################################################################################### |
ampembeng | 17:46780b2de3e4 | 272 | # |
ampembeng | 17:46780b2de3e4 | 273 | # AWS IoT Config Parameters. The user needs to enter these (they should match the parameters in aws_iot_config.h) |
ampembeng | 17:46780b2de3e4 | 274 | # |
ampembeng | 17:46780b2de3e4 | 275 | ####################################################################################################################### |
ampembeng | 19:488dad1e168e | 276 | useWebsocket = False # The FRDM-K64F demo isn't designed to work with web socket |
ampembeng | 19:488dad1e168e | 277 | hardCodeMQTT = False # Set this to true if you want to hard code the MQTT params below |
ampembeng | 19:488dad1e168e | 278 | |
ampembeng | 19:488dad1e168e | 279 | # AWS parameters |
ampembeng | 19:488dad1e168e | 280 | AWS_IOT_MQTT_HOST = "TODO" |
ampembeng | 17:46780b2de3e4 | 281 | AWS_IOT_MQTT_PORT = 8883 |
ampembeng | 19:488dad1e168e | 282 | AWS_IOT_MQTT_CLIENT_ID = "TODO" |
ampembeng | 19:488dad1e168e | 283 | AWS_IOT_MY_THING_NAME = "TODO" |
ampembeng | 19:488dad1e168e | 284 | AWS_MQTT_CONFIG_FILENAME = "C:/Temp/certs/mqtt_config.txt" |
ampembeng | 17:46780b2de3e4 | 285 | AWS_IOT_ROOT_CA_FILENAME = "C:/Temp/certs/rootCA-certificate.crt" |
ampembeng | 17:46780b2de3e4 | 286 | AWS_IOT_PRIVATE_KEY_FILENAME = "C:/Temp/certs/private.pem.key" |
ampembeng | 17:46780b2de3e4 | 287 | AWS_IOT_CERTIFICATE_FILENAME = "C:/Temp/certs/certificate.pem.crt" |
ampembeng | 17:46780b2de3e4 | 288 | |
ampembeng | 17:46780b2de3e4 | 289 | ####################################################################################################################### |
ampembeng | 17:46780b2de3e4 | 290 | # |
ampembeng | 19:488dad1e168e | 291 | # Arg Parser (TODO) |
ampembeng | 17:46780b2de3e4 | 292 | # |
ampembeng | 17:46780b2de3e4 | 293 | ####################################################################################################################### |
ampembeng | 17:46780b2de3e4 | 294 | ''' |
ampembeng | 17:46780b2de3e4 | 295 | try: |
ampembeng | 17:46780b2de3e4 | 296 | opts, args = getopt.getopt(sys.argv[1:], "hwe:k:c:r:", ["help", "endpoint=", "key=","cert=","rootCA=", "websocket"]) |
ampembeng | 17:46780b2de3e4 | 297 | if len(opts) == 0: |
ampembeng | 17:46780b2de3e4 | 298 | raise getopt.GetoptError("No input parameters!") |
ampembeng | 17:46780b2de3e4 | 299 | for opt, arg in opts: |
ampembeng | 17:46780b2de3e4 | 300 | if opt in ("-h", "--help"): |
ampembeng | 17:46780b2de3e4 | 301 | print(helpInfo) |
ampembeng | 17:46780b2de3e4 | 302 | exit(0) |
ampembeng | 17:46780b2de3e4 | 303 | if opt in ("-e", "--endpoint"): |
ampembeng | 17:46780b2de3e4 | 304 | AWS_IOT_MQTT_HOST = arg |
ampembeng | 17:46780b2de3e4 | 305 | if opt in ("-r", "--rootCA"): |
ampembeng | 17:46780b2de3e4 | 306 | AWS_IOT_ROOT_CA_FILENAME = arg |
ampembeng | 17:46780b2de3e4 | 307 | if opt in ("-c", "--cert"): |
ampembeng | 17:46780b2de3e4 | 308 | AWS_IOT_CERTIFICATE_FILENAME = arg |
ampembeng | 17:46780b2de3e4 | 309 | if opt in ("-k", "--key"): |
ampembeng | 17:46780b2de3e4 | 310 | AWS_IOT_PRIVATE_KEY_FILENAME = arg |
ampembeng | 17:46780b2de3e4 | 311 | if opt in ("-w", "--websocket"): |
ampembeng | 17:46780b2de3e4 | 312 | useWebsocket = True |
ampembeng | 17:46780b2de3e4 | 313 | except getopt.GetoptError: |
ampembeng | 17:46780b2de3e4 | 314 | print(usageInfo) |
ampembeng | 17:46780b2de3e4 | 315 | #exit(1) |
ampembeng | 17:46780b2de3e4 | 316 | |
ampembeng | 17:46780b2de3e4 | 317 | # Missing configuration notification |
ampembeng | 17:46780b2de3e4 | 318 | missingConfiguration = False |
ampembeng | 17:46780b2de3e4 | 319 | if not AWS_IOT_MQTT_HOST: |
ampembeng | 17:46780b2de3e4 | 320 | print("Missing '-e' or '--endpoint'") |
ampembeng | 17:46780b2de3e4 | 321 | missingConfiguration = True |
ampembeng | 17:46780b2de3e4 | 322 | if not AWS_IOT_ROOT_CA_FILENAME: |
ampembeng | 17:46780b2de3e4 | 323 | print("Missing '-r' or '--rootCA'") |
ampembeng | 17:46780b2de3e4 | 324 | missingConfiguration = True |
ampembeng | 17:46780b2de3e4 | 325 | if not useWebsocket: |
ampembeng | 17:46780b2de3e4 | 326 | if not AWS_IOT_CERTIFICATE_FILENAME: |
ampembeng | 17:46780b2de3e4 | 327 | print("Missing '-c' or '--cert'") |
ampembeng | 17:46780b2de3e4 | 328 | missingConfiguration = True |
ampembeng | 17:46780b2de3e4 | 329 | if not AWS_IOT_PRIVATE_KEY_FILENAME: |
ampembeng | 17:46780b2de3e4 | 330 | print("Missing '-k' or '--key'") |
ampembeng | 17:46780b2de3e4 | 331 | missingConfiguration = True |
ampembeng | 17:46780b2de3e4 | 332 | if missingConfiguration: |
ampembeng | 17:46780b2de3e4 | 333 | exit(2) |
ampembeng | 17:46780b2de3e4 | 334 | ''' |
ampembeng | 17:46780b2de3e4 | 335 | |
ampembeng | 17:46780b2de3e4 | 336 | # Configure logging |
ampembeng | 17:46780b2de3e4 | 337 | logger = logging.getLogger("AWSIoTPythonSDK.core") |
ampembeng | 17:46780b2de3e4 | 338 | logger.setLevel(logging.DEBUG) |
ampembeng | 17:46780b2de3e4 | 339 | streamHandler = logging.StreamHandler() |
ampembeng | 17:46780b2de3e4 | 340 | formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') |
ampembeng | 17:46780b2de3e4 | 341 | streamHandler.setFormatter(formatter) |
ampembeng | 17:46780b2de3e4 | 342 | logger.addHandler(streamHandler) |
ampembeng | 17:46780b2de3e4 | 343 | |
ampembeng | 17:46780b2de3e4 | 344 | ####################################################################################################################### |
ampembeng | 17:46780b2de3e4 | 345 | # |
ampembeng | 17:46780b2de3e4 | 346 | # Main Code |
ampembeng | 17:46780b2de3e4 | 347 | # |
ampembeng | 17:46780b2de3e4 | 348 | ####################################################################################################################### |
ampembeng | 19:488dad1e168e | 349 | # This block parses the MQTT file. |
ampembeng | 19:488dad1e168e | 350 | ''' Example format for mqtt_config.txt: |
ampembeng | 19:488dad1e168e | 351 | AWS_IOT_MQTT_HOST=1234asdf.iot.us-west-2.amazonaws.com |
ampembeng | 19:488dad1e168e | 352 | AWS_IOT_MQTT_PORT=8883 |
ampembeng | 19:488dad1e168e | 353 | AWS_IOT_MQTT_CLIENT_ID=MyThingName |
ampembeng | 19:488dad1e168e | 354 | WS_IOT_MY_THING_NAME=MyThingName |
ampembeng | 19:488dad1e168e | 355 | ''' |
ampembeng | 19:488dad1e168e | 356 | |
ampembeng | 19:488dad1e168e | 357 | if (not hardCodeMQTT): |
ampembeng | 19:488dad1e168e | 358 | if not os.path.exists(AWS_MQTT_CONFIG_FILENAME): |
ampembeng | 19:488dad1e168e | 359 | print "ERROR: MQTT Config file not found, " + AWS_MQTT_CONFIG_FILENAME |
ampembeng | 19:488dad1e168e | 360 | exit(1) |
ampembeng | 19:488dad1e168e | 361 | |
ampembeng | 19:488dad1e168e | 362 | mqtt_file = open(AWS_MQTT_CONFIG_FILENAME) |
ampembeng | 19:488dad1e168e | 363 | mqtt_tokens = re.split('=|\n', mqtt_file.read()) |
ampembeng | 19:488dad1e168e | 364 | print mqtt_tokens |
ampembeng | 19:488dad1e168e | 365 | |
ampembeng | 19:488dad1e168e | 366 | if (len(mqtt_tokens) != 8): |
ampembeng | 19:488dad1e168e | 367 | print "ERROR: Detected incorrect MQTT file format" |
ampembeng | 19:488dad1e168e | 368 | exit(1) |
ampembeng | 19:488dad1e168e | 369 | |
ampembeng | 19:488dad1e168e | 370 | index = 0 |
ampembeng | 19:488dad1e168e | 371 | for token in mqtt_tokens: |
ampembeng | 19:488dad1e168e | 372 | if (token == "AWS_IOT_MQTT_HOST"): |
ampembeng | 19:488dad1e168e | 373 | AWS_IOT_MQTT_HOST = mqtt_tokens[index+1] |
ampembeng | 19:488dad1e168e | 374 | if (token == "AWS_IOT_MQTT_PORT"): |
ampembeng | 19:488dad1e168e | 375 | AWS_IOT_MQTT_PORT = int(mqtt_tokens[index + 1]) |
ampembeng | 19:488dad1e168e | 376 | if (token == "AWS_IOT_MQTT_CLIENT_ID"): |
ampembeng | 19:488dad1e168e | 377 | AWS_IOT_MQTT_CLIENT_ID = mqtt_tokens[index + 1] |
ampembeng | 19:488dad1e168e | 378 | if (token == "AWS_IOT_MY_THING_NAME"): |
ampembeng | 19:488dad1e168e | 379 | AWS_IOT_MY_THING_NAME = mqtt_tokens[index+1] |
ampembeng | 19:488dad1e168e | 380 | |
ampembeng | 19:488dad1e168e | 381 | index += 1 |
ampembeng | 19:488dad1e168e | 382 | |
ampembeng | 19:488dad1e168e | 383 | print "MQTT Params:" |
ampembeng | 19:488dad1e168e | 384 | print "AWS_IOT_MQTT_HOST = " + AWS_IOT_MQTT_HOST |
ampembeng | 19:488dad1e168e | 385 | print "AWS_IOT_MQTT_PORT = " + str(AWS_IOT_MQTT_PORT) |
ampembeng | 19:488dad1e168e | 386 | print "AWS_IOT_MQTT_CLIENT_ID = " + AWS_IOT_MQTT_CLIENT_ID |
ampembeng | 19:488dad1e168e | 387 | print "AWS_IOT_MY_THING_NAME = " + AWS_IOT_MY_THING_NAME |
ampembeng | 19:488dad1e168e | 388 | |
ampembeng | 19:488dad1e168e | 389 | # Makes sure cert/key files exist |
ampembeng | 17:46780b2de3e4 | 390 | if not os.path.exists(AWS_IOT_ROOT_CA_FILENAME): |
ampembeng | 17:46780b2de3e4 | 391 | print "ERROR: Root CA file not found, " + AWS_IOT_ROOT_CA_FILENAME |
ampembeng | 17:46780b2de3e4 | 392 | exit(1) |
ampembeng | 17:46780b2de3e4 | 393 | if not os.path.exists(AWS_IOT_PRIVATE_KEY_FILENAME): |
ampembeng | 17:46780b2de3e4 | 394 | print "ERROR: Private Key file not found, " + AWS_IOT_PRIVATE_KEY_FILENAME |
ampembeng | 17:46780b2de3e4 | 395 | exit(1) |
ampembeng | 17:46780b2de3e4 | 396 | if not os.path.exists(AWS_IOT_CERTIFICATE_FILENAME): |
ampembeng | 17:46780b2de3e4 | 397 | print "ERROR: AWS IOT cert file not found, " + AWS_IOT_CERTIFICATE_FILENAME |
ampembeng | 17:46780b2de3e4 | 398 | exit(1) |
ampembeng | 17:46780b2de3e4 | 399 | |
ampembeng | 17:46780b2de3e4 | 400 | # Init AWSIoTMQTTShadowClient |
ampembeng | 17:46780b2de3e4 | 401 | myAWSIoTMQTTShadowClient = None |
ampembeng | 17:46780b2de3e4 | 402 | if useWebsocket: |
ampembeng | 17:46780b2de3e4 | 403 | myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient("basicShadowDeltaListener", useWebsocket=True) |
ampembeng | 17:46780b2de3e4 | 404 | myAWSIoTMQTTShadowClient.configureEndpoint(AWS_IOT_MQTT_HOST, 443) |
ampembeng | 17:46780b2de3e4 | 405 | myAWSIoTMQTTShadowClient.configureCredentials(AWS_IOT_ROOT_CA_FILENAME) |
ampembeng | 17:46780b2de3e4 | 406 | else: |
ampembeng | 17:46780b2de3e4 | 407 | myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient("basicShadowDeltaListener") |
ampembeng | 17:46780b2de3e4 | 408 | myAWSIoTMQTTShadowClient.configureEndpoint(AWS_IOT_MQTT_HOST, AWS_IOT_MQTT_PORT) |
ampembeng | 17:46780b2de3e4 | 409 | myAWSIoTMQTTShadowClient.configureCredentials(AWS_IOT_ROOT_CA_FILENAME, AWS_IOT_PRIVATE_KEY_FILENAME, AWS_IOT_CERTIFICATE_FILENAME) |
ampembeng | 17:46780b2de3e4 | 410 | |
ampembeng | 17:46780b2de3e4 | 411 | # AWSIoTMQTTShadowClient configuration |
ampembeng | 17:46780b2de3e4 | 412 | myAWSIoTMQTTShadowClient.configureAutoReconnectBackoffTime(1, 32, 20) |
ampembeng | 17:46780b2de3e4 | 413 | myAWSIoTMQTTShadowClient.configureConnectDisconnectTimeout(10) # 10 sec |
ampembeng | 17:46780b2de3e4 | 414 | myAWSIoTMQTTShadowClient.configureMQTTOperationTimeout(5) # 5 sec |
ampembeng | 17:46780b2de3e4 | 415 | |
ampembeng | 17:46780b2de3e4 | 416 | # Connect to AWS IoT |
ampembeng | 17:46780b2de3e4 | 417 | myAWSIoTMQTTShadowClient.connect() |
ampembeng | 17:46780b2de3e4 | 418 | |
ampembeng | 17:46780b2de3e4 | 419 | # Create a deviceShadow with persistent subscription |
ampembeng | 20:ee34856ae510 | 420 | AIT = myAWSIoTMQTTShadowClient.createShadowHandlerWithName(AWS_IOT_MY_THING_NAME, True) |
ampembeng | 17:46780b2de3e4 | 421 | |
ampembeng | 17:46780b2de3e4 | 422 | # Listen on deltas |
ampembeng | 20:ee34856ae510 | 423 | AIT.shadowRegisterDeltaCallback(customShadowCallback_Delta) |
ampembeng | 17:46780b2de3e4 | 424 | |
ampembeng | 17:46780b2de3e4 | 425 | # Delete shadow JSON doc |
ampembeng | 20:ee34856ae510 | 426 | #AIT.shadowDelete(customShadowCallback_Delete, 5) |
ampembeng | 17:46780b2de3e4 | 427 | |
ampembeng | 17:46780b2de3e4 | 428 | getTimer = Timer(3.0, customShadowTimer_Get, ()) |
ampembeng | 17:46780b2de3e4 | 429 | getTimer.start() |
ampembeng | 17:46780b2de3e4 | 430 | |
ampembeng | 17:46780b2de3e4 | 431 | # GUI loop (loops until closed) |
ampembeng | 17:46780b2de3e4 | 432 | window.mainloop() |
ampembeng | 17:46780b2de3e4 | 433 | |
ampembeng | 17:46780b2de3e4 | 434 | # Kill our timer after the GUI closes |
ampembeng | 17:46780b2de3e4 | 435 | getTimerAlive = FALSE |
ampembeng | 19:488dad1e168e | 436 | getTimer.cancel() |