|
@@ -20,14 +20,18 @@ Import("env")
|
20
|
20
|
|
21
|
21
|
import MarlinBinaryProtocol
|
22
|
22
|
|
23
|
|
-# Internal debug flag
|
24
|
|
-Debug = False
|
25
|
|
-
|
26
|
23
|
#-----------------#
|
27
|
24
|
# Upload Callback #
|
28
|
25
|
#-----------------#
|
29
|
26
|
def Upload(source, target, env):
|
30
|
27
|
|
|
28
|
+ #-------#
|
|
29
|
+ # Debug #
|
|
30
|
+ #-------#
|
|
31
|
+ Debug = False # Set to True to enable script debug
|
|
32
|
+ def debugPrint(data):
|
|
33
|
+ if Debug: print(f"[Debug]: {data}")
|
|
34
|
+
|
31
|
35
|
#------------------#
|
32
|
36
|
# Marlin functions #
|
33
|
37
|
#------------------#
|
|
@@ -39,19 +43,35 @@ def Upload(source, target, env):
|
39
|
43
|
# Port functions #
|
40
|
44
|
#----------------#
|
41
|
45
|
def _GetUploadPort(env):
|
42
|
|
- if Debug: print('Autodetecting upload port...')
|
|
46
|
+ debugPrint('Autodetecting upload port...')
|
43
|
47
|
env.AutodetectUploadPort(env)
|
44
|
|
- port = env.subst('$UPLOAD_PORT')
|
45
|
|
- if not port:
|
|
48
|
+ portName = env.subst('$UPLOAD_PORT')
|
|
49
|
+ if not portName:
|
46
|
50
|
raise Exception('Error detecting the upload port.')
|
47
|
|
- if Debug: print('OK')
|
48
|
|
- return port
|
|
51
|
+ debugPrint('OK')
|
|
52
|
+ return portName
|
49
|
53
|
|
50
|
54
|
#-------------------------#
|
51
|
55
|
# Simple serial functions #
|
52
|
56
|
#-------------------------#
|
|
57
|
+ def _OpenPort():
|
|
58
|
+ # Open serial port
|
|
59
|
+ if port.is_open: return
|
|
60
|
+ debugPrint('Opening upload port...')
|
|
61
|
+ port.open()
|
|
62
|
+ port.reset_input_buffer()
|
|
63
|
+ debugPrint('OK')
|
|
64
|
+
|
|
65
|
+ def _ClosePort():
|
|
66
|
+ # Open serial port
|
|
67
|
+ if port is None: return
|
|
68
|
+ if not port.is_open: return
|
|
69
|
+ debugPrint('Closing upload port...')
|
|
70
|
+ port.close()
|
|
71
|
+ debugPrint('OK')
|
|
72
|
+
|
53
|
73
|
def _Send(data):
|
54
|
|
- if Debug: print(f'>> {data}')
|
|
74
|
+ debugPrint(f'>> {data}')
|
55
|
75
|
strdata = bytearray(data, 'utf8') + b'\n'
|
56
|
76
|
port.write(strdata)
|
57
|
77
|
time.sleep(0.010)
|
|
@@ -60,37 +80,37 @@ def Upload(source, target, env):
|
60
|
80
|
clean_responses = []
|
61
|
81
|
responses = port.readlines()
|
62
|
82
|
for Resp in responses:
|
63
|
|
- # Test: suppress invaid chars (coming from debug info)
|
|
83
|
+ # Suppress invalid chars (coming from debug info)
|
64
|
84
|
try:
|
65
|
85
|
clean_response = Resp.decode('utf8').rstrip().lstrip()
|
66
|
86
|
clean_responses.append(clean_response)
|
67
|
87
|
except:
|
68
|
88
|
pass
|
69
|
|
- if Debug: print(f'<< {clean_response}')
|
|
89
|
+ debugPrint(f'<< {clean_response}')
|
70
|
90
|
return clean_responses
|
71
|
91
|
|
72
|
92
|
#------------------#
|
73
|
93
|
# SDCard functions #
|
74
|
94
|
#------------------#
|
75
|
95
|
def _CheckSDCard():
|
76
|
|
- if Debug: print('Checking SD card...')
|
|
96
|
+ debugPrint('Checking SD card...')
|
77
|
97
|
_Send('M21')
|
78
|
98
|
Responses = _Recv()
|
79
|
99
|
if len(Responses) < 1 or not any('SD card ok' in r for r in Responses):
|
80
|
100
|
raise Exception('Error accessing SD card')
|
81
|
|
- if Debug: print('SD Card OK')
|
|
101
|
+ debugPrint('SD Card OK')
|
82
|
102
|
return True
|
83
|
103
|
|
84
|
104
|
#----------------#
|
85
|
105
|
# File functions #
|
86
|
106
|
#----------------#
|
87
|
107
|
def _GetFirmwareFiles(UseLongFilenames):
|
88
|
|
- if Debug: print('Get firmware files...')
|
|
108
|
+ debugPrint('Get firmware files...')
|
89
|
109
|
_Send(f"M20 F{'L' if UseLongFilenames else ''}")
|
90
|
110
|
Responses = _Recv()
|
91
|
111
|
if len(Responses) < 3 or not any('file list' in r for r in Responses):
|
92
|
112
|
raise Exception('Error getting firmware files')
|
93
|
|
- if Debug: print('OK')
|
|
113
|
+ debugPrint('OK')
|
94
|
114
|
return Responses
|
95
|
115
|
|
96
|
116
|
def _FilterFirmwareFiles(FirmwareList, UseLongFilenames):
|
|
@@ -114,6 +134,17 @@ def Upload(source, target, env):
|
114
|
134
|
raise Exception(f"Firmware file '{FirmwareFile}' not removed")
|
115
|
135
|
return Removed
|
116
|
136
|
|
|
137
|
+ def _RollbackUpload(FirmwareFile):
|
|
138
|
+ if not rollback: return
|
|
139
|
+ print(f"Rollback: trying to delete firmware '{FirmwareFile}'...")
|
|
140
|
+ _OpenPort()
|
|
141
|
+ # Wait for SD card release
|
|
142
|
+ time.sleep(1)
|
|
143
|
+ # Remount SD card
|
|
144
|
+ _CheckSDCard()
|
|
145
|
+ print(' OK' if _RemoveFirmwareFile(FirmwareFile) else ' Error!')
|
|
146
|
+ _ClosePort()
|
|
147
|
+
|
117
|
148
|
|
118
|
149
|
#---------------------#
|
119
|
150
|
# Callback Entrypoint #
|
|
@@ -121,6 +152,7 @@ def Upload(source, target, env):
|
121
|
152
|
port = None
|
122
|
153
|
protocol = None
|
123
|
154
|
filetransfer = None
|
|
155
|
+ rollback = False
|
124
|
156
|
|
125
|
157
|
# Get Marlin evironment vars
|
126
|
158
|
MarlinEnv = env['MARLIN_FEATURES']
|
|
@@ -204,9 +236,9 @@ def Upload(source, target, env):
|
204
|
236
|
if not marlin_custom_firmware_upload:
|
205
|
237
|
raise Exception(f"CUSTOM_FIRMWARE_UPLOAD must be enabled in 'Configuration_adv.h' for '{marlin_motherboard}'")
|
206
|
238
|
|
207
|
|
- # Init serial port
|
|
239
|
+ # Init & Open serial port
|
208
|
240
|
port = serial.Serial(upload_port, baudrate = upload_speed, write_timeout = 0, timeout = 0.1)
|
209
|
|
- port.reset_input_buffer()
|
|
241
|
+ _OpenPort()
|
210
|
242
|
|
211
|
243
|
# Check SD card status
|
212
|
244
|
_CheckSDCard()
|
|
@@ -228,24 +260,26 @@ def Upload(source, target, env):
|
228
|
260
|
print(' OK' if _RemoveFirmwareFile(OldFirmwareFile) else ' Error!')
|
229
|
261
|
|
230
|
262
|
# Close serial
|
231
|
|
- port.close()
|
|
263
|
+ _ClosePort()
|
232
|
264
|
|
233
|
265
|
# Cleanup completed
|
234
|
|
- if Debug: print('Cleanup completed')
|
|
266
|
+ debugPrint('Cleanup completed')
|
235
|
267
|
|
236
|
268
|
# WARNING! The serial port must be closed here because the serial transfer that follow needs it!
|
237
|
269
|
|
238
|
270
|
# Upload firmware file
|
239
|
|
- if Debug: print(f"Copy '{upload_firmware_source_name}' --> '{upload_firmware_target_name}'")
|
|
271
|
+ debugPrint(f"Copy '{upload_firmware_source_name}' --> '{upload_firmware_target_name}'")
|
240
|
272
|
protocol = MarlinBinaryProtocol.Protocol(upload_port, upload_speed, upload_blocksize, float(upload_error_ratio), int(upload_timeout))
|
241
|
273
|
#echologger = MarlinBinaryProtocol.EchoProtocol(protocol)
|
242
|
274
|
protocol.connect()
|
|
275
|
+ # Mark the rollback (delete broken transfer) from this point on
|
|
276
|
+ rollback = True
|
243
|
277
|
filetransfer = MarlinBinaryProtocol.FileTransferProtocol(protocol)
|
244
|
|
- filetransfer.copy(upload_firmware_source_name, upload_firmware_target_name, upload_compression, upload_test)
|
|
278
|
+ transferOK = filetransfer.copy(upload_firmware_source_name, upload_firmware_target_name, upload_compression, upload_test)
|
245
|
279
|
protocol.disconnect()
|
246
|
280
|
|
247
|
281
|
# Notify upload completed
|
248
|
|
- protocol.send_ascii('M117 Firmware uploaded')
|
|
282
|
+ protocol.send_ascii('M117 Firmware uploaded' if transferOK else 'M117 Firmware upload failed')
|
249
|
283
|
|
250
|
284
|
# Remount SD card
|
251
|
285
|
print('Wait for SD card release...')
|
|
@@ -253,34 +287,56 @@ def Upload(source, target, env):
|
253
|
287
|
print('Remount SD card')
|
254
|
288
|
protocol.send_ascii('M21')
|
255
|
289
|
|
256
|
|
- # Trigger firmware update
|
257
|
|
- if upload_reset:
|
258
|
|
- print('Trigger firmware update...')
|
259
|
|
- protocol.send_ascii('M997', True)
|
|
290
|
+ # Transfer failed?
|
|
291
|
+ if not transferOK:
|
|
292
|
+ protocol.shutdown()
|
|
293
|
+ _RollbackUpload(upload_firmware_target_name)
|
|
294
|
+ else:
|
|
295
|
+ # Trigger firmware update
|
|
296
|
+ if upload_reset:
|
|
297
|
+ print('Trigger firmware update...')
|
|
298
|
+ protocol.send_ascii('M997', True)
|
|
299
|
+ protocol.shutdown()
|
260
|
300
|
|
261
|
|
- protocol.shutdown()
|
262
|
|
- print('Firmware update completed')
|
|
301
|
+ print('Firmware update completed' if transferOK else 'Firmware update failed')
|
|
302
|
+ return 0 if transferOK else -1
|
263
|
303
|
|
264
|
304
|
except KeyboardInterrupt:
|
265
|
|
- if port: port.close()
|
|
305
|
+ print('Aborted by user')
|
266
|
306
|
if filetransfer: filetransfer.abort()
|
267
|
|
- if protocol: protocol.shutdown()
|
|
307
|
+ if protocol:
|
|
308
|
+ protocol.disconnect()
|
|
309
|
+ protocol.shutdown()
|
|
310
|
+ _RollbackUpload(upload_firmware_target_name)
|
|
311
|
+ _ClosePort()
|
268
|
312
|
raise
|
269
|
313
|
|
270
|
314
|
except serial.SerialException as se:
|
271
|
|
- if port: port.close()
|
272
|
|
- print(f'Serial excepion: {se}')
|
|
315
|
+ # This exception is raised only for send_ascii data (not for binary transfer)
|
|
316
|
+ print(f'Serial excepion: {se}, transfer aborted')
|
|
317
|
+ if protocol:
|
|
318
|
+ protocol.disconnect()
|
|
319
|
+ protocol.shutdown()
|
|
320
|
+ _RollbackUpload(upload_firmware_target_name)
|
|
321
|
+ _ClosePort()
|
273
|
322
|
raise Exception(se)
|
274
|
323
|
|
275
|
324
|
except MarlinBinaryProtocol.FatalError:
|
276
|
|
- if port: port.close()
|
277
|
|
- if protocol: protocol.shutdown()
|
278
|
|
- print('Too many retries, Abort')
|
|
325
|
+ print('Too many retries, transfer aborted')
|
|
326
|
+ if protocol:
|
|
327
|
+ protocol.disconnect()
|
|
328
|
+ protocol.shutdown()
|
|
329
|
+ _RollbackUpload(upload_firmware_target_name)
|
|
330
|
+ _ClosePort()
|
279
|
331
|
raise
|
280
|
332
|
|
281
|
|
- except:
|
282
|
|
- if port: port.close()
|
283
|
|
- if protocol: protocol.shutdown()
|
|
333
|
+ except Exception as ex:
|
|
334
|
+ print(f"\nException: {ex}, transfer aborted")
|
|
335
|
+ if protocol:
|
|
336
|
+ protocol.disconnect()
|
|
337
|
+ protocol.shutdown()
|
|
338
|
+ _RollbackUpload(upload_firmware_target_name)
|
|
339
|
+ _ClosePort()
|
284
|
340
|
print('Firmware not updated')
|
285
|
341
|
raise
|
286
|
342
|
|