2 from __future__ import absolute_import
12 from Cura.gui import firmwareInstall
13 from Cura.gui import printWindow
14 from Cura.util import machineCom
15 from Cura.util import profile
16 from Cura.util.resources import getPathForImage
18 class InfoBox(wx.Panel):
19 def __init__(self, parent):
20 super(InfoBox, self).__init__(parent)
21 self.SetBackgroundColour('#FFFF80')
23 self.sizer = wx.GridBagSizer(5, 5)
24 self.SetSizer(self.sizer)
26 self.attentionBitmap = wx.Bitmap(getPathForImage('attention.png'))
27 self.errorBitmap = wx.Bitmap(getPathForImage('error.png'))
28 self.readyBitmap = wx.Bitmap(getPathForImage('ready.png'))
30 wx.Bitmap(getPathForImage('busy-0.png')),
31 wx.Bitmap(getPathForImage('busy-1.png')),
32 wx.Bitmap(getPathForImage('busy-2.png')),
33 wx.Bitmap(getPathForImage('busy-3.png'))
36 self.bitmap = wx.StaticBitmap(self, -1, wx.EmptyBitmapRGBA(24, 24, red=255, green=255, blue=255, alpha=1))
37 self.text = wx.StaticText(self, -1, '')
38 self.extraInfoButton = wx.Button(self, -1, 'i', style=wx.BU_EXACTFIT)
39 self.sizer.Add(self.bitmap, pos=(0, 0), flag=wx.ALL, border=5)
40 self.sizer.Add(self.text, pos=(0, 1), flag=wx.TOP | wx.BOTTOM | wx.ALIGN_CENTER_VERTICAL, border=5)
41 self.sizer.Add(self.extraInfoButton, pos=(0,2), flag=wx.ALL|wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, border=5)
42 self.sizer.AddGrowableCol(1)
44 self.extraInfoButton.Show(False)
46 self.extraInfoUrl = ''
48 self.timer = wx.Timer(self)
49 self.Bind(wx.EVT_TIMER, self.doBusyUpdate, self.timer)
50 self.Bind(wx.EVT_BUTTON, self.doExtraInfo, self.extraInfoButton)
53 def SetInfo(self, info):
54 self.SetBackgroundColour('#FFFF80')
55 self.text.SetLabel(info)
56 self.extraInfoButton.Show(False)
59 def SetError(self, info, extraInfoUrl):
60 self.extraInfoUrl = extraInfoUrl
61 self.SetBackgroundColour('#FF8080')
62 self.text.SetLabel(info)
63 self.extraInfoButton.Show(True)
65 self.SetErrorIndicator()
68 def SetAttention(self, info):
69 self.SetBackgroundColour('#FFFF80')
70 self.text.SetLabel(info)
71 self.extraInfoButton.Show(False)
72 self.SetAttentionIndicator()
76 def SetBusy(self, info):
78 self.SetBusyIndicator()
80 def SetBusyIndicator(self):
82 self.bitmap.SetBitmap(self.busyBitmap[self.busyState])
84 def doExtraInfo(self, e):
85 webbrowser.open(self.extraInfoUrl)
87 def doBusyUpdate(self, e):
88 if self.busyState is None:
91 if self.busyState >= len(self.busyBitmap):
93 self.bitmap.SetBitmap(self.busyBitmap[self.busyState])
95 def SetReadyIndicator(self):
97 self.bitmap.SetBitmap(self.readyBitmap)
99 def SetErrorIndicator(self):
100 self.busyState = None
101 self.bitmap.SetBitmap(self.errorBitmap)
103 def SetAttentionIndicator(self):
104 self.busyState = None
105 self.bitmap.SetBitmap(self.attentionBitmap)
108 class InfoPage(wx.wizard.WizardPageSimple):
109 def __init__(self, parent, title):
110 wx.wizard.WizardPageSimple.__init__(self, parent)
112 sizer = wx.GridBagSizer(5, 5)
116 title = wx.StaticText(self, -1, title)
117 title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD))
118 sizer.Add(title, pos=(0, 0), span=(1, 2), flag=wx.ALIGN_CENTRE | wx.ALL)
119 sizer.Add(wx.StaticLine(self, -1), pos=(1, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
120 sizer.AddGrowableCol(1)
124 def AddText(self, info):
125 text = wx.StaticText(self, -1, info)
126 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
130 def AddSeperator(self):
131 self.GetSizer().Add(wx.StaticLine(self, -1), pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
134 def AddHiddenSeperator(self):
137 def AddInfoBox(self):
138 infoBox = InfoBox(self)
139 self.GetSizer().Add(infoBox, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT | wx.EXPAND)
143 def AddRadioButton(self, label, style=0):
144 radio = wx.RadioButton(self, -1, label, style=style)
145 self.GetSizer().Add(radio, pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
149 def AddCheckbox(self, label, checked=False):
150 check = wx.CheckBox(self, -1)
151 text = wx.StaticText(self, -1, label)
152 check.SetValue(checked)
153 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
154 self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 2), flag=wx.ALL)
158 def AddButton(self, label):
159 button = wx.Button(self, -1, label)
160 self.GetSizer().Add(button, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
164 def AddDualButton(self, label1, label2):
165 button1 = wx.Button(self, -1, label1)
166 self.GetSizer().Add(button1, pos=(self.rowNr, 0), flag=wx.RIGHT)
167 button2 = wx.Button(self, -1, label2)
168 self.GetSizer().Add(button2, pos=(self.rowNr, 1))
170 return button1, button2
172 def AddTextCtrl(self, value):
173 ret = wx.TextCtrl(self, -1, value)
174 self.GetSizer().Add(ret, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
178 def AddLabelTextCtrl(self, info, value):
179 text = wx.StaticText(self, -1, info)
180 ret = wx.TextCtrl(self, -1, value)
181 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
182 self.GetSizer().Add(ret, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
186 def AddTextCtrlButton(self, value, buttonText):
187 text = wx.TextCtrl(self, -1, value)
188 button = wx.Button(self, -1, buttonText)
189 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
190 self.GetSizer().Add(button, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
194 def AddBitmap(self, bitmap):
195 bitmap = wx.StaticBitmap(self, -1, bitmap)
196 self.GetSizer().Add(bitmap, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
200 def AddCheckmark(self, label, bitmap):
201 check = wx.StaticBitmap(self, -1, bitmap)
202 text = wx.StaticText(self, -1, label)
203 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
204 self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 1), flag=wx.ALL)
215 class FirstInfoPage(InfoPage):
216 def __init__(self, parent):
217 super(FirstInfoPage, self).__init__(parent, "First time run wizard")
218 self.AddText('Welcome, and thanks for trying Cura!')
220 self.AddText('This wizard will help you with the following steps:')
221 self.AddText('* Configure Cura for your machine')
222 self.AddText('* Upgrade your firmware')
223 self.AddText('* Check if your machine is working safely')
224 self.AddText('* Level your printer bed')
226 #self.AddText('* Calibrate your machine')
227 #self.AddText('* Do your first print')
230 class RepRapInfoPage(InfoPage):
231 def __init__(self, parent):
232 super(RepRapInfoPage, self).__init__(parent, "RepRap information")
234 'RepRap machines are vastly different, and there is no\ndefault configuration in Cura for any of them.')
235 self.AddText('If you like a default profile for your machine added,\nthen make an issue on github.')
237 self.AddText('You will have to manually install Marlin or Sprinter firmware.')
239 self.machineWidth = self.AddLabelTextCtrl('Machine width (mm)', '80')
240 self.machineDepth = self.AddLabelTextCtrl('Machine depth (mm)', '80')
241 self.machineHeight = self.AddLabelTextCtrl('Machine height (mm)', '60')
242 self.nozzleSize = self.AddLabelTextCtrl('Nozzle size (mm)', '0.5')
243 self.heatedBed = self.AddCheckbox('Heated bed')
246 profile.putPreference('machine_width', self.machineWidth.GetValue())
247 profile.putPreference('machine_depth', self.machineDepth.GetValue())
248 profile.putPreference('machine_height', self.machineHeight.GetValue())
249 profile.putProfileSetting('nozzle_size', self.nozzleSize.GetValue())
250 profile.putProfileSetting('wall_thickness', float(profile.getProfileSettingFloat('nozzle_size')) * 2)
251 profile.putPreference('has_heated_bed', str(self.heatedBed.GetValue()))
254 class MachineSelectPage(InfoPage):
255 def __init__(self, parent):
256 super(MachineSelectPage, self).__init__(parent, "Select your machine")
257 self.AddText('What kind of machine do you have:')
259 self.UltimakerRadio = self.AddRadioButton("Ultimaker", style=wx.RB_GROUP)
260 self.UltimakerRadio.SetValue(True)
261 self.UltimakerRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerSelect)
262 self.OtherRadio = self.AddRadioButton("Other (Ex: RepRap)")
263 self.OtherRadio.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
265 def OnUltimakerSelect(self, e):
266 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerFirmwareUpgradePage)
268 def OnOtherSelect(self, e):
269 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().repRapInfoPage)
272 if self.UltimakerRadio.GetValue():
273 profile.putPreference('machine_width', '205')
274 profile.putPreference('machine_depth', '205')
275 profile.putPreference('machine_height', '200')
276 profile.putPreference('machine_type', 'ultimaker')
277 profile.putProfileSetting('nozzle_size', '0.4')
279 profile.putPreference('machine_width', '80')
280 profile.putPreference('machine_depth', '80')
281 profile.putPreference('machine_height', '60')
282 profile.putPreference('machine_type', 'reprap')
283 profile.putPreference('startMode', 'Normal')
284 profile.putProfileSetting('nozzle_size', '0.5')
285 profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
288 class SelectParts(InfoPage):
289 def __init__(self, parent):
290 super(SelectParts, self).__init__(parent, "Select upgraded parts you have")
291 self.AddText('To assist you in having better default settings for your Ultimaker\nCura would like to know which upgrades you have in your machine.')
293 self.springExtruder = self.AddCheckbox('Extruder drive upgrade')
294 self.heatedBed = self.AddCheckbox('Heated printer bed (self build)')
295 self.dualExtrusion = self.AddCheckbox('Dual extrusion (experimental)')
297 self.AddText('If you have an Ultimaker bought after october 2012 you will have the\nExtruder drive upgrade. If you do not have this upgrade,\nit is highly recommended to improve reliablity.')
298 self.AddText('This upgrade can be bought from the Ultimaker webshop shop\nor found on thingiverse as thing:26094')
299 self.springExtruder.SetValue(True)
302 profile.putPreference('ultimaker_extruder_upgrade', str(self.springExtruder.GetValue()))
303 profile.putPreference('has_heated_bed', str(self.heatedBed.GetValue()))
304 if self.dualExtrusion.GetValue():
305 profile.putPreference('extruder_amount', '2')
307 profile.putPreference('extruder_amount', '1')
308 if profile.getPreference('ultimaker_extruder_upgrade') == 'True':
309 profile.putProfileSetting('retraction_enable', 'True')
311 profile.putProfileSetting('retraction_enable', 'False')
314 class FirmwareUpgradePage(InfoPage):
315 def __init__(self, parent):
316 super(FirmwareUpgradePage, self).__init__(parent, "Upgrade Ultimaker Firmware")
318 '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.')
319 self.AddHiddenSeperator()
321 'The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier.')
322 self.AddHiddenSeperator()
324 '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.')
325 upgradeButton, skipUpgradeButton = self.AddDualButton('Upgrade to Marlin firmware', 'Skip upgrade')
326 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
327 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
328 self.AddHiddenSeperator()
329 self.AddText('Do not upgrade to this firmware if:')
330 self.AddText('* You have an older machine based on ATMega1280')
331 self.AddText('* Have other changes in the firmware')
332 button = self.AddButton('Goto this page for a custom firmware')
333 button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
338 def OnUpgradeClick(self, e):
339 if firmwareInstall.InstallFirmware():
340 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
342 def OnSkipClick(self, e):
343 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
345 def OnUrlClick(self, e):
346 webbrowser.open('http://daid.mine.nu/~daid/marlin_build/')
349 class UltimakerCheckupPage(InfoPage):
350 def __init__(self, parent):
351 super(UltimakerCheckupPage, self).__init__(parent, "Ultimaker Checkup")
353 self.checkBitmap = wx.Bitmap(getPathForImage('checkmark.png'))
354 self.crossBitmap = wx.Bitmap(getPathForImage('cross.png'))
355 self.unknownBitmap = wx.Bitmap(getPathForImage('question.png'))
356 self.endStopNoneBitmap = wx.Bitmap(getPathForImage('endstop_none.png'))
357 self.endStopXMinBitmap = wx.Bitmap(getPathForImage('endstop_xmin.png'))
358 self.endStopXMaxBitmap = wx.Bitmap(getPathForImage('endstop_xmax.png'))
359 self.endStopYMinBitmap = wx.Bitmap(getPathForImage('endstop_ymin.png'))
360 self.endStopYMaxBitmap = wx.Bitmap(getPathForImage('endstop_ymax.png'))
361 self.endStopZMinBitmap = wx.Bitmap(getPathForImage('endstop_zmin.png'))
362 self.endStopZMaxBitmap = wx.Bitmap(getPathForImage('endstop_zmax.png'))
365 '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.')
366 b1, b2 = self.AddDualButton('Run checks', 'Skip checks')
367 b1.Bind(wx.EVT_BUTTON, self.OnCheckClick)
368 b2.Bind(wx.EVT_BUTTON, self.OnSkipClick)
370 self.commState = self.AddCheckmark('Communication:', self.unknownBitmap)
371 self.tempState = self.AddCheckmark('Temperature:', self.unknownBitmap)
372 self.stopState = self.AddCheckmark('Endstops:', self.unknownBitmap)
374 self.infoBox = self.AddInfoBox()
375 self.machineState = self.AddText('')
376 self.temperatureLabel = self.AddText('')
377 self.errorLogButton = self.AddButton('Show error log')
378 self.errorLogButton.Show(False)
380 self.endstopBitmap = self.AddBitmap(self.endStopNoneBitmap)
382 self.xMinStop = False
383 self.xMaxStop = False
384 self.yMinStop = False
385 self.yMaxStop = False
386 self.zMinStop = False
387 self.zMaxStop = False
389 self.Bind(wx.EVT_BUTTON, self.OnErrorLog, self.errorLogButton)
392 if self.comm != None:
396 self.endstopBitmap.Show(False)
399 def OnSkipClick(self, e):
400 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
402 def OnCheckClick(self, e=None):
403 self.errorLogButton.Show(False)
404 if self.comm is not None:
408 wx.CallAfter(self.OnCheckClick)
410 self.infoBox.SetBusy('Connecting to machine.')
411 self.commState.SetBitmap(self.unknownBitmap)
412 self.tempState.SetBitmap(self.unknownBitmap)
413 self.stopState.SetBitmap(self.unknownBitmap)
414 self.checkupState = 0
415 self.comm = machineCom.MachineCom(callbackObject=self)
417 def OnErrorLog(self, e):
418 printWindow.LogWindow('\n'.join(self.comm.getLog()))
420 def mcLog(self, message):
423 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
424 if not self.comm.isOperational():
426 if self.checkupState == 0:
427 self.tempCheckTimeout = 20
429 self.checkupState = 1
430 wx.CallAfter(self.infoBox.SetInfo, 'Cooldown before temperature check.')
431 self.comm.sendCommand('M104 S0')
432 self.comm.sendCommand('M104 S0')
434 self.startTemp = temp
435 self.checkupState = 2
436 wx.CallAfter(self.infoBox.SetInfo, 'Checking the heater and temperature sensor.')
437 self.comm.sendCommand('M104 S200')
438 self.comm.sendCommand('M104 S200')
439 elif self.checkupState == 1:
441 self.startTemp = temp
442 self.checkupState = 2
443 wx.CallAfter(self.infoBox.SetInfo, 'Checking the heater and temperature sensor.')
444 self.comm.sendCommand('M104 S200')
445 self.comm.sendCommand('M104 S200')
446 elif self.checkupState == 2:
447 #print "WARNING, TEMPERATURE TEST DISABLED FOR TESTING!"
448 if temp > self.startTemp + 40:
449 self.checkupState = 3
450 wx.CallAfter(self.infoBox.SetAttention, 'Please make sure none of the endstops are pressed.')
451 wx.CallAfter(self.endstopBitmap.Show, True)
452 wx.CallAfter(self.Layout)
453 self.comm.sendCommand('M104 S0')
454 self.comm.sendCommand('M104 S0')
455 self.comm.sendCommand('M119')
456 wx.CallAfter(self.tempState.SetBitmap, self.checkBitmap)
458 self.tempCheckTimeout -= 1
459 if self.tempCheckTimeout < 1:
460 self.checkupState = -1
461 wx.CallAfter(self.tempState.SetBitmap, self.crossBitmap)
462 wx.CallAfter(self.infoBox.SetError, 'Temperature measurement FAILED!', 'http://wiki.ultimaker.com/Cura:_Temperature_measurement_problems')
463 self.comm.sendCommand('M104 S0')
464 self.comm.sendCommand('M104 S0')
465 wx.CallAfter(self.temperatureLabel.SetLabel, 'Head temperature: %d' % (temp))
467 def mcStateChange(self, state):
468 if self.comm is None:
470 if self.comm.isOperational():
471 wx.CallAfter(self.commState.SetBitmap, self.checkBitmap)
472 wx.CallAfter(self.machineState.SetLabel, 'Communication State: %s' % (self.comm.getStateString()))
473 elif self.comm.isError():
474 wx.CallAfter(self.commState.SetBitmap, self.crossBitmap)
475 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
476 wx.CallAfter(self.endstopBitmap.Show, False)
477 wx.CallAfter(self.machineState.SetLabel, '%s' % (self.comm.getErrorString()))
478 wx.CallAfter(self.errorLogButton.Show, True)
479 wx.CallAfter(self.Layout)
481 wx.CallAfter(self.machineState.SetLabel, 'Communication State: %s' % (self.comm.getStateString()))
483 def mcMessage(self, message):
484 if self.checkupState >= 3 and self.checkupState < 10 and 'x_min' in message:
485 for data in message.split(' '):
487 tag, value = data.split(':', 2)
489 self.xMinStop = (value == 'H')
491 self.xMaxStop = (value == 'H')
493 self.yMinStop = (value == 'H')
495 self.yMaxStop = (value == 'H')
497 self.zMinStop = (value == 'H')
499 self.zMaxStop = (value == 'H')
500 self.comm.sendCommand('M119')
502 if self.checkupState == 3:
503 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
504 self.checkupState = 4
505 wx.CallAfter(self.infoBox.SetAttention, 'Please press the right X endstop.')
506 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMaxBitmap)
507 elif self.checkupState == 4:
508 if not self.xMinStop and self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
509 self.checkupState = 5
510 wx.CallAfter(self.infoBox.SetAttention, 'Please press the left X endstop.')
511 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
512 elif self.checkupState == 5:
513 if self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
514 self.checkupState = 6
515 wx.CallAfter(self.infoBox.SetAttention, 'Please press the front Y endstop.')
516 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMinBitmap)
517 elif self.checkupState == 6:
518 if not self.xMinStop and not self.xMaxStop and self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
519 self.checkupState = 7
520 wx.CallAfter(self.infoBox.SetAttention, 'Please press the back Y endstop.')
521 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMaxBitmap)
522 elif self.checkupState == 7:
523 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and self.yMaxStop and not self.zMinStop and not self.zMaxStop:
524 self.checkupState = 8
525 wx.CallAfter(self.infoBox.SetAttention, 'Please press the top Z endstop.')
526 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
527 elif self.checkupState == 8:
528 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and self.zMinStop and not self.zMaxStop:
529 self.checkupState = 9
530 wx.CallAfter(self.infoBox.SetAttention, 'Please press the bottom Z endstop.')
531 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMaxBitmap)
532 elif self.checkupState == 9:
533 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and self.zMaxStop:
534 self.checkupState = 10
536 wx.CallAfter(self.infoBox.SetInfo, 'Checkup finished')
537 wx.CallAfter(self.infoBox.SetReadyIndicator)
538 wx.CallAfter(self.endstopBitmap.Show, False)
539 wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
540 wx.CallAfter(self.OnSkipClick, None)
542 def mcProgress(self, lineNr):
545 def mcZChange(self, newZ):
549 class UltimakerCalibrationPage(InfoPage):
550 def __init__(self, parent):
551 super(UltimakerCalibrationPage, self).__init__(parent, "Ultimaker Calibration")
553 self.AddText("Your Ultimaker requires some calibration.")
554 self.AddText("This calibration is needed for a proper extrusion amount.")
556 self.AddText("The following values are needed:")
557 self.AddText("* Diameter of filament")
558 self.AddText("* Number of steps per mm of filament extrusion")
560 self.AddText("The better you have calibrated these values, the better your prints\nwill become.")
562 self.AddText("First we need the diameter of your filament:")
563 self.filamentDiameter = self.AddTextCtrl(profile.getProfileSetting('filament_diameter'))
565 "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.")
566 self.AddText("Note: This value can be changed later at any time.")
569 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
572 class UltimakerCalibrateStepsPerEPage(InfoPage):
573 def __init__(self, parent):
574 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, "Ultimaker Calibration")
576 if profile.getPreference('steps_per_e') == '0':
577 profile.putPreference('steps_per_e', '865.888')
579 self.AddText("Calibrating the Steps Per E requires some manual actions.")
580 self.AddText("First remove any filament from your machine.")
581 self.AddText("Next put in your filament so the tip is aligned with the\ntop of the extruder drive.")
582 self.AddText("We'll push the filament 100mm")
583 self.extrudeButton = self.AddButton("Extrude 100mm filament")
584 self.AddText("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)")
585 self.lengthInput, self.saveLengthButton = self.AddTextCtrlButton('100', 'Save')
586 self.AddText("This results in the following steps per E:")
587 self.stepsPerEInput = self.AddTextCtrl(profile.getPreference('steps_per_e'))
588 self.AddText("You can repeat these steps to get better calibration.")
591 "If you still have filament in your printer which needs\nheat to remove, press the heat up button below:")
592 self.heatButton = self.AddButton("Heatup for filament removal")
594 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
595 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
596 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
598 def OnSaveLengthClick(self, e):
599 currentEValue = float(self.stepsPerEInput.GetValue())
600 realExtrudeLength = float(self.lengthInput.GetValue())
601 newEValue = currentEValue * 100 / realExtrudeLength
602 self.stepsPerEInput.SetValue(str(newEValue))
603 self.lengthInput.SetValue("100")
605 def OnExtrudeClick(self, e):
606 threading.Thread(target=self.OnExtrudeRun).start()
608 def OnExtrudeRun(self):
609 self.heatButton.Enable(False)
610 self.extrudeButton.Enable(False)
611 currentEValue = float(self.stepsPerEInput.GetValue())
612 self.comm = machineCom.MachineCom()
613 if not self.comm.isOpen():
615 "Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable",
616 'Printer error', wx.OK | wx.ICON_INFORMATION)
617 self.heatButton.Enable(True)
618 self.extrudeButton.Enable(True)
621 line = self.comm.readline()
626 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
629 self.sendGCommand('M302') #Disable cold extrusion protection
630 self.sendGCommand("M92 E%f" % (currentEValue))
631 self.sendGCommand("G92 E0")
632 self.sendGCommand("G1 E100 F600")
635 self.extrudeButton.Enable()
636 self.heatButton.Enable()
638 def OnHeatClick(self, e):
639 threading.Thread(target=self.OnHeatRun).start()
642 self.heatButton.Enable(False)
643 self.extrudeButton.Enable(False)
644 self.comm = machineCom.MachineCom()
645 if not self.comm.isOpen():
647 "Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable",
648 'Printer error', wx.OK | wx.ICON_INFORMATION)
649 self.heatButton.Enable(True)
650 self.extrudeButton.Enable(True)
653 line = self.comm.readline()
655 self.heatButton.Enable(True)
656 self.extrudeButton.Enable(True)
660 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
663 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
665 'Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)',
666 'Machine heatup', wx.OK | wx.ICON_INFORMATION)
667 self.sendGCommand('M104 S0')
670 self.heatButton.Enable(True)
671 self.extrudeButton.Enable(True)
673 def sendGCommand(self, cmd):
674 self.comm.sendCommand(cmd) #Disable cold extrusion protection
676 line = self.comm.readline()
679 if line.startswith('ok'):
683 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
686 class configWizard(wx.wizard.Wizard):
688 super(configWizard, self).__init__(None, -1, "Configuration Wizard")
690 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
691 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
693 self.firstInfoPage = FirstInfoPage(self)
694 self.machineSelectPage = MachineSelectPage(self)
695 self.ultimakerSelectParts = SelectParts(self)
696 self.ultimakerFirmwareUpgradePage = FirmwareUpgradePage(self)
697 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
698 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
699 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
700 self.bedLevelPage = bedLevelWizardMain(self)
701 self.repRapInfoPage = RepRapInfoPage(self)
703 wx.wizard.WizardPageSimple.Chain(self.firstInfoPage, self.machineSelectPage)
704 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
705 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
706 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
707 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
708 #wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
710 self.FitToPage(self.firstInfoPage)
711 self.GetPageAreaSizer().Add(self.firstInfoPage)
713 self.RunWizard(self.firstInfoPage)
716 def OnPageChanging(self, e):
717 e.GetPage().StoreData()
719 def OnPageChanged(self, e):
720 if e.GetPage().AllowNext():
721 self.FindWindowById(wx.ID_FORWARD).Enable()
723 self.FindWindowById(wx.ID_FORWARD).Disable()
724 self.FindWindowById(wx.ID_BACKWARD).Disable()
726 class bedLevelWizardMain(InfoPage):
727 def __init__(self, parent):
728 super(bedLevelWizardMain, self).__init__(parent, "Bed leveling wizard")
730 self.AddText('This wizard will help you in leveling your printer bed')
732 self.AddText('It will do the following steps')
733 self.AddText('* Move the printer head to each corner')
734 self.AddText(' and let you adjust the height of the bed to the nozzle')
735 self.AddText('* Print a line around the bed to check if it is level')
738 self.connectButton = self.AddButton('Connect to printer')
741 self.infoBox = self.AddInfoBox()
742 self.resumeButton = self.AddButton('Resume')
743 self.resumeButton.Enable(False)
745 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
746 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
748 def OnConnect(self, e = None):
749 if self.comm is not None:
753 wx.CallAfter(self.OnConnect)
755 self.connectButton.Enable(False)
756 self.comm = machineCom.MachineCom(callbackObject=self)
757 self.infoBox.SetBusy('Connecting to machine.')
758 self._wizardState = 0
763 def OnResume(self, e):
764 feedZ = profile.getProfileSettingFloat('max_z_speed') * 60
765 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
766 if self._wizardState == 2:
767 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back left corner...')
768 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
769 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getPreferenceFloat('machine_depth'), feedTravel))
770 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
771 self.comm.sendCommand('M400')
772 self._wizardState = 3
773 elif self._wizardState == 4:
774 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back right corner...')
775 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
776 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth') - 20, feedTravel))
777 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
778 self.comm.sendCommand('M400')
779 self._wizardState = 5
780 elif self._wizardState == 6:
781 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to front right corner...')
782 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
783 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getPreferenceFloat('machine_width'), 20, feedTravel))
784 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
785 self.comm.sendCommand('M400')
786 self._wizardState = 7
787 elif self._wizardState == 8:
788 wx.CallAfter(self.infoBox.SetBusy, 'Heating up printer...')
789 self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
790 self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
791 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
792 self._wizardState = 9
793 self.resumeButton.Enable(False)
795 def mcLog(self, message):
796 print 'Log:', message
798 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
799 if self._wizardState == 1:
800 self._wizardState = 2
801 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.')
802 wx.CallAfter(self.resumeButton.Enable, True)
803 elif self._wizardState == 3:
804 self._wizardState = 4
805 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.')
806 wx.CallAfter(self.resumeButton.Enable, True)
807 elif self._wizardState == 5:
808 self._wizardState = 6
809 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.')
810 wx.CallAfter(self.resumeButton.Enable, True)
811 elif self._wizardState == 7:
812 self._wizardState = 8
813 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.')
814 wx.CallAfter(self.resumeButton.Enable, True)
815 elif self._wizardState == 9:
816 if temp < profile.getProfileSettingFloat('print_temperature') - 5:
817 wx.CallAfter(self.infoBox.SetInfo, 'Heating up printer: %d/%d' % (temp, profile.getProfileSettingFloat('print_temperature')))
819 self._wizardState = 10
820 wx.CallAfter(self.infoBox.SetInfo, 'Printing a square on the printer bed at 0.3mm height.')
821 feedZ = profile.getProfileSettingFloat('max_z_speed') * 60
822 feedPrint = profile.getProfileSettingFloat('print_speed') * 60
823 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
824 w = profile.getPreferenceFloat('machine_width')
825 d = profile.getPreferenceFloat('machine_depth')
826 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
827 filamentArea = math.pi * filamentRadius * filamentRadius
828 ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
832 'G1 Z2 F%d' % (feedZ),
834 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
835 'G1 Z0.3 F%d' % (feedZ)]
837 gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
839 for i in xrange(0, 3):
841 eValue += (d - 2*dist) * ePerMM
842 gcodeList.append('G1 X%d Y%d E%f F%d' % (dist, d - dist, eValue, feedPrint))
843 eValue += (w - 2*dist) * ePerMM
844 gcodeList.append('G1 X%d Y%d E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
845 eValue += (d - 2*dist) * ePerMM
846 gcodeList.append('G1 X%d Y%d E%f F%d' % (w - dist, dist, eValue, feedPrint))
847 eValue += (w - 2*dist) * ePerMM
848 gcodeList.append('G1 X%d Y%d E%f F%d' % (dist, dist, eValue, feedPrint))
850 gcodeList.append('M400')
851 self.comm.printGCode(gcodeList)
853 def mcStateChange(self, state):
854 if self.comm is None:
856 if self.comm.isOperational():
857 if self._wizardState == 0:
858 wx.CallAfter(self.infoBox.SetInfo, 'Homing printer...')
859 self.comm.sendCommand('G28')
860 self._wizardState = 1
861 elif self._wizardState == 10 and not self.comm.isPrinting():
862 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('max_z_speed') * 60))
863 self.comm.sendCommand('G92 E0')
864 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
865 self.comm.sendCommand('M104 S0')
866 wx.CallAfter(self.infoBox.SetInfo, 'Calibration finished.\nThe squares on the bed should slightly touch each other.')
867 wx.CallAfter(self.infoBox.SetReadyIndicator)
868 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
869 wx.CallAfter(self.connectButton.Enable, True)
870 self._wizardState = 11
871 elif self.comm.isError():
872 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
874 def mcMessage(self, message):
877 def mcProgress(self, lineNr):
880 def mcZChange(self, newZ):
883 class bedLevelWizard(wx.wizard.Wizard):
885 super(bedLevelWizard, self).__init__(None, -1, "Bed leveling wizard")
887 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
888 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
890 self.mainPage = bedLevelWizardMain(self)
892 self.FitToPage(self.mainPage)
893 self.GetPageAreaSizer().Add(self.mainPage)
895 self.RunWizard(self.mainPage)
898 def OnPageChanging(self, e):
899 e.GetPage().StoreData()
901 def OnPageChanged(self, e):
902 if e.GetPage().AllowNext():
903 self.FindWindowById(wx.ID_FORWARD).Enable()
905 self.FindWindowById(wx.ID_FORWARD).Disable()
906 self.FindWindowById(wx.ID_BACKWARD).Disable()