1 from __future__ import absolute_import
\r
4 import wx, os, platform, types, webbrowser, threading, time, re
\r
7 from gui import machineCom
\r
8 from util import profile
\r
10 class InfoPage(wx.wizard.WizardPageSimple):
\r
11 def __init__(self, parent, title):
\r
12 wx.wizard.WizardPageSimple.__init__(self, parent)
\r
14 sizer = wx.BoxSizer(wx.VERTICAL)
\r
16 self.SetSizer(sizer)
\r
18 title = wx.StaticText(self, -1, title)
\r
19 title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD))
\r
20 sizer.Add(title, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
\r
21 sizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.ALL, 5)
\r
23 def AddText(self,info):
\r
24 text = wx.StaticText(self, -1, info)
\r
25 self.GetSizer().Add(text, 0, wx.LEFT|wx.RIGHT, 5)
\r
28 def AddSeperator(self):
\r
29 self.GetSizer().Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.ALL, 5)
\r
31 def AddHiddenSeperator(self):
\r
34 def AddRadioButton(self, label, style = 0):
\r
35 radio = wx.RadioButton(self, -1, label, style=style)
\r
36 self.GetSizer().Add(radio, 0, wx.EXPAND|wx.ALL, 5)
\r
39 def AddButton(self, label):
\r
40 button = wx.Button(self, -1, label)
\r
41 self.GetSizer().Add(button, 0, wx.LEFT, 5)
\r
44 def AddDualButton(self, label1, label2):
\r
46 p.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
\r
47 button1 = wx.Button(p, -1, label1)
\r
48 p.GetSizer().Add(button1, 0, wx.RIGHT, 8)
\r
49 button2 = wx.Button(p, -1, label2)
\r
50 p.GetSizer().Add(button2, 0)
\r
51 self.GetSizer().Add(p, 0, wx.LEFT, 5)
\r
52 return button1, button2
\r
54 def AllowNext(self):
\r
57 def StoreData(self):
\r
60 class FirstInfoPage(InfoPage):
\r
61 def __init__(self, parent):
\r
62 super(FirstInfoPage, self).__init__(parent, "First time run wizard")
\r
63 self.AddText('Welcome, and thanks for trying Cura!')
\r
65 self.AddText('This wizard will help you with the following steps:')
\r
66 self.AddText('* Configure Cura for your machine')
\r
67 self.AddText('* Upgrade your firmware')
\r
68 self.AddText('* Calibrate your machine')
\r
69 #self.AddText('* Do your first print')
\r
71 class RepRapInfoPage(InfoPage):
\r
72 def __init__(self, parent):
\r
73 super(RepRapInfoPage, self).__init__(parent, "RepRap information")
\r
74 self.AddText('Sorry, but this wizard will not help you with\nconfiguring and calibrating your RepRap.')
\r
76 self.AddText('You will have to manually install Marlin or Sprinter firmware\nand configure Cura.')
\r
78 class MachineSelectPage(InfoPage):
\r
79 def __init__(self, parent):
\r
80 super(MachineSelectPage, self).__init__(parent, "Select your machine")
\r
81 self.AddText('What kind of machine do you have:')
\r
83 self.UltimakerRadio = self.AddRadioButton("Ultimaker", style=wx.RB_GROUP)
\r
84 self.UltimakerRadio.SetValue(True)
\r
85 self.UltimakerRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerSelect)
\r
86 self.OtherRadio = self.AddRadioButton("Other (Ex: RepRap)")
\r
87 self.OtherRadio.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
\r
89 def OnUltimakerSelect(self, e):
\r
90 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerFirmwareUpgradePage)
\r
92 def OnOtherSelect(self, e):
\r
93 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().repRapInfoPage)
\r
95 def StoreData(self):
\r
96 if self.UltimakerRadio.GetValue():
\r
97 profile.putPreference('machine_width', '205')
\r
98 profile.putPreference('machine_depth', '205')
\r
99 profile.putPreference('machine_height', '200')
\r
100 profile.putProfileSetting('nozzle_size', '0.4')
\r
101 profile.putProfileSetting('machine_center_x', '100')
\r
102 profile.putProfileSetting('machine_center_y', '100')
\r
104 profile.putPreference('machine_width', '80')
\r
105 profile.putPreference('machine_depth', '80')
\r
106 profile.putPreference('machine_height', '60')
\r
107 profile.putProfileSetting('nozzle_size', '0.5')
\r
108 profile.putProfileSetting('machine_center_x', '40')
\r
109 profile.putProfileSetting('machine_center_y', '40')
\r
110 profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
\r
112 class FirmwareUpgradePage(InfoPage):
\r
113 def __init__(self, parent):
\r
114 super(FirmwareUpgradePage, self).__init__(parent, "Upgrade Ultimaker Firmware")
\r
115 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
116 self.AddHiddenSeperator()
\r
117 self.AddText('The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier.')
\r
118 self.AddHiddenSeperator()
\r
119 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
120 upgradeButton, skipUpgradeButton = self.AddDualButton('Upgrade to Marlin firmware', 'Skip upgrade')
\r
121 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
\r
122 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
\r
123 self.AddHiddenSeperator()
\r
124 self.AddText('Do not upgrade to this firmware if:')
\r
125 self.AddText('* You have an older machine based on ATMega1280')
\r
126 self.AddText('* Using an LCD panel')
\r
127 self.AddText('* Have other changes in the firmware')
\r
128 button = self.AddButton('Goto this page for a custom firmware')
\r
129 button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
\r
131 def AllowNext(self):
\r
134 def OnUpgradeClick(self, e):
\r
135 if machineCom.InstallFirmware(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../firmware/default.hex")):
\r
136 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
\r
138 def OnSkipClick(self, e):
\r
139 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
\r
141 def OnUrlClick(self, e):
\r
142 webbrowser.open('http://daid.mine.nu/~daid/marlin_build/')
\r
144 class UltimakerCheckupPage(InfoPage):
\r
145 def __init__(self, parent):
\r
146 super(UltimakerCheckupPage, self).__init__(parent, "Ultimaker Checkup")
\r
147 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
148 b1, b2 = self.AddDualButton('Run checks', 'Skip checks')
\r
149 b1.Bind(wx.EVT_BUTTON, self.OnCheckClick)
\r
150 b2.Bind(wx.EVT_BUTTON, self.OnSkipClick)
\r
151 self.AddSeperator();
\r
152 self.checkPanel = None
\r
154 def AllowNext(self):
\r
157 def OnSkipClick(self, e):
\r
158 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
\r
160 def OnCheckClick(self, e):
\r
161 if self.checkPanel != None:
\r
162 self.checkPanel.Destroy()
\r
163 self.checkPanel = wx.Panel(self)
\r
164 self.checkPanel.SetSizer(wx.BoxSizer(wx.VERTICAL))
\r
165 self.GetSizer().Add(self.checkPanel, 0, wx.LEFT|wx.RIGHT, 5)
\r
166 threading.Thread(target=self.OnRun).start()
\r
168 def AddProgressText(self, info):
\r
169 text = wx.StaticText(self.checkPanel, -1, info)
\r
170 self.checkPanel.GetSizer().Add(text, 0)
\r
171 self.checkPanel.Layout()
\r
175 wx.CallAfter(self.AddProgressText, "Connecting to machine...")
\r
176 self.comm = machineCom.MachineCom()
\r
178 wx.CallAfter(self.AddProgressText, "Checking start message...")
\r
179 if self.DoCommCommandWithTimeout(None, 'start') == False:
\r
180 wx.CallAfter(self.AddProgressText, "Error: Missing start message.")
\r
184 #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
187 wx.CallAfter(self.AddProgressText, "Disabling step motors...")
\r
188 if self.DoCommCommandWithTimeout('M84') == False:
\r
189 wx.CallAfter(self.AddProgressText, "Error: Missing reply to Deactivate steppers (M84).")
\r
193 if self.DoCommCommandWithTimeout("M104 S0") == False:
\r
194 wx.CallAfter(self.AddProgressText, "Failed to set temperature")
\r
198 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
200 idleTemp = self.readTemp()
\r
202 wx.CallAfter(self.AddProgressText, "Waiting for head to cool down before temperature test...")
\r
203 while idleTemp > 40:
\r
204 idleTemp = self.readTemp()
\r
207 wx.CallAfter(self.AddProgressText, "Checking heater and temperature sensor...")
\r
208 wx.CallAfter(self.AddProgressText, "(This takes about 30 seconds)")
\r
209 if self.DoCommCommandWithTimeout("M104 S100") == False:
\r
210 wx.CallAfter(self.AddProgressText, "Failed to set temperature")
\r
215 tempInc = self.readTemp() - idleTemp
\r
217 if self.DoCommCommandWithTimeout("M104 S0") == False:
\r
218 wx.CallAfter(self.AddProgressText, "Failed to set temperature")
\r
223 wx.CallAfter(self.AddProgressText, "Your temperature sensor or heater is not working!")
\r
226 wx.CallAfter(self.AddProgressText, "Heater and temperature sensor working\nWarning: head might still be hot!")
\r
228 wx.CallAfter(self.AddProgressText, "Checking endstops")
\r
229 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
230 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
233 wx.CallAfter(self.AddProgressText, "Please press the X end switch in the front left corner.")
\r
234 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
235 wx.CallAfter(self.AddProgressText, "Failed to check the x_min endstop!")
\r
238 wx.CallAfter(self.AddProgressText, "Please press the X end switch in the front right corner.")
\r
239 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
240 wx.CallAfter(self.AddProgressText, "Failed to check the x_max endstop!")
\r
243 wx.CallAfter(self.AddProgressText, "Please press the Y end switch in the front left corner.")
\r
244 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
245 wx.CallAfter(self.AddProgressText, "Failed to check the x_max endstop!")
\r
248 wx.CallAfter(self.AddProgressText, "Please press the Y end switch in the back left corner.")
\r
249 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
250 wx.CallAfter(self.AddProgressText, "Failed to check the x_max endstop!")
\r
253 wx.CallAfter(self.AddProgressText, "Please press the Z end switch in the top.")
\r
254 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
255 wx.CallAfter(self.AddProgressText, "Failed to check the x_max endstop!")
\r
258 wx.CallAfter(self.AddProgressText, "Please press the Z end switch in the bottom.")
\r
259 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
260 wx.CallAfter(self.AddProgressText, "Failed to check the x_max endstop!")
\r
263 wx.CallAfter(self.AddProgressText, "End stops are working.")
\r
265 wx.CallAfter(self.AddProgressText, "Done!")
\r
266 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
\r
269 def readTemp(self):
\r
270 line = self.DoCommCommandWithTimeout("M105", "ok T:")
\r
273 return int(re.search('T:([0-9]*)', line).group(1))
\r
275 def DoCommCommandAndWaitForReply(self, cmd, replyStart, reply):
\r
277 ret = self.DoCommCommandWithTimeout(cmd, replyStart)
\r
284 def DoCommCommandWithTimeout(self, cmd = None, replyStart = 'ok'):
\r
286 self.comm.sendCommand(cmd)
\r
287 t = threading.Timer(5, self.OnSerialTimeout)
\r
290 line = self.comm.readline()
\r
295 if line.startswith(replyStart):
\r
298 return line.rstrip()
\r
300 def OnSerialTimeout(self):
\r
303 class UltimakerCalibrationPage(InfoPage):
\r
304 def __init__(self, parent):
\r
305 super(UltimakerCalibrationPage, self).__init__(parent, "Ultimaker Calibration")
\r
307 self.AddText("Your Ultimaker requires some calibration.");
\r
308 self.AddText("This calibration is needed for a proper extrusion amount.");
\r
309 self.AddSeperator()
\r
310 self.AddText("The following values are needed:");
\r
311 self.AddText("* Diameter of filament");
\r
312 self.AddText("* Number of steps per mm of filament extrusion");
\r
313 self.AddSeperator()
\r
314 self.AddText("The better you have calibrated these values, the better your prints\nwill become.");
\r
315 self.AddSeperator()
\r
316 self.AddText("First we need the diameter of your filament:");
\r
317 self.filamentDiameter = wx.TextCtrl(self, -1, profile.getProfileSetting('filament_diameter'))
\r
318 self.GetSizer().Add(self.filamentDiameter, 0, wx.LEFT, 5)
\r
319 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
320 self.AddText("Note: This value can be changed later at any time.");
\r
322 def StoreData(self):
\r
323 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
\r
325 class UltimakerCalibrateStepsPerEPage(InfoPage):
\r
326 def __init__(self, parent):
\r
327 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, "Ultimaker Calibration")
\r
329 if profile.getPreference('steps_per_e') == '0':
\r
330 profile.putPreference('steps_per_e', '865.888')
\r
332 self.AddText("Calibrating the Steps Per E requires some manual actions.")
\r
333 self.AddText("First remove any filament from your machine.")
\r
334 self.AddText("Next put in your filament so the tip is aligned with the\ntop of the extruder drive.")
\r
335 self.AddText("We'll push the filament 100mm")
\r
336 self.extrudeButton = self.AddButton("Extrude 100mm filament")
\r
337 self.AddText("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)")
\r
339 p.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
\r
340 self.lengthInput = wx.TextCtrl(p, -1, '100')
\r
341 p.GetSizer().Add(self.lengthInput, 0, wx.RIGHT, 8)
\r
342 self.saveLengthButton = wx.Button(p, -1, 'Save')
\r
343 p.GetSizer().Add(self.saveLengthButton, 0)
\r
344 self.GetSizer().Add(p, 0, wx.LEFT, 5)
\r
345 self.AddText("This results in the following steps per E:")
\r
346 self.stepsPerEInput = wx.TextCtrl(self, -1, profile.getPreference('steps_per_e'))
\r
347 self.GetSizer().Add(self.stepsPerEInput, 0, wx.LEFT, 5)
\r
348 self.AddText("You can repeat these steps to get better calibration.")
\r
349 self.AddSeperator()
\r
350 self.AddText("If you still have filament in your printer which needs\nheat to remove, press the heat up button below:")
\r
351 self.heatButton = self.AddButton("Heatup for filament removal")
\r
353 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
\r
354 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
\r
355 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
\r
357 def OnSaveLengthClick(self, e):
\r
358 currentEValue = float(self.stepsPerEInput.GetValue())
\r
359 realExtrudeLength = float(self.lengthInput.GetValue())
\r
360 newEValue = currentEValue * 100 / realExtrudeLength
\r
361 self.stepsPerEInput.SetValue(str(newEValue))
\r
362 self.lengthInput.SetValue("100")
\r
364 def OnExtrudeClick(self, e):
\r
365 threading.Thread(target=self.OnExtrudeRun).start()
\r
367 def OnExtrudeRun(self):
\r
368 self.heatButton.Enable(False)
\r
369 self.extrudeButton.Enable(False)
\r
370 currentEValue = float(self.stepsPerEInput.GetValue())
\r
371 self.comm = machineCom.MachineCom()
\r
373 line = self.comm.readline()
\r
376 if line.startswith('start'):
\r
378 #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
381 self.sendGCommand('M302') #Disable cold extrusion protection
\r
382 self.sendGCommand("M92 E%f" % (currentEValue));
\r
383 self.sendGCommand("G92 E0");
\r
384 self.sendGCommand("G1 E100 F600");
\r
387 self.extrudeButton.Enable()
\r
388 self.heatButton.Enable()
\r
390 def OnHeatClick(self, e):
\r
391 threading.Thread(target=self.OnHeatRun).start()
\r
393 def OnHeatRun(self):
\r
394 self.comm = machineCom.MachineCom()
\r
396 line = self.comm.readline()
\r
399 if line.startswith('start'):
\r
401 #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
404 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
\r
405 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
406 self.sendGCommand('M104 S0')
\r
410 def sendGCommand(self, cmd):
\r
411 self.comm.sendCommand(cmd) #Disable cold extrusion protection
\r
413 line = self.comm.readline()
\r
416 if line.startswith('ok'):
\r
419 def StoreData(self):
\r
420 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
\r
422 class configWizard(wx.wizard.Wizard):
\r
423 def __init__(self):
\r
424 super(configWizard, self).__init__(None, -1, "Configuration Wizard")
\r
426 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
\r
427 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
\r
429 self.firstInfoPage = FirstInfoPage(self)
\r
430 self.machineSelectPage = MachineSelectPage(self)
\r
431 self.ultimakerFirmwareUpgradePage = FirmwareUpgradePage(self)
\r
432 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
\r
433 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
\r
434 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
\r
435 self.repRapInfoPage = RepRapInfoPage(self)
\r
437 wx.wizard.WizardPageSimple.Chain(self.firstInfoPage, self.machineSelectPage)
\r
438 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerFirmwareUpgradePage)
\r
439 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
\r
440 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.ultimakerCalibrationPage)
\r
441 wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
\r
443 self.FitToPage(self.firstInfoPage)
\r
444 self.GetPageAreaSizer().Add(self.firstInfoPage)
\r
446 self.RunWizard(self.firstInfoPage)
\r
449 def OnPageChanging(self, e):
\r
450 e.GetPage().StoreData()
\r
452 def OnPageChanged(self, e):
\r
453 if e.GetPage().AllowNext():
\r
454 self.FindWindowById(wx.ID_FORWARD).Enable()
\r
456 self.FindWindowById(wx.ID_FORWARD).Disable()
\r
457 self.FindWindowById(wx.ID_BACKWARD).Disable()
\r