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 if not self.comm.isOpen():
\r
179 wx.CallAfter(self.AddProgressText, "Error: Failed to open serial port to machine")
\r
180 wx.CallAfter(self.AddProgressText, "If this keeps happening, try disconnecting and reconnecting the USB cable")
\r
183 wx.CallAfter(self.AddProgressText, "Checking start message...")
\r
184 if self.DoCommCommandWithTimeout(None, 'start') == False:
\r
185 wx.CallAfter(self.AddProgressText, "Error: Missing start message.")
\r
189 #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
192 wx.CallAfter(self.AddProgressText, "Disabling step motors...")
\r
193 if self.DoCommCommandWithTimeout('M84') == False:
\r
194 wx.CallAfter(self.AddProgressText, "Error: Missing reply to Deactivate steppers (M84).")
\r
198 if self.DoCommCommandWithTimeout("M104 S0") == False:
\r
199 wx.CallAfter(self.AddProgressText, "Failed to set temperature")
\r
203 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
205 idleTemp = self.readTemp()
\r
207 wx.CallAfter(self.AddProgressText, "Waiting for head to cool down before temperature test...")
\r
208 while idleTemp > 40:
\r
209 idleTemp = self.readTemp()
\r
212 wx.CallAfter(self.AddProgressText, "Checking heater and temperature sensor...")
\r
213 wx.CallAfter(self.AddProgressText, "(This takes about 30 seconds)")
\r
214 if self.DoCommCommandWithTimeout("M104 S100") == False:
\r
215 wx.CallAfter(self.AddProgressText, "Failed to set temperature")
\r
220 tempInc = self.readTemp() - idleTemp
\r
222 if self.DoCommCommandWithTimeout("M104 S0") == False:
\r
223 wx.CallAfter(self.AddProgressText, "Failed to set temperature")
\r
228 wx.CallAfter(self.AddProgressText, "Your temperature sensor or heater is not working!")
\r
231 wx.CallAfter(self.AddProgressText, "Heater and temperature sensor working\nWarning: head might still be hot!")
\r
233 wx.CallAfter(self.AddProgressText, "Checking endstops")
\r
234 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
235 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
238 wx.CallAfter(self.AddProgressText, "Please press the X end switch in the front left corner.")
\r
239 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
240 wx.CallAfter(self.AddProgressText, "Failed to check the x_min endstop!")
\r
243 wx.CallAfter(self.AddProgressText, "Please press the X end switch in the front right corner.")
\r
244 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
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 front left corner.")
\r
249 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
250 wx.CallAfter(self.AddProgressText, "Failed to check the x_max endstop!")
\r
253 wx.CallAfter(self.AddProgressText, "Please press the Y end switch in the back left corner.")
\r
254 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
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 top.")
\r
259 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
260 wx.CallAfter(self.AddProgressText, "Failed to check the x_max endstop!")
\r
263 wx.CallAfter(self.AddProgressText, "Please press the Z end switch in the bottom.")
\r
264 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
265 wx.CallAfter(self.AddProgressText, "Failed to check the x_max endstop!")
\r
268 wx.CallAfter(self.AddProgressText, "End stops are working.")
\r
270 wx.CallAfter(self.AddProgressText, "Done!")
\r
271 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
\r
274 def readTemp(self):
\r
275 line = self.DoCommCommandWithTimeout("M105", "ok T:")
\r
278 return int(re.search('T:([0-9]*)', line).group(1))
\r
280 def DoCommCommandAndWaitForReply(self, cmd, replyStart, reply):
\r
282 ret = self.DoCommCommandWithTimeout(cmd, replyStart)
\r
289 def DoCommCommandWithTimeout(self, cmd = None, replyStart = 'ok'):
\r
291 self.comm.sendCommand(cmd)
\r
292 t = threading.Timer(5, self.OnSerialTimeout)
\r
295 line = self.comm.readline()
\r
296 if line == '' or line == None:
\r
299 print line.rstrip()
\r
300 if line.startswith(replyStart):
\r
303 return line.rstrip()
\r
305 def OnSerialTimeout(self):
\r
308 class UltimakerCalibrationPage(InfoPage):
\r
309 def __init__(self, parent):
\r
310 super(UltimakerCalibrationPage, self).__init__(parent, "Ultimaker Calibration")
\r
312 self.AddText("Your Ultimaker requires some calibration.");
\r
313 self.AddText("This calibration is needed for a proper extrusion amount.");
\r
314 self.AddSeperator()
\r
315 self.AddText("The following values are needed:");
\r
316 self.AddText("* Diameter of filament");
\r
317 self.AddText("* Number of steps per mm of filament extrusion");
\r
318 self.AddSeperator()
\r
319 self.AddText("The better you have calibrated these values, the better your prints\nwill become.");
\r
320 self.AddSeperator()
\r
321 self.AddText("First we need the diameter of your filament:");
\r
322 self.filamentDiameter = wx.TextCtrl(self, -1, profile.getProfileSetting('filament_diameter'))
\r
323 self.GetSizer().Add(self.filamentDiameter, 0, wx.LEFT, 5)
\r
324 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
325 self.AddText("Note: This value can be changed later at any time.");
\r
327 def StoreData(self):
\r
328 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
\r
330 class UltimakerCalibrateStepsPerEPage(InfoPage):
\r
331 def __init__(self, parent):
\r
332 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, "Ultimaker Calibration")
\r
334 if profile.getPreference('steps_per_e') == '0':
\r
335 profile.putPreference('steps_per_e', '865.888')
\r
337 self.AddText("Calibrating the Steps Per E requires some manual actions.")
\r
338 self.AddText("First remove any filament from your machine.")
\r
339 self.AddText("Next put in your filament so the tip is aligned with the\ntop of the extruder drive.")
\r
340 self.AddText("We'll push the filament 100mm")
\r
341 self.extrudeButton = self.AddButton("Extrude 100mm filament")
\r
342 self.AddText("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)")
\r
344 p.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
\r
345 self.lengthInput = wx.TextCtrl(p, -1, '100')
\r
346 p.GetSizer().Add(self.lengthInput, 0, wx.RIGHT, 8)
\r
347 self.saveLengthButton = wx.Button(p, -1, 'Save')
\r
348 p.GetSizer().Add(self.saveLengthButton, 0)
\r
349 self.GetSizer().Add(p, 0, wx.LEFT, 5)
\r
350 self.AddText("This results in the following steps per E:")
\r
351 self.stepsPerEInput = wx.TextCtrl(self, -1, profile.getPreference('steps_per_e'))
\r
352 self.GetSizer().Add(self.stepsPerEInput, 0, wx.LEFT, 5)
\r
353 self.AddText("You can repeat these steps to get better calibration.")
\r
354 self.AddSeperator()
\r
355 self.AddText("If you still have filament in your printer which needs\nheat to remove, press the heat up button below:")
\r
356 self.heatButton = self.AddButton("Heatup for filament removal")
\r
358 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
\r
359 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
\r
360 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
\r
362 def OnSaveLengthClick(self, e):
\r
363 currentEValue = float(self.stepsPerEInput.GetValue())
\r
364 realExtrudeLength = float(self.lengthInput.GetValue())
\r
365 newEValue = currentEValue * 100 / realExtrudeLength
\r
366 self.stepsPerEInput.SetValue(str(newEValue))
\r
367 self.lengthInput.SetValue("100")
\r
369 def OnExtrudeClick(self, e):
\r
370 threading.Thread(target=self.OnExtrudeRun).start()
\r
372 def OnExtrudeRun(self):
\r
373 self.heatButton.Enable(False)
\r
374 self.extrudeButton.Enable(False)
\r
375 currentEValue = float(self.stepsPerEInput.GetValue())
\r
376 self.comm = machineCom.MachineCom()
\r
377 if not self.comm.isOpen():
\r
378 wx.CallAfter(self.AddProgressText, "Error: Failed to open serial port to machine")
\r
379 wx.CallAfter(self.AddProgressText, "If this keeps happening, try disconnecting and reconnecting the USB cable")
\r
382 line = self.comm.readline()
\r
385 if line.startswith('start'):
\r
387 #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
390 self.sendGCommand('M302') #Disable cold extrusion protection
\r
391 self.sendGCommand("M92 E%f" % (currentEValue));
\r
392 self.sendGCommand("G92 E0");
\r
393 self.sendGCommand("G1 E100 F600");
\r
396 self.extrudeButton.Enable()
\r
397 self.heatButton.Enable()
\r
399 def OnHeatClick(self, e):
\r
400 threading.Thread(target=self.OnHeatRun).start()
\r
402 def OnHeatRun(self):
\r
403 self.comm = machineCom.MachineCom()
\r
404 if not self.comm.isOpen():
\r
405 wx.CallAfter(self.AddProgressText, "Error: Failed to open serial port to machine")
\r
406 wx.CallAfter(self.AddProgressText, "If this keeps happening, try disconnecting and reconnecting the USB cable")
\r
409 line = self.comm.readline()
\r
412 if line.startswith('start'):
\r
414 #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
417 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
\r
418 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
419 self.sendGCommand('M104 S0')
\r
423 def sendGCommand(self, cmd):
\r
424 self.comm.sendCommand(cmd) #Disable cold extrusion protection
\r
426 line = self.comm.readline()
\r
429 if line.startswith('ok'):
\r
432 def StoreData(self):
\r
433 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
\r
435 class configWizard(wx.wizard.Wizard):
\r
436 def __init__(self):
\r
437 super(configWizard, self).__init__(None, -1, "Configuration Wizard")
\r
439 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
\r
440 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
\r
442 self.firstInfoPage = FirstInfoPage(self)
\r
443 self.machineSelectPage = MachineSelectPage(self)
\r
444 self.ultimakerFirmwareUpgradePage = FirmwareUpgradePage(self)
\r
445 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
\r
446 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
\r
447 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
\r
448 self.repRapInfoPage = RepRapInfoPage(self)
\r
450 wx.wizard.WizardPageSimple.Chain(self.firstInfoPage, self.machineSelectPage)
\r
451 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerFirmwareUpgradePage)
\r
452 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
\r
453 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.ultimakerCalibrationPage)
\r
454 wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
\r
456 self.FitToPage(self.firstInfoPage)
\r
457 self.GetPageAreaSizer().Add(self.firstInfoPage)
\r
459 self.RunWizard(self.firstInfoPage)
\r
462 def OnPageChanging(self, e):
\r
463 e.GetPage().StoreData()
\r
465 def OnPageChanged(self, e):
\r
466 if e.GetPage().AllowNext():
\r
467 self.FindWindowById(wx.ID_FORWARD).Enable()
\r
469 self.FindWindowById(wx.ID_FORWARD).Disable()
\r
470 self.FindWindowById(wx.ID_BACKWARD).Disable()
\r