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.putPreference('steps_per_e', '865.888')
\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 machineCom.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 wx.CallAfter(self.AddProgressText, "Checking start message...")
\r
180 if self.DoCommCommandWithTimeout(None, 'start') == False:
\r
181 wx.CallAfter(self.AddProgressText, "Error: Missing start message.")
\r
185 wx.CallAfter(self.AddProgressText, "Disabling step motors...")
\r
186 if self.DoCommCommandWithTimeout('M84') == False:
\r
187 wx.CallAfter(self.AddProgressText, "Error: Missing reply to Deactivate steppers (M84).")
\r
188 wx.CallAfter(self.AddProgressText, "Possible cause: Temperature MIN/MAX.\nCheck temperature sensor connections.")
\r
192 if self.DoCommCommandWithTimeout("M104 S0") == False:
\r
193 wx.CallAfter(self.AddProgressText, "Failed to set temperature")
\r
197 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
199 idleTemp = self.readTemp()
\r
201 wx.CallAfter(self.AddProgressText, "Waiting for head to cool down before temperature test...")
\r
202 while idleTemp > 40:
\r
203 idleTemp = self.readTemp()
\r
206 wx.CallAfter(self.AddProgressText, "Checking heater and temperature sensor...")
\r
207 wx.CallAfter(self.AddProgressText, "(This takes about 30 seconds)")
\r
208 if self.DoCommCommandWithTimeout("M104 S100") == False:
\r
209 wx.CallAfter(self.AddProgressText, "Failed to set temperature")
\r
214 tempInc = self.readTemp() - idleTemp
\r
216 if self.DoCommCommandWithTimeout("M104 S0") == False:
\r
217 wx.CallAfter(self.AddProgressText, "Failed to set temperature")
\r
222 wx.CallAfter(self.AddProgressText, "Your temperature sensor or heater is not working!")
\r
225 wx.CallAfter(self.AddProgressText, "Heater and temperature sensor working\nWarning: head might still be hot!")
\r
227 wx.CallAfter(self.AddProgressText, "Checking endstops")
\r
228 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
229 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
232 wx.CallAfter(self.AddProgressText, "Please press the X end switch in the front left corner.")
\r
233 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
234 wx.CallAfter(self.AddProgressText, "Failed to check the x_min endstop!")
\r
237 wx.CallAfter(self.AddProgressText, "Please press the X end switch in the front right corner.")
\r
238 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
239 wx.CallAfter(self.AddProgressText, "Failed to check the x_max endstop!")
\r
242 wx.CallAfter(self.AddProgressText, "Please press the Y end switch in the front left corner.")
\r
243 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
244 wx.CallAfter(self.AddProgressText, "Failed to check the x_max endstop!")
\r
247 wx.CallAfter(self.AddProgressText, "Please press the Y end switch in the back left corner.")
\r
248 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
249 wx.CallAfter(self.AddProgressText, "Failed to check the x_max endstop!")
\r
252 wx.CallAfter(self.AddProgressText, "Please press the Z end switch in the top.")
\r
253 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
254 wx.CallAfter(self.AddProgressText, "Failed to check the x_max endstop!")
\r
257 wx.CallAfter(self.AddProgressText, "Please press the Z end switch in the bottom.")
\r
258 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
259 wx.CallAfter(self.AddProgressText, "Failed to check the x_max endstop!")
\r
262 wx.CallAfter(self.AddProgressText, "End stops are working.")
\r
264 wx.CallAfter(self.AddProgressText, "Done!")
\r
265 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
\r
268 def readTemp(self):
\r
269 line = self.DoCommCommandWithTimeout("M105", "ok T:")
\r
272 return int(re.search('T:([0-9]*)', line).group(1))
\r
274 def DoCommCommandAndWaitForReply(self, cmd, replyStart, reply):
\r
276 ret = self.DoCommCommandWithTimeout(cmd, replyStart)
\r
283 def DoCommCommandWithTimeout(self, cmd = None, replyStart = 'ok'):
\r
285 self.comm.sendCommand(cmd)
\r
286 t = threading.Timer(5, self.OnSerialTimeout)
\r
289 line = self.comm.readline()
\r
293 if line.startswith(replyStart):
\r
296 return line.rstrip()
\r
298 def OnSerialTimeout(self):
\r
301 class UltimakerCalibrationPage(InfoPage):
\r
302 def __init__(self, parent):
\r
303 super(UltimakerCalibrationPage, self).__init__(parent, "Ultimaker Calibration")
\r
305 self.AddText("Your Ultimaker requires some calibration.");
\r
306 self.AddText("This calibration is needed for a proper extrusion amount.");
\r
307 self.AddSeperator()
\r
308 self.AddText("The following values are needed:");
\r
309 self.AddText("* Diameter of filament");
\r
310 self.AddText("* Number of steps per mm of filament extrusion");
\r
311 self.AddSeperator()
\r
312 self.AddText("The better you have calibrated these values, the better your prints\nwill become.");
\r
313 self.AddSeperator()
\r
314 self.AddText("First we need the diameter of your filament:");
\r
315 self.filamentDiameter = wx.TextCtrl(self, -1, profile.getProfileSetting('filament_diameter'))
\r
316 self.GetSizer().Add(self.filamentDiameter, 0, wx.LEFT, 5)
\r
317 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
318 self.AddText("Note: This value can be changed later at any time.");
\r
320 def StoreData(self):
\r
321 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
\r
323 class UltimakerCalibrateStepsPerEPage(InfoPage):
\r
324 def __init__(self, parent):
\r
325 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, "Ultimaker Calibration")
\r
327 self.AddText("Calibrating the Steps Per E requires some manual actions.")
\r
328 self.AddText("First remove any filament from your machine.")
\r
329 self.AddText("Next put in your filament so the tip is aligned with the\ntop of the extruder drive.")
\r
330 self.AddText("We'll push the filament 100mm")
\r
331 self.extrudeButton = self.AddButton("Extrude 100mm filament")
\r
332 self.AddText("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)")
\r
334 p.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
\r
335 self.lengthInput = wx.TextCtrl(p, -1, '100')
\r
336 p.GetSizer().Add(self.lengthInput, 0, wx.RIGHT, 8)
\r
337 self.saveLengthButton = wx.Button(p, -1, 'Save')
\r
338 p.GetSizer().Add(self.saveLengthButton, 0)
\r
339 self.GetSizer().Add(p, 0, wx.LEFT, 5)
\r
340 self.AddText("This results in the following steps per E:")
\r
341 self.stepsPerEInput = wx.TextCtrl(self, -1, profile.getPreference('steps_per_e'))
\r
342 self.GetSizer().Add(self.stepsPerEInput, 0, wx.LEFT, 5)
\r
343 self.AddText("You can repeat these steps to get better calibration.")
\r
344 self.AddSeperator()
\r
345 self.AddText("If you still have filament in your printer which needs\nheat to remove, press the heat up button below:")
\r
346 self.heatButton = self.AddButton("Heatup for filament removal")
\r
348 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
\r
349 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
\r
350 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
\r
352 def OnSaveLengthClick(self, e):
\r
353 currentEValue = float(self.stepsPerEInput.GetValue())
\r
354 realExtrudeLength = float(self.lengthInput.GetValue())
\r
355 newEValue = currentEValue * 100 / realExtrudeLength
\r
356 self.stepsPerEInput.SetValue(str(newEValue))
\r
357 self.lengthInput.SetValue("100")
\r
359 def OnExtrudeClick(self, e):
\r
360 threading.Thread(target=self.OnExtrudeRun).start()
\r
362 def OnExtrudeRun(self):
\r
363 self.heatButton.Enable(False)
\r
364 self.extrudeButton.Enable(False)
\r
365 currentEValue = float(self.stepsPerEInput.GetValue())
\r
366 self.comm = machineCom.MachineCom()
\r
368 line = self.comm.readline()
\r
371 if line.startswith('start'):
\r
373 self.sendGCommand('M302') #Disable cold extrusion protection
\r
374 self.sendGCommand("M92 E%f" % (currentEValue));
\r
375 self.sendGCommand("G92 E0");
\r
376 self.sendGCommand("G1 E100 F600");
\r
379 self.extrudeButton.Enable()
\r
380 self.heatButton.Enable()
\r
382 def OnHeatClick(self, e):
\r
383 threading.Thread(target=self.OnHeatRun).start()
\r
385 def OnHeatRun(self):
\r
386 self.comm = machineCom.MachineCom()
\r
388 line = self.comm.readline()
\r
391 if line.startswith('start'):
\r
393 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
\r
394 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
395 self.sendGCommand('M104 S0')
\r
399 def sendGCommand(self, cmd):
\r
400 self.comm.sendCommand(cmd) #Disable cold extrusion protection
\r
402 line = self.comm.readline()
\r
405 if line.startswith('ok'):
\r
408 def StoreData(self):
\r
409 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
\r
411 class configWizard(wx.wizard.Wizard):
\r
412 def __init__(self):
\r
413 super(configWizard, self).__init__(None, -1, "Configuration Wizard")
\r
415 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
\r
416 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
\r
418 self.firstInfoPage = FirstInfoPage(self)
\r
419 self.machineSelectPage = MachineSelectPage(self)
\r
420 self.ultimakerFirmwareUpgradePage = FirmwareUpgradePage(self)
\r
421 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
\r
422 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
\r
423 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
\r
424 self.repRapInfoPage = RepRapInfoPage(self)
\r
426 wx.wizard.WizardPageSimple.Chain(self.firstInfoPage, self.machineSelectPage)
\r
427 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerFirmwareUpgradePage)
\r
428 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
\r
429 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.ultimakerCalibrationPage)
\r
430 wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
\r
432 self.FitToPage(self.firstInfoPage)
\r
433 self.GetPageAreaSizer().Add(self.firstInfoPage)
\r
435 self.RunWizard(self.firstInfoPage)
\r
438 def OnPageChanging(self, e):
\r
439 e.GetPage().StoreData()
\r
441 def OnPageChanged(self, e):
\r
442 if e.GetPage().AllowNext():
\r
443 self.FindWindowById(wx.ID_FORWARD).Enable()
\r
445 self.FindWindowById(wx.ID_FORWARD).Disable()
\r
446 self.FindWindowById(wx.ID_BACKWARD).Disable()
\r