diff --git a/config.py b/config.py index bd7ad34..43f2711 100644 --- a/config.py +++ b/config.py @@ -1,7 +1,11 @@ #!/bin/env python +import os import wx import re +import time + +VERSION = "0.1" try: from agw import customtreectrl as CT @@ -10,299 +14,2220 @@ except ImportError: from config_helptext import helpText +supportedCPUs = ['ATmega168', 'ATmega328P', 'ATmega644P', 'ATmega644PA', + 'ATmega1280', 'ATmega1284P', 'ATmega2560', 'AT90USB1286'] + BSIZE = (90, 60) +BSIZESMALL = (90, 30) + +reDefine = re.compile("\s*#define\s+(\S+)\s+(\S+)") +reDefQS = re.compile("\s*#define\s+(\S+)\s+(\"[^\"]*\")") +reDefTS = re.compile("\s*(DEFINE_TEMP_SENSOR\\([^)]*\\))") +reDefHT = re.compile("\s*(DEFINE_HEATER\\([^)]*\\))") +reDefBool = re.compile("\s*#define\s+(\S+)\s+") +reIncArduino = re.compile("\s*#include\s+\"arduino.h\"") + +reSensor3 = re.compile(".*\\(\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*\\)") +reSensor4 = re.compile(".*\\(\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*\\)") +reHeater = re.compile(".*\\(\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*\\)") + +reInteger = re.compile("^\d+$") +reFloat = re.compile("^\d+(\.\d+)?$") +rePin = re.compile("^[AD]IO\d+$") + +reAVR = re.compile("__AVR_(\w+)__") + +defineValueFormat = "#define %-30.30s %s\n" +defineBoolFormat = "#define %s\n" +defineHeaterFormat = "#define HEATER_%s HEATER_%s\n" +defineULFormat = "#define %-30.30s %sUL\n" +defineDCExtruderFormat = "#define %-30.30s HEATER_%s\n" + + +class Page: + def __init__(self): + self.modified = False + self.valid = True + self.fieldValid = {} + self.textControls = {} + self.checkBoxes = {} + self.radioButtons = {} + self.choices = {} + + def addTextCtrl(self, name, labelWidth, ht, validator): + lsz = wx.BoxSizer(wx.HORIZONTAL) + st = wx.StaticText(self, wx.ID_ANY, self.labels[name], + size = (labelWidth, -1), style = wx.ALIGN_RIGHT) + lsz.Add(st) + + tc = wx.TextCtrl(self, wx.ID_ANY, "", style = wx.TE_RIGHT, name = name) + self.fieldValid[name] = True + tc.Bind(wx.EVT_TEXT, validator) + self.textControls[name] = tc + lsz.Add(tc) + if ht: + hts = ht + else: + if name in helpText.keys(): + hts = helpText[name] + else: + hts = "" + tc.SetToolTipString(hts) + + return lsz + + def addCheckBox(self, name, ht, validator): + if name in self.labels.keys(): + lbl = self.labels[name] + else: + lbl = name + cb = wx.CheckBox(self, wx.ID_ANY, lbl) + cb.Bind(wx.EVT_CHECKBOX, validator) + self.checkBoxes[name] = cb + if ht: + hts = ht + else: + if name in helpText.keys(): + hts = helpText[name] + else: + hts = "" + cb.SetToolTipString(hts) + + return cb + + def addRadioButton(self, name, style, ht, validator): + rb = wx.RadioButton(self, wx.ID_ANY, self.labels[name], style = style) + self.Bind(wx.EVT_RADIOBUTTON, validator, rb) + self.radioButtons[name] = rb + if ht: + hts = ht + else: + if name in helpText.keys(): + hts = helpText[name] + else: + hts = "" + rb.SetToolTipString(hts) + + return rb + + def addChoice(self, name, choices, selection, labelWidth, ht, validator): + lsz = wx.BoxSizer(wx.HORIZONTAL) + st = wx.StaticText(self, wx.ID_ANY, self.labels[name], + size = (labelWidth, -1), style = wx.ALIGN_RIGHT) + lsz.Add(st) + + ch = wx.Choice(self, wx.ID_ANY, choices = choices, name = name) + ch.Bind(wx.EVT_CHOICE, validator) + ch.SetSelection(selection) + lsz.Add(ch) + self.choices[name] = ch + if ht: + hts = ht + else: + if name in helpText.keys(): + hts = helpText[name] + else: + hts = "" + ch.SetToolTipString(hts) + return lsz + + def setChoice(self, name, cfgValues, choices, default): + if name in cfgValues.keys(): + bv = cfgValues[name] + else: + bv = self.defaultClock + + try: + s = choices.index(bv) + except: + try: + s = choices.index(default) + except: + s = 0 + + self.choices[name].SetSelection(s) + + def onTextCtrlInteger(self, evt): + self.assertModified(True) + tc = evt.GetEventObject() + name = tc.GetName() + w = tc.GetValue().strip() + if w == "": + valid = True + else: + m = reInteger.match(w) + if m: + valid = True + else: + valid = False + + self.setFieldValidity(name, valid) + + if valid: + tc.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) + else: + tc.SetBackgroundColour("pink") + tc.Refresh() + evt.Skip() + + def onTextCtrlFloat(self, evt): + self.assertModified(True) + tc = evt.GetEventObject() + name = tc.GetName() + w = tc.GetValue().strip() + if w == "": + valid = True + else: + m = reFloat.match(w) + if m: + valid = True + else: + valid = False + + self.setFieldValidity(name, valid) + + if valid: + tc.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) + else: + tc.SetBackgroundColour("pink") + tc.Refresh() + evt.Skip() + + def onTextCtrlPin(self, evt): + self.assertModified(True) + tc = evt.GetEventObject() + self.validatePin(tc) + evt.Skip() + + def onTextCtrl(self, evt): + self.assertModified(True) + evt.Skip() + + def onChoice(self, evt): + self.assertModified(True) + evt.Skip() + + def validatePin(self, tc): + name = tc.GetName() + w = tc.GetValue().strip() + + if w == "": + valid = True + else: + m = reInteger.match(w) + if m: + valid = True + else: + if self.includeArduino: + m = rePin.match(w) + if m: + valid = True + else: + valid = False + else: + valid = False + + self.setFieldValidity(name, valid) + + if valid: + tc.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) + else: + tc.SetBackgroundColour("pink") + tc.Refresh() + + def onCheckBox(self, evt): + self.assertModified(True) + evt.Skip() + + def assertModified(self, flag): + if flag != self.modified: + self.parent.assertModified(self.id, flag) + self.modified = flag + + def setFieldValidity(self, name, flag): + self.fieldValid[name] = flag + + pgValid = True + for k in self.fieldValid.keys(): + if not self.fieldValid[k]: + pgValid = False + break + + self.assertValid(pgValid) + + def assertValid(self, flag): + if flag != self.valid: + self.parent.assertValid(self.id, flag) + self.valid = flag + + +class MechanicalPage(wx.Panel, Page): + def __init__(self, parent, nb, idPg): + wx.Panel.__init__(self, nb, wx.ID_ANY) + Page.__init__(self) + self.id = idPg + self.parent = parent + + self.spmKeys = ['STEPS_PER_M_X', 'STEPS_PER_M_Y', 'STEPS_PER_M_Z', + 'STEPS_PER_M_E'] + + self.mfrKeys = ['MAXIMUM_FEEDRATE_X', 'MAXIMUM_FEEDRATE_Y', + 'MAXIMUM_FEEDRATE_Z', 'MAXIMUM_FEEDRATE_E'] + + self.msrKeys = ['SEARCH_FEEDRATE_X', 'SEARCH_FEEDRATE_Y', + 'SEARCH_FEEDRATE_Z'] + + self.eclKeys = ['ENDSTOP_CLEARANCE_X', 'ENDSTOP_CLEARANCE_Y', + 'ENDSTOP_CLEARANCE_Z'] + + self.minmaxKeys = ['X_MIN', 'X_MAX', 'Y_MIN', 'Y_MAX', 'Z_MIN', 'Z_MAX'] + + self.labels = {'STEPS_PER_M_X': "X:", 'STEPS_PER_M_Y': "Y:", + 'STEPS_PER_M_Z': "Z:", 'STEPS_PER_M_E' : "E:", + 'MAXIMUM_FEEDRATE_X': "X:", 'MAXIMUM_FEEDRATE_Y': "Y:", + 'MAXIMUM_FEEDRATE_Z': "Z:", 'MAXIMUM_FEEDRATE_E': "E:", + 'SEARCH_FEEDRATE_X': "X:", 'SEARCH_FEEDRATE_Y': "Y:", + 'SEARCH_FEEDRATE_Z': "Z:", + 'ENDSTOP_CLEARANCE_X': "X:", 'ENDSTOP_CLEARANCE_Y': "Y:", + 'ENDSTOP_CLEARANCE_Z': "Z:", + 'X_MIN': "Min X:", 'X_MAX': "Max X:", 'Y_MIN': "Min Y:", + 'Y_MAX': "Max Y:", 'Z_MIN': "Min Z:", 'Z_MAX': "Max Z:", + 'E_ABSOLUTE': "Absolute E Coordinates"} + + labelWidth = 40; + + sz = wx.GridBagSizer() + sz.AddSpacer((10, 10), pos = (0, 0)) + + b = wx.StaticBox(self, wx.ID_ANY, "Steps Per Meter") + kh = 'STEPS_PER_M' + ht = None + if kh in helpText.keys(): + ht = helpText[kh] + sbox = wx.StaticBoxSizer(b, wx.VERTICAL) + sbox.AddSpacer((5, 5)) + for k in self.spmKeys: + tc = self.addTextCtrl(k, labelWidth, ht, self.onTextCtrlInteger) + sbox.Add(tc) + sbox.AddSpacer((5, 5)) + + sz.Add(sbox, pos = (1, 1)) + + b = wx.StaticBox(self, wx.ID_ANY, "Maximum Feedrate") + kh = 'MAXIMUM_FEEDRATE' + ht = None + if kh in helpText.keys(): + ht = helpText[kh] + sbox = wx.StaticBoxSizer(b, wx.VERTICAL) + sbox.AddSpacer((5, 5)) + for k in self.mfrKeys: + tc = self.addTextCtrl(k, labelWidth, ht, self.onTextCtrlInteger) + sbox.Add(tc) + sbox.AddSpacer((5, 5)) + + sz.Add(sbox, pos = (1, 5)) + + b = wx.StaticBox(self, wx.ID_ANY, "Search Feedrate") + kh = 'SEARCH_FEEDRATE' + ht = None + if kh in helpText.keys(): + ht = helpText[kh] + sbox = wx.StaticBoxSizer(b, wx.VERTICAL) + sbox.AddSpacer((5, 5)) + for k in self.msrKeys: + tc = self.addTextCtrl(k, labelWidth, ht, self.onTextCtrlInteger) + sbox.Add(tc) + sbox.AddSpacer((5, 5)) + + sz.Add(sbox, pos = (1, 7)) + + b = wx.StaticBox(self, wx.ID_ANY, "Endstop Clearance") + kh = 'ENDSTOP_CLEARANCE' + ht = None + if kh in helpText.keys(): + ht = helpText[kh] + sbox = wx.StaticBoxSizer(b, wx.VERTICAL) + sbox.AddSpacer((5, 5)) + for k in self.eclKeys: + tc = self.addTextCtrl(k, labelWidth, ht, self.onTextCtrlInteger) + sbox.Add(tc) + sbox.AddSpacer((5, 5)) + + sz.Add(sbox, pos = (3, 1)) + + b = wx.StaticBox(self, wx.ID_ANY, "Travel Limits") + kh = 'MINMAX' + ht = None + if kh in helpText.keys(): + ht = helpText[kh] + sbox = wx.StaticBoxSizer(b, wx.VERTICAL) + sbox.AddSpacer((5, 5)) + for k in self.minmaxKeys: + tc = self.addTextCtrl(k, labelWidth, ht, self.onTextCtrlInteger) + sbox.Add(tc) + sbox.AddSpacer((5, 5)) + + sz.Add(sbox, pos = (3, 3)) + + cb = self.addCheckBox('E_ABSOLUTE', None, self.onCheckBox) + + sz.Add(cb, pos = (3, 7), flag = wx.ALIGN_CENTER_VERTICAL) + + bsz = wx.BoxSizer(wx.VERTICAL) + b = wx.Button(self, wx.ID_ANY, "Calculate\nBelt Driven", size = BSIZE) + b.SetToolTipString("Open the calculator for Axes that are belt-driven") + + bsz.Add(b, 1, wx.ALL, 5) + b = wx.Button(self, wx.ID_ANY, "Calculate\nScrew Driven", size = BSIZE) + bsz.Add(b, 1, wx.ALL, 5) + b.SetToolTipString("Open the calculator for Axes that are screw-driven") + + sz.Add(bsz, pos = (1, 3)) + self.SetSizer(sz) + + def insertValues(self, cfgValues): + self.assertValid(True) + for k in self.fieldValid.keys(): + self.fieldValid[k] = True + for k in self.textControls.keys(): + if k in cfgValues.keys(): + self.textControls[k].SetValue(cfgValues[k]) + else: + self.textControls[k].SetValue("") + + for k in self.checkBoxes.keys(): + if k in cfgValues.keys() and cfgValues[k]: + self.checkBoxes[k].SetValue(True) + else: + self.checkBoxes[k].SetValue(False) + self.assertModified(False) + + def saveValues(self, fp): + self.assertModified(False) + fp.write( +"\n/***************************************************************************\\\n\ +* *\n\ +* 1. MECHANICAL/HARDWARE *\n\ +* *\n\ +\\***************************************************************************/\n") + + for k in self.spmKeys + self.mfrKeys + self.msrKeys + self.eclKeys + \ + self.minmaxKeys: + v = self.textControls[k].GetValue() + if v != "": + fp.write(defineValueFormat % (k, v)) + + k = 'E_ABSOLUTE' + cb = self.checkBoxes[k] + if cb.IsChecked(): + fp.write(defineBoolFormat % k) + + +class AccelerationPage(wx.Panel, Page): + def __init__(self, parent, nb, idPg): + wx.Panel.__init__(self, nb, wx.ID_ANY) + Page.__init__(self) + self.parent = parent + self.id = idPg + + self.accTypeKeys = ['ACCELERATION_REPRAP', 'ACCELERATION_RAMPING', + 'ACCELERATION_TEMPORAL'] + self.jerkKeys = ['MAX_JERK_X', 'MAX_JERK_Y', 'MAX_JERK_Z', 'MAX_JERK_E'] + + self.labels = {'ACCELERATION_REPRAP': "RepRap", + 'ACCELERATION_RAMPING': "Ramping", + 'ACCELERATION_TEMPORAL': "Temporal", + 'ACCELERATION' : "Acceleration:", 'LOOKAHEAD': "Look Ahead", + 'MAX_JERK_X': "X:", 'MAX_JERK_Y': "Y:", + 'MAX_JERK_Z': "Z:", 'MAX_JERK_E': "E:"} + + sz = wx.GridBagSizer() + sz.AddSpacer((20, 40), pos = (0, 0)) + b = wx.StaticBox(self, wx.ID_ANY, "Acceleration Type:") + sbox = wx.StaticBoxSizer(b, wx.VERTICAL) + sbox.AddSpacer((5, 5)) + style = wx.RB_GROUP + for k in self.accTypeKeys: + rb = self.addRadioButton(k, style, None, self.onAccTypeSelect) + style = 0 + + sbox.Add(rb, 1, wx.LEFT + wx.RIGHT, 16) + sbox.AddSpacer((5, 5)) + + rb = wx.RadioButton(self, wx.ID_ANY, "None", style = style) + rb.SetValue(True) + self.Bind(wx.EVT_RADIOBUTTON, self.onAccTypeSelect, rb) + sbox.Add(rb, 1, wx.LEFT + wx.RIGHT, 16) + sbox.AddSpacer((5, 5)) + sz.Add(sbox, pos = (1, 1)) + + b = wx.StaticBox(self, wx.ID_ANY, "Ramping Parameters:") + sbox = wx.StaticBoxSizer(b, wx.VERTICAL) + sbox.AddSpacer((5, 5)) + + k = 'ACCELERATION' + tc = self.addTextCtrl(k, 80, None, self.onTextCtrlInteger) + self.textControls[k].Enable(False) + + sbox.Add(tc) + sbox.AddSpacer((5, 5)) + + k = 'LOOKAHEAD' + cb = self.addCheckBox(k, None, self.onCheckBox) + self.checkBoxes[k].Enable(False) + + sbox.Add(cb, 1, wx.ALIGN_CENTER_HORIZONTAL) + sbox.AddSpacer((5, 5)) + + sz.Add(sbox, pos = (1, 3)) + + b = wx.StaticBox(self, wx.ID_ANY, "Maximum Jerk") + kh = 'MAX_JERK' + ht = None + if kh in helpText.keys(): + ht = helpText[kh] + sbox = wx.StaticBoxSizer(b, wx.VERTICAL) + sbox.AddSpacer((5, 5)) + for k in self.jerkKeys: + tc = self.addTextCtrl(k, 40, ht, self.onTextCtrlInteger) + + sbox.Add(tc) + sbox.AddSpacer((5, 5)) + + sz.AddSpacer((80, 20), pos = (1, 4)) + sz.Add(sbox, pos = (1, 5)) + + self.SetSizer(sz) + + def onAccTypeSelect(self, evt): + self.assertModified(True) + rb = evt.GetEventObject() + label = rb.GetLabel() + + if label == self.labels['ACCELERATION_RAMPING']: + ena = True + else: + ena = False + + self.checkBoxes['LOOKAHEAD'].Enable(ena) + self.textControls['ACCELERATION'].Enable(ena) + evt.Skip() + + def insertValues(self, cfgValues): + self.assertValid(True) + for k in self.fieldValid.keys(): + self.fieldValid[k] = True + self.checkBoxes['LOOKAHEAD'].Enable(False) + self.textControls['ACCELERATION'].Enable(False) + for k in self.textControls.keys(): + if k in cfgValues.keys(): + self.textControls[k].SetValue(cfgValues[k]) + else: + self.textControls[k].SetValue("") + + for tag in ['ACCELERATION_REPRAP', 'ACCELERATION_RAMPING', + 'ACCELERATION_TEMPORAL']: + if tag in cfgValues.keys() and cfgValues[tag]: + self.radioButtons[tag].SetValue(True) + if tag == 'ACCELERATION_RAMPING': + self.checkBoxes['LOOKAHEAD'].Enable(True) + self.textControls['ACCELERATION'].Enable(True) + + k = 'LOOKAHEAD' + if k in cfgValues.keys() and cfgValues[k]: + self.checkBoxes[k].SetValue(True) + else: + self.checkBoxes[k].SetValue(False) + self.assertModified(False) + + def saveValues(self, fp): + self.assertModified(False) + fp.write( +"\n/***************************************************************************\\\n\ +* *\n\ +* 2. ACCELERATION *\n\ +* *\n\ +\\***************************************************************************/\n") + + for tag in ['ACCELERATION_REPRAP', 'ACCELERATION_RAMPING', + 'ACCELERATION_TEMPORAL']: + rb = self.radioButtons[tag] + if rb.GetValue(): + fp.write(defineBoolFormat % tag) + + k = 'ACCELERATION' + v = self.textControls[k].GetValue() + if v != "": + fp.write(defineValueFormat % (k, v)) + + k = 'LOOKAHEAD' + cb = self.checkBoxes[k] + if cb.IsChecked(): + fp.write(defineBoolFormat % k) + + for k in ['MAX_JERK_X', 'MAX_JERK_Y', 'MAX_JERK_Z', 'MAX_JERK_E']: + v = self.textControls[k].GetValue() + if v != "": + fp.write(defineValueFormat % (k, v)) + + +class PinoutsPage(wx.Panel, Page): + def __init__(self, parent, nb, idPg): + wx.Panel.__init__(self, nb, wx.ID_ANY) + Page.__init__(self) + self.parent = parent + self.id = idPg + + self.pinXKeys = [('X_STEP_PIN', 0), ('X_DIR_PIN', 0), ('X_MIN_PIN', 0), + ('X_MAX_PIN', 0), ('X_ENABLE_PIN', 0), + ('X_INVERT_DIR', 1), ('X_INVERT_MIN', 1), + ('X_INVERT_MAX', 1), ('X_INVERT_ENABLE', 1)] + self.pinYKeys = [('Y_STEP_PIN', 0), ('Y_DIR_PIN', 0), ('Y_MIN_PIN', 0), + ('Y_MAX_PIN', 0), ('Y_ENABLE_PIN', 0), + ('Y_INVERT_DIR', 1), ('Y_INVERT_MIN', 1), + ('Y_INVERT_MAX', 1), ('Y_INVERT_ENABLE', 1)] + self.pinZKeys = [('Z_STEP_PIN', 0), ('Z_DIR_PIN', 0), ('Z_MIN_PIN', 0), + ('Z_MAX_PIN', 0), ('Z_ENABLE_PIN', 0), + ('Z_INVERT_DIR', 1), ('Z_INVERT_MIN', 1), + ('Z_INVERT_MAX', 1), ('Z_INVERT_ENABLE', 1)] + self.pinEKeys = [('E_STEP_PIN', 0), ('E_DIR_PIN', 0), ('E_ENABLE_PIN', 0), + ('E_INVERT_DIR', 1), ('E_INVERT_ENABLE', 1)] + self.pinMiscKeys = [('USE_INTERNAL_PULLUPS', 1), ('TX_ENABLE_PIN', 0), + ('RX_ENABLE_PIN', 0), ('PS_ON_PIN', 0), + ('PS_MOSFET_PIN', 0), ('STEPPER_ENABLE_PIN', 0), + ('STEPPER_INVERT_ENABLE', 1), ('SD_CARD_DETECT', 0), + ('SD_WRITE_PROTECT', 0), ('DEBUG_LED_PIN', 0)] + + self.labels = {'INCLUDE_ARDUINO': "Include arduino.h header file", + 'USE_INTERNAL_PULLUPS': "Use Internal Pullups", + + 'TX_ENABLE_PIN': "Tx Enable Pin:", + 'RX_ENABLE_PIN': "Rx Enable Pin:", + + 'X_STEP_PIN': "Step Pin:", 'X_DIR_PIN': "Direction Pin:", + 'X_MIN_PIN': "Minimum Pin:", 'X_MAX_PIN': "Maximum Pin:", + 'X_ENABLE_PIN': "Enable Pin:", + 'X_INVERT_DIR': "Invert Direction", + 'X_INVERT_MIN': "Invert Minimum", + 'X_INVERT_MAX': "Invert Maximum", + 'X_INVERT_ENABLE': "Invert Enable", + + 'Y_STEP_PIN': "Step Pin:", 'Y_DIR_PIN': "Direction Pin:", + 'Y_MIN_PIN': "Minimum Pin:", 'Y_MAX_PIN': "Maximum Pin:", + 'Y_ENABLE_PIN': "Enable Pin:", + 'Y_INVERT_DIR': "Invert Direction", + 'Y_INVERT_MIN': "Invert Minimum", + 'Y_INVERT_MAX': "Invert Maximum", + 'Y_INVERT_ENABLE': "Invert Enable", + + 'Z_STEP_PIN': "Step Pin:", 'Z_DIR_PIN': "Direction Pin:", + 'Z_MIN_PIN': "Minimum Pin:", 'Z_MAX_PIN': "Maximum Pin:", + 'Z_ENABLE_PIN': "Enable Pin:", + 'Z_INVERT_DIR': "Invert Direction", + 'Z_INVERT_MIN': "Invert Minimum", + 'Z_INVERT_MAX': "Invert Maximum", + 'Z_INVERT_ENABLE': "Invert Enable", + + 'E_STEP_PIN': "Step Pin:", 'E_DIR_PIN': "Direction Pin:", + 'E_ENABLE_PIN': "Enable Pin:", + 'E_INVERT_DIR': "Invert Direction", + 'E_INVERT_ENABLE': "Invert Enable", + + 'PS_ON_PIN': "PSU On Pin:", + 'PS_MOSFET_PIN': "PSU MOSFET Pin:", + + 'STEPPER_ENABLE_PIN': "Stepper Enable Pin:", + 'STEPPER_INVERT_ENABLE': "Stepper Invert Enable", + + 'SD_CARD_DETECT': "SD Detect Pin:", + 'SD_WRITE_PROTECT': "Write Protect Pin:", + 'DEBUG_LED_PIN': "Debug LED Pin:"} + + labelWidth = 80 + labelWidthM = 100 + self.includeArduino = False + + sz = wx.GridBagSizer() + sz.AddSpacer((10, 10), pos = (0, 0)) + + b = wx.StaticBox(self, wx.ID_ANY, "X Axis") + sbox = wx.StaticBoxSizer(b, wx.VERTICAL) + sbox.AddSpacer((5, 5)) + for k, ctype in self.pinXKeys: + if ctype == 0: + tc = self.addTextCtrl(k, labelWidth, None, self.onTextCtrlPin) + sbox.Add(tc) + else: + cb = self.addCheckBox(k, None, self.onCheckBox) + sbox.Add(cb, 1, wx.LEFT, 30) + + sbox.AddSpacer((5, 5)) + + sz.Add(sbox, pos = (1, 1)) + + b = wx.StaticBox(self, wx.ID_ANY, "Y Axis") + sbox = wx.StaticBoxSizer(b, wx.VERTICAL) + sbox.AddSpacer((5, 5)) + for k, ctype in self.pinYKeys: + if ctype == 0: + tc = self.addTextCtrl(k, labelWidth, None, self.onTextCtrlPin) + sbox.Add(tc) + else: + cb = self.addCheckBox(k, None, self.onCheckBox) + sbox.Add(cb, 1, wx.LEFT, 30) + + sbox.AddSpacer((5, 5)) + + sz.Add(sbox, pos = (1, 3)) + + b = wx.StaticBox(self, wx.ID_ANY, "Z Axis") + sbox = wx.StaticBoxSizer(b, wx.VERTICAL) + sbox.AddSpacer((5, 5)) + for k, ctype in self.pinZKeys: + if ctype == 0: + tc = self.addTextCtrl(k, labelWidth, None, self.onTextCtrlPin) + sbox.Add(tc) + else: + cb = self.addCheckBox(k, None, self.onCheckBox) + sbox.Add(cb, 1, wx.LEFT, 30) + + sbox.AddSpacer((5, 5)) + + sz.Add(sbox, pos = (1, 5)) + + b = wx.StaticBox(self, wx.ID_ANY, "E Axis") + sbox = wx.StaticBoxSizer(b, wx.VERTICAL) + sbox.AddSpacer((5, 5)) + for k, ctype in self.pinEKeys: + if ctype == 0: + tc = self.addTextCtrl(k, labelWidth, None, self.onTextCtrlPin) + sbox.Add(tc) + else: + cb = self.addCheckBox(k, None, self.onCheckBox) + sbox.Add(cb, 1, wx.LEFT, 30) + + sbox.AddSpacer((5, 5)) + + sz.Add(sbox, pos = (1, 7)) + + k = 'USE_INTERNAL_PULLUPS' + cb = self.addCheckBox(k, None, self.onCheckBox) + sz.Add(cb, pos = (3, 1), flag = wx.ALIGN_CENTER_HORIZONTAL) + + k = 'TX_ENABLE_PIN' + tc = self.addTextCtrl(k, labelWidthM, None, self.onTextCtrlPin) + sz.Add(tc, pos = (5, 1)) + + k = 'RX_ENABLE_PIN' + tc = self.addTextCtrl(k, labelWidthM, None, self.onTextCtrlPin) + sz.Add(tc, pos = (6, 1)) + + k = 'PS_ON_PIN' + tc = self.addTextCtrl(k, labelWidthM, None, self.onTextCtrlPin) + sz.Add(tc, pos = (3, 3)) + + k = 'PS_MOSFET_PIN' + tc = self.addTextCtrl(k, labelWidthM, None, self.onTextCtrlPin) + sz.Add(tc, pos = (4, 3)) + + k = 'STEPPER_ENABLE_PIN' + tc = self.addTextCtrl(k, labelWidthM, None, self.onTextCtrlPin) + sz.Add(tc, pos = (6, 3)) + + k = 'STEPPER_INVERT_ENABLE' + cb = self.addCheckBox(k, None, self.onCheckBox) + sz.Add(cb, pos = (7, 3), flag = wx.ALIGN_CENTER_HORIZONTAL) + + k = 'SD_CARD_DETECT' + tc = self.addTextCtrl(k, labelWidthM, None, self.onTextCtrlPin) + sz.Add(tc, pos = (3, 5)) + + k = 'SD_WRITE_PROTECT' + tc = self.addTextCtrl(k, labelWidthM, None, self.onTextCtrlPin) + sz.Add(tc, pos = (4, 5)) + + k = 'DEBUG_LED_PIN' + tc = self.addTextCtrl(k, labelWidthM, None, self.onTextCtrlPin) + sz.Add(tc, pos = (3, 7)) + + k = 'INCLUDE_ARDUINO' + cb = self.addCheckBox(k, None, self.onCheckBoxArduino) + sz.Add(cb, pos = (5, 7), flag = wx.ALIGN_CENTER_HORIZONTAL) + + self.SetSizer(sz) + + def revalidatePins(self): + for k in self.textControls.keys(): + self.validatePin(self.textControls[k]) + self.parent.revalidatePins(self.includeArduino) + + def onCheckBoxArduino(self, evt): + self.assertModified(True) + cb = evt.GetEventObject() + self.includeArduino = cb.IsChecked() + self.revalidatePins() + evt.Skip() + + def setIncludeArduino(self, flag): + self.includeArduino = flag + + def insertValues(self, cfgValues): + self.assertValid(True) + for k in self.fieldValid.keys(): + self.fieldValid[k] = True + for k in self.textControls.keys(): + if k in cfgValues.keys(): + self.textControls[k].SetValue(cfgValues[k]) + else: + self.textControls[k].SetValue("") + + for k in self.checkBoxes.keys(): + if k in cfgValues.keys() and cfgValues[k]: + self.checkBoxes[k].SetValue(True) + elif (k == 'INCLUDE_ARDUINO') and self.includeArduino: + self.checkBoxes[k].SetValue(True) + else: + self.checkBoxes[k].SetValue(False) + self.revalidatePins() + self.assertModified(False) + + def saveValues(self, fp): + self.assertModified(False) + fp.write( +"\n/***************************************************************************\\\n\ +* *\n\ +* 3. PINOUTS *\n\ +* *\n\ +\\***************************************************************************/\n") + + if self.includeArduino: + fp.write("\n#include \"arduino.h\"\n\n") + + for k, cttype in self.pinXKeys + self.pinYKeys + self.pinZKeys + \ + self.pinEKeys + self.pinMiscKeys: + if cttype == 1: + if self.checkBoxes[k].IsChecked(): + fp.write(defineBoolFormat % k) + + else: + v = self.textControls[k].GetValue() + if v != "": + fp.write(defineValueFormat % (k, v)) + + +class SensorsPage(wx.Panel, Page): + def __init__(self, parent, nb, idPg): + wx.Panel.__init__(self, nb, wx.ID_ANY) + Page.__init__(self) + self.parent = parent + self.id = idPg + + self.includeArduino = False + + self.sensorTypeKeys = ['TEMP_MAX6675', 'TEMP_THERMISTOR', 'TEMP_AD595', + 'TEMP_PT100', 'TEMP_INTERCOM'] + self.sensorType = {'TEMP_MAX6675': 'TT_MAX6675', + 'TEMP_THERMISTOR': 'TT_THERMISTOR', + 'TEMP_AD595': 'TT_AD595', + 'TEMP_PT100': 'TT_PT100', + 'TEMP_INTERCOM': 'TT_INTERCOM'} + self.labels = {'TEMP_HYSTERESIS': "Temperature hysteresis:", + 'TEMP_RESIDENCY_TIME': "Temperature residency time:", + 'TEMP_EWMA': "Temperature EMWA:", + 'TEMP_MAX6675': "MAX6675", + 'TEMP_THERMISTOR': "Thermistor", + 'TEMP_AD595': 'AD595', + 'TEMP_PT100': "PT100", 'TEMP_INTERCOM': "Intercom"} + + labelWidth = 160 + + sz = wx.GridBagSizer() + sz.AddSpacer((30, 30), pos = (0, 0)) + + self.sensors = [] + + b = wx.StaticBox(self, wx.ID_ANY, "Sensor Types") + sbox = wx.StaticBoxSizer(b, wx.VERTICAL) + sbox.AddSpacer((5, 5)) + ht = None + if 'TEMP_TYPES' in helpText.keys(): + ht = helpText['TEMP_TYPES'] + for k in self.sensorTypeKeys: + cb = self.addCheckBox(k, ht, self.onCheckBoxSensorType) + sbox.Add(cb, 1, wx.LEFT + wx.RIGHT, 10) + sbox.AddSpacer((5, 5)) + + sz.Add(sbox, pos = (1, 1), span = (5, 1)) + + k = 'TEMP_HYSTERESIS' + tc = self.addTextCtrl(k, labelWidth, None, self.onTextCtrlInteger) + sz.Add(tc, pos = (1, 3)) + + k = 'TEMP_RESIDENCY_TIME' + tc = self.addTextCtrl(k, labelWidth, None, self.onTextCtrlInteger) + sz.Add(tc, pos = (3, 3)) + + k = 'TEMP_EWMA' + tc = self.addTextCtrl(k, labelWidth, None, self.onTextCtrlEWMA) + sz.Add(tc, pos = (5, 3)) + + self.lb = SensorList(self) + sz.Add(self.lb, pos = (7, 1), span = (1, 3)) + + bsz = wx.BoxSizer(wx.VERTICAL) + self.bAdd = wx.Button(self, wx.ID_ANY, "Add", size = BSIZESMALL) + self.Bind(wx.EVT_BUTTON, self.doAdd, self.bAdd) + self.bAdd.Enable(False) + if 'ADDSENSOR' in helpText.keys(): + self.bAdd.SetToolTipString(helpText['ADDSENSOR']) + + bsz.Add(self.bAdd) + + bsz.AddSpacer((10, 10)) + self.bDelete = wx.Button(self, wx.ID_ANY, "Delete", size = BSIZESMALL) + self.bDelete.Enable(False) + self.Bind(wx.EVT_BUTTON, self.doDelete, self.bDelete) + bsz.Add(self.bDelete) + if 'DELSENSOR' in helpText.keys(): + self.bDelete.SetToolTipString(helpText['DELSENSOR']) + + sz.Add(bsz, pos = (7, 4)) + + self.SetSizer(sz) + + def onCheckBoxSensorType(self, evt): + self.assertModified(True) + + ct = 0 + for tt in self.sensorTypeKeys: + if self.checkBoxes[tt].IsChecked(): + ct += 1 + + if ct == 0: + self.bAdd.Enable(False) + else: + self.bAdd.Enable(True) + + evt.Skip() + + def onTextCtrlEWMA(self, evt): + self.assertModified(True) + tc = evt.GetEventObject() + name = tc.GetName() + w = tc.GetValue().strip() + if w == "": + valid = True + else: + m = reFloat.match(w) + if m: + v = float(w) + if v < 0.1 or v > 1.0: + valid = False + else: + valid = True + else: + valid = False + + self.setFieldValidity(name, valid) + + if valid: + tc.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) + else: + tc.SetBackgroundColour("pink") + tc.Refresh() + evt.Skip() + + def revalidatePins(self, flag): + self.includeArduino = flag + + tableValid = True + for i in range(len(self.sensors)): + pn = self.sensors[i][2] + if pn == "": + valid = False + else: + m = reInteger.match(pn) + if m: + valid = True + else: + if self.includeArduino: + m = rePin.match(pn) + if m: + valid = True + else: + valid = False + else: + valid = False + self.lb.setItemValidity(i, valid) + if not valid: + tableValid = False + + self.setFieldValidity('SENSORS', tableValid) + + def setItemSelected(self, n): + self.selection = n + if n is None: + self.bDelete.Enable(False) + else: + self.bDelete.Enable(True) + + def doAdd(self, evt): + sl = [] + for tt in self.sensorTypeKeys: + if self.checkBoxes[tt].IsChecked(): + sl.append(self.sensorType[tt]) + + nm = [] + for s in self.sensors: + nm.append(s[0]) + + dlg = AddSensorDlg(self, nm, sl, self.includeArduino) + rc = dlg.ShowModal() + if rc == wx.ID_OK: + tt = dlg.getValues() + + dlg.Destroy() + + if rc != wx.ID_OK: + return + + self.sensors.append(tt) + self.lb.updateList(self.sensors) + self.limitSensorTypeControls() + + def doDelete(self, evt): + if self.selection is None: + return + + self.assertModified(True) + + del self.sensors[self.selection] + self.lb.updateList(self.sensors) + self.limitSensorTypeControls() + + def limitSensorTypeControls(self): + using = {} + for s in self.sensors: + using[s[1]] = True + + for k in self.sensorTypeKeys: + self.checkBoxes[k].Enable(True) + + for tt in using.keys(): + if not tt.startswith("TT_"): + continue + + k = "TEMP_" + tt[3:] + if k in self.sensorTypeKeys: + self.checkBoxes[k].Enable(False) + + def insertValues(self, cfgValues): + for k in self.textControls.keys(): + if k in cfgValues.keys(): + self.textControls[k].SetValue(cfgValues[k]) + else: + self.textControls[k].SetValue("") + + for k in self.checkBoxes.keys(): + if k in cfgValues.keys() and cfgValues[k]: + self.checkBoxes[k].SetValue(True) + else: + self.checkBoxes[k].SetValue(False) + + ct = 0 + for k in self.sensorTypeKeys: + if self.checkBoxes[k].IsChecked(): ct += 1 + self.bAdd.Enable(ct != 0) + + self.assertModified(False) + + def setSensors(self, sensors): + self.sensors = sensors + self.lb.updateList(self.sensors) + self.revalidatePins(self.includeArduino) + self.limitSensorTypeControls() + + def saveValues(self, fp): + self.assertModified(False) + fp.write( +"\n/***************************************************************************\\\n\ +* *\n\ +* 4. TEMPERATURE SENSORS *\n\ +* *\n\ +\\***************************************************************************/\n") + + for k in self.checkBoxes.keys(): + if self.checkBoxes[k].IsChecked(): + fp.write(defineBoolFormat % k) + + for k in self.textControls.keys(): + v = self.textControls[k].GetValue() + if v != "": + fp.write(defineValueFormat % (k, v)) + + for s in self.sensors: + sstr = ",".join(s) + fp.write("DEFINE_TEMP_SENSOR(" + sstr + ")\n") + + +class SensorList(wx.ListCtrl): + def __init__(self, parent): + self.parent = parent + self.currentItem = None + wx.ListCtrl.__init__(self, parent, wx.ID_ANY, size = (495 + 4, 100), + style = wx.LC_REPORT | wx.LC_VIRTUAL | wx.LC_HRULES | wx.LC_VRULES) + + self.valid = [] + + self.InsertColumn(0, "Name") + self.InsertColumn(1, "Sensor Type") + self.InsertColumn(2, "Pin") + self.InsertColumn(3, "Additional") + self.SetColumnWidth(0, 55) + self.SetColumnWidth(1, 105) + self.SetColumnWidth(2, 55) + self.SetColumnWidth(3, 280) + + self.SetItemCount(0) + + self.attr2 = wx.ListItemAttr() + self.attr2.SetBackgroundColour("light blue") + self.attr3 = wx.ListItemAttr() + self.attr3.SetBackgroundColour("pink") + + self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected) + self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnItemDeselected) + + def updateList(self, sensorList): + self.sensorList = sensorList + self.valid = [True] * len(sensorList) + self.currentItem = None + self.parent.setItemSelected(None) + i = self.GetFirstSelected() + while i != -1: + self.Select(i, False) + i = self.GetFirstSelected() + + self.SetItemCount(len(sensorList)) + + def setItemValidity(self, i, flag = False): + if i < 0 or i >= len(self.sensorList): + return + + self.valid[i] = flag + self.Refresh() + + def OnItemSelected(self, event): + self.currentItem = event.m_itemIndex + self.parent.setItemSelected(self.currentItem) + + def OnItemDeselected(self, event): + self.currentItem = None + self.parent.setItemSelected(None) + + def getColumnText(self, index, col): + item = self.GetItem(index, col) + return item.GetText() + + def OnGetItemText(self, item, col): + if item < 0 or item >= len(self.sensorList): + return "Error - no sensors" + + s = self.sensorList[item] + + if col == 0: + return s[0] + elif col == 1: + return s[1] + elif col == 2: + return s[2] + elif len(s) == 3: + return "" + else: + return s[3] + + def OnGetItemAttr(self, item): + if not self.valid[item]: + return self.attr3 + + if item % 2 == 1: + return self.attr2 + else: + return None + + +class AddSensorDlg(wx.Dialog): + def __init__(self, parent, names, sl, arduinoFlag): + wx.Dialog.__init__(self, parent, wx.ID_ANY, "Add Temperature Sensor", + size = (400, 204)) + self.Bind(wx.EVT_CLOSE, self.onCancel) + + self.names = names + self.arduinoFlag = arduinoFlag + + self.nameValid = False + self.pinValid = False + + sz = wx.BoxSizer(wx.VERTICAL) + + csz = wx.GridBagSizer() + csz.AddSpacer((10, 10), pos = (0, 0)) + + + b = wx.StaticBox(self, wx.ID_ANY, "Sensor Type:") + sbox = wx.StaticBoxSizer(b, wx.VERTICAL) + sbox.AddSpacer((5, 5)) + style = wx.RB_GROUP + self.rbs = {} + for k in sl: + rb = wx.RadioButton(self, wx.ID_ANY, k, style = style) + self.rbs[k] = rb + self.Bind(wx.EVT_RADIOBUTTON, self.onSensorType, rb) + if k in helpText.keys(): + hts = helpText[k] + else: + hts = "" + rb.SetToolTipString(hts) + style = 0 + + sbox.Add(rb, 1, wx.LEFT + wx.RIGHT, 16) + sbox.AddSpacer((5, 5)) + + csz.Add(sbox, pos = (1, 3)) + + + vsz = wx.BoxSizer(wx.VERTICAL) + vsz.AddSpacer((10, 10)) + + lsz = wx.BoxSizer(wx.HORIZONTAL) + st = wx.StaticText(self, wx.ID_ANY, "Sensor Name:", size = (80, -1), + style = wx.ALIGN_RIGHT) + lsz.Add(st) + + self.tcName = wx.TextCtrl(self, wx.ID_ANY, "") + self.tcName.SetBackgroundColour("pink") + self.tcName.Bind(wx.EVT_TEXT, self.onNameEntry) + lsz.Add(self.tcName) + self.tcName.SetToolTipString("Enter a unique name for this sensor") + + vsz.Add(lsz) + + lsz = wx.BoxSizer(wx.HORIZONTAL) + st = wx.StaticText(self, wx.ID_ANY, "Pin:", size = (80, -1), + style = wx.ALIGN_RIGHT) + lsz.Add(st) + + self.tcPin = wx.TextCtrl(self, wx.ID_ANY, "", style = wx.TE_RIGHT) + self.tcPin.Bind(wx.EVT_TEXT, self.onPinEntry) + self.tcPin.SetBackgroundColour("pink") + lsz.Add(self.tcPin) + self.tcPin.SetToolTipString("Enter a pin number/name for this sensor") + + vsz.Add(lsz) + + lsz = wx.BoxSizer(wx.HORIZONTAL) + st = wx.StaticText(self, wx.ID_ANY, "Additional:", size = (80, -1), + style = wx.ALIGN_RIGHT) + lsz.Add(st) + + self.tcAddtl = wx.TextCtrl(self, wx.ID_ANY, "") + self.tcAddtl.Bind(wx.EVT_TEXT, self.onAddtlEntry) + self.selectSensorType(sl[0]) + lsz.Add(self.tcAddtl) + self.tcAddtl.SetToolTipString("Enter additional information required by " + "the sensor type") + + vsz.Add(lsz) + csz.Add(vsz, pos = (1, 1)) + + csz.AddSpacer((10, 10), pos = (1, 4)) + + sz.Add(csz) + sz.AddSpacer((30, 30)) + + bsz = wx.BoxSizer(wx.HORIZONTAL) + + self.bSave = wx.Button(self, wx.ID_ANY, "Save", size = BSIZESMALL) + self.bSave.Bind(wx.EVT_BUTTON, self.onSave) + bsz.Add(self.bSave) + self.bSave.Enable(False) + + bsz.AddSpacer(30, 100) + + self.bCancel = wx.Button(self, wx.ID_ANY, "Cancel", size = BSIZESMALL) + self.bCancel.Bind(wx.EVT_BUTTON, self.onCancel) + bsz.Add(self.bCancel) + + sz.Add(bsz, flag = wx.ALIGN_CENTER_HORIZONTAL) + self.SetSizer(sz) + + def onNameEntry(self, evt): + tc = evt.GetEventObject() + w = tc.GetValue().strip() + if w == "": + self.nameValid = False + else: + if w in self.names: + self.nameValid = False + else: + self.nameValid = True + + if self.nameValid: + tc.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) + else: + tc.SetBackgroundColour("pink") + tc.Refresh() + + self.checkDlgValidity() + evt.Skip() + + def onPinEntry(self, evt): + tc = evt.GetEventObject() + self.validatePin(tc) + self.checkDlgValidity() + evt.Skip() + + def validatePin(self, tc): + w = tc.GetValue().strip() + if w == "": + self.pinValid = False + else: + m = reInteger.match(w) + if m: + self.pinValid = True + else: + if self.arduinoFlag: + m = rePin.match(w) + if m: + self.pinValid = True + else: + self.pinValid = False + else: + self.pinValid = False + + if self.pinValid: + tc.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) + else: + tc.SetBackgroundColour("pink") + tc.Refresh() + + def checkDlgValidity(self): + if self.nameValid and self.pinValid: + self.bSave.Enable(True) + else: + self.bSave.Enable(False) + + def onAddtlEntry(self, evt): + evt.Skip() + + def selectSensorType(self, lbl): + if lbl == 'TT_THERMISTOR': + self.tcAddtl.Enable(True); + else: + self.tcAddtl.Enable(False); + + def onSensorType(self, evt): + rb = evt.GetEventObject() + label = rb.GetLabel() + + self.selectSensorType(label) + + evt.Skip() + + def getValues(self): + nm = self.tcName.GetValue() + pin = self.tcPin.GetValue() + addtl = self.tcAddtl.GetValue() + + for k in self.rbs: + if self.rbs[k].GetValue(): + stype = k + break + + if stype is None: + stype = "??" + + if stype in ['TT_THERMISTOR']: + return (nm, stype, pin, addtl) + else: + return (nm, stype, pin) + + + def onSave(self, evt): + self.EndModal(wx.ID_OK) + + def onCancel(self, evt): + self.EndModal(wx.ID_CANCEL) + + +class HeatersPage(wx.Panel, Page): + def __init__(self, parent, nb, idPg): + wx.Panel.__init__(self, nb, wx.ID_ANY) + Page.__init__(self) + self.parent = parent + self.id = idPg + + self.includeArduino = False + + self.labels = {'HEATER_SANITY_CHECK': "Heater Sanity Check"} + + sz = wx.GridBagSizer() + sz.AddSpacer((30, 30), pos = (0, 0)) + + self.heaters = [] + + k = 'HEATER_SANITY_CHECK' + cb = self.addCheckBox(k, None, self.onCheckBox) + sz.Add(cb, pos = (3, 1), flag = wx.ALIGN_CENTER_HORIZONTAL) + + self.lb = HeaterList(self) + sz.Add(self.lb, pos = (1, 1), span = (1, 3)) + + bsz = wx.BoxSizer(wx.VERTICAL) + self.bAdd = wx.Button(self, wx.ID_ANY, "Add", size = BSIZESMALL) + self.Bind(wx.EVT_BUTTON, self.doAdd, self.bAdd) + if 'ADDHEATER' in helpText.keys(): + self.bAdd.SetToolTipString(helpText['ADDHEATER']) + + bsz.Add(self.bAdd) + + bsz.AddSpacer((10, 10)) + self.bDelete = wx.Button(self, wx.ID_ANY, "Delete", size = BSIZESMALL) + self.bDelete.Enable(False) + self.Bind(wx.EVT_BUTTON, self.doDelete, self.bDelete) + bsz.Add(self.bDelete) + if 'DELHEATER' in helpText.keys(): + self.bDelete.SetToolTipString(helpText['DELHEATER']) + + sz.Add(bsz, pos = (1, 4)) + + self.SetSizer(sz) + + def revalidatePins(self, flag): + self.includeArduino = flag + + tableValid = True + for i in range(len(self.heaters)): + pn = self.heaters[i][1] + if pn == "": + valid = False + else: + m = reInteger.match(pn) + if m: + valid = True + else: + if self.includeArduino: + m = rePin.match(pn) + if m: + valid = True + else: + valid = False + else: + valid = False + self.lb.setItemValidity(i, valid) + if not valid: + tableValid = False + + self.setFieldValidity('HEATERS', tableValid) + + def setItemSelected(self, n): + self.selection = n + if n is None: + self.bDelete.Enable(False) + else: + self.bDelete.Enable(True) + + def doAdd(self, evt): + nm = [] + for s in self.heaters: + nm.append(s[0]) + + dlg = AddHeaterDlg(self, nm, self.includeArduino) + rc = dlg.ShowModal() + if rc == wx.ID_OK: + ht = dlg.getValues() + + dlg.Destroy() + + if rc != wx.ID_OK: + return + + self.heaters.append(ht) + self.lb.updateList(self.heaters) + self.revalidatePins(self.includeArduino) + self.parent.setHeaters(self.heaters) + + def doDelete(self, evt): + if self.selection is None: + return + + self.assertModified(True) + + del self.heaters[self.selection] + self.lb.updateList(self.heaters) + self.revalidatePins(self.includeArduino) + self.parent.setHeaters(self.heaters) + + def insertValues(self, cfgValues): + for k in self.checkBoxes.keys(): + if k in cfgValues.keys() and cfgValues[k]: + self.checkBoxes[k].SetValue(True) + else: + self.checkBoxes[k].SetValue(False) + + self.assertModified(False) + + def setHeaters(self, heaters): + self.heaters = heaters + self.lb.updateList(self.heaters) + self.revalidatePins(self.includeArduino) + + def saveValues(self, fp): + self.assertModified(False) + fp.write( +"\n/***************************************************************************\\\n\ +* *\n\ +* 5. HEATERS *\n\ +* *\n\ +\\***************************************************************************/\n") + + for k in self.checkBoxes.keys(): + if self.checkBoxes[k].IsChecked(): + fp.write(defineBoolFormat % k) + + names = [] + for s in self.heaters: + names.append(s[0]) + sstr = ",".join(s) + fp.write("DEFINE_HEATER(" + sstr + ")\n") + + for n in names: + if n != n.upper(): + fp.write(defineHeaterFormat % (n.upper(), n)) + + +class HeaterList(wx.ListCtrl): + def __init__(self, parent): + self.parent = parent + self.currentItem = None + wx.ListCtrl.__init__(self, parent, wx.ID_ANY, size = (165 + 4, 100), + style = wx.LC_REPORT | wx.LC_VIRTUAL | wx.LC_HRULES | wx.LC_VRULES) + + self.valid = [] + + self.InsertColumn(0, "Name") + self.InsertColumn(1, "Pin") + self.InsertColumn(2, "PWM") + self.SetColumnWidth(0, 55) + self.SetColumnWidth(1, 55) + self.SetColumnWidth(2, 55) + + self.SetItemCount(0) + + self.attr2 = wx.ListItemAttr() + self.attr2.SetBackgroundColour("light blue") + self.attr3 = wx.ListItemAttr() + self.attr3.SetBackgroundColour("pink") + + self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected) + self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnItemDeselected) + + def updateList(self, heaterList): + self.heaterList = heaterList + self.valid = [True] * len(heaterList) + self.currentItem = None + self.parent.setItemSelected(None) + i = self.GetFirstSelected() + while i != -1: + self.Select(i, False) + i = self.GetFirstSelected() + + self.SetItemCount(len(heaterList)) + + def setItemValidity(self, i, flag = False): + if i < 0 or i >= len(self.heaterList): + return + + self.valid[i] = flag + self.Refresh() + + def OnItemSelected(self, event): + self.currentItem = event.m_itemIndex + self.parent.setItemSelected(self.currentItem) + + def OnItemDeselected(self, event): + self.currentItem = None + self.parent.setItemSelected(None) + + def getColumnText(self, index, col): + item = self.GetItem(index, col) + return item.GetText() + + def OnGetItemText(self, item, col): + if item < 0 or item >= len(self.heaterList): + return "Error - no heaters" + + s = self.heaterList[item] + + if col == 0: + return s[0] + elif col == 1: + return s[1] + elif col == 2: + if s[2] == "1": + return "True" + else: + return "False" + + def OnGetItemAttr(self, item): + if not self.valid[item]: + return self.attr3 + + if item % 2 == 1: + return self.attr2 + else: + return None + + +class AddHeaterDlg(wx.Dialog): + def __init__(self, parent, names, arduinoFlag): + wx.Dialog.__init__(self, parent, wx.ID_ANY, "Add Heater", size = (400, 204)) + self.Bind(wx.EVT_CLOSE, self.onCancel) + + self.names = names + self.arduinoFlag = arduinoFlag + + self.nameValid = False + self.pinValid = False + + sz = wx.BoxSizer(wx.VERTICAL) + gsz = wx.GridBagSizer() + gsz.AddSpacer((20, 20), pos = (0, 0)) + + lsz = wx.BoxSizer(wx.HORIZONTAL) + st = wx.StaticText(self, wx.ID_ANY, "Heater Name:", size = (80, -1), + style = wx.ALIGN_RIGHT) + lsz.Add(st) + + self.tcName = wx.TextCtrl(self, wx.ID_ANY, "") + self.tcName.SetBackgroundColour("pink") + self.tcName.Bind(wx.EVT_TEXT, self.onNameEntry) + lsz.Add(self.tcName) + self.tcName.SetToolTipString("Enter a unique name for this heater") + + gsz.Add(lsz, pos = (1, 1)) + + lsz = wx.BoxSizer(wx.HORIZONTAL) + st = wx.StaticText(self, wx.ID_ANY, "Pin:", size = (80, -1), + style = wx.ALIGN_RIGHT) + lsz.Add(st) + + self.tcPin = wx.TextCtrl(self, wx.ID_ANY, "", style = wx.TE_RIGHT) + self.tcPin.Bind(wx.EVT_TEXT, self.onPinEntry) + self.tcPin.SetBackgroundColour("pink") + lsz.Add(self.tcPin) + self.tcPin.SetToolTipString("Enter a pin number/name for this heater") + + gsz.Add(lsz, pos = (3, 1)) + + self.cbPwm = wx.CheckBox(self, wx.ID_ANY, "PWM") + self.cbPwm.SetToolTipString("Help for PWM") + + gsz.AddSpacer((50, 15), pos = (1, 2)) + gsz.Add(self.cbPwm, pos = (1, 3)) + gsz.AddSpacer((20, 20), pos = (4, 4)) + + sz.Add(gsz) + sz.AddSpacer((30, 30)) + + bsz = wx.BoxSizer(wx.HORIZONTAL) + + self.bSave = wx.Button(self, wx.ID_ANY, "Save", size = BSIZESMALL) + self.bSave.Bind(wx.EVT_BUTTON, self.onSave) + bsz.Add(self.bSave) + self.bSave.Enable(False) + + bsz.AddSpacer(30, 100) + + self.bCancel = wx.Button(self, wx.ID_ANY, "Cancel", size = BSIZESMALL) + self.bCancel.Bind(wx.EVT_BUTTON, self.onCancel) + bsz.Add(self.bCancel) + + sz.Add(bsz, flag = wx.ALIGN_CENTER_HORIZONTAL) + self.SetSizer(sz) + + def onNameEntry(self, evt): + tc = evt.GetEventObject() + w = tc.GetValue().strip() + if w == "": + self.nameValid = False + else: + if w in self.names: + self.nameValid = False + else: + self.nameValid = True + + if self.nameValid: + tc.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) + else: + tc.SetBackgroundColour("pink") + tc.Refresh() + + self.checkDlgValidity() + evt.Skip() + + def onPinEntry(self, evt): + tc = evt.GetEventObject() + self.validatePin(tc) + self.checkDlgValidity() + evt.Skip() + + def validatePin(self, tc): + w = tc.GetValue().strip() + if w == "": + self.pinValid = False + else: + m = reInteger.match(w) + if m: + self.pinValid = True + else: + if self.arduinoFlag: + m = rePin.match(w) + if m: + self.pinValid = True + else: + self.pinValid = False + else: + self.pinValid = False + + if self.pinValid: + tc.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) + else: + tc.SetBackgroundColour("pink") + tc.Refresh() + + def checkDlgValidity(self): + if self.nameValid and self.pinValid: + self.bSave.Enable(True) + else: + self.bSave.Enable(False) + + def getValues(self): + nm = self.tcName.GetValue() + pin = self.tcPin.GetValue() + if self.cbPwm.IsChecked(): + pwm = "1" + else: + pwm = "0" + + return (nm, pin, pwm) + + def onSave(self, evt): + self.EndModal(wx.ID_OK) + + def onCancel(self, evt): + self.EndModal(wx.ID_CANCEL) + + +class CommunicationsPage(wx.Panel, Page): + def __init__(self, parent, nb, idPg): + wx.Panel.__init__(self, nb, wx.ID_ANY) + Page.__init__(self) + self.parent = parent + self.id = idPg + self.defaultBaud = '115200' + + self.bauds = ['19200', '38400', '57600', self.defaultBaud] + + self.labels = {'BAUD': "Baud Rate:", 'USB_SERIAL': "USB Serial", + 'XONXOFF': "Use XON/XOFF Flow Control"} + + sz = wx.GridBagSizer() + sz.AddSpacer((20, 40), pos = (0, 0)) + + ch = self.addChoice('BAUD', self.bauds, self.bauds.index(self.defaultBaud), + 60, None, self.onChoice) + sz.Add(ch, pos = (1, 1)) + + k = 'USB_SERIAL' + cb = self.addCheckBox(k, None, self.onCheckBox) + sz.Add(cb, pos = (3, 1)) + + k = 'XONXOFF' + cb = self.addCheckBox(k, None, self.onCheckBox) + sz.Add(cb, pos = (5, 1)) + + self.SetSizer(sz) + + def insertValues(self, cfgValues): + self.assertValid(True) + for k in self.fieldValid.keys(): + self.fieldValid[k] = True + + for k in self.checkBoxes.keys(): + if k in cfgValues.keys() and cfgValues[k]: + self.checkBoxes[k].SetValue(True) + else: + self.checkBoxes[k].SetValue(False) + + self.setChoice('BAUD', cfgValues, self.bauds, self.defaultBaud) + + self.assertModified(False) + + def saveValues(self, fp): + self.assertModified(False) + fp.write( +"\n/***************************************************************************\\\n\ +* *\n\ +* 6. COMMUNICATION *\n\ +* *\n\ +\\***************************************************************************/\n") + + k = 'BAUD' + v = self.choices[k].GetSelection() + if v != "": + fp.write(defineValueFormat % (k, self.bauds[v])) + + for k in self.checkBoxes.keys(): + cb = self.checkBoxes[k] + if cb.IsChecked(): + fp.write(defineBoolFormat % k) + + +class MiscellaneousPage(wx.Panel, Page): + def __init__(self, parent, nb, idPg): + wx.Panel.__init__(self, nb, wx.ID_ANY) + Page.__init__(self) + self.parent = parent + self.id = idPg + + self.labels = {'MOTHERBOARD': "Motherboard", 'F_CPU': "CPU Clock Rate:", + 'EECONFIG': "Enable EEPROM Storage", + 'DEBUG': "Turn on debugging", 'BANG_BANG': "Enable", + 'BANG_BANG_ON': "On PWM level:", + 'BANG_BANG_OFF': "Off PWM level:", + 'MOVEBUFFER_SIZE': "Move buffer size:", + 'DC_EXTRUDER': "Heater:", 'DC_EXTRUDER_PWM': "PWM:", + 'USE_WATCHDOG': "Use the watchdog timer", + 'REFERENCE': "Analog Reference:", + 'STEP_INTERRUPT_INTERRUPTIBLE': "STEP Interrupt", + 'TH_COUNT': "Temperature History size:", + 'FAST_PWM': "Fast PWM", 'ENDSTOP_STEPS': "Endstop steps:", + 'CANNED_CYCLE': "Canned Cycle:"} + + self.prefixKeys = ['MOTHERBOARD', 'F_CPU'] + + self.defaultClock = '16000000' + self.clocks = ['8000000', self.defaultClock, '20000000'] + self.heaterNameNone = "" + self.heaterNames = [self.heaterNameNone] + self.processors = [] + + self.defaultRef = 'REFERENCE_AVCC' + self.references = [self.defaultRef, 'REFERENCE_AREF', + 'REFERENCE_1V1', 'REFERENCE_2V56'] + + sz = wx.GridBagSizer() + sz.AddSpacer((20, 40), pos = (0, 0)) + sz.AddSpacer((40, 40), pos = (0, 2)) + sz.AddSpacer((40, 40), pos = (0, 4)) + sz.AddSpacer((20, 30), pos = (1, 0)) + sz.AddSpacer((20, 30), pos = (2, 0)) + sz.AddSpacer((20, 30), pos = (3, 0)) + sz.AddSpacer((20, 30), pos = (4, 0)) + sz.AddSpacer((20, 30), pos = (5, 0)) + sz.AddSpacer((20, 30), pos = (6, 0)) + + labelWidth = 140 + + k = 'MOTHERBOARD' + cb = self.addCheckBox(k, None, self.onCheckBox) + sz.Add(cb, pos = (1, 1)) + + k = 'EECONFIG' + cb = self.addCheckBox(k, None, self.onCheckBox) + sz.Add(cb, pos = (2, 1)) + + k = 'DEBUG' + cb = self.addCheckBox(k, None, self.onCheckBox) + sz.Add(cb, pos = (3, 1)) + + k = 'USE_WATCHDOG' + cb = self.addCheckBox(k, None, self.onCheckBox) + sz.Add(cb, pos = (4, 1)) + + k = 'STEP_INTERRUPT_INTERRUPTIBLE' + cb = self.addCheckBox(k, None, self.onCheckBox) + sz.Add(cb, pos = (5, 1)) + + k = 'FAST_PWM' + cb = self.addCheckBox(k, None, self.onCheckBox) + sz.Add(cb, pos = (6, 1)) + + b = wx.StaticBox(self, wx.ID_ANY, "BANG BANG Bed Control") + sbox = wx.StaticBoxSizer(b, wx.VERTICAL) + sbox.AddSpacer((5, 5)) + + k = 'BANG_BANG' + cb = self.addCheckBox(k, None, self.onCheckBox) + sbox.Add(cb, 1, wx.LEFT, 60) + sbox.AddSpacer((5, 20)) + + k = 'BANG_BANG_ON' + tc = self.addTextCtrl(k, 80, None, self.onTextCtrlInteger) + sbox.Add(tc) + sbox.AddSpacer((5, 5)) + + k = 'BANG_BANG_OFF' + tc = self.addTextCtrl(k, 80, None, self.onTextCtrlInteger) + sbox.Add(tc) + sbox.AddSpacer((5, 5)) + + sz.Add(sbox, pos = (7, 1), flag = wx.ALIGN_CENTER_HORIZONTAL) + + k = 'F_CPU' + ch = self.addChoice(k, self.clocks, self.clocks.index(self.defaultClock), + labelWidth, None, self.onChoice) + sz.Add(ch, pos = (1, 3)) + + k = 'REFERENCE' + ch = self.addChoice(k, self.references, self.references.index(self.defaultRef), + labelWidth, None, self.onChoice) + sz.Add(ch, pos = (2, 3)) + + b = wx.StaticBox(self, wx.ID_ANY, "DC Motor Extruder") + sbox = wx.StaticBoxSizer(b, wx.VERTICAL) + sbox.AddSpacer((5, 5)) + + k = 'DC_EXTRUDER' + ch = self.addChoice(k, self.heaterNames, 0, 60, None, self.onChoice) + sbox.Add(ch) + sbox.AddSpacer((5, 5)) + + k = 'DC_EXTRUDER_PWM' + tc = self.addTextCtrl(k, 60, None, self.onTextCtrlInteger) + sbox.Add(tc) + sbox.AddSpacer((5, 5)) + + sz.Add(sbox, pos = (7, 3), flag = wx.ALIGN_CENTER_HORIZONTAL) + + k = 'MOVEBUFFER_SIZE' + tc = self.addTextCtrl(k, labelWidth, None, self.onTextCtrlInteger) + sz.Add(tc, pos = (1, 5)) + + k = 'TH_COUNT' + tc = self.addTextCtrl(k, labelWidth, None, self.onTextCtrlInteger) + sz.Add(tc, pos = (2, 5)) + + k = 'ENDSTOP_STEPS' + tc = self.addTextCtrl(k, labelWidth, None, self.onTextCtrlInteger) + sz.Add(tc, pos = (3, 5)) + + k = 'CANNED_CYCLE' + tc = self.addTextCtrl(k, labelWidth, None, self.onTextCtrl) + sz.Add(tc, pos = (4, 5)) + + b = wx.StaticBox(self, wx.ID_ANY, "Processor Type(s):") + sbox = wx.StaticBoxSizer(b, wx.VERTICAL) + sbox.AddSpacer((125, 5)) + + ht = "Choose the processor(s) this configuration will work with" + for k in supportedCPUs: + cb = self.addCheckBox(k, ht, self.onCheckBox) + sbox.Add(cb) + sbox.AddSpacer((5, 5)) + + sbox.AddSpacer((5, 5)) + sz.Add(sbox, pos = (6, 5), span = (3, 1), flag = wx.ALIGN_CENTER_HORIZONTAL) + + self.SetSizer(sz) + + def setHeaters(self, hlist, cfgValues = None): + k = 'DC_EXTRUDER' + if cfgValues and k in cfgValues.keys(): + currentChoice = cfgValues[k][len("HEATER_"):] + + else: + v = self.choices[k].GetSelection() + currentChoice = self.heaterNames[v] + h = [s[0] for s in hlist] + self.heaterNames = [self.heaterNameNone] + h + self.choices[k].Clear() + for h in self.heaterNames: + self.choices[k].Append(h) + + try: + v = self.heaterNames.index(currentChoice) + except: + v = 0 + self.choices[k].SetSelection(v) + + def setProcessors(self, plist): + self.processors = plist + for p in supportedCPUs: + if p in self.processors: + self.checkBoxes[p].SetValue(True) + else: + self.checkBoxes[p].SetValue(False) + + def insertValues(self, cfgValues): + self.assertValid(True) + for k in self.fieldValid.keys(): + self.fieldValid[k] = True + + for k in self.checkBoxes.keys(): + if k in cfgValues.keys() and cfgValues[k]: + self.checkBoxes[k].SetValue(True) + else: + self.checkBoxes[k].SetValue(False) + + for k in self.textControls.keys(): + if k in cfgValues.keys(): + self.textControls[k].SetValue(cfgValues[k]) + else: + self.textControls[k].SetValue("") + + self.setChoice('F_CPU', cfgValues, self.clocks, self.defaultClock) + self.setChoice('REFERENCE', cfgValues, self.references, self.defaultRef) + + self.assertModified(False) + + def savePrefixValues(self, fp): + if len(self.processors) != 0: + nest = 0 + for i in self.processors: + fp.write("%s#ifndef __AVR_%s__\n" % (" " * nest, i)) + nest += 1 + fp.write("%s#error incorrect cpu type in Makefile!\n" % (" " * nest)) + for i in self.processors: + nest -= 1 + fp.write("%s#endif\n" % (" " * nest)) + + fp.write("\n\n") + + for k in self.checkBoxes.keys(): + if k not in self.prefixKeys: + continue + + cb = self.checkBoxes[k] + if cb.IsChecked(): + fp.write(defineBoolFormat % k) + + for k in self.textControls.keys(): + if k not in self.prefixKeys: + continue + + v = self.textControls[k].GetValue() + if v != "": + fp.write(defineValueFormat % (k, v)) + + k = 'F_CPU' + v = self.choices[k].GetSelection() + fp.write(defineULFormat % (k, self.clocks[v])) + + def saveValues(self, fp): + self.assertModified(False) + fp.write( +"\n/***************************************************************************\\\n\ +* *\n\ +* 7. MISCELLANEOUS *\n\ +* *\n\ +\\***************************************************************************/\n") + for k in self.checkBoxes.keys(): + if k in self.prefixKeys: + continue + + cb = self.checkBoxes[k] + if cb.IsChecked(): + fp.write(defineBoolFormat % k) + + for k in self.textControls.keys(): + if k in self.prefixKeys: + continue + + v = self.textControls[k].GetValue() + if v != "": + fp.write(defineValueFormat % (k, v)) + + k = 'REFERENCE' + v = self.choices[k].GetSelection() + fp.write(defineValueFormat % (k, self.references[v])) + + k = 'DC_EXTRUDER' + v = self.choices[k].GetSelection() + if self.heaterNames[v] != self.heaterNameNone: + fp.write(defineDCExtruderFormat % (k, self.heaterNames[v])) class MyFrame(wx.Frame): - def __init__(self): - self.reDefine = re.compile("\s*#define\s+(\S+)\s+(\S+)") - self.reDefQS = re.compile("\s*#define\s+(\S+)\s+(\"[^\"]*\")") - self.reDefTS = re.compile("\s*#define\s+(DEFINE_TEMP_SENSOR\\([^)]*\\))") - self.reDefHT = re.compile("\s*#define\s+(DEFINE_HEATER\\([^)]*\\))") - self.reDefBool = re.compile("\s*#define\s+(\S+)\s+") - - self.t = 0 - self.seq = 1 wx.Frame.__init__(self, None, -1, "Teacup Firmware Configurator", - size = (450, 600)) + size = (980, 450)) self.Bind(wx.EVT_CLOSE, self.onClose) self.cfgValues = {} + self.heaters = [] + self.dir = "." panel = wx.Panel(self, -1) sz = wx.BoxSizer(wx.HORIZONTAL) bsz = wx.BoxSizer(wx.VERTICAL) - b = wx.Button(panel, wx.ID_ANY, "Load\nConfig", size = BSIZE) - panel.Bind(wx.EVT_BUTTON, self.onLoadConfig, b) - bsz.Add(b) + self.bLoadConfig = wx.Button(panel, wx.ID_ANY, "Load\nConfig", size = BSIZE) + panel.Bind(wx.EVT_BUTTON, self.onLoadConfig, self.bLoadConfig) + bsz.Add(self.bLoadConfig) + self.bLoadConfig.SetToolTipString("Choose a configuration file and load its contents") - style = wx.SUNKEN_BORDER | wx.WANTS_CHARS - agwStyle = CT.TR_HAS_BUTTONS | CT.TR_HAS_VARIABLE_ROW_HEIGHT | CT.TR_ALIGN_WINDOWS + bsz.AddSpacer((5, 5)) - self.tree = CT.CustomTreeCtrl(panel, wx.ID_ANY, wx.DefaultPosition, - (100, 100), style, agwStyle) - self.tree.Bind(CT.EVT_TREE_ITEM_CHECKED, self.onItemChecked) - self.tree.Bind(CT.EVT_TREE_ITEM_GETTOOLTIP, self.onToolTip) - self.root = self.tree.AddRoot("Teacup Configuration") + self.bSaveConfig = wx.Button(panel, wx.ID_ANY, "Save\nConfig", size = BSIZE) + panel.Bind(wx.EVT_BUTTON, self.onSaveConfig, self.bSaveConfig) + bsz.Add(self.bSaveConfig) + self.bSaveConfig.SetToolTipString("Save the current configuration into a C header file") - self.stMech = self.mechSubTree(self.tree, self.root) - self.stAcc = self.accSubTree(self.tree, self.root) + self.nb = wx.Notebook(panel, wx.ID_ANY, size = (21, 21), style = wx.BK_DEFAULT) - pins = self.tree.AppendItem(self.root, "3. Pinouts", - data = "Pinouts") - sensors = self.tree.AppendItem(self.root, "4. Temperature Sensors", - data = "Sensors") - heaters = self.tree.AppendItem(self.root, "5. Heaters", - data = "Heaters") - comm = self.tree.AppendItem(self.root, "6. Communications", - data = "Communications") - misc = self.tree.AppendItem(self.root, "7. Miscellaneous", - data = "Miscellaneous") + self.pages = [] + self.titles = [] + self.pageModified = [] + self.pageValid = [] - sz.Add(self.tree, 1, wx.EXPAND + wx.ALL, 5) + self.pgMech = MechanicalPage(self, self.nb, len(self.pages)) + text = "Mechanical" + self.nb.AddPage(self.pgMech, text) + self.pages.append(self.pgMech) + self.titles.append(text) + self.pageModified.append(False) + self.pageValid.append(True) + + self.pgAcc = AccelerationPage(self, self.nb, len(self.pages)) + text = "Acceleration" + self.nb.AddPage(self.pgAcc, text) + self.pages.append(self.pgAcc) + self.titles.append(text) + self.pageModified.append(False) + self.pageValid.append(True) + + self.pgPins = PinoutsPage(self, self.nb, len(self.pages)) + text = "Pinouts" + self.nb.AddPage(self.pgPins, text) + self.pages.append(self.pgPins) + self.titles.append(text) + self.pageModified.append(False) + self.pageValid.append(True) + + self.pgSensors = SensorsPage(self, self.nb, len(self.pages)) + text = "Temperature Sensors" + self.nb.AddPage(self.pgSensors, text) + self.pages.append(self.pgSensors) + self.titles.append(text) + self.pageModified.append(False) + self.pageValid.append(True) + + self.pgHeaters = HeatersPage(self, self.nb, len(self.pages)) + text = "Heaters" + self.nb.AddPage(self.pgHeaters, text) + self.pages.append(self.pgHeaters) + self.titles.append(text) + self.pageModified.append(False) + self.pageValid.append(True) + + self.pgCommunications = CommunicationsPage(self, self.nb, len(self.pages)) + text = "Communications" + self.nb.AddPage(self.pgCommunications, text) + self.pages.append(self.pgCommunications) + self.titles.append(text) + self.pageModified.append(False) + self.pageValid.append(True) + + self.pgMiscellaneous = MiscellaneousPage(self, self.nb, len(self.pages)) + text = "Miscellaneous" + self.nb.AddPage(self.pgMiscellaneous, text) + self.pages.append(self.pgMiscellaneous) + self.titles.append(text) + self.pageModified.append(False) + self.pageValid.append(True) + + sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5) sz.Add(bsz, 0, wx.ALL, 5) panel.SetSizer(sz) - def mechSubTree(self, tree, root): - spmKeys = ['SMX', 'SMY', 'SMZ', 'SME'] + def buildHeaterPage(self, nb): + pg = wx.Panel(nb, wx.ID_ANY) + return pg - mfrKeys = ['MFRX', 'MFRY', 'MFRZ', 'MFRE'] + def buildCommPage(self, nb): + pg = wx.Panel(nb, wx.ID_ANY) + return pg - msrKeys = ['MSRX', 'MSRY', 'MSRZ'] + def buildMiscPage(self, nb): + pg = wx.Panel(nb, wx.ID_ANY) + return pg - eclKeys = ['ECX', 'ECY', 'ECZ'] + def assertModified(self, pg, flag = True): + self.pageModified[pg] = flag + self.modifyTab(pg) - minmaxKeys = ['MINX', 'MAXX', 'MINY', 'MAXY', 'MINZ', 'MAXZ'] + def assertValid(self, pg, flag = True): + self.pageValid[pg] = flag + self.modifyTab(pg) - mechLabels = {'SPM': "Steps/Meter", - 'SMX': "X:", 'SMY': "Y:", 'SMZ': "Z:", 'SME' : "E:", - 'MFR': "Max Feed Rate", - 'MFRX': "X:", 'MFRY': "Y:", 'MFRZ': "Z:", 'MFRE': "E:", - 'MSR': "Search Feed Rate", - 'MSRX': "X:", 'MSRY': "Y:", 'MSRZ': "Z:", - 'ECL': "Endstop Clearance", - 'ECX': "X:", 'ECY': "Y:", 'ECZ': "Z:", - 'MINMAX': "Minimum/Maximum X/Y/Z", - 'MINX': "Min X:", 'MAXX': "Max X:", 'MINY': "Min Y:", - 'MAXY': "Max Y:", 'MINZ': "Min Z:", 'MAXZ': "Max Z:", - 'ABSE': "Absolute E Coordinates"} - - self.mechCfgKeys = {'SMX': "STEPS_PER_M_X", 'SMY': "STEPS_PER_M_Y", - 'SMZ': "STEPS_PER_M_Z", 'SME' : "STEPS_PER_M_E", - 'MFRX': "MAXIMUM_FEEDRATE_X", - 'MFRY': "MAXIMUM_FEEDRATE_Y", - 'MFRZ': "MAXIMUM_FEEDRATE_Z", - 'MFRE': "MAXIMUM_FEEDRATE_E", - 'MSRX': "SEARCH_FEEDRATE_X", - 'MSRY': "SEARCH_FEEDRATE_Y", - 'MSRZ': "SEARCH_FEEDRATE_Z", - 'ECX': "ENDSTOP_CLEARANCE_X", - 'ECY': "ENDSTOP_CLEARANCE_Y", - 'ECZ': "ENDSTOP_CLEARANCE_Z", - 'MINX': "X_MIN", 'MAXX': "X_MAX", 'MINY': "Y_MIN", - 'MAXY': "Y_MAX", 'MINZ': "Z_MIN", 'MAXZ': "Z_MAX", - 'ABSE': "E_ABSOLUTE"} - - self.tcMech = {} - self.brMech = {} - mech = tree.AppendItem(root, "1. Mechanical/Hardware", data = "Mechanical") - - for tag, tbl, cttype in [('SPM', spmKeys, 0), ('MFR', mfrKeys, 0), - ('MSR', msrKeys, 0), ('ECL', eclKeys, 0), - ('MINMAX', minmaxKeys, 1)]: - st = tree.AppendItem(mech, mechLabels[tag], ct_type = cttype, data = tag) - self.brMech[tag] = st - for k in tbl: - ck = self.mechCfgKeys[k] - if ck in self.cfgValues.keys(): - v = self.cfgValues[ck] - else: - v = "" - tc = wx.TextCtrl(tree, wx.ID_ANY, v, style = wx.TE_RIGHT) - if k in helpText.keys(): - tc.SetToolTipString(helpText[k]) - tc.Bind(wx.EVT_CHAR, self.onTextCtrl) - self.tcMech[k] = tc - self.brMech[k] = tree.AppendItem(st, mechLabels[k], ct_type = 0, - wnd = tc, data = k) - - allAbsent = True - for i in minmaxKeys: - k = self.mechCfgKeys[i] - if k in self.cfgValues.keys(): - allAbsent = False - break - - if allAbsent: - self.tree.CheckItem(self.brMech['MINMAX'], False) + if False in self.pageValid: + self.bSaveConfig.Enable(False) else: - self.tree.CheckItem(self.brMech['MINMAX'], True) + self.bSaveConfig.Enable(True) - br = tree.AppendItem(mech, mechLabels['ABSE'], ct_type = 1, data = "ABSE") - ck = self.mechCfgKeys['ABSE'] - if ck in self.cfgValues.keys() and self.cfgValues[ck]: - self.tree.CheckItem(br, True) + def modifyTab(self, pg): + if self.pageModified[pg] and not self.pageValid[pg]: + pfx = "?* " + elif self.pageModified[pg]: + pfx = "* " + elif not self.pageValid[pg]: + pfx = "? " else: - self.tree.CheckItem(br, False) - self.brMech['ABSE'] = br - return mech + pfx = "" - def insertMechValues(self): - for k in self.mechCfgKeys.keys(): - if k in self.tcMech.keys(): - ck = self.mechCfgKeys[k] - if ck in self.cfgValues.keys(): - self.tcMech[k].SetValue(self.cfgValues[ck]) + self.nb.SetPageText(pg, pfx + self.titles[pg]) - allAbsent = True - for i in ['MINX', 'MAXX', 'MINY', 'MAXY', 'MINZ', 'MAXZ']: - k = self.mechCfgKeys[i] - if k in self.cfgValues.keys(): - allAbsent = False - break + def revalidatePins(self, flag): + self.pgSensors.revalidatePins(flag) + self.pgHeaters.revalidatePins(flag) - if allAbsent: - self.tree.CheckItem(self.brMech['MINMAX'], False) - else: - self.tree.CheckItem(self.brMech['MINMAX'], True) - - br = self.brMech['ABSE'] - ck = self.mechCfgKeys['ABSE'] - if ck in self.cfgValues.keys() and self.cfgValues[ck]: - self.tree.CheckItem(br, True) - else: - self.tree.CheckItem(br, False) - - def accSubTree(self, tree, root): - accLabels = {'ACTYPE': "Acceleration Type:", - 'ACRR': "RepRap", 'ACRP': "Ramping", 'ACTP': "Temporal", - 'ACCEL' : "Acceleration", 'LKAH': "Look Ahead", - 'JERK': "Maximum Jerk:", - 'JERKX': "X", 'JERKY': "Y", 'JERKZ': "Z", 'JERKE': "E"} - - self.accCfgKeys = {'ACRR': "ACCELERATION_REPRAP", - 'ACRP': "ACCELERATION_RAMPING", - 'ACTP': "ACCELERATION_TEMPORAL", - 'ACCEL': "ACCELERATION", 'LKAH': "LOOKAHEAD", - 'JERKX': "MAX_JERK_X", 'JERKY': "MAX_JERK_Y", - 'JERKZ': "MAX_JERK_Z", 'JERKE': "MAX_JERK_E"} - - self.tcAcc = {} - self.brAcc = {} - - acc = tree.AppendItem(root, "2. Acceleration", data = "Acceleration") - - st = tree.AppendItem(acc, accLabels['ACTYPE'], data = "ACTYPE") - self.brAcc['ACTYPE'] = st - - for tag in ['ACRR', 'ACRP', 'ACTP']: - br = tree.AppendItem(st, accLabels[tag], ct_type = 2, data = tag) - self.brAcc[tag] = br - ck = self.accCfgKeys[tag] - if ck in self.cfgValues.keys() and self.cfgValues[ck]: - self.tree.CheckItem(br, True) - - tag = 'ACRP' - ck = self.accCfgKeys['ACCEL'] - if ck in self.cfgValues.keys(): - v = self.cfgValues[ck] - else: - v = "" - tc = wx.TextCtrl(tree, wx.ID_ANY, v, style = wx.TE_RIGHT) - tc.SetToolTipString(helpText['ACCEL']) - tc.Bind(wx.EVT_CHAR, self.onTextCtrl) - self.tcAcc['ACCEL'] = tc - - br = self.brAcc[tag] - self.brAcc['ACCEL'] = tree.AppendItem(br, accLabels['ACCEL'], ct_type = 0, - wnd = self.tcAcc['ACCEL'], - data = 'ACCEL') - self.brAcc['LKAH'] = tree.AppendItem(br, accLabels['LKAH'], ct_type = 1, - data = 'LKAH') - - br = self.brAcc['LKAH'] - ck = self.accCfgKeys['LKAH'] - if ck in self.cfgValues.keys() and self.cfgValues[ck]: - self.tree.CheckItem(br, True) - else: - self.tree.CheckItem(br, False) - - st = tree.AppendItem(acc, accLabels['JERK'], data = 'JERK') - self.brAcc['JERK'] = st - - for k in ['JERKX', 'JERKY', 'JERKZ', 'JERKE']: - ck = self.accCfgKeys[k] - if ck in self.cfgValues.keys(): - v = self.cfgValues[ck] - else: - v = "" - tc = wx.TextCtrl(tree, wx.ID_ANY, v, style = wx.TE_RIGHT) - if k in helpText.keys(): - tc.SetToolTipString(helpText[k]) - tc.Bind(wx.EVT_CHAR, self.onTextCtrl) - self.tcAcc[k] = tc - self.brAcc[k] = tree.AppendItem(st, accLabels[k], ct_type = 0, - wnd = tc, data = k) - - return acc - - def insertAccValues(self): - for k in self.accCfgKeys.keys(): - if k in self.tcAcc.keys(): - ck = self.accCfgKeys[k] - if ck in self.cfgValues.keys(): - self.tcAcc[k].SetValue(self.cfgValues[ck]) - - for tag in ['ACRR', 'ACRP', 'ACTP']: - br = self.brAcc[tag] - ck = self.accCfgKeys[tag] - if ck in self.cfgValues.keys() and self.cfgValues[ck]: - self.tree.CheckItem(br, True) - - br = self.brAcc['LKAH'] - ck = self.accCfgKeys['LKAH'] - if ck in self.cfgValues.keys() and self.cfgValues[ck]: - self.tree.CheckItem(br, True) - else: - self.tree.CheckItem(br, False) - - def onItemChecked(self, evt): - item = evt.GetItem() - match = None - for k in self.brMech.keys(): - if item == self.brMech[k]: - match = k - break - - if match is None: - return - - if match == 'MINMAX': - self.tree.EnableChildren(item, self.tree.IsItemChecked(item)) - - def onToolTip(self, evt): - item = evt.GetItem() - if item: - data = self.tree.GetPyData(item) - if data and data in helpText.keys(): - evt.SetToolTip(wx.ToolTip(helpText[data])) + def setHeaters(self, hlist): + self.pgMiscellaneous.setHeaters(hlist) def onClose(self, evt): + if not self.confirmLoseChanges("Exit"): + return + self.Destroy() - def onTextCtrl(self, event): - char = chr(event.GetKeyCode()) - event.Skip() + def confirmLoseChanges(self, msg): + if True not in self.pageModified: + return True + + dlg = wx.MessageDialog(self, "Are you sure you want to " + msg + + "?\nThere are changes that will be lost.", + "Changes pending", + wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION) + rc = dlg.ShowModal() + dlg.Destroy() + + if rc != wx.ID_YES: + return False + + return True def onLoadConfig(self, evt): + if not self.confirmLoseChanges("Load a new Configuration"): + return + wildcard = "C Header files (*.h)|*.h" dlg = wx.FileDialog(self, message = "Choose a Config file", - defaultDir = ".", defaultFile = "", + defaultDir = self.dir, defaultFile = "", wildcard = wildcard, style = wx.OPEN | wx.CHANGE_DIR) path = None @@ -313,9 +2238,16 @@ class MyFrame(wx.Frame): if path is None: return + self.dir = os.path.dirname(path) self.loadConfigFile(path) - self.insertMechValues() - self.insertAccValues() + + for pg in self.pages: + pg.insertValues(self.cfgValues) + + self.pgSensors.setSensors(self.sensors) + self.pgHeaters.setHeaters(self.heaters) + self.pgMiscellaneous.setHeaters(self.heaters, self.cfgValues) + self.pgMiscellaneous.setProcessors(self.processors) def loadConfigFile(self, fn): try: @@ -323,46 +2255,159 @@ class MyFrame(wx.Frame): except: return False + self.pgPins.setIncludeArduino(False) + self.sensors = [] + self.heaters = [] + self.processors = [] + self.cfgValues = {} self.cfgValues['E_ABSOLUTE'] = False + + prevLines = "" for ln in lst: + if ln.rstrip().endswith("\\"): + prevLines += ln.rstrip()[:-1] + continue + + if prevLines != "": + ln = prevLines + ln + prevLines = "" + + if ln.lstrip().startswith("//"): + continue + + if ln.lstrip().startswith("#if"): + m = re.findall(reAVR, ln) + inv = [] + for p in m: + if p in supportedCPUs: + self.processors.append(p) + else: + inv.append(p) + if len(inv) > 0: + if len(inv) == 1: + a = " is" + b = "it" + else: + a = "s are" + b = "them" + dlg = wx.MessageDialog(self, + "The following processor type %s not supported:\n %s\n\ +Please add %s to \"supportedCPUs\"" % (a, ", ".join(inv), b), + "Unsupported Processor Type(s)", + wx.OK + wx.ICON_INFORMATION) + + dlg.ShowModal() + dlg.Destroy() + continue + if ln.lstrip().startswith("#define"): - m = self.reDefTS.search(ln) - if m: - t = m.groups() - if len(t) == 1: - print "TSkey (%s)" % t[0] - continue - - m = self.reDefHT.search(ln) - if m: - t = m.groups() - if len(t) == 1: - print "HTkey (%s)" % t[0] - continue - - m = self.reDefQS.search(ln) + m = reDefQS.search(ln) if m: t = m.groups() if len(t) == 2: self.cfgValues[t[0]] = t[1] continue - m = self.reDefine.search(ln) + m = reDefine.search(ln) if m: t = m.groups() if len(t) == 2: self.cfgValues[t[0]] = t[1] continue - m = self.reDefBool.search(ln) + m = reDefBool.search(ln) if m: t = m.groups() if len(t) == 1: self.cfgValues[t[0]] = True + else: + m = reDefTS.search(ln) + if m: + t = m.groups() + if len(t) == 1: + s = self.parseSensor(t[0]) + if s: + self.sensors.append(s) + continue + + m = reDefHT.search(ln) + if m: + t = m.groups() + if len(t) == 1: + s = self.parseHeater(t[0]) + if s: + self.heaters.append(s) + continue + + m = reIncArduino.search(ln) + if m: + self.pgPins.setIncludeArduino(True) return True + def parseSensor(self, s): + m = reSensor4.search(s) + if m: + t = m.groups() + if len(t) == 4: + return t + m = reSensor3.search(s) + if m: + t = m.groups() + if len(t) == 3: + return t + return None + + def parseHeater(self, s): + m = reHeater.search(s) + if m: + t = m.groups() + if len(t) == 3: + return t + return None + + def onSaveConfig(self, evt): + wildcard = "C Header files (*.h)|*.h" + + dlg = wx.FileDialog(self, message = "Save as ...", defaultDir = self.dir, + defaultFile = "", wildcard = wildcard, + style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) + + val = dlg.ShowModal() + + if val != wx.ID_OK: + dlg.Destroy() + return + + path = dlg.GetPath() + dlg.Destroy() + + ext = os.path.splitext(os.path.basename(path))[1] + self.dir = os.path.dirname(path) + + if ext == "": + path += ".h" + + fp = file(path, 'w') + + timestamp = time.strftime("%Y-%b-%d %H:%M:%S", time.localtime()) + + fp.write("// Generated by Teacup Configurator version %s - %s\n\n" % + (VERSION, timestamp)) + + self.pgMiscellaneous.savePrefixValues(fp) + + for pg in self.pages: + pg.saveValues(fp) + + fp.close() + + dlg = wx.MessageDialog(self, "File \"%s\" successfully written" % path, + "Save Successful", wx.OK + wx.ICON_INFORMATION) + dlg.ShowModal() + dlg.Destroy() + if __name__ == '__main__': app = wx.PySimpleApp() diff --git a/config_helptext.py b/config_helptext.py index 11abc24..055575a 100644 --- a/config_helptext.py +++ b/config_helptext.py @@ -1,34 +1,23 @@ helpText = { -'SPM': "steps per meter ( = steps per mm * 1000 ) \ +'STEPS_PER_M': "steps per meter ( = steps per mm * 1000 ) \ calculate these values appropriate for your machine.\n\ for threaded rods, this is:\n\n\ \t(steps motor per turn) / (pitch of the thread) * 1000\n\n\ for belts, this is\n\n\ \t(steps per motor turn) / (number of gear teeth) / (belt module) * 1000\n\n\ half-stepping doubles the number, quarter stepping requires * 4, etc.\n\ -valid range = 20 to 4,0960,000 (0.02 to 40960 steps/mm). \ +valid range = 20 to 4,0960,000 (0.02 to 40960 steps/mm). \ all numbers are integers, so no decimal point", -'SMX': "steps per meter for the X axis", -'SMY': "steps per meter for the Y axis", -'SMZ': "steps per meter for the Z axis", -'SME': "steps per meter for the E axis", -'MFR': "maximum feed rate - in mm/min - for G0 rapid moves and as a cap for \ -all other feedrates", -'MFRX': "maximum feed rate for the X axis (mm/min)", -'MFRY': "maximum feed rate for the Y axis (mm/min)", -'MFRZ': "maximum feed rate for the Z axis (mm/min)", -'MFRE': "maximum feed rate for the E axis (mm/min)", +'MAXIMUM_FEEDRATE': "maximum feed rate - in mm/min - for G0 rapid moves and \ +as a cap for all other feedrates", -'MSR': "search feed rate - in mm/min - used when doing precision endstop \ -search and as a default feed rate", -'MSRX': "search feed rate for the X axis (mm/min)", -'MSRY': "search feed rate for the Y axis (mm/min)", -'MSRZ': "search feed rate for the Z axis (mm/min)", +'SEARCH_FEEDRATE': "search feed rate - in mm/min - used when doing precision \ +endstop search and as a default feed rate", -'ECL': "When hitting an endstop, Teacup properly decelerates instead of \ -doing an abrupt stop\nto save your mechanics. Ineviteably, this means it \ -overshoots the endstop trigger point by some distance.\n\n\ +'ENDSTOP_CLEARANCE': "When hitting an endstop, Teacup properly decelerates \ +instead of doing an abrupt stop\nto save your mechanics. Inevitably, this \ +means it overshoots the endstop trigger point by some distance.\n\n\ To deal with this, Teacup adapts homing movement speeds to what your endstops \ can deal with.\nThe higher the allowed acceleration and the more clearance \ the endstop comes with, the faster Teacup\nwill do homing movements.\n\n\ @@ -39,57 +28,202 @@ SEARCH_FEEDRATE_{XYZ} is used, but expect very slow\nhoming movements.\n\n\ Units: micrometers\n\ Sane values: 0 to 20000 (0 to 20 mm)\n\ Valid range: 0 to 1000000", -'ECX': "endstop clearance for the X axis (mm * 1000)", -'ECY': "endstop clearance for the Y axis (mm * 1000)", -'ECZ': "endstop clearance for the Z axis (mm * 1000)", 'MINMAX': "soft axis limits, in mm.\n\ndefine them to your machine's size \ relative to what your host considers to be the origin.", -'MINX': "Minimum limit for the X axis:", -'MAXX': "maximum limit for the X axis:", -'MINY': "minimum limit for the Y axis:", -'MAXY': "maximum limit for the Y axis:", -'MINZ': "minimum limit for the Z axis", -'MAXZ': "maximum limit for the Z axis", -'ABSE': "some G-code creators produce relative length commands for the \ -extruder,\nothers absolute ones. G-code using absolute lengths can be \ +'E_ABSOLUTE': "some G-Code creators produce relative length commands for the \ +extruder,\nothers absolute ones. G-Code using absolute lengths can be \ recognized when there\nare G92 E0 commands from time to time. if you have \ G92 E0 in your G-code, check this box.", -'ACTYPE': "Acceleration algorithm", -'ACRR': "acceleration, reprap style.\n\n\ +'ACCELERATION_REPRAP': "acceleration, reprap style.\n\n\ Each movement starts at the speed of the previous command and accelerates or \ decelerates\nlinearly to reach target speed at the end of the movement.", -'ACRP': "acceleration and deceleration ramping.\n\n\ +'ACCELERATION_RAMPING': "acceleration and deceleration ramping.\n\n\ Each movement starts at (almost) no speed, linearly accelerates to target \ speed and decelerates\njust in time to smoothly stop at the target.", -'ACTP': "This algorithm causes the timer to fire when any axis needs to step, \ -instead of\nsynchronising to the axis with the most steps ala bresenham", -'ACCEL' : "how fast to accelerate when using acceleration ramping.\n\n\ +'ACCELERATION_TEMPORAL': "This algorithm causes the timer to fire when any \ +axis needs to step, instead of\n\synchronising to the axis with the most \ +steps ala bresenham", +'ACCELERATION' : "how fast to accelerate when using acceleration ramping.\n\n\ given in mm/s^2, decimal allowed, useful range 1. to 10,000.\n\ Start with 10. for milling (high precision) or 1000. for printing", -'LKAH': "Define this to enable look-ahead during *ramping* acceleration to \ +'LOOKAHEAD': "Define this to enable look-ahead during *ramping* acceleration to \ smoothly transition\nbetween moves instead of performing a dead stop every \ -move. Enabling look-ahead requires about\n3600 bytes of flash memory.", -'JERK': "When performing look-ahead, we need to decide what an acceptable \ +move. Enabling look-ahead requires about\n3600 bytes of flash memory.", +'MAX_JERK': "When performing look-ahead, we need to decide what an acceptable \ jerk to the\nmechanics is. Look-ahead attempts to instantly change direction \ at movement\ncrossings, which means instant changes in the speed of the axes \ participating\nin the movement. Define here how big the speed bumps on each \ of the axes is\nallowed to be.\n\n\ If you want a full stop before and after moving a specific axis, define\n\ -maximum jerk of this axis to 0. This is often wanted for the Z axis. If you want\n\ -to ignore jerk on an axis, define it to twice the maximum feedrate of this axis.\n\n\ -Having these values too low results in more than neccessary slowdown at\n\ -movement crossings, but is otherwise harmless. Too high values can result\n\ -in stepper motors suddenly stalling. If angles between movements in your\n\ -G-code are small and your printer runs through entire curves full speed,\n\ -there's no point in raising the values.\n\n\ +maximum jerk of this axis to 0. This is often wanted for the Z axis. If you \ +want\nto ignore jerk on an axis, define it to twice the maximum feedrate of \ +this axis.\n\nHaving these values too low results in more than necessary \ +slowdown at\nmovement crossings, but is otherwise harmless. Too high values \ +can result\nin stepper motors suddenly stalling. If angles between movements \ +in your\nG-code are small and your printer runs through entire curves full \ +speed,\nthere's no point in raising the values.\n\n\ Units: mm/min\n\ Sane values: 0 to 400\n\ Valid range: 0 to 65535", -'JERKX': "maximum jerk for the X axis", -'JERKY': "maximum jerk for the Y axis", -'JERKZ': "maximum jerk for the Z axis", -'JERKE': "maximum jerk for the E axis", + +'INCLUDE_ARDUINO': "Include arduino.h header file in the C++ source code. \ +This allows you\nto define pins using the atmel/avr conventions (e.g. DI012 \ +or AI01)", + +'USE_INTERNAL_PULLUPS': "Use Internal Pullups. the ATmega has internal pullup \ +resistors on it's input pins\nwhich are counterproductive with the commonly \ +used electronic endstops, so this should be unchecked.\n\For other endstops, \ +like mechanical ones, you may want to check this.", + +'TX_ENABLE_PIN': "Tx Enable Pin. Not yet used", +'RX_ENABLE_PIN': "Rx Enable Pin. Not yet used", + +'X_STEP_PIN': "the pin for the stepper motor step signal", +'X_DIR_PIN': "the pin for the stepper motor direction signal", +'X_MIN_PIN': "the pin for the endstop at the axis minimum position", +'X_MAX_PIN': "the pin for the endstop at the axis maximum position", +'X_ENABLE_PIN': "the pin for the stepper motor enable signal", +'X_INVERT_DIR': "true or false - invert the stepper motor direction", +'X_INVERT_MIN': "true or false - invert the signal received from the minimum endstop", +'X_INVERT_MAX': "true or false - invert the signal received from the maximum endstop", +'X_INVERT_ENABLE': "true or false - invert the stepper motor enable signal", + +'Y_STEP_PIN': "the pin for the stepper motor step signal", +'Y_DIR_PIN': "the pin for the stepper motor direction signal", +'Y_MIN_PIN': "the pin for the endstop at the axis minimum position", +'Y_MAX_PIN': "the pin for the endstop at the axis maximum position", +'Y_ENABLE_PIN': "the pin for the stepper motor enable signal", +'Y_INVERT_DIR': "true or false - invert the stepper motor direction", +'Y_INVERT_MIN': "true or false - invert the signal received from the minimum endstop", +'Y_INVERT_MAX': "true or false - invert the signal received from the maximum endstop", +'Y_INVERT_ENABLE': "true or false - invert the stepper motor enable signal", + +'Z_STEP_PIN': "the pin for the stepper motor step signal", +'Z_DIR_PIN': "the pin for the stepper motor direction signal", +'Z_MIN_PIN': "the pin for the endstop at the axis minimum position", +'Z_MAX_PIN': "the pin for the endstop at the axis maximum position", +'Z_ENABLE_PIN': "the pin for the stepper motor enable signal", +'Z_INVERT_DIR': "true or false - invert the stepper motor direction", +'Z_INVERT_MIN': "true or false - invert the signal received from the minimum endstop", +'Z_INVERT_MAX': "true or false - invert the signal received from the maximum endstop", +'Z_INVERT_ENABLE': "true or false - invert the stepper motor enable signal", + +'E_STEP_PIN': "the pin for the stepper motor step signal", +'E_DIR_PIN': "the pin for the stepper motor direction signal", +'E_ENABLE_PIN': "the pin for the stepper motor enable signal", +'E_INVERT_DIR': "true or false - invert the stepper motor direction", +'E_INVERT_ENABLE': "true or false - invert the stepper motor enable signal", + +'PS_ON_PIN': "Which pin is used to turn the power supply off", +'PS_MOSFET_PIN': "???", + +'STEPPER_ENABLE_PIN': "???", +'STEPPER_INVERT_ENABLE': "???", + +'SD_CARD_DETECT': "the pin used to detect if an SD card has been inserted.", +'SD_WRITE_PROTECT': "the pin used to determine if an SD card is write-protected", +'DEBUG_LED_PIN': "Enable flashing of a LED during motor stepping.\n\n\ +Disabled by default. Turning this on this makes the binary a few bytes larger\n\ + and adds a few cycles to the step timing interrupt. This is also used for\n\ + for precision profiling", + +'TEMP_HYSTERESIS': "Actual temperature must be target +/- this hysteresis \ +before target\ntemperature is considered to be achieved. Also, BANG_BANG \ +tries to stay\nwithin half of this hysteresis. Unit is degrees Celcius", +'TEMP_RESIDENCY_TIME': "actual temperature must be close to target (within\n\ +set temperature +- TEMP_HYSTERESIS) for this long before target is achieved\n\ +(and a M116 succeeds). Unit is seconds.", +'TEMP_EWMA': "Smooth noisy temperature sensors. Good hardware shouldn't be\n\ +noisy. Set to 1.0 for unfiltered data (and a 140 bytes smaller binary)\n\n\ +Valid range: 0.001 - 1.0", +'TEMP_TYPES': "which temperature sensors are you using? Check every type of \ +sensor you use\nto enable the appropriate code. Intercom is the gen3-style \ +separate extruder board. Note\nthat you will not be able to uncheck a sensor \ +type that is in use", + +'ADDSENSOR': 'Define your temperature sensors. One entry for each sensor, \ +only limited\nby the number of available ATmega pins.\n\n\ +Types are only those chosen on the main page.\n\n\ +The "additional" field is used for TT_THERMISTOR only. It defines the\n\ +name of the table(s) in ThermistorTable.h to use. Typically, this is\n\ +THERMISTOR_EXTRUDER for the first or only table, or THERMISTOR_BED for\n\ +the second table. See also early in ThermistorTable.{single|double}.h,\n\n\ +For a GEN3 set type to TT_INTERCOM and pin to AIO0. The pin won\'t be used in \ +this case.', +'DELSENSOR': 'Remove the selected temperature sensor from the configuration', + +"HEATER_SANITY_CHECK": "check if heater responds to changes in target \ +temperature, disable\nand spit errors if not. largely untested, please \ +comment in forum if this works, or doesn't work for you!", + +'ADDHEATER': "Add a heater to the configuration", +'DELHEATER': "Remove a heater from the configuration", + +'BAUD': "Baud rate for the serial RS232 protocol connection to the host. \ +Usually 115200,\nother common values are 19200, 38400 or 57600. Ignored when \ +USB_SERIAL is checked.", +'USB_SERIAL': "Define this for using USB instead of the serial RS232 protocol. \ +Works on\nUSB-equipped ATmegas, like the ATmega32U4, only", +'XONXOFF': "Xon/Xoff flow control. Redundant when using RepRap Host for \ +sending GCode,\nbut mandatory when sending GCode files with a plain terminal \ +emulator, like GtkTerm (Linux),\nCoolTerm (Mac) or HyperTerminal (Windows). \ +Can also be set in Makefile", + +'MOTHERBOARD': "This is the motherboard, as opposed to the extruder.", +'F_CPU': "The CPU clock rate", +'EECONFIG': "Enable EEPROM configuration storage.\n\n\ +Enabled by default. Commenting this out makes the binary several hundred\n\ +bytes smaller, so you might want to disable EEPROM storage on small MCUs,\n\ +like the ATmega168.", +'DEBUG': "enables extra output, and some extra M-codes.\n\n\ +WARNING: this WILL break most host-side talkers that expect particular \ +responses\nfrom firmware such as reprap host and replicatorG. Use with serial \ +terminal\nor other suitable talker only.", +'BANG_BANG': "Drops PID loop from heater control, reduces code size\n\ +significantly (1300 bytes!)", +'BANG_BANG_ON': "PWM value for 'on'", +'BANG_BANG_OFF': "PWM value for 'off'", +'MOVEBUFFER_SIZE': "Move buffer size, in number of moves\n\nnote that each \ +move takes a fair chunk of ram (~69 bytes) so don't make the buffer too big.\n\ +A bigger serial readbuffer may help more than increasing this unless your \ +gcodes are more than\n70 characters long on average. However, a larger \ +movebuffer will probably help with lots of \nshort consecutive moves, as each \ +move takes a bunch of math (hence time) to set up, so a longer\nbuffer allows \ +more of the math to be done during preceding longer moves", +'DC_EXTRUDER': 'If you have a DC motor extruder, configure it as a "heater" \ +on the heater page,\nand choose the name you used there in this field. You \ +probably also want to comment out E_STEP_PIN\nand E_DIR_PIN on the Pinouts \ +page.', +'DC_EXTRUDER_PWM': "The PWM value at which to operate the DC Extruder motor", +'USE_WATCHDOG': "Teacup implements a watchdog, which has to be reset every \ +250ms or it will\nreboot the controller. As rebooting (and letting the GCode\ +sending application trying to\ncontinue the build with a then different Home \ +point) is probably even worse than just hanging,\nand there is no better \ +restore code in place, this is disabled for now.", +'REFERENCE': "which analog reference to use. see analog.h for choices", +'STEP_INTERRUPT_INTERRUPTIBLE': "this option makes the step interrupt \ +interruptible (nested).\nthis should help immensely with dropped serial \ +characters, but may also make\ndebugging infuriating due to the complexities \ +arising from nested interrupts.\n\nnote disable this option if you're using a \ +'168 or for some reason your ram usage\nis above 90%. This option hugely \ +increases likelihood of stack smashing.", +'TH_COUNT': "Temperature history count. This is how many temperature readings \ +to keep in\norder to calculate derivative in PID loop. Higher values make PID \ +derivative term more\nstable at the expense of reaction time", +'FAST_PWM': "Teacup offers two PWM frequencies, 76(61) Hz and 78000(62500) \ +Hz on a\n20(16) MHz electronics. The slower one is the default, as it's the \ +safer choice.\nThe drawback is in a quiet environment you might notice the \ +heaters and power supply humming.\n\nUncomment this option if you want to get \ +rid of this humming or want faster PWM for other reasons.", +'ENDSTOP_STEPS': "Number of steps to run into the endstops intentionally\n\n\ +As Endstops trigger false alarm sometimes, Teacup debounces them by counting \ +a number of\nconsecutive positives. Valid range is 1...255. Use 4 or less for \ +reliable endstops, 8 or\neven more for flaky ones.", +'CANNED_CYCLE': "G-code commands in this string will be executed over and \ +over again, without\nuser interaction or even a serial connection. It's \ +purpose is e.g. for\nexhibitions or when using Teacup for other purposes than \ +printing. You can\nadd any G-code supported by Teacup.", }