1 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
9 from Cura.util import serialWrapper as serial
11 from Cura.avr_isp import stk500v2
12 from Cura.avr_isp import ispBase
13 from Cura.avr_isp import intelHex
15 from Cura.gui.util import taskbar
16 from Cura.util import machineCom
17 from Cura.util import profile
18 from Cura.util import resources
20 def getDefaultFirmware(machineIndex = None):
22 'ultimaker2go':"MarlinUltimaker2go.hex",
23 'Witbox':"MarlinWitbox.hex",
24 'lulzbot_mini': "Mini-Single-or-Flexystruder-LBHexagon-2015Q2.hex",
25 'lulzbot_mini_flexystruder': "Mini-Single-or-Flexystruder-LBHexagon-2015Q2.hex",
26 'lulzbot_TAZ_4_SingleV1': "Taz4-5-Single-or-Flexystruder-Budaschnozzle-2014Q3.hex",
27 'lulzbot_TAZ_5_SingleV1': "Taz4-5-Single-or-Flexystruder-Budaschnozzle-2014Q3.hex",
28 'lulzbot_TAZ_4_05nozzle': "Taz4-Single-Extruder-LBHexagon-2015Q3.hex",
29 'lulzbot_TAZ_5_05nozzle': "Taz5-Single-Extruder-LBHexagon-2015Q3.hex",
30 'lulzbot_TAZ_4_035nozzle': "Taz4-Single-Extruder-LBHexagon-2015Q3.hex",
31 'lulzbot_TAZ_5_035nozzle': "Taz5-Single-Extruder-LBHexagon-2015Q3.hex",
32 'lulzbot_TAZ_4_FlexystruderV1': "Taz4-5-Single-or-Flexystruder-Budaschnozzle-2014Q3.hex",
33 'lulzbot_TAZ_5_FlexystruderV1': "Taz4-5-Single-or-Flexystruder-Budaschnozzle-2014Q3.hex",
34 'lulzbot_TAZ_4_FlexystruderV2': "Taz4-5-Flexystruder-LBHexagon-2015Q3.hex",
35 'lulzbot_TAZ_5_FlexystruderV2': "Taz4-5-Flexystruder-LBHexagon-2015Q3.hex",
36 'lulzbot_TAZ_4_DualV1': "Taz4-5-Dual-or-FlexyDually-Budaschnozzle-2015Q1.hex",
37 'lulzbot_TAZ_5_DualV1': "Taz4-5-Dual-or-FlexyDually-Budaschnozzle-2015Q1.hex",
38 'lulzbot_TAZ_4_DualV2': "Taz4-5-Dual-LBHexagon-2015Q3.hex",
39 'lulzbot_TAZ_5_DualV2': "Taz4-5-Dual-LBHexagon-2015Q3.hex",
40 'lulzbot_TAZ_4_FlexyDuallyV1': "Taz4-5-Dual-or-FlexyDually-Budaschnozzle-2015Q1.hex",
41 'lulzbot_TAZ_5_FlexyDuallyV1': "Taz4-5-Dual-or-FlexyDually-Budaschnozzle-2015Q1.hex",
42 'lulzbot_TAZ_4_FlexyDuallyV2': "Taz4-5-FlexyDually-LBHexagon-2015Q3.hex",
43 'lulzbot_TAZ_5_FlexyDuallyV2': "Taz4-5-FlexyDually-LBHexagon-2015Q3.hex",
44 'lulzbot_TAZ_6_Single_v2.1': "Marlin_v1.0.2.13.hex"
46 machine_type = profile.getMachineSetting('machine_type', machineIndex)
47 extruders = profile.getMachineSettingFloat('extruder_amount', machineIndex)
48 heated_bed = profile.getMachineSetting('has_heated_bed', machineIndex) == 'True'
50 if sys.platform.startswith('linux'):
52 if machine_type == 'ultimaker':
53 name = 'MarlinUltimaker'
58 name += '-%d' % (baudrate)
61 return resources.getPathForFirmware(name + '.hex')
63 if machine_type == 'ultimaker_plus':
64 name = 'MarlinUltimaker-UMOP-%d' % (baudrate)
69 return resources.getPathForFirmware(name + '.hex')
70 if machine_type == 'ultimaker2':
74 return resources.getPathForFirmware("MarlinUltimaker2-dual.hex")
75 return resources.getPathForFirmware("MarlinUltimaker2.hex")
76 if machine_type == 'ultimaker2extended':
80 return resources.getPathForFirmware("MarlinUltimaker2extended-dual.hex")
81 return resources.getPathForFirmware("MarlinUltimaker2extended.hex")
82 if firmwareDict.has_key(machine_type):
83 return resources.getPathForFirmware(firmwareDict[machine_type])
86 def InstallFirmware(parent = None, filename = None, port = None, machineIndex = None):
87 dlg = InstallFirmwareDialog(parent, filename, port, machineIndex)
92 class InstallFirmwareDialog(wx.Dialog):
93 def __init__(self, parent = None, filename = None, port = None, machineIndex = None):
94 super(InstallFirmwareDialog, self).__init__(parent=parent, title=_("Firmware install for %s") % (profile.getMachineName(machineIndex).title()), size=(250, 100))
96 port = profile.getMachineSetting('serial_port')
98 filename = getDefaultFirmware(machineIndex)
99 self._machine_type = profile.getMachineSetting('machine_type', machineIndex)
100 if self._machine_type == 'reprap':
101 wx.MessageBox(_("Cura only supports firmware updates for ATMega2560 based hardware.\nSo updating your RepRap with Cura might or might not work."), _("Firmware update"), wx.OK | wx.ICON_INFORMATION)
103 sizer = wx.BoxSizer(wx.VERTICAL)
105 self.progressLabel = wx.StaticText(self, -1, 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nX\nX')
106 sizer.Add(self.progressLabel, 0, flag=wx.ALIGN_CENTER|wx.ALL, border=5)
107 self.progressGauge = wx.Gauge(self, -1)
108 sizer.Add(self.progressGauge, 0, flag=wx.EXPAND)
109 self.okButton = wx.Button(self, -1, _("OK"))
110 self.okButton.Disable()
111 self.okButton.Bind(wx.EVT_BUTTON, self.OnOk)
112 sizer.Add(self.okButton, 0, flag=wx.ALIGN_CENTER|wx.ALL, border=5)
115 self.filename = filename
121 self.show_connect_dialog = False
124 if self.filename is None:
125 wx.MessageBox(_("I am sorry, but Cura does not ship with a default firmware for your machine configuration."), _("Firmware update"), wx.OK | wx.ICON_ERROR)
128 self.thread = threading.Thread(target=self.OnRun)
129 self.thread.daemon = True
133 # Creating a MessageBox in a separate thread while main thread is locked inside a ShowModal
134 # will cause Python to crash with X errors. So we need to show the dialog here instead
135 if self.show_connect_dialog:
136 wx.MessageBox(_("Failed to find machine for firmware upgrade\nIs your machine connected to the PC?"),
137 _("Firmware update"), wx.OK | wx.ICON_ERROR)
141 wx.CallAfter(self.updateLabel, _("Reading firmware..."))
142 hexFile = intelHex.readHex(self.filename)
143 wx.CallAfter(self.updateLabel, _("Connecting to machine..."))
144 programmer = stk500v2.Stk500v2()
145 programmer.progressCallback = self.OnProgress
146 if self.port == 'AUTO':
147 wx.CallAfter(self.updateLabel, _("Please connect the printer to your\ncomputer with a USB cable and power it on."))
148 while not programmer.isConnected():
149 for self.port in machineCom.serialList(True):
151 programmer.connect(self.port)
153 except ispBase.IspError:
161 programmer.connect(self.port)
162 except ispBase.IspError:
168 if not programmer.isConnected():
169 self.show_connect_dialog = True
170 wx.CallAfter(self.Close)
173 if self._machine_type == 'ultimaker':
174 if programmer.hasChecksumFunction():
175 wx.CallAfter(self.updateLabel, _("Failed to install firmware:\nThis firmware is not compatible with this machine.\nTrying to install UMO firmware on an UM2 or UMO+?"))
177 wx.CallAfter(self.okButton.Enable)
179 if self._machine_type == 'ultimaker_plus' or self._machine_type == 'ultimaker2':
180 if not programmer.hasChecksumFunction():
181 wx.CallAfter(self.updateLabel, _("Failed to install firmware:\nThis firmware is not compatible with this machine.\nTrying to install UM2 or UMO+ firmware on an UMO?"))
183 wx.CallAfter(self.okButton.Enable)
186 wx.CallAfter(self.updateLabel, _("Uploading firmware..."))
188 programmer.programChip(hexFile)
190 wx.CallAfter(self.updateLabel, _("Done!\nInstalled firmware: %s") % (os.path.basename(self.filename)))
191 except ispBase.IspError as e:
192 wx.CallAfter(self.updateLabel, _("Failed to write firmware.\n") + str(e))
195 wx.CallAfter(self.okButton.Enable)
197 def updateLabel(self, text):
198 self.progressLabel.SetLabel(text)
201 def OnProgress(self, value, max):
203 wx.CallAfter(self.progressGauge.SetRange, max)
204 wx.CallAfter(self.progressGauge.SetValue, value)
205 taskbar.setProgress(self.GetParent(), value, max)
209 taskbar.setBusy(self.GetParent(), False)
211 def OnClose(self, e):
215 class AutoUpdateFirmware(wx.Dialog):
216 def __init__(self, parent, filename = None, port = None, machineIndex = None):
217 super(AutoUpdateFirmware, self).__init__(parent=parent, title=_("Auto Firmware install"), size=(250, 500))
219 port = profile.getMachineSetting('serial_port')
222 sizer = wx.BoxSizer(wx.VERTICAL)
224 self.progressLabel = wx.StaticText(self, -1, 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nX\nX')
225 sizer.Add(self.progressLabel, 0, flag=wx.ALIGN_CENTER|wx.ALL, border=5)
226 self.progressGauge = wx.Gauge(self, -1)
227 sizer.Add(self.progressGauge, 0, flag=wx.EXPAND)
228 self.okButton = wx.Button(self, -1, _("OK"))
229 self.okButton.Bind(wx.EVT_BUTTON, self.OnOk)
230 sizer.Add(self.okButton, 0, flag=wx.ALIGN_CENTER|wx.ALL, border=5)
232 f = wx.Font(8, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
233 self._termLog = wx.TextCtrl(self, style=wx.TE_MULTILINE | wx.TE_DONTWRAP)
234 self._termLog.SetFont(f)
235 self._termLog.SetEditable(0)
236 self._termLog.SetMinSize((1, 400))
237 self._termInput = wx.TextCtrl(self, style=wx.TE_PROCESS_ENTER)
238 self._termInput.SetFont(f)
239 sizer.Add(self._termLog, 0, flag=wx.ALIGN_CENTER|wx.ALL|wx.EXPAND)
240 sizer.Add(self._termInput, 0, flag=wx.ALIGN_CENTER|wx.ALL|wx.EXPAND)
242 self.Bind(wx.EVT_TEXT_ENTER, self.OnTermEnterLine, self._termInput)
246 self.filename = filename
252 self.thread = threading.Thread(target=self.OnRun)
253 self.thread.daemon = True
256 self.read_thread = threading.Thread(target=self.OnSerialRead)
257 self.read_thread.daemon = True
258 self.read_thread.start()
264 def _addTermLog(self, line):
265 if self._termLog is not None:
266 if len(self._termLog.GetValue()) > 10000:
267 self._termLog.SetValue(self._termLog.GetValue()[-10000:])
268 self._termLog.SetInsertionPointEnd()
269 if type(line) != unicode:
270 line = unicode(line, 'utf-8', 'replace')
271 self._termLog.AppendText(line.encode('utf-8', 'replace'))
273 def OnTermEnterLine(self, e):
274 lines = self._termInput.GetValue().split(';')
278 self._addTermLog('> %s\n' % (line))
279 if self._serial is not None:
280 self._serial.write(line + '\n')
285 new_mtime = os.stat(self.filename).st_mtime
286 if mtime != new_mtime:
288 if self._serial is not None:
294 self._serial = serial.Serial(self.port, 115200)
299 def OnSerialRead(self):
301 if self._serial is None:
305 line = self._serial.readline()
306 wx.CallAfter(self._addTermLog, line)
311 wx.CallAfter(self.okButton.Disable)
312 wx.CallAfter(self.updateLabel, _("Reading firmware..."))
313 hexFile = intelHex.readHex(self.filename)
314 wx.CallAfter(self.updateLabel, _("Connecting to machine..."))
315 programmer = stk500v2.Stk500v2()
316 programmer.progressCallback = self.OnProgress
317 if self.port == 'AUTO':
318 wx.CallAfter(self.updateLabel, _("Please connect the printer to your\ncomputer with a USB cable and power it on."))
319 while not programmer.isConnected():
320 for self.port in machineCom.serialList(True):
322 programmer.connect(self.port)
324 except ispBase.IspError:
332 programmer.connect(self.port)
333 except ispBase.IspError:
336 if not programmer.isConnected():
337 wx.CallAfter(self.updateLabel, _("Failed to connect to programmer.\n"))
340 wx.CallAfter(self.updateLabel, _("Uploading firmware..."))
342 programmer.programChip(hexFile)
343 wx.CallAfter(self.updateLabel, _("Done!\nInstalled firmware: %s") % (os.path.basename(self.filename)))
344 except ispBase.IspError as e:
345 wx.CallAfter(self.updateLabel, _("Failed to write firmware.\n") + str(e))
348 wx.CallAfter(self.okButton.Enable)
350 def updateLabel(self, text):
351 self.progressLabel.SetLabel(text)
354 def OnProgress(self, value, max):
355 wx.CallAfter(self.progressGauge.SetRange, max)
356 wx.CallAfter(self.progressGauge.SetValue, value)
357 taskbar.setProgress(self.GetParent(), value, max)
361 taskbar.setBusy(self.GetParent(), False)
363 def OnClose(self, e):