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.putPreference('machine_type', 'ultimaker')
\r
102 profile.putProfileSetting('nozzle_size', '0.4')
\r
103 profile.putProfileSetting('machine_center_x', '100')
\r
104 profile.putProfileSetting('machine_center_y', '100')
\r
106 profile.putPreference('machine_width', '80')
\r
107 profile.putPreference('machine_depth', '80')
\r
108 profile.putPreference('machine_height', '60')
\r
109 profile.putPreference('machine_type', 'reprap')
\r
110 profile.putProfileSetting('nozzle_size', '0.5')
\r
111 profile.putProfileSetting('machine_center_x', '40')
\r
112 profile.putProfileSetting('machine_center_y', '40')
\r
113 profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
\r
115 class FirmwareUpgradePage(InfoPage):
\r
116 def __init__(self, parent):
\r
117 super(FirmwareUpgradePage, self).__init__(parent, "Upgrade Ultimaker Firmware")
\r
118 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
119 self.AddHiddenSeperator()
\r
120 self.AddText('The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier.')
\r
121 self.AddHiddenSeperator()
\r
122 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
123 upgradeButton, skipUpgradeButton = self.AddDualButton('Upgrade to Marlin firmware', 'Skip upgrade')
\r
124 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
\r
125 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
\r
126 self.AddHiddenSeperator()
\r
127 self.AddText('Do not upgrade to this firmware if:')
\r
128 self.AddText('* You have an older machine based on ATMega1280')
\r
129 self.AddText('* Have other changes in the firmware')
\r
130 button = self.AddButton('Goto this page for a custom firmware')
\r
131 button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
\r
133 def AllowNext(self):
\r
136 def OnUpgradeClick(self, e):
\r
137 if firmwareInstall.InstallFirmware():
\r
138 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
\r
140 def OnSkipClick(self, e):
\r
141 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
\r
143 def OnUrlClick(self, e):
\r
144 webbrowser.open('http://daid.mine.nu/~daid/marlin_build/')
\r
146 class UltimakerCheckupPage(InfoPage):
\r
147 def __init__(self, parent):
\r
148 super(UltimakerCheckupPage, self).__init__(parent, "Ultimaker Checkup")
\r
149 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
150 b1, b2 = self.AddDualButton('Run checks', 'Skip checks')
\r
151 b1.Bind(wx.EVT_BUTTON, self.OnCheckClick)
\r
152 b2.Bind(wx.EVT_BUTTON, self.OnSkipClick)
\r
153 self.AddSeperator();
\r
154 self.checkPanel = None
\r
156 def AllowNext(self):
\r
159 def OnSkipClick(self, e):
\r
160 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
\r
162 def OnCheckClick(self, e):
\r
163 if self.checkPanel != None:
\r
164 self.checkPanel.Destroy()
\r
165 self.checkPanel = wx.Panel(self)
\r
166 self.checkPanel.SetSizer(wx.BoxSizer(wx.VERTICAL))
\r
167 self.GetSizer().Add(self.checkPanel, 0, wx.LEFT|wx.RIGHT, 5)
\r
168 threading.Thread(target=self.OnRun).start()
\r
170 def AddProgressText(self, info):
\r
171 text = wx.StaticText(self.checkPanel, -1, info)
\r
172 self.checkPanel.GetSizer().Add(text, 0)
\r
173 self.checkPanel.Layout()
\r
177 wx.CallAfter(self.AddProgressText, "Connecting to machine...")
\r
178 self.comm = machineCom.MachineCom()
\r
180 if not self.comm.isOpen():
\r
181 wx.CallAfter(self.AddProgressText, "Error: Failed to open serial port to machine")
\r
182 wx.CallAfter(self.AddProgressText, "If this keeps happening, try disconnecting and reconnecting the USB cable")
\r
185 wx.CallAfter(self.AddProgressText, "Checking start message...")
\r
186 if self.DoCommCommandWithTimeout(None, 'start') == False:
\r
187 wx.CallAfter(self.AddProgressText, "Error: Missing start message.")
\r
191 #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
194 wx.CallAfter(self.AddProgressText, "Disabling step motors...")
\r
195 if self.DoCommCommandWithTimeout('M84') == False:
\r
196 wx.CallAfter(self.AddProgressText, "Error: Missing reply to Deactivate steppers (M84).")
\r
200 if self.DoCommCommandWithTimeout("M104 S0") == False:
\r
201 wx.CallAfter(self.AddProgressText, "Failed to set temperature")
\r
205 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
207 idleTemp = self.readTemp()
\r
209 wx.CallAfter(self.AddProgressText, "Waiting for head to cool down before temperature test...")
\r
210 while idleTemp > 40:
\r
211 idleTemp = self.readTemp()
\r
214 wx.CallAfter(self.AddProgressText, "Checking heater and temperature sensor...")
\r
215 wx.CallAfter(self.AddProgressText, "(This takes about 30 seconds)")
\r
216 if self.DoCommCommandWithTimeout("M104 S100") == False:
\r
217 wx.CallAfter(self.AddProgressText, "Failed to set temperature")
\r
222 tempInc = self.readTemp() - idleTemp
\r
224 if self.DoCommCommandWithTimeout("M104 S0") == False:
\r
225 wx.CallAfter(self.AddProgressText, "Failed to set temperature")
\r
230 wx.CallAfter(self.AddProgressText, "Your temperature sensor or heater is not working!")
\r
233 wx.CallAfter(self.AddProgressText, "Heater and temperature sensor working\nWarning: head might still be hot!")
\r
235 wx.CallAfter(self.AddProgressText, "Checking endstops")
\r
236 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
237 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
240 wx.CallAfter(self.AddProgressText, "Please press the X end switch in the front left corner.")
\r
241 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
242 wx.CallAfter(self.AddProgressText, "Failed to check the x_min endstop!")
\r
245 wx.CallAfter(self.AddProgressText, "Please press the X end switch in the front right corner.")
\r
246 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
247 wx.CallAfter(self.AddProgressText, "Failed to check the x_max endstop!")
\r
250 wx.CallAfter(self.AddProgressText, "Please press the Y end switch in the front left corner.")
\r
251 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
252 wx.CallAfter(self.AddProgressText, "Failed to check the x_max endstop!")
\r
255 wx.CallAfter(self.AddProgressText, "Please press the Y end switch in the back left corner.")
\r
256 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
257 wx.CallAfter(self.AddProgressText, "Failed to check the x_max endstop!")
\r
260 wx.CallAfter(self.AddProgressText, "Please press the Z end switch in the top.")
\r
261 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
262 wx.CallAfter(self.AddProgressText, "Failed to check the x_max endstop!")
\r
265 wx.CallAfter(self.AddProgressText, "Please press the Z end switch in the bottom.")
\r
266 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
267 wx.CallAfter(self.AddProgressText, "Failed to check the x_max endstop!")
\r
270 wx.CallAfter(self.AddProgressText, "End stops are working.")
\r
272 wx.CallAfter(self.AddProgressText, "Done!")
\r
273 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
\r
276 def readTemp(self):
\r
277 line = self.DoCommCommandWithTimeout("M105", "ok T:")
\r
280 return int(re.search('T:([0-9]*)', line).group(1))
\r
282 def DoCommCommandAndWaitForReply(self, cmd, replyStart, reply):
\r
284 ret = self.DoCommCommandWithTimeout(cmd, replyStart)
\r
291 def DoCommCommandWithTimeout(self, cmd = None, replyStart = 'ok'):
\r
293 self.comm.sendCommand(cmd)
\r
294 t = threading.Timer(5, self.OnSerialTimeout)
\r
297 line = self.comm.readline()
\r
298 if line == '' or line == None:
\r
301 print line.rstrip()
\r
302 if line.startswith(replyStart):
\r
305 return line.rstrip()
\r
307 def OnSerialTimeout(self):
\r
310 class UltimakerCalibrationPage(InfoPage):
\r
311 def __init__(self, parent):
\r
312 super(UltimakerCalibrationPage, self).__init__(parent, "Ultimaker Calibration")
\r
314 self.AddText("Your Ultimaker requires some calibration.");
\r
315 self.AddText("This calibration is needed for a proper extrusion amount.");
\r
316 self.AddSeperator()
\r
317 self.AddText("The following values are needed:");
\r
318 self.AddText("* Diameter of filament");
\r
319 self.AddText("* Number of steps per mm of filament extrusion");
\r
320 self.AddSeperator()
\r
321 self.AddText("The better you have calibrated these values, the better your prints\nwill become.");
\r
322 self.AddSeperator()
\r
323 self.AddText("First we need the diameter of your filament:");
\r
324 self.filamentDiameter = wx.TextCtrl(self, -1, profile.getProfileSetting('filament_diameter'))
\r
325 self.GetSizer().Add(self.filamentDiameter, 0, wx.LEFT, 5)
\r
326 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
327 self.AddText("Note: This value can be changed later at any time.");
\r
329 def StoreData(self):
\r
330 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
\r
332 class UltimakerCalibrateStepsPerEPage(InfoPage):
\r
333 def __init__(self, parent):
\r
334 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, "Ultimaker Calibration")
\r
336 if profile.getPreference('steps_per_e') == '0':
\r
337 profile.putPreference('steps_per_e', '865.888')
\r
339 self.AddText("Calibrating the Steps Per E requires some manual actions.")
\r
340 self.AddText("First remove any filament from your machine.")
\r
341 self.AddText("Next put in your filament so the tip is aligned with the\ntop of the extruder drive.")
\r
342 self.AddText("We'll push the filament 100mm")
\r
343 self.extrudeButton = self.AddButton("Extrude 100mm filament")
\r
344 self.AddText("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)")
\r
346 p.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
\r
347 self.lengthInput = wx.TextCtrl(p, -1, '100')
\r
348 p.GetSizer().Add(self.lengthInput, 0, wx.RIGHT, 8)
\r
349 self.saveLengthButton = wx.Button(p, -1, 'Save')
\r
350 p.GetSizer().Add(self.saveLengthButton, 0)
\r
351 self.GetSizer().Add(p, 0, wx.LEFT, 5)
\r
352 self.AddText("This results in the following steps per E:")
\r
353 self.stepsPerEInput = wx.TextCtrl(self, -1, profile.getPreference('steps_per_e'))
\r
354 self.GetSizer().Add(self.stepsPerEInput, 0, wx.LEFT, 5)
\r
355 self.AddText("You can repeat these steps to get better calibration.")
\r
356 self.AddSeperator()
\r
357 self.AddText("If you still have filament in your printer which needs\nheat to remove, press the heat up button below:")
\r
358 self.heatButton = self.AddButton("Heatup for filament removal")
\r
360 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
\r
361 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
\r
362 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
\r
364 def OnSaveLengthClick(self, e):
\r
365 currentEValue = float(self.stepsPerEInput.GetValue())
\r
366 realExtrudeLength = float(self.lengthInput.GetValue())
\r
367 newEValue = currentEValue * 100 / realExtrudeLength
\r
368 self.stepsPerEInput.SetValue(str(newEValue))
\r
369 self.lengthInput.SetValue("100")
\r
371 def OnExtrudeClick(self, e):
\r
372 threading.Thread(target=self.OnExtrudeRun).start()
\r
374 def OnExtrudeRun(self):
\r
375 self.heatButton.Enable(False)
\r
376 self.extrudeButton.Enable(False)
\r
377 currentEValue = float(self.stepsPerEInput.GetValue())
\r
378 self.comm = machineCom.MachineCom()
\r
379 if not self.comm.isOpen():
\r
380 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
381 self.heatButton.Enable(True)
\r
382 self.extrudeButton.Enable(True)
\r
385 line = self.comm.readline()
\r
388 if line.startswith('start'):
\r
390 #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
393 self.sendGCommand('M302') #Disable cold extrusion protection
\r
394 self.sendGCommand("M92 E%f" % (currentEValue));
\r
395 self.sendGCommand("G92 E0");
\r
396 self.sendGCommand("G1 E100 F600");
\r
399 self.extrudeButton.Enable()
\r
400 self.heatButton.Enable()
\r
402 def OnHeatClick(self, e):
\r
403 threading.Thread(target=self.OnHeatRun).start()
\r
405 def OnHeatRun(self):
\r
406 self.heatButton.Enable(False)
\r
407 self.extrudeButton.Enable(False)
\r
408 self.comm = machineCom.MachineCom()
\r
409 if not self.comm.isOpen():
\r
410 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
411 self.heatButton.Enable(True)
\r
412 self.extrudeButton.Enable(True)
\r
415 line = self.comm.readline()
\r
417 self.heatButton.Enable(True)
\r
418 self.extrudeButton.Enable(True)
\r
420 if line.startswith('start'):
\r
422 #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
425 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
\r
426 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
427 self.sendGCommand('M104 S0')
\r
430 self.heatButton.Enable(True)
\r
431 self.extrudeButton.Enable(True)
\r
433 def sendGCommand(self, cmd):
\r
434 self.comm.sendCommand(cmd) #Disable cold extrusion protection
\r
436 line = self.comm.readline()
\r
439 if line.startswith('ok'):
\r
442 def StoreData(self):
\r
443 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
\r
445 class configWizard(wx.wizard.Wizard):
\r
446 def __init__(self):
\r
447 super(configWizard, self).__init__(None, -1, "Configuration Wizard")
\r
449 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
\r
450 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
\r
452 self.firstInfoPage = FirstInfoPage(self)
\r
453 self.machineSelectPage = MachineSelectPage(self)
\r
454 self.ultimakerFirmwareUpgradePage = FirmwareUpgradePage(self)
\r
455 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
\r
456 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
\r
457 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
\r
458 self.repRapInfoPage = RepRapInfoPage(self)
\r
460 wx.wizard.WizardPageSimple.Chain(self.firstInfoPage, self.machineSelectPage)
\r
461 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerFirmwareUpgradePage)
\r
462 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
\r
463 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.ultimakerCalibrationPage)
\r
464 wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
\r
466 self.FitToPage(self.firstInfoPage)
\r
467 self.GetPageAreaSizer().Add(self.firstInfoPage)
\r
469 self.RunWizard(self.firstInfoPage)
\r
472 def OnPageChanging(self, e):
\r
473 e.GetPage().StoreData()
\r
475 def OnPageChanged(self, e):
\r
476 if e.GetPage().AllowNext():
\r
477 self.FindWindowById(wx.ID_FORWARD).Enable()
\r
479 self.FindWindowById(wx.ID_FORWARD).Disable()
\r
480 self.FindWindowById(wx.ID_BACKWARD).Disable()
\r