1 from __future__ import absolute_import
\r
4 import wx, os, platform, types, webbrowser, threading, time, re
\r
7 from gui import firmwareInstall
\r
8 from util import machineCom
\r
9 from util import profile
\r
11 class InfoPage(wx.wizard.WizardPageSimple):
\r
12 def __init__(self, parent, title):
\r
13 wx.wizard.WizardPageSimple.__init__(self, parent)
\r
15 sizer = wx.BoxSizer(wx.VERTICAL)
\r
17 self.SetSizer(sizer)
\r
19 title = wx.StaticText(self, -1, title)
\r
20 title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD))
\r
21 sizer.Add(title, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
\r
22 sizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.ALL, 5)
\r
24 def AddText(self,info):
\r
25 text = wx.StaticText(self, -1, info)
\r
26 self.GetSizer().Add(text, 0, wx.LEFT|wx.RIGHT, 5)
\r
29 def AddSeperator(self):
\r
30 self.GetSizer().Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.ALL, 5)
\r
32 def AddHiddenSeperator(self):
\r
35 def AddRadioButton(self, label, style = 0):
\r
36 radio = wx.RadioButton(self, -1, label, style=style)
\r
37 self.GetSizer().Add(radio, 0, wx.EXPAND|wx.ALL, 5)
\r
40 def AddButton(self, label):
\r
41 button = wx.Button(self, -1, label)
\r
42 self.GetSizer().Add(button, 0, wx.LEFT, 5)
\r
45 def AddDualButton(self, label1, label2):
\r
47 p.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
\r
48 button1 = wx.Button(p, -1, label1)
\r
49 p.GetSizer().Add(button1, 0, wx.RIGHT, 8)
\r
50 button2 = wx.Button(p, -1, label2)
\r
51 p.GetSizer().Add(button2, 0)
\r
52 self.GetSizer().Add(p, 0, wx.LEFT, 5)
\r
53 return button1, button2
\r
55 def AllowNext(self):
\r
58 def StoreData(self):
\r
61 class FirstInfoPage(InfoPage):
\r
62 def __init__(self, parent):
\r
63 super(FirstInfoPage, self).__init__(parent, "First time run wizard")
\r
64 self.AddText('Welcome, and thanks for trying Cura!')
\r
66 self.AddText('This wizard will help you with the following steps:')
\r
67 self.AddText('* Configure Cura for your machine')
\r
68 self.AddText('* Upgrade your firmware')
\r
69 self.AddText('* Calibrate your machine')
\r
70 #self.AddText('* Do your first print')
\r
72 class RepRapInfoPage(InfoPage):
\r
73 def __init__(self, parent):
\r
74 super(RepRapInfoPage, self).__init__(parent, "RepRap information")
\r
75 self.AddText('Sorry, but this wizard will not help you with\nconfiguring and calibrating your RepRap.')
\r
77 self.AddText('You will have to manually install Marlin or Sprinter firmware\nand configure Cura.')
\r
79 class MachineSelectPage(InfoPage):
\r
80 def __init__(self, parent):
\r
81 super(MachineSelectPage, self).__init__(parent, "Select your machine")
\r
82 self.AddText('What kind of machine do you have:')
\r
84 self.UltimakerRadio = self.AddRadioButton("Ultimaker", style=wx.RB_GROUP)
\r
85 self.UltimakerRadio.SetValue(True)
\r
86 self.UltimakerRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerSelect)
\r
87 self.OtherRadio = self.AddRadioButton("Other (Ex: RepRap)")
\r
88 self.OtherRadio.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
\r
90 def OnUltimakerSelect(self, e):
\r
91 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerFirmwareUpgradePage)
\r
93 def OnOtherSelect(self, e):
\r
94 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().repRapInfoPage)
\r
96 def StoreData(self):
\r
97 if self.UltimakerRadio.GetValue():
\r
98 profile.putPreference('machine_width', '205')
\r
99 profile.putPreference('machine_depth', '205')
\r
100 profile.putPreference('machine_height', '200')
\r
101 profile.putProfileSetting('nozzle_size', '0.4')
\r
102 profile.putProfileSetting('machine_center_x', '100')
\r
103 profile.putProfileSetting('machine_center_y', '100')
\r
105 profile.putPreference('machine_width', '80')
\r
106 profile.putPreference('machine_depth', '80')
\r
107 profile.putPreference('machine_height', '60')
\r
108 profile.putProfileSetting('nozzle_size', '0.5')
\r
109 profile.putProfileSetting('machine_center_x', '40')
\r
110 profile.putProfileSetting('machine_center_y', '40')
\r
111 profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
\r
113 class FirmwareUpgradePage(InfoPage):
\r
114 def __init__(self, parent):
\r
115 super(FirmwareUpgradePage, self).__init__(parent, "Upgrade Ultimaker Firmware")
\r
116 self.AddText('Firmware is the piece of software running directly on your 3D printer.\nThis firmware controls the step motors, regulates the temperature\nand ultimately makes your printer work.')
\r
117 self.AddHiddenSeperator()
\r
118 self.AddText('The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier.')
\r
119 self.AddHiddenSeperator()
\r
120 self.AddText('Cura requires these new features and thus\nyour firmware will most likely need to be upgraded.\nYou will get the chance to do so now.')
\r
121 upgradeButton, skipUpgradeButton = self.AddDualButton('Upgrade to Marlin firmware', 'Skip upgrade')
\r
122 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
\r
123 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
\r
124 self.AddHiddenSeperator()
\r
125 self.AddText('Do not upgrade to this firmware if:')
\r
126 self.AddText('* You have an older machine based on ATMega1280')
\r
127 self.AddText('* Using an LCD panel')
\r
128 self.AddText('* Have other changes in the firmware')
\r
129 button = self.AddButton('Goto this page for a custom firmware')
\r
130 button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
\r
132 def AllowNext(self):
\r
135 def OnUpgradeClick(self, e):
\r
136 if firmwareInstall.InstallFirmware(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../firmware/default.hex")):
\r
137 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
\r
139 def OnSkipClick(self, e):
\r
140 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
\r
142 def OnUrlClick(self, e):
\r
143 webbrowser.open('http://daid.mine.nu/~daid/marlin_build/')
\r
145 class UltimakerCheckupPage(InfoPage):
\r
146 def __init__(self, parent):
\r
147 super(UltimakerCheckupPage, self).__init__(parent, "Ultimaker Checkup")
\r
148 self.AddText('It is a good idea to do a few sanity checks now on your Ultimaker.\nYou can skip these if you know your machine is functional.')
\r
149 b1, b2 = self.AddDualButton('Run checks', 'Skip checks')
\r
150 b1.Bind(wx.EVT_BUTTON, self.OnCheckClick)
\r
151 b2.Bind(wx.EVT_BUTTON, self.OnSkipClick)
\r
152 self.AddSeperator();
\r
153 self.checkPanel = None
\r
155 def AllowNext(self):
\r
158 def OnSkipClick(self, e):
\r
159 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
\r
161 def OnCheckClick(self, e):
\r
162 if self.checkPanel != None:
\r
163 self.checkPanel.Destroy()
\r
164 self.checkPanel = wx.Panel(self)
\r
165 self.checkPanel.SetSizer(wx.BoxSizer(wx.VERTICAL))
\r
166 self.GetSizer().Add(self.checkPanel, 0, wx.LEFT|wx.RIGHT, 5)
\r
167 threading.Thread(target=self.OnRun).start()
\r
169 def AddProgressText(self, info):
\r
170 text = wx.StaticText(self.checkPanel, -1, info)
\r
171 self.checkPanel.GetSizer().Add(text, 0)
\r
172 self.checkPanel.Layout()
\r
176 wx.CallAfter(self.AddProgressText, "Connecting to machine...")
\r
177 self.comm = machineCom.MachineCom()
\r
179 if not self.comm.isOpen():
\r
180 wx.CallAfter(self.AddProgressText, "Error: Failed to open serial port to machine")
\r
181 wx.CallAfter(self.AddProgressText, "If this keeps happening, try disconnecting and reconnecting the USB cable")
\r
184 wx.CallAfter(self.AddProgressText, "Checking start message...")
\r
185 if self.DoCommCommandWithTimeout(None, 'start') == False:
\r
186 wx.CallAfter(self.AddProgressText, "Error: Missing start message.")
\r
190 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
\r
193 wx.CallAfter(self.AddProgressText, "Disabling step motors...")
\r
194 if self.DoCommCommandWithTimeout('M84') == False:
\r
195 wx.CallAfter(self.AddProgressText, "Error: Missing reply to Deactivate steppers (M84).")
\r
199 if self.DoCommCommandWithTimeout("M104 S0") == False:
\r
200 wx.CallAfter(self.AddProgressText, "Failed to set temperature")
\r
204 wx.MessageBox('Please move the printer head to the center of the machine\nalso move the platform so it is not at the highest or lowest position,\nand make sure the machine is powered on.', 'Machine check', wx.OK | wx.ICON_INFORMATION)
\r
206 idleTemp = self.readTemp()
\r
208 wx.CallAfter(self.AddProgressText, "Waiting for head to cool down before temperature test...")
\r
209 while idleTemp > 40:
\r
210 idleTemp = self.readTemp()
\r
213 wx.CallAfter(self.AddProgressText, "Checking heater and temperature sensor...")
\r
214 wx.CallAfter(self.AddProgressText, "(This takes about 30 seconds)")
\r
215 if self.DoCommCommandWithTimeout("M104 S100") == False:
\r
216 wx.CallAfter(self.AddProgressText, "Failed to set temperature")
\r
221 tempInc = self.readTemp() - idleTemp
\r
223 if self.DoCommCommandWithTimeout("M104 S0") == False:
\r
224 wx.CallAfter(self.AddProgressText, "Failed to set temperature")
\r
229 wx.CallAfter(self.AddProgressText, "Your temperature sensor or heater is not working!")
\r
232 wx.CallAfter(self.AddProgressText, "Heater and temperature sensor working\nWarning: head might still be hot!")
\r
234 wx.CallAfter(self.AddProgressText, "Checking endstops")
\r
235 if self.DoCommCommandWithTimeout('M119', 'x_min') != "x_min:L x_max:L y_min:L y_max:L z_min:L z_max:L":
\r
236 wx.CallAfter(self.AddProgressText, "Error: There is a problem in your endstops!\nOne of them seems to be pressed while it shouldn't\ncheck the cable connections and the switches themselfs.")
\r
239 wx.CallAfter(self.AddProgressText, "Please press the X end switch in the front left corner.")
\r
240 if not self.DoCommCommandAndWaitForReply('M119', 'x_min', "x_min:H x_max:L y_min:L y_max:L z_min:L z_max:L"):
\r
241 wx.CallAfter(self.AddProgressText, "Failed to check the x_min endstop!")
\r
244 wx.CallAfter(self.AddProgressText, "Please press the X end switch in the front right corner.")
\r
245 if not self.DoCommCommandAndWaitForReply('M119', 'x_min', "x_min:L x_max:H y_min:L y_max:L z_min:L z_max:L"):
\r
246 wx.CallAfter(self.AddProgressText, "Failed to check the x_max endstop!")
\r
249 wx.CallAfter(self.AddProgressText, "Please press the Y end switch in the front left corner.")
\r
250 if not self.DoCommCommandAndWaitForReply('M119', 'x_min', "x_min:L x_max:L y_min:H y_max:L z_min:L z_max:L"):
\r
251 wx.CallAfter(self.AddProgressText, "Failed to check the x_max endstop!")
\r
254 wx.CallAfter(self.AddProgressText, "Please press the Y end switch in the back left corner.")
\r
255 if not self.DoCommCommandAndWaitForReply('M119', 'x_min', "x_min:L x_max:L y_min:L y_max:H z_min:L z_max:L"):
\r
256 wx.CallAfter(self.AddProgressText, "Failed to check the x_max endstop!")
\r
259 wx.CallAfter(self.AddProgressText, "Please press the Z end switch in the top.")
\r
260 if not self.DoCommCommandAndWaitForReply('M119', 'x_min', "x_min:L x_max:L y_min:L y_max:L z_min:H z_max:L"):
\r
261 wx.CallAfter(self.AddProgressText, "Failed to check the x_max endstop!")
\r
264 wx.CallAfter(self.AddProgressText, "Please press the Z end switch in the bottom.")
\r
265 if not self.DoCommCommandAndWaitForReply('M119', 'x_min', "x_min:L x_max:L y_min:L y_max:L z_min:L z_max:H"):
\r
266 wx.CallAfter(self.AddProgressText, "Failed to check the x_max endstop!")
\r
269 wx.CallAfter(self.AddProgressText, "End stops are working.")
\r
271 wx.CallAfter(self.AddProgressText, "Done!")
\r
272 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
\r
275 def readTemp(self):
\r
276 line = self.DoCommCommandWithTimeout("M105", "ok T:")
\r
279 return int(re.search('T:([0-9]*)', line).group(1))
\r
281 def DoCommCommandAndWaitForReply(self, cmd, replyStart, reply):
\r
283 ret = self.DoCommCommandWithTimeout(cmd, replyStart)
\r
290 def DoCommCommandWithTimeout(self, cmd = None, replyStart = 'ok'):
\r
292 self.comm.sendCommand(cmd)
\r
293 t = threading.Timer(5, self.OnSerialTimeout)
\r
296 line = self.comm.readline()
\r
297 if line == '' or line == None:
\r
300 print line.rstrip()
\r
301 if line.startswith(replyStart):
\r
304 return line.rstrip()
\r
306 def OnSerialTimeout(self):
\r
309 class UltimakerCalibrationPage(InfoPage):
\r
310 def __init__(self, parent):
\r
311 super(UltimakerCalibrationPage, self).__init__(parent, "Ultimaker Calibration")
\r
313 self.AddText("Your Ultimaker requires some calibration.");
\r
314 self.AddText("This calibration is needed for a proper extrusion amount.");
\r
315 self.AddSeperator()
\r
316 self.AddText("The following values are needed:");
\r
317 self.AddText("* Diameter of filament");
\r
318 self.AddText("* Number of steps per mm of filament extrusion");
\r
319 self.AddSeperator()
\r
320 self.AddText("The better you have calibrated these values, the better your prints\nwill become.");
\r
321 self.AddSeperator()
\r
322 self.AddText("First we need the diameter of your filament:");
\r
323 self.filamentDiameter = wx.TextCtrl(self, -1, profile.getProfileSetting('filament_diameter'))
\r
324 self.GetSizer().Add(self.filamentDiameter, 0, wx.LEFT, 5)
\r
325 self.AddText("If you do not own digital Calipers that can measure\nat least 2 digits then use 2.89mm.\nWhich is the average diameter of most filament.");
\r
326 self.AddText("Note: This value can be changed later at any time.");
\r
328 def StoreData(self):
\r
329 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
\r
331 class UltimakerCalibrateStepsPerEPage(InfoPage):
\r
332 def __init__(self, parent):
\r
333 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, "Ultimaker Calibration")
\r
335 if profile.getPreference('steps_per_e') == '0':
\r
336 profile.putPreference('steps_per_e', '865.888')
\r
338 self.AddText("Calibrating the Steps Per E requires some manual actions.")
\r
339 self.AddText("First remove any filament from your machine.")
\r
340 self.AddText("Next put in your filament so the tip is aligned with the\ntop of the extruder drive.")
\r
341 self.AddText("We'll push the filament 100mm")
\r
342 self.extrudeButton = self.AddButton("Extrude 100mm filament")
\r
343 self.AddText("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)")
\r
345 p.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
\r
346 self.lengthInput = wx.TextCtrl(p, -1, '100')
\r
347 p.GetSizer().Add(self.lengthInput, 0, wx.RIGHT, 8)
\r
348 self.saveLengthButton = wx.Button(p, -1, 'Save')
\r
349 p.GetSizer().Add(self.saveLengthButton, 0)
\r
350 self.GetSizer().Add(p, 0, wx.LEFT, 5)
\r
351 self.AddText("This results in the following steps per E:")
\r
352 self.stepsPerEInput = wx.TextCtrl(self, -1, profile.getPreference('steps_per_e'))
\r
353 self.GetSizer().Add(self.stepsPerEInput, 0, wx.LEFT, 5)
\r
354 self.AddText("You can repeat these steps to get better calibration.")
\r
355 self.AddSeperator()
\r
356 self.AddText("If you still have filament in your printer which needs\nheat to remove, press the heat up button below:")
\r
357 self.heatButton = self.AddButton("Heatup for filament removal")
\r
359 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
\r
360 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
\r
361 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
\r
363 def OnSaveLengthClick(self, e):
\r
364 currentEValue = float(self.stepsPerEInput.GetValue())
\r
365 realExtrudeLength = float(self.lengthInput.GetValue())
\r
366 newEValue = currentEValue * 100 / realExtrudeLength
\r
367 self.stepsPerEInput.SetValue(str(newEValue))
\r
368 self.lengthInput.SetValue("100")
\r
370 def OnExtrudeClick(self, e):
\r
371 threading.Thread(target=self.OnExtrudeRun).start()
\r
373 def OnExtrudeRun(self):
\r
374 self.heatButton.Enable(False)
\r
375 self.extrudeButton.Enable(False)
\r
376 currentEValue = float(self.stepsPerEInput.GetValue())
\r
377 self.comm = machineCom.MachineCom()
\r
378 if not self.comm.isOpen():
\r
379 wx.MessageBox("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable", 'Printer error', wx.OK | wx.ICON_INFORMATION)
\r
380 self.heatButton.Enable(True)
\r
381 self.extrudeButton.Enable(True)
\r
384 line = self.comm.readline()
\r
387 if line.startswith('start'):
\r
389 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
\r
392 self.sendGCommand('M302') #Disable cold extrusion protection
\r
393 self.sendGCommand("M92 E%f" % (currentEValue));
\r
394 self.sendGCommand("G92 E0");
\r
395 self.sendGCommand("G1 E100 F600");
\r
398 self.extrudeButton.Enable()
\r
399 self.heatButton.Enable()
\r
401 def OnHeatClick(self, e):
\r
402 threading.Thread(target=self.OnHeatRun).start()
\r
404 def OnHeatRun(self):
\r
405 self.heatButton.Enable(False)
\r
406 self.extrudeButton.Enable(False)
\r
407 self.comm = machineCom.MachineCom()
\r
408 if not self.comm.isOpen():
\r
409 wx.MessageBox("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable", 'Printer error', wx.OK | wx.ICON_INFORMATION)
\r
410 self.heatButton.Enable(True)
\r
411 self.extrudeButton.Enable(True)
\r
414 line = self.comm.readline()
\r
416 self.heatButton.Enable(True)
\r
417 self.extrudeButton.Enable(True)
\r
419 if line.startswith('start'):
\r
421 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
\r
424 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
\r
425 wx.MessageBox('Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)', 'Machine heatup', wx.OK | wx.ICON_INFORMATION)
\r
426 self.sendGCommand('M104 S0')
\r
429 self.heatButton.Enable(True)
\r
430 self.extrudeButton.Enable(True)
\r
432 def sendGCommand(self, cmd):
\r
433 self.comm.sendCommand(cmd) #Disable cold extrusion protection
\r
435 line = self.comm.readline()
\r
438 if line.startswith('ok'):
\r
441 def StoreData(self):
\r
442 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
\r
444 class configWizard(wx.wizard.Wizard):
\r
445 def __init__(self):
\r
446 super(configWizard, self).__init__(None, -1, "Configuration Wizard")
\r
448 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
\r
449 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
\r
451 self.firstInfoPage = FirstInfoPage(self)
\r
452 self.machineSelectPage = MachineSelectPage(self)
\r
453 self.ultimakerFirmwareUpgradePage = FirmwareUpgradePage(self)
\r
454 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
\r
455 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
\r
456 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
\r
457 self.repRapInfoPage = RepRapInfoPage(self)
\r
459 wx.wizard.WizardPageSimple.Chain(self.firstInfoPage, self.machineSelectPage)
\r
460 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerFirmwareUpgradePage)
\r
461 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
\r
462 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.ultimakerCalibrationPage)
\r
463 wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
\r
465 self.FitToPage(self.firstInfoPage)
\r
466 self.GetPageAreaSizer().Add(self.firstInfoPage)
\r
468 self.RunWizard(self.firstInfoPage)
\r
471 def OnPageChanging(self, e):
\r
472 e.GetPage().StoreData()
\r
474 def OnPageChanged(self, e):
\r
475 if e.GetPage().AllowNext():
\r
476 self.FindWindowById(wx.ID_FORWARD).Enable()
\r
478 self.FindWindowById(wx.ID_FORWARD).Disable()
\r
479 self.FindWindowById(wx.ID_BACKWARD).Disable()
\r