forked from arihantdaga/EnigmaIOT
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathEnigmaIoTUpdate.py
227 lines (186 loc) · 7.15 KB
/
EnigmaIoTUpdate.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
import base64
import paho.mqtt.client as mqtt
import time
import hashlib
import argparse
import os
import json
# EnigmaIoTUpdate -f <file.bin> -d <address> -t <basetopic> -u <mqttuser> -P <mqttpass> -s <mqttserver>
# -p <mqttport> <-s> -D <speed>
args = None
sleepyNode = True
resultTopic = "/result/#"
sleepSetTopic = "/set/sleeptime"
sleepResultTopic = "/result/sleeptime"
otaSetTopic = "/set/ota"
otaResultTopic = "/result/ota"
otaOutOfSequenceError = "OTA out of sequence error"
otaOK = "OTA finished OK"
# otaLength = 0
otaFinished = False
idx = 0
OTA_OUT_OF_SEQUENCE = 4
OTA_FINISHED = 6
def on_connect(client, userdata, flags, rc):
global args
if rc == 0:
print("Connected with result code " + str(rc))
mqtt.Client.connected_flag = True
else:
print("Error connecting. Code =" + str(rc))
return
sleep_topic = args.baseTopic + "/" + args.address + resultTopic
client.subscribe(sleep_topic)
print("Subscribed")
def on_message(client, userdata, msg):
global sleepyNode
global idx, otaFinished
payload = json.loads(msg.payload)
if msg.topic.find(sleepResultTopic) >= 0 and payload['sleeptime'] == 0:
sleepyNode = False
print(msg.topic + " " + str(msg.payload))
# payload = msg.payload.decode('utf-8')
if msg.topic.find(otaResultTopic) >= 0:
if payload['status'] == OTA_OUT_OF_SEQUENCE:
print(payload['last_chunk'], end='')
idx = int(payload['last_chunk'])
elif payload['status'] == OTA_FINISHED:
print(" OTA Finished ", end='')
otaFinished = True
def main():
global args
global sleepyNode
global otaFinished
opt = argparse.ArgumentParser(description='This program allows updating EnigmaIOT node over the air using'
'MQTT messages.')
opt.add_argument("-f", "--file",
type=str,
dest="filename",
default="program.bin",
help="File to program into device")
opt.add_argument("-d", "--daddress",
type=str,
dest="address",
help="Device address")
opt.add_argument("-t", "--topic",
type=str,
dest="baseTopic",
default="enigmaiot",
help="Base topic for MQTT messages")
opt.add_argument("-u", "--user",
type=str,
dest="mqttUser",
default="",
help="MQTT server username")
opt.add_argument("-P", "--password",
type=str,
dest="mqttPass",
default="",
help="MQTT server user password")
opt.add_argument("-S", "--server",
type=str,
dest="mqttServer",
default="127.0.0.1",
help="MQTT server address or name")
opt.add_argument("-p", "--port",
type=int,
dest="mqttPort",
default=1883,
help="MQTT server port")
opt.add_argument("-s", "--secure",
action="store_true",
dest="mqttSecure",
help="Use secure TLS in MQTT connection. Normally you should use port 8883")
opt.add_argument("--unsecure",
action="store_false",
dest="mqttSecure",
default=False,
help="Use secure plain TCP in MQTT connection. Normally you should use port 1883")
opt.add_argument("-D", "--speed",
type=str,
dest="otaSpeed",
default="fast",
help="OTA update speed profile: 'fast', 'medium' or 'slow' Throttle this down in case of"
"problems with OTA update. Default: %default")
# (options, args) = opt.parse_args()
args = opt.parse_args()
if not args.address:
opt.error('Destination address not supplied')
# print(options)
ota_topic = args.baseTopic + "/" + args.address + otaSetTopic
mqttclientname = "EnigmaIoTUpdate"
ota_length = os.stat(args.filename).st_size
delay_options = {"fast": 0.02, "medium": 0.06, "slow": 0.18}
packet_delay = delay_options.get(args.otaSpeed, 0.07)
with open(args.filename, "rb") as binary_file:
chunked_file = []
encoded_string = []
n = 212 # Max 215 - 2. Divisible by 4 => 212
for chunk in iter(lambda: binary_file.read(n), b""):
chunked_file.append(chunk)
for chunk in chunked_file:
encoded_string.append(base64.b64encode(bytes(chunk)).decode('ascii'))
# chunked_string = [encoded_string[i:i+n] for i in range(0, len(encoded_string), n)]
binary_file.seek(0);
hash_md5 = hashlib.md5()
for chunk in iter(lambda: binary_file.read(4096), b""):
hash_md5.update(chunk)
# print(hash_md5.hexdigest())
binary_file.close()
mqtt.Client.connected_flag = False
client = mqtt.Client(mqttclientname, True)
client.username_pw_set(username=args.mqttUser, password=args.mqttPass)
if args.mqttSecure:
client.tls_set()
client.on_connect = on_connect
client.on_message = on_message
client.connect(host=args.mqttServer, port=args.mqttPort)
while not client.connected_flag: # wait in loop
print("Connecting to MQTT server")
client.loop()
time.sleep(1)
# client.loop_start()
sleep_topic = args.baseTopic + "/" + args.address + sleepSetTopic
client.publish(sleep_topic, "0")
while sleepyNode:
print("Waiting for non sleepy confirmation")
client.loop()
time.sleep(1)
print("Sending hash: " + hash_md5.hexdigest())
md5_str = hash_md5.hexdigest()
# msg 0, file size, number of chunks, md5 checksum
print("Sending %d bytes in %d chunks" % (ota_length,len(encoded_string)))
client.publish(ota_topic, "0," + str(ota_length) + "," + str(len(encoded_string)) + "," + md5_str)
# for i in range(0, len(chunked_string), 1):
print("Sending file: " + args.filename)
global idx
# remove to simulate lost message
# error = False
while idx < len(encoded_string):
client.loop()
time.sleep(packet_delay)
# time.sleep(0.2)
# if i not in range(10,13):
i = idx + 1
client.publish(ota_topic, str(i) + "," + encoded_string[idx])
idx = idx + 1
# remove to simulate lost message
# if idx == 100 and not error:
# error = True
# idx = idx + 1
if i % 2 == 0:
print(".", end='')
if i % 160 == 0:
print(" %.f%%" % (i / len(encoded_string) * 100))
if i == len(encoded_string):
for i in range(0, 40):
client.loop()
time.sleep(0.5)
if otaFinished:
print(" OTA OK ", end='')
break
print("100%")
# time.sleep(5)
client.disconnect()
if __name__ == '__main__':
main()