From d7789ee21721a061eb50ec5fd4d18ed73a6948e3 Mon Sep 17 00:00:00 2001 From: jbernardis Date: Tue, 13 Jan 2015 20:08:14 -0500 Subject: [PATCH] Configtool: major restructuring. We distinguish between printers and boards now. Most of the code was split into one file per tab/window and moved into configtool/. --- config.py | 2446 +----------------------------- config/board.gen7-v1.4.h | 219 +++ config/board.ramps-v1.3.h | 215 +++ config/config-syntax.txt | 160 ++ config/printer.mendel.h | 339 +++++ config/printer.wolfstrap.h | 339 +++++ config_helptext.py | 229 --- configtool/__init__.py | 0 configtool/accelerationpage.py | 124 ++ configtool/addheaterdlg.py | 115 ++ configtool/addsensordlg.py | 174 +++ configtool/boardpanel.py | 515 +++++++ configtool/calcbelt.py | 251 +++ configtool/calcscrew.py | 269 ++++ configtool/communicationspage.py | 48 + configtool/cpupage.py | 81 + configtool/data.py | 52 + configtool/heaterlist.py | 91 ++ configtool/heaterspage.py | 125 ++ configtool/mechanicalpage.py | 210 +++ configtool/miscellaneouspage.py | 291 ++++ configtool/page.py | 235 +++ configtool/pinoutspage.py | 196 +++ configtool/printerpanel.py | 353 +++++ configtool/sensorlist.py | 94 ++ configtool/sensorpage.py | 190 +++ 26 files changed, 4765 insertions(+), 2596 deletions(-) mode change 100644 => 100755 config.py create mode 100644 config/board.gen7-v1.4.h create mode 100644 config/board.ramps-v1.3.h create mode 100644 config/config-syntax.txt create mode 100644 config/printer.mendel.h create mode 100644 config/printer.wolfstrap.h delete mode 100644 config_helptext.py create mode 100644 configtool/__init__.py create mode 100644 configtool/accelerationpage.py create mode 100644 configtool/addheaterdlg.py create mode 100644 configtool/addsensordlg.py create mode 100644 configtool/boardpanel.py create mode 100644 configtool/calcbelt.py create mode 100644 configtool/calcscrew.py create mode 100644 configtool/communicationspage.py create mode 100644 configtool/cpupage.py create mode 100644 configtool/data.py create mode 100644 configtool/heaterlist.py create mode 100644 configtool/heaterspage.py create mode 100644 configtool/mechanicalpage.py create mode 100644 configtool/miscellaneouspage.py create mode 100644 configtool/page.py create mode 100644 configtool/pinoutspage.py create mode 100644 configtool/printerpanel.py create mode 100644 configtool/sensorlist.py create mode 100644 configtool/sensorpage.py diff --git a/config.py b/config.py old mode 100644 new mode 100755 index 43f2711..df0f132 --- a/config.py +++ b/config.py @@ -1,2416 +1,128 @@ -#!/bin/env python +#!/usr/bin/env python -import os import wx -import re -import time +import os.path +import inspect -VERSION = "0.1" +cmd_folder = os.path.realpath(os.path.abspath(os.path.split(inspect.getfile( + inspect.currentframe()))[0])) -try: - from agw import customtreectrl as CT -except ImportError: - import wx.lib.agw.customtreectrl as CT +from configtool.printerpanel import PrinterPanel +from configtool.boardpanel import BoardPanel +from configtool.data import VERSION -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" +ID_LOAD_PRINTER = 1000 +ID_SAVE_PRINTER = 1001 +ID_SAVE_PRINTER_AS = 1002 +ID_LOAD_BOARD = 1010 +ID_SAVE_BOARD = 1011 +ID_SAVE_BOARD_AS = 1012 -class Page: +class ConfigFrame(wx.Frame): 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): - wx.Frame.__init__(self, None, -1, "Teacup Firmware Configurator", - size = (980, 450)) + wx.Frame.__init__(self, None, -1, + "Teacup Firmware Configurator - " + VERSION, + size = (880, 500)) self.Bind(wx.EVT_CLOSE, self.onClose) - self.cfgValues = {} - self.heaters = [] - self.dir = "." - panel = wx.Panel(self, -1) + self.heaters = [] + sz = wx.BoxSizer(wx.HORIZONTAL) - bsz = wx.BoxSizer(wx.VERTICAL) - 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") + self.nb = wx.Notebook(panel, wx.ID_ANY, size = (880, 500), + style = wx.BK_DEFAULT) - bsz.AddSpacer((5, 5)) + self.pgPrinter = PrinterPanel(self, self.nb, cmd_folder) + self.nb.AddPage(self.pgPrinter, "Printer") - 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.pgBoard = BoardPanel(self, self.nb, cmd_folder) + self.nb.AddPage(self.pgBoard, "Board") - self.nb = wx.Notebook(panel, wx.ID_ANY, size = (21, 21), style = wx.BK_DEFAULT) - - self.pages = [] - self.titles = [] - self.pageModified = [] - self.pageValid = [] - - 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) + panel.Fit() sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5) - sz.Add(bsz, 0, wx.ALL, 5) - - panel.SetSizer(sz) - - def buildHeaterPage(self, nb): - pg = wx.Panel(nb, wx.ID_ANY) - return pg - - def buildCommPage(self, nb): - pg = wx.Panel(nb, wx.ID_ANY) - return pg - - def buildMiscPage(self, nb): - pg = wx.Panel(nb, wx.ID_ANY) - return pg - - def assertModified(self, pg, flag = True): - self.pageModified[pg] = flag - self.modifyTab(pg) - - def assertValid(self, pg, flag = True): - self.pageValid[pg] = flag - self.modifyTab(pg) - - if False in self.pageValid: - self.bSaveConfig.Enable(False) - else: - self.bSaveConfig.Enable(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: - pfx = "" - - self.nb.SetPageText(pg, pfx + self.titles[pg]) - - def revalidatePins(self, flag): - self.pgSensors.revalidatePins(flag) - self.pgHeaters.revalidatePins(flag) - - def setHeaters(self, hlist): - self.pgMiscellaneous.setHeaters(hlist) + self.SetSizer(sz) + self.makeMenu() def onClose(self, evt): - if not self.confirmLoseChanges("Exit"): + if not self.pgPrinter.confirmLoseChanges("exit"): + return + + if not self.pgBoard.confirmLoseChanges("exit"): return self.Destroy() - def confirmLoseChanges(self, msg): - if True not in self.pageModified: - return True + def setPrinterTabText(self, txt): + self.nb.SetPageText(0, txt) - 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() + def setBoardTabText(self, txt): + self.nb.SetPageText(1, txt) - if rc != wx.ID_YES: - return False + def setHeaters(self, ht): + self.heaters = ht + self.pgPrinter.setHeaters(ht) - return True + def makeMenu(self): + file_menu = wx.Menu() - def onLoadConfig(self, evt): - if not self.confirmLoseChanges("Load a new Configuration"): - return + file_menu.Append(ID_LOAD_PRINTER, "Load printer", + "Load a printer configuration file.") + self.Bind(wx.EVT_MENU, self.pgPrinter.onLoadConfig, id = ID_LOAD_PRINTER) - wildcard = "C Header files (*.h)|*.h" + file_menu.Append(ID_SAVE_PRINTER, "Save printer", + "Save printer configuration.") + self.Bind(wx.EVT_MENU, self.pgPrinter.onSaveConfig, id = ID_SAVE_PRINTER) + file_menu.Enable(ID_SAVE_PRINTER, False) - dlg = wx.FileDialog(self, message = "Choose a Config file", - defaultDir = self.dir, defaultFile = "", - wildcard = wildcard, style = wx.OPEN | wx.CHANGE_DIR) + file_menu.Append(ID_SAVE_PRINTER_AS, "Save printer as...", + "Save printer configuration to a new file.") + self.Bind(wx.EVT_MENU, self.pgPrinter.onSaveConfigAs, + id = ID_SAVE_PRINTER_AS) + file_menu.Enable(ID_SAVE_PRINTER_AS, False) - path = None - if dlg.ShowModal() == wx.ID_OK: - path = dlg.GetPath() + file_menu.AppendSeparator() - dlg.Destroy() - if path is None: - return + file_menu.Append(ID_LOAD_BOARD, "Load board", + "Load a board configuration file.") + self.Bind(wx.EVT_MENU, self.pgBoard.onLoadConfig, id = ID_LOAD_BOARD) - self.dir = os.path.dirname(path) - self.loadConfigFile(path) + file_menu.Append(ID_SAVE_BOARD, "Save board", "Save board configuration.") + self.Bind(wx.EVT_MENU, self.pgBoard.onSaveConfig, id = ID_SAVE_BOARD) + file_menu.Enable(ID_SAVE_BOARD, False) - for pg in self.pages: - pg.insertValues(self.cfgValues) + file_menu.Append(ID_SAVE_BOARD_AS, "Save board as...", + "Save board configuration to a new file.") + self.Bind(wx.EVT_MENU, self.pgBoard.onSaveConfigAs, id = ID_SAVE_BOARD_AS) + file_menu.Enable(ID_SAVE_BOARD_AS, False) - self.pgSensors.setSensors(self.sensors) - self.pgHeaters.setHeaters(self.heaters) - self.pgMiscellaneous.setHeaters(self.heaters, self.cfgValues) - self.pgMiscellaneous.setProcessors(self.processors) + file_menu.AppendSeparator() - def loadConfigFile(self, fn): - try: - lst = list(open(fn)) - except: - return False + file_menu.Append(wx.ID_EXIT, "E&xit", "Exit the application.") + self.Bind(wx.EVT_MENU, self.onClose, id = wx.ID_EXIT) - self.pgPins.setIncludeArduino(False) - self.sensors = [] - self.heaters = [] - self.processors = [] + self.fileMenu = file_menu - self.cfgValues = {} - self.cfgValues['E_ABSOLUTE'] = False + menu_bar = wx.MenuBar() - prevLines = "" - for ln in lst: - if ln.rstrip().endswith("\\"): - prevLines += ln.rstrip()[:-1] - continue + menu_bar.Append(file_menu, "&File") - if prevLines != "": - ln = prevLines + ln - prevLines = "" + self.SetMenuBar(menu_bar) - if ln.lstrip().startswith("//"): - continue + def enableSavePrinter(self, flag): + self.fileMenu.Enable(ID_SAVE_PRINTER, flag) + self.fileMenu.Enable(ID_SAVE_PRINTER_AS, flag) - 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 = reDefQS.search(ln) - if m: - t = m.groups() - if len(t) == 2: - self.cfgValues[t[0]] = t[1] - continue - - m = reDefine.search(ln) - if m: - t = m.groups() - if len(t) == 2: - self.cfgValues[t[0]] = t[1] - continue - - 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() + def enableSaveBoard(self, flag): + self.fileMenu.Enable(ID_SAVE_BOARD, flag) + self.fileMenu.Enable(ID_SAVE_BOARD_AS, flag) if __name__ == '__main__': app = wx.PySimpleApp() - frame = MyFrame() + frame = ConfigFrame() frame.Show(True) app.MainLoop() diff --git a/config/board.gen7-v1.4.h b/config/board.gen7-v1.4.h new file mode 100644 index 0000000..39b203d --- /dev/null +++ b/config/board.gen7-v1.4.h @@ -0,0 +1,219 @@ + +/***************************************************************************\ +* * +* 1. CPU * +* * +\***************************************************************************/ + +//PROCESSORS_START +#ifndef __AVR_ATmega644__ + #ifndef __AVR_ATmega644P__ + #ifndef __AVR_ATmega1284__ + #ifndef __AVR_ATmega1284P__ + #error Wrong CPU type. + #endif + #endif + #endif +#endif +//PROCESSORS_END + +/** \def F_CPU + CPU clock rate. #ifndef required for Arduino compatibility. +*/ +#ifndef F_CPU +#define F_CPU 20000000UL +#endif + +/** \def MOTHERBOARD + This is the motherboard, as opposed to the extruder. See extruder/ directory + for GEN3 extruder firmware. +*/ +#define MOTHERBOARD + + +/***************************************************************************\ +* * +* 2. PINOUTS * +* * +\***************************************************************************/ + +#include "../arduino.h" + +#define X_STEP_PIN DIO29 +#define X_DIR_PIN DIO28 +#define X_MIN_PIN DIO0 +//#define X_MAX_PIN xxxx +//#define X_ENABLE_PIN xxxx +//#define X_INVERT_DIR +//#define X_INVERT_MIN +//#define X_INVERT_MAX +//#define X_INVERT_ENABLE + +#define Y_STEP_PIN DIO27 +#define Y_DIR_PIN DIO26 +#define Y_MIN_PIN DIO1 +//#define Y_MAX_PIN xxxx +//#define Y_ENABLE_PIN xxxx +//#define Y_INVERT_DIR +//#define Y_INVERT_MIN +//#define Y_INVERT_MAX +//#define Y_INVERT_ENABLE + +#define Z_STEP_PIN DIO23 +#define Z_DIR_PIN DIO22 +#define Z_MIN_PIN DIO2 +//#define Z_MAX_PIN xxxx +//#define Z_ENABLE_PIN xxxx +//#define Z_INVERT_DIR +//#define Z_INVERT_MIN +//#define Z_INVERT_MAX +//#define Z_INVERT_ENABLE + +#define E_STEP_PIN DIO19 +#define E_DIR_PIN DIO18 +//#define E_ENABLE_PIN xxxx +//#define E_INVERT_DIR +//#define E_INVERT_ENABLE + +#define PS_ON_PIN DIO15 +//#define PS_MOSFET_PIN xxxx +#define STEPPER_ENABLE_PIN DIO25 +#define STEPPER_INVERT_ENABLE + +/** \def DEBUG_LED_PIN + + Enable flashing of a LED during motor stepping. + + Disabled by default. Uncommenting this makes the binary a few bytes larger + and adds a few cycles to the step timing interrrupt in timer.c. Also used + for precision profiling (profiling works even without actually having such + a LED in hardware), see + http://reprap.org/wiki/Teacup_Firmware#Doing_precision_profiling +*/ +//#define DEBUG_LED_PIN DIO21 + + +/***************************************************************************\ +* * +* 3. TEMPERATURE SENSORS * +* * +\***************************************************************************/ + +#ifndef DEFINE_TEMP_SENSOR + #define DEFINE_TEMP_SENSOR(...) +#endif + +/** \def TEMP_MAX6675 TEMP_THERMISTOR TEMP_AD595 TEMP_PT100 TEMP_INTERCOM + Which temperature sensor types are you using? Leave all used ones + uncommented, comment out all others to save binary size and enhance + performance. +*/ +//#define TEMP_MAX6675 +#define TEMP_THERMISTOR +//#define TEMP_AD595 +//#define TEMP_PT100 +//#define TEMP_INTERCOM + +/** \def TEMP_SENSOR_PIN + Temperature sensor pins a user should be able to choose from in configtool. + All commented out. +*/ +//#define TEMP_SENSOR_PIN AIO0 +//#define TEMP_SENSOR_PIN AIO1 +//#define TEMP_SENSOR_PIN AIO7 + +/** \def DEFINE_TEMP_SENSOR + Define your temperature sensors here. One line for each sensor, only + limited by the number of available ATmega pins. + + Types are same as TEMP_ list above - TT_MAX6675, TT_THERMISTOR, TT_AD595, + TT_PT100, TT_INTERCOM. See list in temp.c. + + The "additional" field is used for TT_THERMISTOR only. It defines the + name of the table(s) in ThermistorTable.h to use. Typically, this is + THERMISTOR_EXTRUDER for the first or only table, or THERMISTOR_BED for + the second table. See also early in ThermistorTable.{single|double}.h. + + For a GEN3 set temp_type to TT_INTERCOM and temp_pin to AIO0. The pin + won't be used in this case. +*/ +// name type pin additional +//DEFINE_TEMP_SENSORS_START +DEFINE_TEMP_SENSOR(extruder, TT_THERMISTOR, AIO1, THERMISTOR_EXTRUDER) +DEFINE_TEMP_SENSOR(bed, TT_THERMISTOR, AIO0, THERMISTOR_BED) +//DEFINE_TEMP_SENSORS_END + + +/***************************************************************************\ +* * +* 4. HEATERS * +* * +\***************************************************************************/ + +#ifndef DEFINE_HEATER + #define DEFINE_HEATER(...) +#endif + +/** \def HEATER_PIN + Heater pins a user should be able to choose from in configtool. All + commented out. +*/ +//#define HEATER_PIN DIO3 +//#define HEATER_PIN DIO4 +//#define HEATER_PIN DIO5 + +/** \def DEFINE_HEATER + Define your heaters and devices here. + + To attach a heater to a temp sensor above, simply use exactly the same + name - copy+paste is your friend. Some common names are 'extruder', + 'bed', 'fan', 'motor', ... names with special meaning can be found + in gcode_process.c. Currently, these are: + HEATER_extruder (M104) + HEATER_bed (M140) + HEATER_fan (M106) + + Devices don't neccessarily have a temperature sensor, e.g. fans or + milling spindles. Operate such devices by setting their power (M106), + instead of setting their temperature (M104). + + Also note, the index of a heater (M106 P#) can differ from the index of + its attached temperature sensor (M104 P#) in case sensor-less devices + are defined or the order of the definitions differs. The first defined + device has the index 0 (zero). + + Set 'pwm' to ... + 1 for using PWM on a PWM-able pin and on/off on other pins. + 0 for using on/off on a PWM-able pin, too. + Using PWM usually gives smoother temperature control but can conflict + with slow switches, like solid state relays. PWM frequency can be + influenced globally with FAST_PWM, see below. +*/ +// name port pwm +//DEFINE_HEATERS_START +DEFINE_HEATER(extruder, DIO4, 1) +DEFINE_HEATER(bed, DIO3, 1) + +#define HEATER_EXTRUDER HEATER_extruder +#define HEATER_BED HEATER_bed +//DEFINE_HEATERS_END + + +/***************************************************************************\ +* * +* 5. COMMUNICATION OPTIONS * +* * +\***************************************************************************/ + +/** \def BAUD + Baud rate for the serial RS232 protocol connection to the host. Usually + 115200, other common values are 19200, 38400 or 57600. Ignored when USB_SERIAL + is defined. +*/ +#define BAUD 115200 + +/** \def USB_SERIAL + Define this for using USB instead of the serial RS232 protocol. Works on + USB-equipped ATmegas, like the ATmega32U4, only. +*/ +//#define USB_SERIAL diff --git a/config/board.ramps-v1.3.h b/config/board.ramps-v1.3.h new file mode 100644 index 0000000..322cc42 --- /dev/null +++ b/config/board.ramps-v1.3.h @@ -0,0 +1,215 @@ + +/***************************************************************************\ +* * +* 1. CPU * +* * +\***************************************************************************/ + +//PROCESSORS_START +#ifndef __AVR_ATmega1280__ + #ifndef __AVR_ATmega2560__ + #error Wrong CPU type. + #endif +#endif +//PROCESSORS_END + +/** \def F_CPU + CPU clock rate. #ifndef required for Arduino compatibility. +*/ +#ifndef F_CPU +#define F_CPU 16000000UL +#endif + +/** \def MOTHERBOARD + This is the motherboard, as opposed to the extruder. See extruder/ directory + for GEN3 extruder firmware. +*/ +#define MOTHERBOARD + + +/***************************************************************************\ +* * +* 2. PINOUTS * +* * +\***************************************************************************/ + +#include "../arduino.h" + +#define X_STEP_PIN DIO54 +#define X_DIR_PIN DIO55 +#define X_MIN_PIN DIO3 +//#define X_MAX_PIN DIO2 +#define X_ENABLE_PIN DIO38 +//#define X_INVERT_DIR +//#define X_INVERT_MIN +//#define X_INVERT_MAX +#define X_INVERT_ENABLE + +#define Y_STEP_PIN DIO60 +#define Y_DIR_PIN DIO61 +#define Y_MIN_PIN DIO14 +//#define Y_MAX_PIN DIO15 +#define Y_ENABLE_PIN DIO56 +//#define Y_INVERT_DIR +//#define Y_INVERT_MIN +//#define Y_INVERT_MAX +#define Y_INVERT_ENABLE + +#define Z_STEP_PIN DIO46 +#define Z_DIR_PIN DIO48 +#define Z_MIN_PIN DIO18 +//#define Z_MAX_PIN DIO19 +#define Z_ENABLE_PIN DIO62 +//#define Z_INVERT_DIR +//#define Z_INVERT_MIN +//#define Z_INVERT_MAX +#define Z_INVERT_ENABLE + +#define E_STEP_PIN DIO26 +#define E_DIR_PIN DIO28 +#define E_ENABLE_PIN DIO24 +//#define E_INVERT_DIR +#define E_INVERT_ENABLE + +//#define PS_ON_PIN xxxx +//#define PS_MOSFET_PIN xxxx +//#define STEPPER_ENABLE_PIN xxxx +//#define STEPPER_INVERT_ENABLE + +/** \def DEBUG_LED_PIN + + Enable flashing of a LED during motor stepping. + + Disabled by default. Uncommenting this makes the binary a few bytes larger + and adds a few cycles to the step timing interrrupt in timer.c. Also used + for precision profiling (profiling works even without actually having such + a LED in hardware), see + http://reprap.org/wiki/Teacup_Firmware#Doing_precision_profiling +*/ +//#define DEBUG_LED_PIN DIO13 + + +/***************************************************************************\ +* * +* 3. TEMPERATURE SENSORS * +* * +\***************************************************************************/ + +#ifndef DEFINE_TEMP_SENSOR + #define DEFINE_TEMP_SENSOR(...) +#endif + +/** \def TEMP_MAX6675 TEMP_THERMISTOR TEMP_AD595 TEMP_PT100 TEMP_INTERCOM + Which temperature sensor types are you using? Leave all used ones + uncommented, comment out all others to save binary size and enhance + performance. +*/ +//#define TEMP_MAX6675 +#define TEMP_THERMISTOR +//#define TEMP_AD595 +//#define TEMP_PT100 +//#define TEMP_INTERCOM + +/** \def TEMP_SENSOR_PIN + Temperature sensor pins a user should be able to choose from in configtool. + All commented out. +*/ +//#define TEMP_SENSOR_PIN AIO13 +//#define TEMP_SENSOR_PIN AIO14 +//#define TEMP_SENSOR_PIN AIO15 + +/** \def DEFINE_TEMP_SENSOR + Define your temperature sensors here. One line for each sensor, only + limited by the number of available ATmega pins. + + Types are same as TEMP_ list above - TT_MAX6675, TT_THERMISTOR, TT_AD595, + TT_PT100, TT_INTERCOM. See list in temp.c. + + The "additional" field is used for TT_THERMISTOR only. It defines the + name of the table(s) in ThermistorTable.h to use. Typically, this is + THERMISTOR_EXTRUDER for the first or only table, or THERMISTOR_BED for + the second table. See also early in ThermistorTable.{single|double}.h. + + For a GEN3 set temp_type to TT_INTERCOM and temp_pin to AIO0. The pin + won't be used in this case. +*/ +// name type pin additional +//DEFINE_TEMP_SENSORS_START +DEFINE_TEMP_SENSOR(extruder, TT_THERMISTOR, AIO13, THERMISTOR_EXTRUDER) +DEFINE_TEMP_SENSOR(bed, TT_THERMISTOR, AIO14, THERMISTOR_BED) +//DEFINE_TEMP_SENSORS_END + + +/***************************************************************************\ +* * +* 4. HEATERS * +* * +\***************************************************************************/ + +#ifndef DEFINE_HEATER + #define DEFINE_HEATER(...) +#endif + +/** \def HEATER_PIN + Heater pins a user should be able to choose from in configtool. All + commented out. +*/ +//#define HEATER_PIN DIO5 +//#define HEATER_PIN DIO8 +//#define HEATER_PIN DIO10 + +/** \def DEFINE_HEATER + Define your heaters and devices here. + + To attach a heater to a temp sensor above, simply use exactly the same + name - copy+paste is your friend. Some common names are 'extruder', + 'bed', 'fan', 'motor', ... names with special meaning can be found + in gcode_process.c. Currently, these are: + HEATER_extruder (M104) + HEATER_bed (M140) + HEATER_fan (M106) + + Devices don't neccessarily have a temperature sensor, e.g. fans or + milling spindles. Operate such devices by setting their power (M106), + instead of setting their temperature (M104). + + Also note, the index of a heater (M106 P#) can differ from the index of + its attached temperature sensor (M104 P#) in case sensor-less devices + are defined or the order of the definitions differs. The first defined + device has the index 0 (zero). + + Set 'pwm' to ... + 1 for using PWM on a PWM-able pin and on/off on other pins. + 0 for using on/off on a PWM-able pin, too. + Using PWM usually gives smoother temperature control but can conflict + with slow switches, like solid state relays. PWM frequency can be + influenced globally with FAST_PWM, see below. +*/ +// name port pwm +//DEFINE_HEATERS_START +DEFINE_HEATER(extruder, DIO10, 1) +DEFINE_HEATER(bed, DIO8, 1) + +#define HEATER_EXTRUDER HEATER_extruder +#define HEATER_BED HEATER_bed +//DEFINE_HEATERS_END + + +/***************************************************************************\ +* * +* 5. COMMUNICATION OPTIONS * +* * +\***************************************************************************/ + +/** \def BAUD + Baud rate for the serial RS232 protocol connection to the host. Usually + 115200, other common values are 19200, 38400 or 57600. Ignored when USB_SERIAL + is defined. +*/ +#define BAUD 115200 + +/** \def USB_SERIAL + Define this for using USB instead of the serial RS232 protocol. Works on + USB-equipped ATmegas, like the ATmega32U4, only. +*/ +//#define USB_SERIAL diff --git a/config/config-syntax.txt b/config/config-syntax.txt new file mode 100644 index 0000000..93106f6 --- /dev/null +++ b/config/config-syntax.txt @@ -0,0 +1,160 @@ + +Syntax rules for configuration files in order to be used with the graphical +configuration utility. + + +Variable Names - both board and printer files +--------------------------------------------- + +Every #define variable used in the configuration must appear in the file. +If the variable is being given a value, it must appear normally i.e. + +#define NAME value + +or + +#define NAME + +If a variable is NOT to be defined, then it must be included in a single line +comment: + +//#define NAME + +It is immaterial if the name as a comment has a value. This line just marks +place where the variable will be inserted if it is defined. Although there +may be arbitrary spaces before and after the "//", no other intervening +characters may be placed on this line. + +The above is based on the variable NAME, not on value. For example, for +acceleration there are three different and mutually exclusive variable names: + +//#define ACCELERATION_REPRAP +#define ACCELERATION_RAMPING +//#define ACCELERATION_TEMPORAL + +All of these names MUST appear in the header file (with at most one of them +un-commented out). + +However, the variable KINEMATICS only has a finite set of values and would +seem to be a similar field, but there is only one variable name: KINEMATICS +- so it should only appear once. + +This is ok: + +//#define KINEMATICS KINEMATICS_STRAIGHT + +as is this: + +#define KINEMATICS KINEMATICS_STRAIGHT + +but this is not: + +#define KINEMATICS KINEMATICS_STRAIGHT +//#define KINEMATICS KINEMATICS_COREXY + + +Help Text - both board and printer files +---------------------------------------- + +The help text that is displayed on the GUI comes directly from the +configuration file itself. In order to be parsed correctly, help text must +be formatted as follows: + +The start of a help text block is as follows: + +/** \def NAME1 NAME2 NAME2 NAME4 ... + +All subsequent lines are copied exactly as entered into the help text until +the end delimiter is reached. The end delimiter is a line that starts with '*/'. + +Note that it is possible to specify multiple names on the start line. This +allows identical help text for similar or related fields. + + +Processor Preamble - Board file +------------------------------- + +The utility parses out the processor types from the config file when it loads +it, and it provides an interface where processors may be added or removed. +When a file is saved, the utility needs to know 1) where to insert the new +processor preamble, and 2) what to remove from the old file. To achieve this, +there is a sequence that identifies the start of the processor preamble, and +a sequence that indicates the end. As follows: + +//PROCESSORS_START +#ifndef __AVR_ATmega1280__ + #ifndef __AVR_ATmega2560__ + #error Wrong CPU type. + #endif +#endif +//PROCESSORS_END + +when a file is saved, all of the lines between these two delimiters are removed +and a new preamble is generated based on the values from the screen. The +supported processor types are defined in a python list in the data.py file. As +additional processors are supported, then can be added to this list. + +If the START/END delimiters are not present, then the processor preamble will +not be generated and the content from the original file will be retained. + + +Temperature Sensor Considerations - Board file +---------------------------------------------- + +The board configuration file has a section that includes the definition of +temperature sensors. Because the number of temperature sensors may grow or +shrink, or might even be 0, the utility needs to know where to place these +definitions. For this reason, delimiters are used to mark the beginning +and end of this section: + +//DEFINE_TEMP_SENSORS_START +DEFINE_TEMP_SENSOR(extruder, TT_THERMISTOR, AIO13, THERMISTOR_EXTRUDER) +DEFINE_TEMP_SENSOR(bed, TT_THERMISTOR, AIO14, THERMISTOR_BED) +//DEFINE_TEMP_SENSORS_END + +If the START/END delimiters are not found in the file, then the temperature +sensor definitions will not be generated and the original contents of the +file will be retained. + +In addition to this, it is possible to limit the number of pins that are +available to the user to use for thermistors. To do this, enter as many +lines of the following form as are needed. Note that these lines are +commented out. If no such lines are defined, then ALL pin names will be +allowed. + +//#define THERMISTOR_PIN AIO13 +//#define THERMISTOR_PIN AIO14 +//#define THERMISTOR_PIN AIO15 + + +Heater Considerations - Board file +---------------------------------- + +The board configuration file has a section that includes the definition of +heaters. Because the number of heaters may grow or shrink, or might even be 0, +the utility needs to know where to place these definitions. For this reason, +delimiters are used to mark the beginning and end of this section: + +//DEFINE_HEATERS_START +DEFINE_HEATER(extruder, DIO10, 1) +DEFINE_HEATER(bed, DIO8, 1) + +#define HEATER_EXTRUDER HEATER_extruder +#define HEATER_BED HEATER_bed +//DEFINE_HEATERS_END + +Note that these delimiters also enclose the defines for the heater names. This +is different than in the sensor case where these definitions are not necessary. + +If the START/END delimiters are not found in the file, then the temperature +sensor definitions will not be generated and the original contents of the +file will be retained. + +In addition to this, it is possible to limit the number of pins that are +available to the user to use for heaters. To do this, enter as many lines of +the following form as are needed. Note that these lines are commented out. +If no such lines are defined, then ALL pin names will be allowed. + +//#define HEATER_PIN DIO10 +//#define HEATER_PIN DIO8 +//#define HEATER_PIN DIO5 diff --git a/config/printer.mendel.h b/config/printer.mendel.h new file mode 100644 index 0000000..6cc3135 --- /dev/null +++ b/config/printer.mendel.h @@ -0,0 +1,339 @@ + +/***************************************************************************\ +* * +* 6. MECHANICAL/HARDWARE * +* * +\***************************************************************************/ + +/** \def KINEMATICS + This defines the type of kinematics your printer uses. That's essential! + + Valid values (see dda_kinematics.h): + + KINEMATICS_STRAIGHT Motors move axis directions directly. This is the + traditional type, found in many printers, including + Mendel, Prusa i3, Mendel90, Ormerod, Mantis. + + KINEMATICS_COREXY A bot using CoreXY kinematics. Typical for CoreXY are + long and crossing toothed belts and a print head moving + on the X-Y-plane. +*/ +#define KINEMATICS KINEMATICS_STRAIGHT + +/** \def STEPS_PER_M_X STEPS_PER_M_Y STEPS_PER_M_Z STEPS_PER_M_E + Steps per meter ( = steps per mm * 1000 ), calculate these values + appropriate for your machine. + + All numbers are integers, so no decimal point, please :-) + + Valid range: 20 to 4'0960'000 (0.02 to 40960 steps/mm) +*/ +#define STEPS_PER_M_X 40000 +#define STEPS_PER_M_Y 40000 +#define STEPS_PER_M_Z 320000 +#define STEPS_PER_M_E 96271 + +/** \def MAXIMUM_FEEDRATE_X MAXIMUM_FEEDRATE_Y MAXIMUM_FEEDRATE_Z MAXIMUM_FEEDRATE_E + Used for G0 rapid moves and as a cap for all other feedrates. +*/ +#define MAXIMUM_FEEDRATE_X 6000 +#define MAXIMUM_FEEDRATE_Y 6000 +#define MAXIMUM_FEEDRATE_Z 200 +#define MAXIMUM_FEEDRATE_E 6000 + +/** \def SEARCH_FEEDRATE_X SEARCH_FEEDRATE_Y SEARCH_FEEDRATE_Z + Used when doing precision endstop search and as default feedrate. No + SEARCH_FEEDRATE_E, as E can't be searched. +*/ +#define SEARCH_FEEDRATE_X 200 +#define SEARCH_FEEDRATE_Y 200 +#define SEARCH_FEEDRATE_Z 50 + +/** \def ENDSTOP_CLEARANCE_X ENDSTOP_CLEARANCE_Y ENDSTOP_CLEARANCE_Z + + When hitting an endstop, Teacup properly decelerates instead of doing an + aprupt stop to save your mechanics. Ineviteably, this means it overshoots + the endstop trigger point by some distance. + + To deal with this, Teacup adapts homing movement speeds to what your + endstops can deal with. The higher the allowed acceleration ( = deceleration, + see #define ACCELERATION) and the more clearance the endstop comes with, + the faster Teacup will do homing movements. + + Set here how many micrometers (mm * 1000) your endstop allows the carriage + to overshoot the trigger point. Typically 1000 or 2000 for mechanical + endstops, more for optical ones. You can set it to zero, in which case + SEARCH_FEEDRATE_{XYZ} is used, but expect very slow homing movements. + + Units: micrometers + Sane values: 0 to 20000 (0 to 20 mm) + Valid range: 0 to 1000000 +*/ +#define ENDSTOP_CLEARANCE_X 1000 +#define ENDSTOP_CLEARANCE_Y 1000 +#define ENDSTOP_CLEARANCE_Z 100 + +/** \def X_MIN X_MAX Y_MIN Y_MAX Z_MIN Z_MAX + Soft axis limits, in mm. Define them to your machine's size relative to what + your host considers to be the origin. +*/ +//#define X_MIN 0.0 +//#define X_MAX 200.0 + +//#define Y_MIN 0.0 +//#define Y_MAX 200.0 + +//#define Z_MIN 0.0 +//#define Z_MAX 140.0 + +/** \def E_ABSOLUTE + Some G-code creators produce relative length commands for the extruder, + others absolute ones. G-code using absolute lengths can be recognized when + there are G92 E0 commands from time to time. If you have G92 E0 in your + G-code, define this flag. + + This is the startup default and can be changed with M82/M83 while running. +*/ +#define E_ABSOLUTE + +/** \def ACCELERATION_REPRAP ACCELERATION_RAMPING ACCELERATION_TEMPORAL + Choose optionally one of ACCELERATION_REPRAP, ACCELERATION_RAMPING or + ACCELERATION_TEMPORAL. With none of them defined, movements are done + without acceleration. Recommended is ACCELERATION_RAMPING. +*/ +//#define ACCELERATION_REPRAP +#define ACCELERATION_RAMPING +//#define ACCELERATION_TEMPORAL + +/** \def ACCELERATION + How fast to accelerate when using ACCELERATION_RAMPING. Start with 10 for + milling (high precision) or 1000 for printing. + + Units: mm/s^2 + Useful range: 1 to 10'000 +*/ +#define ACCELERATION 1000 /* float */ + +/** \def LOOKAHEAD + Define this to enable look-ahead during *ramping* acceleration to smoothly + transition between moves instead of performing a dead stop every move. + Enabling look-ahead requires about 3600 bytes of flash memory. +*/ +#define LOOKAHEAD + +/** \def MAX_JERK_X MAX_JERK_Y MAX_JERK_Z MAX_JERK_E + When performing look-ahead, we need to decide what an acceptable jerk to the + mechanics is. Look-ahead attempts to instantly change direction at movement + crossings, which means instant changes in the speed of the axes participating + in the movement. Define here how big the speed bumps on each of the axes is + allowed to be. + + If you want a full stop before and after moving a specific axis, define + MAX_JERK of this axis to 0. This is often wanted for the Z axis. If you want + to ignore jerk on an axis, define it to twice the maximum feedrate of this + axis. + + Having these values too low results in more than neccessary slowdown at + movement crossings, but is otherwise harmless. Too high values can result + in stepper motors suddenly stalling. If angles between movements in your + G-code are small and your printer runs through entire curves full speed, + there's no point in raising the values. + + Units: mm/min + Sane values: 0 to 400 + Valid range: 0 to 65535 +*/ +#define MAX_JERK_X 200 +#define MAX_JERK_Y 200 +#define MAX_JERK_Z 0 +#define MAX_JERK_E 200 + + +/***************************************************************************\ +* * +* 7. MISCELLANEOUS OPTIONS * +* * +\***************************************************************************/ + +/** \def USE_INTERNAL_PULLUPS + The ATmega has internal pullup resistors on it's input pins which are + counterproductive with the commonly used eletronic endstops, so they should + be switched off. For other endstops, like mechanical ones, you may want to + uncomment this. +*/ +//#define USE_INTERNAL_PULLUPS + +/** \def TEMP_HYSTERESIS + Actual temperature must be target +/- this hysteresis before target + temperature is considered to be achieved. Also, BANG_BANG tries to stay + within half of this hysteresis. + + Unit: degree Celsius +*/ +#define TEMP_HYSTERESIS 10 + +/** \def TEMP_RESIDENCY_TIME + Actual temperature must be close to target (within set temperature + +- TEMP_HYSTERESIS) for this long before target is achieved (and a M116 + succeeds). + + Unit: seconds +*/ +#define TEMP_RESIDENCY_TIME 60 + +/** \def TEMP_EWMA + Smooth noisy temperature sensors. Good hardware shouldn't be noisy. Set to + 1.0 for unfiltered data (and a 140 bytes smaller binary). + + Instrument Engineer's Handbook, 4th ed, Vol 2 p126 says values of + 0.05 to 0.1 are typical. Smaller is smoother but slower adjusting, larger is + quicker but rougher. If you need to use this, set the PID parameter to zero + (M132 S0) to make the PID loop insensitive to noise. + + Valid range: 0.001 to 1.0 +*/ +#define TEMP_EWMA 1.0 /* float */ + +/** \def HEATER_SANITY_CHECK + Check if heater responds to changes in target temperature, disable and spit + errors if not largely untested, please comment in forum if this works, or + doesn't work for you! +*/ +//#define HEATER_SANITY_CHECK + +/** \def XONXOFF + Xon/Xoff flow control. + + Redundant when using RepRap Host for sending G-code, but mandatory when + sending G-code files with a plain terminal emulator, like GtkTerm (Linux), + CoolTerm (Mac) or HyperTerminal (Windows). +*/ +//#define XONXOFF + +/** \def EECONFIG + Enable EEPROM configuration storage. + + Enabled by default. Commenting this out makes the binary several hundred + bytes smaller, so you might want to disable EEPROM storage on small MCUs, + like the ATmega168. +*/ +#define EECONFIG + +/** \def DEBUG + Enables /heaps/ of extra output, and some extra M-codes. + + WARNING: this WILL break most host-side talkers that expect particular + responses from firmware such as reprap host and replicatorG use with serial + terminal or other suitable talker only. +*/ +//#define DEBUG + +/** \def BANG_BANG + Drops PID loop from heater control, reduces code size significantly + (1300 bytes!). +*/ +//#define BANG_BANG + +/** \def BANG_BANG_ON + PWM value for Bang Bang 'on'. +*/ +//#define BANG_BANG_ON 200 + +/** \def BANG_BANG_OFF + PWM value for Bang Bang 'off'. +*/ +//#define BANG_BANG_OFF 45 + +/** \def MOVEBUFFER_SIZE + Move buffer size, in number of moves. + + Note that each move takes a fair chunk of ram (107 bytes as of this writing), + so don't make the buffer too big. However, a larger movebuffer will probably + help with lots of short consecutive moves, as each move takes a bunch of + math (hence time) to set up so a longer buffer allows more of the math to + be done during preceding longer moves. +*/ +#define MOVEBUFFER_SIZE 8 + +/** \def DC_EXTRUDER DC_EXTRUDER_PWM + If you have a DC motor extruder, configure it as a "heater" above and define + this value as the index or name. You probably also want to comment out + E_STEP_PIN and E_DIR_PIN in the Pinouts section above. +*/ +//#define DC_EXTRUDER HEATER_motor +//#define DC_EXTRUDER_PWM 180 + +/** \def USE_WATCHDOG + Teacup implements a watchdog, which has to be reset every 250ms or it will + reboot the controller. As rebooting (and letting the GCode sending + application trying to continue the build with a then different Home point) + is probably even worse than just hanging, and there is no better restore + code in place, this is disabled for now. +*/ +//#define USE_WATCHDOG + +/** \def REFERENCE + Which analog reference to use. See analog.h for choices. +*/ +#define REFERENCE REFERENCE_AVCC + +/** \def STEP_INTERRUPT_INTERRUPTIBLE + This option makes the step interrupt interruptible (nested). This should + help immensely with dropped serial characters, but may also make debugging + infuriating due to the complexities arising from nested interrupts. + + Note: disable this option if you're using a '168 or for some reason your + ram usage is above 90%. This option hugely increases likelihood of stack + smashing. +*/ +#define STEP_INTERRUPT_INTERRUPTIBLE 1 + +/** \def TH_COUNT + Temperature history count. This is how many temperature readings to keep in + order to calculate derivative in PID loop higher values make PID derivative + term more stable at the expense of reaction time. +*/ +#define TH_COUNT 8 + +/** \def FAST_PWM + Teacup offers two PWM frequencies, 76(61) Hz and 78000(62500) Hz on a + 20(16) MHz electronics. The slower one is the default, as it's the safer + choice and reduces MOSFET heating. Drawback is, in a quiet environment you + might notice the heaters and your power supply humming. + + Uncomment this option if you want to get rid of this humming and can afford + a hotter MOSFET or want faster PWM for other reasons. + + See also: http://reprap.org/wiki/Gen7_Research#MOSFET_heat_and_PWM +*/ +//#define FAST_PWM + +/** \def PID_SCALE + This is the scaling of internally stored PID values. 1024L is a good value. +*/ +#define PID_SCALE 1024L + +/** \def ENDSTOP_STEPS + Number of steps to run into the endstops intentionally. As endstops trigger + false alarm sometimes, Teacup debounces them by counting a number of + consecutive positives. + + Valid range: 1...255. Use 4 or less for reliable endstops, 8 or even more + for flaky ones. +*/ +#define ENDSTOP_STEPS 4 + +/** \def CANNED_CYCLE + G-code commands in this string will be executed over and over again, without + user interaction or even a serial connection. It's purpose is e.g. for + exhibitions or when using Teacup for other purposes than printing. You can + add any G-code supported by Teacup. + + Note: don't miss these newlines (\n) and backslashes (\). +*/ +/* +#define CANNED_CYCLE "G1 X100 F3000\n" \ +"G4 P500\n" \ +"G1 X0\n" \ +"G4 P500\n" +*/ diff --git a/config/printer.wolfstrap.h b/config/printer.wolfstrap.h new file mode 100644 index 0000000..eb1933a --- /dev/null +++ b/config/printer.wolfstrap.h @@ -0,0 +1,339 @@ + +/***************************************************************************\ +* * +* 6. MECHANICAL/HARDWARE * +* * +\***************************************************************************/ + +/** \def KINEMATICS + This defines the type of kinematics your printer uses. That's essential! + + Valid values (see dda_kinematics.h): + + KINEMATICS_STRAIGHT Motors move axis directions directly. This is the + traditional type, found in many printers, including + Mendel, Prusa i3, Mendel90, Ormerod, Mantis. + + KINEMATICS_COREXY A bot using CoreXY kinematics. Typical for CoreXY are + long and crossing toothed belts and a print head moving + on the X-Y-plane. +*/ +#define KINEMATICS KINEMATICS_STRAIGHT + +/** \def STEPS_PER_M_X STEPS_PER_M_Y STEPS_PER_M_Z STEPS_PER_M_E + Steps per meter ( = steps per mm * 1000 ), calculate these values + appropriate for your machine. + + All numbers are integers, so no decimal point, please :-) + + Valid range: 20 to 4'0960'000 (0.02 to 40960 steps/mm) +*/ +#define STEPS_PER_M_X 1280000 +#define STEPS_PER_M_Y 1280000 +#define STEPS_PER_M_Z 1280000 +#define STEPS_PER_M_E 96271 + +/** \def MAXIMUM_FEEDRATE_X MAXIMUM_FEEDRATE_Y MAXIMUM_FEEDRATE_Z MAXIMUM_FEEDRATE_E + Used for G0 rapid moves and as a cap for all other feedrates. +*/ +#define MAXIMUM_FEEDRATE_X 600 +#define MAXIMUM_FEEDRATE_Y 600 +#define MAXIMUM_FEEDRATE_Z 600 +#define MAXIMUM_FEEDRATE_E 2000 + +/** \def SEARCH_FEEDRATE_X SEARCH_FEEDRATE_Y SEARCH_FEEDRATE_Z + Used when doing precision endstop search and as default feedrate. No + SEARCH_FEEDRATE_E, as E can't be searched. +*/ +#define SEARCH_FEEDRATE_X 50 +#define SEARCH_FEEDRATE_Y 50 +#define SEARCH_FEEDRATE_Z 50 + +/** \def ENDSTOP_CLEARANCE_X ENDSTOP_CLEARANCE_Y ENDSTOP_CLEARANCE_Z + + When hitting an endstop, Teacup properly decelerates instead of doing an + aprupt stop to save your mechanics. Ineviteably, this means it overshoots + the endstop trigger point by some distance. + + To deal with this, Teacup adapts homing movement speeds to what your + endstops can deal with. The higher the allowed acceleration ( = deceleration, + see #define ACCELERATION) and the more clearance the endstop comes with, + the faster Teacup will do homing movements. + + Set here how many micrometers (mm * 1000) your endstop allows the carriage + to overshoot the trigger point. Typically 1000 or 2000 for mechanical + endstops, more for optical ones. You can set it to zero, in which case + SEARCH_FEEDRATE_{XYZ} is used, but expect very slow homing movements. + + Units: micrometers + Sane values: 0 to 20000 (0 to 20 mm) + Valid range: 0 to 1000000 +*/ +#define ENDSTOP_CLEARANCE_X 1000 +#define ENDSTOP_CLEARANCE_Y 1000 +#define ENDSTOP_CLEARANCE_Z 100 + +/** \def X_MIN X_MAX Y_MIN Y_MAX Z_MIN Z_MAX + Soft axis limits, in mm. Define them to your machine's size relative to what + your host considers to be the origin. +*/ +//#define X_MIN 0.0 +//#define X_MAX 200.0 + +//#define Y_MIN 0.0 +//#define Y_MAX 200.0 + +//#define Z_MIN 0.0 +//#define Z_MAX 140.0 + +/** \def E_ABSOLUTE + Some G-code creators produce relative length commands for the extruder, + others absolute ones. G-code using absolute lengths can be recognized when + there are G92 E0 commands from time to time. If you have G92 E0 in your + G-code, define this flag. + + This is the startup default and can be changed with M82/M83 while running. +*/ +#define E_ABSOLUTE + +/** \def ACCELERATION_REPRAP ACCELERATION_RAMPING ACCELERATION_TEMPORAL + Choose optionally one of ACCELERATION_REPRAP, ACCELERATION_RAMPING or + ACCELERATION_TEMPORAL. With none of them defined, movements are done + without acceleration. Recommended is ACCELERATION_RAMPING. +*/ +//#define ACCELERATION_REPRAP +#define ACCELERATION_RAMPING +//#define ACCELERATION_TEMPORAL + +/** \def ACCELERATION + How fast to accelerate when using ACCELERATION_RAMPING. Start with 10 for + milling (high precision) or 1000 for printing. + + Units: mm/s^2 + Useful range: 1 to 10'000 +*/ +#define ACCELERATION 100 /* float */ + +/** \def LOOKAHEAD + Define this to enable look-ahead during *ramping* acceleration to smoothly + transition between moves instead of performing a dead stop every move. + Enabling look-ahead requires about 3600 bytes of flash memory. +*/ +#define LOOKAHEAD + +/** \def MAX_JERK_X MAX_JERK_Y MAX_JERK_Z MAX_JERK_E + When performing look-ahead, we need to decide what an acceptable jerk to the + mechanics is. Look-ahead attempts to instantly change direction at movement + crossings, which means instant changes in the speed of the axes participating + in the movement. Define here how big the speed bumps on each of the axes is + allowed to be. + + If you want a full stop before and after moving a specific axis, define + MAX_JERK of this axis to 0. This is often wanted for the Z axis. If you want + to ignore jerk on an axis, define it to twice the maximum feedrate of this + axis. + + Having these values too low results in more than neccessary slowdown at + movement crossings, but is otherwise harmless. Too high values can result + in stepper motors suddenly stalling. If angles between movements in your + G-code are small and your printer runs through entire curves full speed, + there's no point in raising the values. + + Units: mm/min + Sane values: 0 to 400 + Valid range: 0 to 65535 +*/ +#define MAX_JERK_X 20 +#define MAX_JERK_Y 20 +#define MAX_JERK_Z 0 +#define MAX_JERK_E 200 + + +/***************************************************************************\ +* * +* 7. MISCELLANEOUS OPTIONS * +* * +\***************************************************************************/ + +/** \def USE_INTERNAL_PULLUPS + The ATmega has internal pullup resistors on it's input pins which are + counterproductive with the commonly used eletronic endstops, so they should + be switched off. For other endstops, like mechanical ones, you may want to + uncomment this. +*/ +//#define USE_INTERNAL_PULLUPS + +/** \def TEMP_HYSTERESIS + Actual temperature must be target +/- this hysteresis before target + temperature is considered to be achieved. Also, BANG_BANG tries to stay + within half of this hysteresis. + + Unit: degree Celsius +*/ +#define TEMP_HYSTERESIS 10 + +/** \def TEMP_RESIDENCY_TIME + Actual temperature must be close to target (within set temperature + +- TEMP_HYSTERESIS) for this long before target is achieved (and a M116 + succeeds). + + Unit: seconds +*/ +#define TEMP_RESIDENCY_TIME 60 + +/** \def TEMP_EWMA + Smooth noisy temperature sensors. Good hardware shouldn't be noisy. Set to + 1.0 for unfiltered data (and a 140 bytes smaller binary). + + Instrument Engineer's Handbook, 4th ed, Vol 2 p126 says values of + 0.05 to 0.1 are typical. Smaller is smoother but slower adjusting, larger is + quicker but rougher. If you need to use this, set the PID parameter to zero + (M132 S0) to make the PID loop insensitive to noise. + + Valid range: 0.001 to 1.0 +*/ +#define TEMP_EWMA 1.0 /* float */ + +/** \def HEATER_SANITY_CHECK + Check if heater responds to changes in target temperature, disable and spit + errors if not largely untested, please comment in forum if this works, or + doesn't work for you! +*/ +//#define HEATER_SANITY_CHECK + +/** \def XONXOFF + Xon/Xoff flow control. + + Redundant when using RepRap Host for sending G-code, but mandatory when + sending G-code files with a plain terminal emulator, like GtkTerm (Linux), + CoolTerm (Mac) or HyperTerminal (Windows). +*/ +//#define XONXOFF + +/** \def EECONFIG + Enable EEPROM configuration storage. + + Enabled by default. Commenting this out makes the binary several hundred + bytes smaller, so you might want to disable EEPROM storage on small MCUs, + like the ATmega168. +*/ +#define EECONFIG + +/** \def DEBUG + Enables /heaps/ of extra output, and some extra M-codes. + + WARNING: this WILL break most host-side talkers that expect particular + responses from firmware such as reprap host and replicatorG use with serial + terminal or other suitable talker only. +*/ +//#define DEBUG + +/** \def BANG_BANG + Drops PID loop from heater control, reduces code size significantly + (1300 bytes!). +*/ +//#define BANG_BANG + +/** \def BANG_BANG_ON + PWM value for Bang Bang 'on'. +*/ +//#define BANG_BANG_ON 200 + +/** \def BANG_BANG_OFF + PWM value for Bang Bang 'off'. +*/ +//#define BANG_BANG_OFF 45 + +/** \def MOVEBUFFER_SIZE + Move buffer size, in number of moves. + + Note that each move takes a fair chunk of ram (107 bytes as of this writing), + so don't make the buffer too big. However, a larger movebuffer will probably + help with lots of short consecutive moves, as each move takes a bunch of + math (hence time) to set up so a longer buffer allows more of the math to + be done during preceding longer moves. +*/ +#define MOVEBUFFER_SIZE 8 + +/** \def DC_EXTRUDER DC_EXTRUDER_PWM + If you have a DC motor extruder, configure it as a "heater" above and define + this value as the index or name. You probably also want to comment out + E_STEP_PIN and E_DIR_PIN in the Pinouts section above. +*/ +//#define DC_EXTRUDER HEATER_motor +//#define DC_EXTRUDER_PWM 180 + +/** \def USE_WATCHDOG + Teacup implements a watchdog, which has to be reset every 250ms or it will + reboot the controller. As rebooting (and letting the GCode sending + application trying to continue the build with a then different Home point) + is probably even worse than just hanging, and there is no better restore + code in place, this is disabled for now. +*/ +//#define USE_WATCHDOG + +/** \def REFERENCE + Which analog reference to use. See analog.h for choices. +*/ +#define REFERENCE REFERENCE_AVCC + +/** \def STEP_INTERRUPT_INTERRUPTIBLE + This option makes the step interrupt interruptible (nested). This should + help immensely with dropped serial characters, but may also make debugging + infuriating due to the complexities arising from nested interrupts. + + Note: disable this option if you're using a '168 or for some reason your + ram usage is above 90%. This option hugely increases likelihood of stack + smashing. +*/ +#define STEP_INTERRUPT_INTERRUPTIBLE 1 + +/** \def TH_COUNT + Temperature history count. This is how many temperature readings to keep in + order to calculate derivative in PID loop higher values make PID derivative + term more stable at the expense of reaction time. +*/ +#define TH_COUNT 8 + +/** \def FAST_PWM + Teacup offers two PWM frequencies, 76(61) Hz and 78000(62500) Hz on a + 20(16) MHz electronics. The slower one is the default, as it's the safer + choice and reduces MOSFET heating. Drawback is, in a quiet environment you + might notice the heaters and your power supply humming. + + Uncomment this option if you want to get rid of this humming and can afford + a hotter MOSFET or want faster PWM for other reasons. + + See also: http://reprap.org/wiki/Gen7_Research#MOSFET_heat_and_PWM +*/ +//#define FAST_PWM + +/** \def PID_SCALE + This is the scaling of internally stored PID values. 1024L is a good value. +*/ +#define PID_SCALE 1024L + +/** \def ENDSTOP_STEPS + Number of steps to run into the endstops intentionally. As endstops trigger + false alarm sometimes, Teacup debounces them by counting a number of + consecutive positives. + + Valid range: 1...255. Use 4 or less for reliable endstops, 8 or even more + for flaky ones. +*/ +#define ENDSTOP_STEPS 4 + +/** \def CANNED_CYCLE + G-code commands in this string will be executed over and over again, without + user interaction or even a serial connection. It's purpose is e.g. for + exhibitions or when using Teacup for other purposes than printing. You can + add any G-code supported by Teacup. + + Note: don't miss these newlines (\n) and backslashes (\). +*/ +/* +#define CANNED_CYCLE "G1 X100 F3000\n" \ +"G4 P500\n" \ +"G1 X0\n" \ +"G4 P500\n" +*/ diff --git a/config_helptext.py b/config_helptext.py deleted file mode 100644 index 055575a..0000000 --- a/config_helptext.py +++ /dev/null @@ -1,229 +0,0 @@ -helpText = { -'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). \ -all numbers are integers, so no decimal point", - -'MAXIMUM_FEEDRATE': "maximum feed rate - in mm/min - for G0 rapid moves and \ -as a cap for all other feedrates", - -'SEARCH_FEEDRATE': "search feed rate - in mm/min - used when doing precision \ -endstop search and as a default feed rate", - -'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\ -Set here how many micrometers (mm * 1000) your endstop allows the carriage to \ -overshoot the\ntrigger point. Typically 1000 or 2000 for mechanical endstops, \ -more for optical ones.\nYou can set it to zero, in which case \ -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", - -'MINMAX': "soft axis limits, in mm.\n\ndefine them to your machine's size \ -relative to what your host considers to be the origin.", - -'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.", - -'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.", -'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.", -'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", -'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.", -'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\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", - -'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.", -} diff --git a/configtool/__init__.py b/configtool/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/configtool/accelerationpage.py b/configtool/accelerationpage.py new file mode 100644 index 0000000..4dde81f --- /dev/null +++ b/configtool/accelerationpage.py @@ -0,0 +1,124 @@ + +import wx +from configtool.page import Page + + +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, self.onAccTypeSelect) + style = 0 + + sbox.Add(rb, 1, wx.LEFT + wx.RIGHT, 16) + sbox.AddSpacer((5, 5)) + + self.rbNone = wx.RadioButton(self, wx.ID_ANY, "None", style = style) + self.rbNone.SetValue(True) + self.Bind(wx.EVT_RADIOBUTTON, self.onAccTypeSelect, self.rbNone) + sbox.Add(self.rbNone, 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, self.onTextCtrlFloat) + self.textControls[k].Enable(False) + + sbox.Add(tc) + sbox.AddSpacer((5, 5)) + + k = 'LOOKAHEAD' + cb = self.addCheckBox(k, 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") + sbox = wx.StaticBoxSizer(b, wx.VERTICAL) + sbox.AddSpacer((5, 5)) + for k in self.jerkKeys: + tc = self.addTextCtrl(k, 40, 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) + self.enableAll(False) + + def enableAll(self, flag = True): + self.rbNone.Enable(flag) + Page.enableAll(self, flag) + + 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) + self.enableAll(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) diff --git a/configtool/addheaterdlg.py b/configtool/addheaterdlg.py new file mode 100644 index 0000000..7fd43a8 --- /dev/null +++ b/configtool/addheaterdlg.py @@ -0,0 +1,115 @@ + +import wx +from configtool.data import pinNames, BSIZESMALL + + +class AddHeaterDlg(wx.Dialog): + def __init__(self, parent, names, pins): + wx.Dialog.__init__(self, parent, wx.ID_ANY, "Add heater", size = (400, 204)) + self.Bind(wx.EVT_CLOSE, self.onCancel) + + self.names = names + + self.nameValid = 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.chPin = wx.Choice(self, wx.ID_ANY, choices = pins) + self.chPin.Bind(wx.EVT_CHOICE, self.onChoice) + self.chPin.SetSelection(0) + lsz.Add(self.chPin) + self.chPin.SetToolTipString("Choose a pin for this heater.") + + gsz.Add(lsz, pos = (3, 1)) + + self.cbPwm = wx.CheckBox(self, wx.ID_ANY, "PWM") + self.cbPwm.SetToolTipString("Use Pulse Width Modulation in case the " + "choosen pin allows to do so.") + + 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 onChoice(self, evt): + pass + + def checkDlgValidity(self): + if self.nameValid: + self.bSave.Enable(True) + else: + self.bSave.Enable(False) + + def getValues(self): + nm = self.tcName.GetValue() + pin = pinNames[self.chPin.GetSelection()] + 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) diff --git a/configtool/addsensordlg.py b/configtool/addsensordlg.py new file mode 100644 index 0000000..8720bac --- /dev/null +++ b/configtool/addsensordlg.py @@ -0,0 +1,174 @@ + +import wx +from configtool.data import pinNames, BSIZESMALL + + +class AddSensorDlg(wx.Dialog): + def __init__(self, parent, names, sl, pins): + 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.nameValid = 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) + 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) + vsz.AddSpacer((10, 10)) + + lsz = wx.BoxSizer(wx.HORIZONTAL) + st = wx.StaticText(self, wx.ID_ANY, "Pin:", size = (80, -1), + style = wx.ALIGN_RIGHT) + lsz.Add(st) + + self.choiceList = pinNames + self.chPin = wx.Choice(self, wx.ID_ANY, choices = pins) + self.chPin.Bind(wx.EVT_CHOICE, self.onChoice) + self.chPin.SetSelection(0) + lsz.Add(self.chPin) + self.chPin.SetToolTipString("Choose a pin name for this sensor.") + + vsz.Add(lsz) + vsz.AddSpacer((10, 10)) + + 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 checkDlgValidity(self): + if self.nameValid: + 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 onChoice(self, evt): + pass + + def onSensorType(self, evt): + rb = evt.GetEventObject() + label = rb.GetLabel() + + self.selectSensorType(label) + + evt.Skip() + + def getValues(self): + nm = self.tcName.GetValue() + pin = pinNames[self.chPin.GetSelection()] + 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) diff --git a/configtool/boardpanel.py b/configtool/boardpanel.py new file mode 100644 index 0000000..e7e98b9 --- /dev/null +++ b/configtool/boardpanel.py @@ -0,0 +1,515 @@ + +import os +import wx +import re + +from configtool.data import (supportedCPUs, defineValueFormat, + defineBoolFormat, defineHeaterFormat, reCommDefBL, + reCommDefBoolBL, reHelpTextStart, reHelpTextEnd, + reStartSensors, reEndSensors, reStartHeaters, + reEndHeaters, reStartProcessors, reEndProcessors, + reCandHeatPins, reCandThermPins, reAVR, reDefine, + reDefineBL, reDefQS, reDefQSm, reDefQSm2, + reDefBool, reDefBoolBL, reDefHT, reDefTS, + reHeater, reSensor3, reSensor4) +from configtool.pinoutspage import PinoutsPage +from configtool.sensorpage import SensorsPage +from configtool.heaterspage import HeatersPage +from configtool.communicationspage import CommunicationsPage +from configtool.cpupage import CpuPage + + +class BoardPanel(wx.Panel): + def __init__(self, parent, nb, folder): + wx.Panel.__init__(self, nb, wx.ID_ANY) + self.parent = parent + + self.cfgValues = {} + self.heaters = [] + self.sensors = [] + self.processors = [] + self.candHeatPins = [] + self.candThermPins = [] + self.dir = os.path.join(folder, "config") + + sz = wx.BoxSizer(wx.HORIZONTAL) + + self.nb = wx.Notebook(self, wx.ID_ANY, size = (21, 21), + style = wx.BK_DEFAULT) + + self.pages = [] + self.titles = [] + self.pageModified = [] + self.pageValid = [] + + self.pgCpu = CpuPage(self, self.nb, len(self.pages)) + text = "CPU" + self.nb.AddPage(self.pgCpu, text) + self.pages.append(self.pgCpu) + 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) + + sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5) + + self.SetSizer(sz) + + def assertModified(self, pg, flag = True): + self.pageModified[pg] = flag + self.modifyTab(pg) + + def isModified(self): + return (True in self.pageModified) + + def assertValid(self, pg, flag = True): + self.pageValid[pg] = flag + self.modifyTab(pg) + + if False in self.pageValid: + self.parent.enableSaveBoard(False) + else: + self.parent.enableSaveBoard(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: + pfx = "" + + self.nb.SetPageText(pg, pfx + self.titles[pg]) + + def setHeaters(self, ht): + self.parent.setHeaters(ht) + + def onClose(self, evt): + if not self.confirmLoseChanges("exit"): + return + + self.Destroy() + + def confirmLoseChanges(self, msg): + if True not in self.pageModified: + return True + + dlg = wx.MessageDialog(self, "Are you sure you want to " + msg + "?\n" + "There are changes to your board " + "configuration 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 board configuration"): + return + + wildcard = "Board configuration (board.*.h)|board.*.h" + + dlg = wx.FileDialog(self, message = "Choose a board config file", + defaultDir = self.dir, defaultFile = "", + wildcard = wildcard, style = wx.OPEN | wx.CHANGE_DIR) + + path = None + if dlg.ShowModal() == wx.ID_OK: + path = dlg.GetPath() + + dlg.Destroy() + if path is None: + return + + + self.dir = os.path.dirname(path) + rc = self.loadConfigFile(path) + + if not rc: + dlg = wx.MessageDialog(self, "Unable to process file %s." % path, + "File error", wx.OK + wx.ICON_ERROR) + dlg.ShowModal() + dlg.Destroy() + return + + self.parent.enableSaveBoard(True) + self.parent.setBoardTabText("Board <%s>" % os.path.basename(path)) + self.pgHeaters.setCandidatePins(self.candHeatPins) + self.pgSensors.setCandidatePins(self.candThermPins) + + for pg in self.pages: + pg.insertValues(self.cfgValues) + pg.setHelpText(self.helpText) + + self.pgSensors.setSensors(self.sensors) + self.pgHeaters.setHeaters(self.heaters) + self.pgCpu.setProcessors(self.processors) + + def loadConfigFile(self, fn): + try: + self.cfgBuffer = list(open(fn)) + except: + return False + + self.configFile = fn + + self.processors = [] + self.sensors = [] + self.heaters = [] + self.candHeatPins = [] + self.candThermPins = [] + gatheringHelpText = False + helpTextString = "" + helpKey = None + + self.cfgValues = {} + self.helpText = {} + + prevLines = "" + for ln in self.cfgBuffer: + if gatheringHelpText: + if reHelpTextEnd.match(ln): + gatheringHelpText = False + hk = helpKey.split() + for k in hk: + self.helpText[k] = helpTextString + helpTextString = "" + helpKey = None + continue + else: + helpTextString += ln + continue + + m = reHelpTextStart.match(ln) + if m: + t = m.groups() + gatheringHelpText = True + helpKey = t[0] + continue + + if ln.rstrip().endswith("\\"): + prevLines += ln.rstrip()[:-1] + continue + + if prevLines != "": + ln = prevLines + ln + prevLines = "" + + if ln.lstrip().startswith("//"): + m = reCandThermPins.match(ln) + if m: + t = m.groups() + if len(t) == 1: + self.candThermPins.append(t[0]) + continue + + m = reCandHeatPins.match(ln) + if m: + t = m.groups() + if len(t) == 1: + self.candHeatPins.append(t[0]) + continue + + 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\nPlease add %s to " + "\"supportedCPUs\".") % + (a, ", ".join(inv), b), + "Unsupported processor type", + wx.OK + wx.ICON_INFORMATION) + + dlg.ShowModal() + dlg.Destroy() + continue + + if ln.lstrip().startswith("#define"): + m = reDefQS.search(ln) + if m: + t = m.groups() + if len(t) == 2: + m = reDefQSm.search(ln) + if m: + t = m.groups() + tt = re.findall(reDefQSm2, t[1]) + if len(tt) == 1: + self.cfgValues[t[0]] = tt[0] + continue + elif len(tt) > 1: + self.cfgValues[t[0]] = tt + continue + + m = reDefine.search(ln) + if m: + t = m.groups() + if len(t) == 2: + self.cfgValues[t[0]] = t[1] + continue + + 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 + + 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): + path = self.configFile + if self.saveConfigFile(path): + dlg = wx.MessageDialog(self, "File %s successfully written." % path, + "Save successful", wx.OK + wx.ICON_INFORMATION) + else: + dlg = wx.MessageDialog(self, "Unable to write to file %s." % path, + "File error", wx.OK + wx.ICON_ERROR) + dlg.ShowModal() + dlg.Destroy() + + + def onSaveConfigAs(self, evt): + wildcard = "Board configuration (board.*.h)|board.*.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() + + if self.saveConfigFile(path): + dlg = wx.MessageDialog(self, "File %s successfully written." % path, + "Save successful", wx.OK + wx.ICON_INFORMATION) + self.parent.setBoardTabText("Board <%s>" % os.path.basename(path)) + else: + dlg = wx.MessageDialog(self, "Unable to write to file %s." % path, + "File error", wx.OK + wx.ICON_ERROR) + dlg.ShowModal() + dlg.Destroy() + + def saveConfigFile(self, path): + ext = os.path.splitext(os.path.basename(path))[1] + self.dir = os.path.dirname(path) + + if ext == "": + path += ".h" + + try: + fp = file(path, 'w') + except: + return False + + self.configFile = path + + values = {} + + for pg in self.pages: + v1 = pg.getValues() + for k in v1.keys(): + values[k] = v1[k] + + self.processors = self.pgCpu.getProcessors() + + + skipToSensorEnd = False + skipToHeaterEnd = False + skipToProcessorEnd = False + for ln in self.cfgBuffer: + m = reStartSensors.match(ln) + if m: + fp.write(ln) + for s in self.sensors: + sstr = ", ".join(s) + fp.write("DEFINE_TEMP_SENSOR(%s)\n" % sstr) + skipToSensorEnd = True + continue + + if skipToSensorEnd: + m = reEndSensors.match(ln) + if m: + fp.write(ln) + skipToSensorEnd = False + continue + + m = reStartHeaters.match(ln) + if m: + fp.write(ln) + for s in self.heaters: + sstr = ", ".join(s) + fp.write("DEFINE_HEATER(%s)\n" % sstr) + + fp.write("\n") + for s in self.heaters: + fp.write(defineHeaterFormat % (s[0].upper(), s[0])) + skipToHeaterEnd = True + continue + + if skipToHeaterEnd: + m = reEndHeaters.match(ln) + if m: + fp.write(ln) + skipToHeaterEnd = False + continue + + m = reStartProcessors.match(ln) + if m: + fp.write(ln) + for i in range(len(self.processors)): + fp.write("%s#ifndef __AVR_%s__\n" % (i * " ", self.processors[i])) + fp.write("%s#error Wrong CPU type.\n" % ((i + 1) * " ")) + for s in self.processors: + fp.write("%s#endif\n" % (i * " ")) + i -= 1 + + skipToProcessorEnd = True + continue + + if skipToProcessorEnd: + m = reEndProcessors.match(ln) + if m: + fp.write(ln) + skipToProcessorEnd = False + continue + + m = reDefineBL.match(ln) + if m: + t = m.groups() + if len(t) == 2: + if t[0] in values.keys() and values[t[0]] != "": + fp.write(defineValueFormat % (t[0], values[t[0]])) + else: + fp.write("//" + ln) + continue + + m = reDefBoolBL.match(ln) + if m: + t = m.groups() + if len(t) == 1: + if t[0] in values.keys() and values[t[0]]: + fp.write(defineBoolFormat % t[0]) + else: + fp.write("//" + ln) + continue + + m = reCommDefBL.match(ln) + if m: + t = m.groups() + if len(t) == 2: + if t[0] in values.keys() and values[t[0]] != "": + fp.write(defineValueFormat % (t[0], values[t[0]])) + else: + fp.write(ln) + continue + + m = reCommDefBoolBL.match(ln) + if m: + t = m.groups() + if len(t) == 1: + if t[0] in values.keys() and values[t[0]]: + fp.write(defineBoolFormat % t[0]) + else: + fp.write(ln) + continue + + fp.write(ln) + + fp.close() + return True diff --git a/configtool/calcbelt.py b/configtool/calcbelt.py new file mode 100644 index 0000000..78b050d --- /dev/null +++ b/configtool/calcbelt.py @@ -0,0 +1,251 @@ + +import wx + +from configtool.data import BSIZESMALL, reFloat + + +class CalcBelt(wx.Dialog): + def __init__(self, parent, cbUse): + wx.Dialog.__init__(self, parent, wx.ID_ANY, + "Steps calculator for belt driven axes", + size = (360, 300)) + self.Bind(wx.EVT_CLOSE, self.onExit) + + self.use = cbUse + labelWidth = 110 + + sz = wx.BoxSizer(wx.VERTICAL) + sz.AddSpacer((10, 10)) + + lsz = wx.BoxSizer(wx.HORIZONTAL) + st = wx.StaticText(self, wx.ID_ANY, "Step Angle:", size = (labelWidth, -1), + style = wx.ALIGN_RIGHT) + lsz.Add(st) + lsz.AddSpacer((5, 5)) + + stepAngles = ["1.8 (200 per revolution)", "0.9 (400 per revolution)", + "7.5 (48 per revolution)"] + self.stepAngleValues = [200, 400, 48] + tc = wx.Choice(self, wx.ID_ANY, choices = stepAngles) + tc.SetSelection(0) + tc.Bind(wx.EVT_CHOICE, self.onChoice) + lsz.Add(tc) + tc.SetToolTipString("Step angle. Depends on your type of stepper motor.") + self.tcStep = tc + + sz.Add(lsz) + sz.AddSpacer((10, 10)) + + lsz = wx.BoxSizer(wx.HORIZONTAL) + st = wx.StaticText(self, wx.ID_ANY, "Microstepping:", + size = (labelWidth, -1), style = wx.ALIGN_RIGHT) + lsz.Add(st) + lsz.AddSpacer((5, 5)) + + microStepping = ["1 - full step", "1/2 - half step", "1/4 - quarter step", + "1/8", "1/16", "1/32", "1/64", "1/128"] + self.microSteppingValues = [1, 2, 4, 8, 16, 32, 64, 128] + tc = wx.Choice(self, wx.ID_ANY, choices = microStepping) + tc.Bind(wx.EVT_CHOICE, self.onChoice) + tc.SetSelection(4) + lsz.Add(tc) + tc.SetToolTipString("Microstepping. Most boards allow to change this by " + "setting jumpers. The value here must match the " + "setting on the board in conjunction with the type " + "of stepper driver chip.") + self.tcMicroStepping = tc + + sz.Add(lsz) + sz.AddSpacer((10, 10)) + + lsz = wx.BoxSizer(wx.HORIZONTAL) + st = wx.StaticText(self, wx.ID_ANY, "Belt Pitch (in mm):", + size = (labelWidth, -1), style = wx.ALIGN_RIGHT) + lsz.Add(st) + lsz.AddSpacer((5, 5)) + + tc = wx.TextCtrl(self, wx.ID_ANY, "2", style = wx.TE_RIGHT) + tc.Bind(wx.EVT_TEXT, self.onTextCtrlFloat) + lsz.Add(tc) + tc.SetToolTipString("Belt pitch. Distance between two teeth on the belt.") + self.tcBeltPitch = tc + + lsz.AddSpacer((5, 5)) + + beltPresets = ["-", "2mm Pitch (GT2)", "MXL Pitch (2.03mm)", + "T2.5 (2.5mm)", "3mm Pitch (GT2, HTD)", + "5mm Pitch (T5, GTD, HTD)", "0.2\" XL belt (5.08mm)"] + self.beltPresetValues = [-1, 2.0, 2.03, 2.5, 3.0, 5.0, 5.08] + tc = wx.Choice(self, wx.ID_ANY, choices = beltPresets) + tc.SetSelection(0) + tc.Bind(wx.EVT_CHOICE, self.onPresetChoice) + lsz.Add(tc) + tc.SetToolTipString("Belt pitch presets.") + self.tcPresets = tc + + sz.Add(lsz) + sz.AddSpacer((10, 10)) + + lsz = wx.BoxSizer(wx.HORIZONTAL) + st = wx.StaticText(self, wx.ID_ANY, "Pulley Teeth Count:", + size = (labelWidth, -1), style = wx.ALIGN_RIGHT) + lsz.Add(st) + lsz.AddSpacer((5, 5)) + + tc = wx.TextCtrl(self, wx.ID_ANY, "8", style = wx.TE_RIGHT) + tc.Bind(wx.EVT_TEXT, self.onTextCtrlFloat) + lsz.Add(tc) + tc.SetToolTipString("Pulley teeth count. Count them!") + self.tcPulleyTeeth = tc + + sz.Add(lsz) + sz.AddSpacer((30, 30)) + + lsz = wx.BoxSizer(wx.HORIZONTAL) + st = wx.StaticText(self, wx.ID_ANY, "Result:", size = (labelWidth, -1), + style = wx.ALIGN_RIGHT) + lsz.Add(st) + lsz.AddSpacer((5, 5)) + + tc = wx.StaticText(self, wx.ID_ANY, "", size = (200, -1), + style = wx.ALIGN_LEFT) + lsz.Add(tc) + self.tcResult = tc + + sz.Add(lsz) + lsz = wx.BoxSizer(wx.HORIZONTAL) + st = wx.StaticText(self, wx.ID_ANY, "Resolution:", size = (labelWidth, -1), + style = wx.ALIGN_RIGHT) + lsz.Add(st) + lsz.AddSpacer((5, 5)) + + tc = wx.StaticText(self, wx.ID_ANY, "", size = (200, -1), + style = wx.ALIGN_LEFT) + lsz.Add(tc) + self.tcResolution = tc + + sz.Add(lsz) + + sz.AddSpacer((20, 20)) + + bsz = wx.BoxSizer(wx.HORIZONTAL) + b = wx.Button(self, wx.ID_ANY, "Use for X", size = BSIZESMALL) + self.Bind(wx.EVT_BUTTON, self.onUseForX, b) + bsz.Add(b) + self.bUseForX = b + bsz.AddSpacer((5, 5)) + + b = wx.Button(self, wx.ID_ANY, "Use for Y", size = BSIZESMALL) + self.Bind(wx.EVT_BUTTON, self.onUseForY, b) + bsz.Add(b) + self.bUseForY = b + bsz.AddSpacer((5, 5)) + + b = wx.Button(self, wx.ID_ANY, "Use for Z", size = BSIZESMALL) + self.Bind(wx.EVT_BUTTON, self.onUseForZ, b) + bsz.Add(b) + self.bUseForZ = b + bsz.AddSpacer((5, 5)) + + b = wx.Button(self, wx.ID_ANY, "Use for E", size = BSIZESMALL) + self.Bind(wx.EVT_BUTTON, self.onUseForE, b) + bsz.Add(b) + self.bUseForE = b + + sz.Add(bsz) + + self.enableUseButtons(False) + + self.SetSizer(sz) + + self.Fit() + + self.calculate() + + + def calculate(self): + self.tcResult.SetLabel("") + self.tcResolution.SetLabel("") + self.enableUseButtons(False) + s = self.tcStep.GetSelection() + sv = self.stepAngleValues[s] + + try: + bp = float(self.tcBeltPitch.GetValue()) + except: + return + + try: + pt = int(self.tcPulleyTeeth.GetValue()) + except: + return + + s = self.tcMicroStepping.GetSelection() + msv = self.microSteppingValues[s] + + length = pt * bp + steps = sv * msv + + resultmm = steps / length + self.result = int(resultmm * 1000.0) + + self.tcResult.SetLabel("%d steps/m (%.3f steps/mm)" % + (self.result, resultmm)) + self.tcResolution.SetLabel("%.3f micrometers" % (length / steps * 1000.0)) + self.enableUseButtons(True) + + def enableUseButtons(self, flag): + self.bUseForX.Enable(flag) + self.bUseForY.Enable(flag) + self.bUseForZ.Enable(flag) + self.bUseForE.Enable(flag) + + def onUseForX(self, evt): + self.use('STEPS_PER_M_X', self.result) + + def onUseForY(self, evt): + self.use('STEPS_PER_M_Y', self.result) + + def onUseForZ(self, evt): + self.use('STEPS_PER_M_Z', self.result) + + def onUseForE(self, evt): + self.use('STEPS_PER_M_E', self.result) + + def onPresetChoice(self, evt): + s = self.tcPresets.GetSelection() + sv = self.beltPresetValues[s] + if sv < 0: + return + + s = "%f" % sv + s = s.rstrip("0") + if s[-1] == ".": + s += "0" + self.tcBeltPitch.SetValue(s) + + def onChoice(self, evt): + self.calculate() + + def onTextCtrlFloat(self, evt): + tc = evt.GetEventObject() + w = tc.GetValue().strip() + if w == "": + valid = False + else: + m = reFloat.match(w) + if m: + valid = True + else: + valid = False + + if valid: + tc.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) + else: + tc.SetBackgroundColour("pink") + tc.Refresh() + self.calculate() + evt.Skip() + + def onExit(self, evt): + self.EndModal(wx.ID_OK) diff --git a/configtool/calcscrew.py b/configtool/calcscrew.py new file mode 100644 index 0000000..8a450b7 --- /dev/null +++ b/configtool/calcscrew.py @@ -0,0 +1,269 @@ + +import wx +from configtool.data import BSIZESMALL, reFloat + + +class CalcScrew(wx.Dialog): + def __init__(self, parent, cbUse): + wx.Dialog.__init__(self, parent, wx.ID_ANY, + "Steps calculator for screw driven axes", + size = (400, 204)) + self.Bind(wx.EVT_CLOSE, self.onExit) + + self.use = cbUse + labelWidth = 110 + + sz = wx.BoxSizer(wx.VERTICAL) + sz.AddSpacer((10, 10)) + + lsz = wx.BoxSizer(wx.HORIZONTAL) + st = wx.StaticText(self, wx.ID_ANY, "Step Angle:", size = (labelWidth, -1), + style = wx.ALIGN_RIGHT) + lsz.Add(st) + lsz.AddSpacer((5, 5)) + + stepAngles = ["1.8 (200 per revolution)", "0.9 (400 per revolution)", + "7.5 (48 per revolution)"] + self.stepAngleValues = [200, 400, 48] + tc = wx.Choice(self, wx.ID_ANY, choices = stepAngles) + tc.SetSelection(0) + tc.Bind(wx.EVT_CHOICE, self.onChoice) + lsz.Add(tc) + tc.SetToolTipString("Step angle. Depends on your type of stepper motor.") + self.tcStep = tc + + sz.Add(lsz) + sz.AddSpacer((10, 10)) + + lsz = wx.BoxSizer(wx.HORIZONTAL) + st = wx.StaticText(self, wx.ID_ANY, "Microstepping:", + size = (labelWidth, -1), style = wx.ALIGN_RIGHT) + lsz.Add(st) + lsz.AddSpacer((5, 5)) + + microStepping = ["1 - full step", "1/2 - half step", "1/4 - quarter step", + "1/8", "1/16", "1/32", "1/64", "1/128"] + self.microSteppingValues = [1, 2, 4, 8, 16, 32, 64, 128] + tc = wx.Choice(self, wx.ID_ANY, choices = microStepping) + tc.Bind(wx.EVT_CHOICE, self.onChoice) + tc.SetSelection(4) + lsz.Add(tc) + tc.SetToolTipString("Microstepping. Most boards allow to change this by " + "setting jumpers. The value here must match the " + "setting on the board in conjunction with the type " + "of stepper driver chip.") + self.tcMicroStepping = tc + + sz.Add(lsz) + sz.AddSpacer((10, 10)) + + lsz = wx.BoxSizer(wx.HORIZONTAL) + st = wx.StaticText(self, wx.ID_ANY, "Screw Pitch (mm/rev):", + size = (labelWidth, -1), style = wx.ALIGN_RIGHT) + lsz.Add(st) + lsz.AddSpacer((5, 5)) + + tc = wx.TextCtrl(self, wx.ID_ANY, "2", style = wx.TE_RIGHT) + tc.Bind(wx.EVT_TEXT, self.onTextCtrlFloat) + lsz.Add(tc) + tc.SetToolTipString("Screw pitch. Defined by the pitch of the screw.") + self.tcScrewPitch = tc + + lsz.AddSpacer((5, 5)) + + screwPresets = ["-", "M8 - metric (1.25 mm/rev)", "M6 - metric (1 mm/rev)", + "M5 - metric (0.8 mm/rev)", "12 (12 mm/rev)", + "16 (16 mm/rev)", "25 (25 mm/rev)", + "5/15\"-18 imperial coarse (1.41111 mm/rev)", + "3/16\"-20 imperial (1.270 mm/rev)", + "1/4\"-16 ACME (1.5875 mm/rev)"] + self.screwPresetValues = [-1, 1.25, 1.00, 0.8, 12.0, 16.0, 25.0, 1.41111, + 1.270, 1.5875] + tc = wx.Choice(self, wx.ID_ANY, choices = screwPresets) + tc.SetSelection(0) + tc.Bind(wx.EVT_CHOICE, self.onPresetChoice) + lsz.Add(tc) + tc.SetToolTipString("Screw pitch presets.") + self.tcPresets = tc + + sz.Add(lsz) + sz.AddSpacer((10, 10)) + + lsz = wx.BoxSizer(wx.HORIZONTAL) + st = wx.StaticText(self, wx.ID_ANY, "Gear Ratio:", size = (labelWidth, -1), + style = wx.ALIGN_RIGHT) + lsz.Add(st) + lsz.AddSpacer((5, 5)) + + tc = wx.TextCtrl(self, wx.ID_ANY, "1", size = (40, -1), style = wx.TE_RIGHT) + tc.Bind(wx.EVT_TEXT, self.onTextCtrlFloat) + lsz.Add(tc) + tc.SetToolTipString("Gear ratio. 1:1 if there is no gear.") + self.tcRatioTop = tc + + lsz.AddSpacer((5, 5)) + st = wx.StaticText(self, wx.ID_ANY, ":") + lsz.Add(st) + lsz.AddSpacer((5, 5)) + + tc = wx.TextCtrl(self, wx.ID_ANY, "1", size = (40, -1), style = wx.TE_RIGHT) + tc.Bind(wx.EVT_TEXT, self.onTextCtrlFloat) + lsz.Add(tc) + tc.SetToolTipString("Gear ratio. 1:1 if there is no gear.") + self.tcRatioBottom = tc + + sz.Add(lsz) + sz.AddSpacer((30, 30)) + + lsz = wx.BoxSizer(wx.HORIZONTAL) + st = wx.StaticText(self, wx.ID_ANY, "Result:", size = (labelWidth, -1), + style = wx.ALIGN_RIGHT) + lsz.Add(st) + lsz.AddSpacer((5, 5)) + + tc = wx.StaticText(self, wx.ID_ANY, "", size = (300, -1), + style = wx.ALIGN_LEFT) + lsz.Add(tc) + self.tcResult = tc + + sz.Add(lsz) + lsz = wx.BoxSizer(wx.HORIZONTAL) + st = wx.StaticText(self, wx.ID_ANY, "Resolution:", size = (labelWidth, -1), + style = wx.ALIGN_RIGHT) + lsz.Add(st) + lsz.AddSpacer((5, 5)) + + tc = wx.StaticText(self, wx.ID_ANY, "", size = (300, -1), + style = wx.ALIGN_LEFT) + lsz.Add(tc) + self.tcResolution = tc + + sz.Add(lsz) + + sz.AddSpacer((20, 20)) + + bsz = wx.BoxSizer(wx.HORIZONTAL) + b = wx.Button(self, wx.ID_ANY, "Use for X", size = BSIZESMALL) + self.Bind(wx.EVT_BUTTON, self.onUseForX, b) + bsz.Add(b) + self.bUseForX = b + bsz.AddSpacer((5, 5)) + + b = wx.Button(self, wx.ID_ANY, "Use for Y", size = BSIZESMALL) + self.Bind(wx.EVT_BUTTON, self.onUseForY, b) + bsz.Add(b) + self.bUseForY = b + bsz.AddSpacer((5, 5)) + + b = wx.Button(self, wx.ID_ANY, "Use for Z", size = BSIZESMALL) + self.Bind(wx.EVT_BUTTON, self.onUseForZ, b) + bsz.Add(b) + self.bUseForZ = b + bsz.AddSpacer((5, 5)) + + b = wx.Button(self, wx.ID_ANY, "Use for E", size = BSIZESMALL) + self.Bind(wx.EVT_BUTTON, self.onUseForE, b) + bsz.Add(b) + self.bUseForE = b + + sz.Add(bsz, flag = wx.ALIGN_CENTER_HORIZONTAL) + + self.enableUseButtons(False) + + self.SetSizer(sz) + + self.Fit() + + self.calculate() + + def calculate(self): + self.tcResult.SetLabel("") + self.tcResolution.SetLabel("") + self.enableUseButtons(False) + s = self.tcStep.GetSelection() + sv = self.stepAngleValues[s] + + try: + sp = float(self.tcScrewPitch.GetValue()) + except: + return + + try: + ratioA = float(self.tcRatioTop.GetValue()) + except: + return + + try: + ratioB = float(self.tcRatioBottom.GetValue()) + except: + return + + s = self.tcMicroStepping.GetSelection() + msv = self.microSteppingValues[s] + + ratio = ratioA / ratioB + steps = sv * msv + + resultmm = steps / sp / ratio + self.result = int(resultmm * 1000.0) + + self.tcResult.SetLabel("%d steps/m (%.3f steps/mm)" % + (self.result, resultmm)) + self.tcResolution.SetLabel("%.3f micrometers" % (1.0 / resultmm * 1000.0)) + self.enableUseButtons(True) + + def enableUseButtons(self, flag): + self.bUseForX.Enable(flag) + self.bUseForY.Enable(flag) + self.bUseForZ.Enable(flag) + self.bUseForE.Enable(flag) + + def onUseForX(self, evt): + self.use('STEPS_PER_M_X', self.result) + + def onUseForY(self, evt): + self.use('STEPS_PER_M_Y', self.result) + + def onUseForZ(self, evt): + self.use('STEPS_PER_M_Z', self.result) + + def onUseForE(self, evt): + self.use('STEPS_PER_M_E', self.result) + + def onPresetChoice(self, evt): + s = self.tcPresets.GetSelection() + sv = self.screwPresetValues[s] + if sv < 0: + return + + s = "%f" % sv + s = s.rstrip("0") + if s[-1] == ".": + s += "0" + self.tcScrewPitch.SetValue(s) + + def onChoice(self, evt): + self.calculate() + + def onTextCtrlFloat(self, evt): + tc = evt.GetEventObject() + w = tc.GetValue().strip() + if w == "": + valid = False + else: + m = reFloat.match(w) + if m: + valid = True + else: + valid = False + + if valid: + tc.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) + else: + tc.SetBackgroundColour("pink") + tc.Refresh() + self.calculate() + evt.Skip() + + def onExit(self, evt): + self.EndModal(wx.ID_OK) diff --git a/configtool/communicationspage.py b/configtool/communicationspage.py new file mode 100644 index 0000000..4bf36dc --- /dev/null +++ b/configtool/communicationspage.py @@ -0,0 +1,48 @@ + +import wx +from configtool.page import Page + + +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"} + + sz = wx.GridBagSizer() + sz.AddSpacer((20, 40), pos = (0, 0)) + + ch = self.addChoice('BAUD', self.bauds, self.bauds.index(self.defaultBaud), + 60, self.onChoice) + sz.Add(ch, pos = (1, 1)) + + sz.AddSpacer((100, 10), pos = (1, 2)) + + k = 'USB_SERIAL' + cb = self.addCheckBox(k, self.onCheckBox) + sz.Add(cb, pos = (1, 3)) + + self.SetSizer(sz) + self.enableAll(False) + + def insertValues(self, cfgValues): + self.assertValid(True) + self.enableAll(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.defaultBaud) + + self.assertModified(False) diff --git a/configtool/cpupage.py b/configtool/cpupage.py new file mode 100644 index 0000000..4e71ddf --- /dev/null +++ b/configtool/cpupage.py @@ -0,0 +1,81 @@ + +import wx +from configtool.page import Page +from configtool.data import supportedCPUs + + +class CpuPage(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:"} + self.defaultClock = '16000000UL' + self.clocks = ['8000000UL', self.defaultClock, '20000000UL'] + self.processors = [] + + sz = wx.GridBagSizer() + sz.AddSpacer((20, 40), pos = (0, 0)) + + + k = 'MOTHERBOARD' + cb = self.addCheckBox(k, self.onCheckBox) + sz.Add(cb, pos = (1, 1)) + sz.AddSpacer((100, 10), pos = (1, 2)) + + + k = 'F_CPU' + ch = self.addChoice(k, self.clocks, self.clocks.index(self.defaultClock), + 80, self.onChoice) + sz.Add(ch, pos = (1, 3)) + sz.AddSpacer((100, 10), pos = (1, 4)) + + b = wx.StaticBox(self, wx.ID_ANY, "Processor Type(s)") + sbox = wx.StaticBoxSizer(b, wx.VERTICAL) + + ht = "Choose the processor(s) this configuration will work with." + for k in supportedCPUs: + cb = self.addCheckBox(k, self.onCheckBox) + cb.SetToolTipString(ht) + sbox.Add(cb) + sbox.AddSpacer((120, 5)) + + sbox.AddSpacer((5, 5)) + sz.Add(sbox, pos = (1, 5), span = (3, 1)) + + self.SetSizer(sz) + self.enableAll(False) + + 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 getProcessors(self): + plist = [] + for p in supportedCPUs: + if self.checkBoxes[p].IsChecked(): + plist.append(p) + + return plist + + def insertValues(self, cfgValues): + self.assertValid(True) + self.enableAll(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('F_CPU', cfgValues, self.defaultClock) + + self.assertModified(False) diff --git a/configtool/data.py b/configtool/data.py new file mode 100644 index 0000000..cf3cf34 --- /dev/null +++ b/configtool/data.py @@ -0,0 +1,52 @@ + +import re + + +VERSION = "0.1" + +supportedCPUs = ['ATmega168', 'ATmega328P', 'ATmega644P', 'ATmega644PA', + 'ATmega1280', 'ATmega1284P', 'ATmega2560', 'AT90USB1286'] + +pinNames = ["AIO%d" % x for x in range(16)] + ["DIO%d" % x for x in range(64)] +pinNamesWithBlank = ["-"] + pinNames + +BSIZE = (90, 60) +BSIZESMALL = (90, 30) + +reDefQSm = re.compile("\s*#define\s+(\S+)\s+(.*)") +reDefQSm2 = re.compile("\s*(\"[^\"]*\")") + +reDefine = re.compile("\s*#define\s+(\w+)\s+(\S+)") +reDefineBL = re.compile("^\s*#define\s+(\w+)\s+(\S+)") +reCommDefBL = re.compile("^\s*//\s*#define\s+(\w+)\s+(\S+)") +reDefQS = re.compile("\s*#define\s+(\w+)\s+(\"[^\"]*\")") +reDefTS = re.compile("\s*(DEFINE_TEMP_SENSOR\\([^)]*\\))") +reDefHT = re.compile("\s*(DEFINE_HEATER\\([^)]*\\))") +reDefBool = re.compile("\s*#define\s+(\w+)\s+") +reDefBoolBL = re.compile("^\s*#define\s+(\w+)\s+") +reCommDefBoolBL = re.compile("^\s*//\s*#define\s+(\S+)\s+") +reStartSensors = re.compile("^\s*//\s*DEFINE_TEMP_SENSORS_START") +reEndSensors = re.compile("^\s*//\s*DEFINE_TEMP_SENSORS_END") +reStartHeaters = re.compile("^\s*//\s*DEFINE_HEATERS_START") +reEndHeaters = re.compile("^\s*//\s*DEFINE_HEATERS_END") +reStartProcessors = re.compile("^\s*//\s*PROCESSORS_START") +reEndProcessors = re.compile("^\s*//\s*PROCESSORS_END") +reCandHeatPins = re.compile("^\s*//\s*#define\s+HEATER_PIN\s+(\w+)") +reCandThermPins = re.compile("^\s*//\s*#define\s+TEMP_SENSOR_PIN\s+(\w+)") + +reHelpTextStart = re.compile("^\s*/\*\*\s+\\\\def\s+(.*)") +reHelpTextEnd = re.compile("^\s*\*/") + +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+U?L?$") +reFloat = re.compile("^\d+(\.\d*)?$") + +reAVR = re.compile("__AVR_(\w+)__") + +defineValueFormat = "#define %-24s %s\n" +defineBoolFormat = "#define %s\n" +defineHeaterFormat = "#define HEATER_%s HEATER_%s\n" +defineDCExtruderFormat = "#define %-24s HEATER_%s\n" diff --git a/configtool/heaterlist.py b/configtool/heaterlist.py new file mode 100644 index 0000000..8857796 --- /dev/null +++ b/configtool/heaterlist.py @@ -0,0 +1,91 @@ + +import wx + + +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.heaterList = [] + + 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 setRowValidity(self, i, flag = False): + if i < 0 or i >= len(self.heaterList): + return + + self.valid[i] = flag + self.Refresh() + + def setTableValidity(self, flag = False): + for i in range(len(self.heaterList)): + self.setRowValidity(i, flag) + + 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 diff --git a/configtool/heaterspage.py b/configtool/heaterspage.py new file mode 100644 index 0000000..e4b3aa0 --- /dev/null +++ b/configtool/heaterspage.py @@ -0,0 +1,125 @@ + +import wx +from configtool.page import Page +from configtool.data import pinNames, BSIZESMALL +from configtool.heaterlist import HeaterList +from configtool.addheaterdlg import AddHeaterDlg + + +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 + + sz = wx.GridBagSizer() + sz.AddSpacer((30, 30), pos = (0, 0)) + + self.heaters = [] + self.validPins = pinNames + + + 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) + self.bAdd.SetToolTipString("Add a heater to the configuration.") + + 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) + self.bDelete.SetToolTipString("Remove the selected heater from the " + "configuration.") + + sz.Add(bsz, pos = (1, 4)) + + self.SetSizer(sz) + self.enableAll(False) + + def enableAll(self, flag = True): + self.bAdd.Enable(flag) + Page.enableAll(self, flag) + + 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.validPins) + 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.validateTable() + 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.validateTable() + self.parent.setHeaters(self.heaters) + + def insertValues(self, cfgValues): + self.enableAll(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.assertModified(False) + + def setHeaters(self, heaters): + self.heaters = heaters + self.lb.updateList(self.heaters) + self.validateTable() + self.parent.setHeaters(self.heaters) + + def setCandidatePins(self, plist): + if not plist or len(plist) == 0: + self.validPins = pinNames + else: + self.validPins = plist + + self.validateTable() + + def validateTable(self): + self.lb.setTableValidity(True) + self.setFieldValidity('HEATERLIST', True) + for i in range(len(self.heaters)): + if self.heaters[i][1] not in self.validPins: + self.lb.setRowValidity(i, False) + self.setFieldValidity('HEATERLIST', False) + + def setHelpText(self, ht): + Page.setHelpText(self, ht) + + k = 'DEFINE_HEATER' + if k in ht.keys(): + self.bAdd.SetToolTipString(ht[k]) diff --git a/configtool/mechanicalpage.py b/configtool/mechanicalpage.py new file mode 100644 index 0000000..230a2bd --- /dev/null +++ b/configtool/mechanicalpage.py @@ -0,0 +1,210 @@ + +import wx +from configtool.data import BSIZE +from configtool.page import Page +from configtool.calcbelt import CalcBelt +from configtool.calcscrew import CalcScrew + + +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.kinematicsKeys = ['KINEMATICS_STRAIGHT', 'KINEMATICS_COREXY'] + + 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", + 'KINEMATICS_STRAIGHT': "Straight", + 'KINEMATICS_COREXY': "CoreXY"} + + labelWidth = 40; + + sz = wx.GridBagSizer() + sz.AddSpacer((10, 10), pos = (0, 0)) + sz.AddSpacer((90, 10), pos = (0, 4)) + + b = wx.StaticBox(self, wx.ID_ANY, "Steps Per Meter") + sbox = wx.StaticBoxSizer(b, wx.VERTICAL) + sbox.AddSpacer((5, 5)) + for k in self.spmKeys: + tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlFloat) + sbox.Add(tc) + sbox.AddSpacer((5, 5)) + + sz.Add(sbox, pos = (1, 1)) + + b = wx.StaticBox(self, wx.ID_ANY, "Maximum Feedrate") + sbox = wx.StaticBoxSizer(b, wx.VERTICAL) + sbox.AddSpacer((5, 5)) + for k in self.mfrKeys: + tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlFloat) + sbox.Add(tc) + sbox.AddSpacer((5, 5)) + + sz.Add(sbox, pos = (1, 5)) + + b = wx.StaticBox(self, wx.ID_ANY, "Search Feedrate") + sbox = wx.StaticBoxSizer(b, wx.VERTICAL) + sbox.AddSpacer((5, 5)) + for k in self.msrKeys: + tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlFloat) + sbox.Add(tc) + sbox.AddSpacer((5, 5)) + + sz.Add(sbox, pos = (1, 7)) + + b = wx.StaticBox(self, wx.ID_ANY, "Endstop Clearance") + sbox = wx.StaticBoxSizer(b, wx.VERTICAL) + sbox.AddSpacer((5, 5)) + for k in self.eclKeys: + tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlFloat) + sbox.Add(tc) + sbox.AddSpacer((5, 5)) + + sz.Add(sbox, pos = (3, 5)) + + b = wx.StaticBox(self, wx.ID_ANY, "Travel Limits") + sbox = wx.StaticBoxSizer(b, wx.VERTICAL) + sbox.AddSpacer((5, 5)) + for k in self.minmaxKeys: + tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlFloat) + sbox.Add(tc) + sbox.AddSpacer((5, 5)) + + sz.Add(sbox, pos = (3, 7)) + + vsz = wx.BoxSizer(wx.VERTICAL) + + b = wx.StaticBox(self, wx.ID_ANY, "Kinematics") + sbox = wx.StaticBoxSizer(b, wx.VERTICAL) + sbox.AddSpacer((5, 5)) + style = wx.RB_GROUP + for k in self.kinematicsKeys: + rb = self.addRadioButton(k, style, self.onKinematicsSelect) + style = 0 + + sbox.Add(rb, 1, wx.LEFT + wx.RIGHT, 16) + sbox.AddSpacer((5, 5)) + + vsz.Add(sbox, 1, wx.LEFT, 40) + + cb = self.addCheckBox('E_ABSOLUTE', self.onCheckBox) + + vsz.Add(cb, 1, wx.LEFT, 40) + + sz.Add(vsz, pos = (3, 1)) + + 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.") + self.Bind(wx.EVT_BUTTON, self.onCalcBelt, b) + self.bCalcBelt = b + + 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.") + self.Bind(wx.EVT_BUTTON, self.onCalcScrew, b) + self.bCalcScrew = b + + sz.Add(bsz, pos = (1, 3)) + self.enableAll(False) + self.SetSizer(sz) + + def enableAll(self, flag = True): + self.bCalcBelt.Enable(flag) + self.bCalcScrew.Enable(flag) + Page.enableAll(self, flag) + + def onKinematicsSelect(self, evt): + self.assertModified(True) + evt.Skip() + + def onCalcBelt(self, evt): + dlg = CalcBelt(self, self.cbCalcBelt) + dlg.ShowModal() + dlg.Destroy() + + def cbCalcBelt(self, field, value): + s = "%d" % value + self.textControls[field].SetValue(s) + + def onCalcScrew(self, evt): + dlg = CalcScrew(self, self.cbCalcScrew) + dlg.ShowModal() + dlg.Destroy() + + def cbCalcScrew(self, field, value): + s = "%d" % value + self.textControls[field].SetValue(s) + + def setHelpText(self, ht): + Page.setHelpText(self, ht) + if 'KINEMATICS' in ht.keys(): + for k in self.kinematicsKeys: + self.radioButtons[k].SetToolTipString(ht['KINEMATICS']) + + 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) + + if 'KINEMATICS' in cfgValues.keys(): + k = cfgValues['KINEMATICS'] + if k in self.kinematicsKeys: + self.radioButtons[k].SetValue(True) + else: + self.radioButtons[self.kinematicsKeys[0]].SetValue(True) + else: + self.radioButtons[self.kinematicsKeys[0]].SetValue(True) + + self.assertModified(False) + self.enableAll(True) + + def getValues(self): + result = Page.getValues(self) + + for tag in self.kinematicsKeys: + rb = self.radioButtons[tag] + if rb.GetValue(): + result['KINEMATICS'] = tag + break + + return result diff --git a/configtool/miscellaneouspage.py b/configtool/miscellaneouspage.py new file mode 100644 index 0000000..c688f8d --- /dev/null +++ b/configtool/miscellaneouspage.py @@ -0,0 +1,291 @@ + +import wx +from configtool.page import Page +from configtool.data import reFloat + + +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 = {'USE_INTERNAL_PULLUPS': "Use Internal Pullups", + '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': "Movebuffer 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:", + 'PID_SCALE': "PID Scaling Factor:", + 'TEMP_HYSTERESIS': "Temperature Hysteresis:", + 'TEMP_RESIDENCY_TIME': "Temperature Residency Time:", + 'TEMP_EWMA': "Temperature EWMA:", + 'HEATER_SANITY_CHECK': "Heater Sanity Check"} + + self.heaterNameNone = "" + self.heaterNames = [self.heaterNameNone] + self.boardHeaters = [] + 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)) + sz.AddSpacer((20, 30), pos = (7, 0)) + sz.AddSpacer((20, 30), pos = (8, 0)) + + labelWidth = 140 + + k = 'EECONFIG' + cb = self.addCheckBox(k, self.onCheckBox) + sz.Add(cb, pos = (1, 1)) + + k = 'USE_INTERNAL_PULLUPS' + cb = self.addCheckBox(k, self.onCheckBox) + sz.Add(cb, pos = (2, 1)) + + k = 'DEBUG' + cb = self.addCheckBox(k, self.onCheckBox) + sz.Add(cb, pos = (3, 1)) + + k = 'USE_WATCHDOG' + cb = self.addCheckBox(k, self.onCheckBox) + sz.Add(cb, pos = (4, 1)) + + k = 'STEP_INTERRUPT_INTERRUPTIBLE' + cb = self.addCheckBox(k, self.onCheckBox) + sz.Add(cb, pos = (5, 1)) + + k = 'FAST_PWM' + cb = self.addCheckBox(k, self.onCheckBox) + sz.Add(cb, pos = (6, 1)) + + k = 'XONXOFF' + cb = self.addCheckBox(k, self.onCheckBox) + sz.Add(cb, pos = (7, 1)) + + k = 'HEATER_SANITY_CHECK' + cb = self.addCheckBox(k, self.onCheckBox) + sz.Add(cb, pos = (8, 1)) + + k = 'REFERENCE' + ch = self.addChoice(k, self.references, + self.references.index(self.defaultRef), + labelWidth, self.onChoice) + sz.Add(ch, pos = (1, 3)) + + 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, self.onCheckBox) + sbox.Add(cb, 1, wx.LEFT, 60) + sbox.AddSpacer((5, 20)) + + k = 'BANG_BANG_ON' + tc = self.addTextCtrl(k, 80, self.onTextCtrlInteger) + sbox.Add(tc) + sbox.AddSpacer((5, 5)) + + k = 'BANG_BANG_OFF' + tc = self.addTextCtrl(k, 80, self.onTextCtrlInteger) + sbox.Add(tc) + sbox.AddSpacer((5, 5)) + + sz.Add(sbox, pos = (3, 3), span = (4, 1), flag = wx.ALIGN_CENTER_HORIZONTAL) + + 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, self.onChoice) + sbox.Add(ch) + sbox.AddSpacer((5, 5)) + + k = 'DC_EXTRUDER_PWM' + tc = self.addTextCtrl(k, 60, self.onTextCtrlInteger) + sbox.Add(tc) + sbox.AddSpacer((5, 5)) + + sz.Add(sbox, pos = (8,3), flag = wx.ALIGN_CENTER_HORIZONTAL) + + k = 'MOVEBUFFER_SIZE' + tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger) + sz.Add(tc, pos = (1, 5)) + + k = 'TH_COUNT' + tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger) + sz.Add(tc, pos = (2, 5)) + + k = 'ENDSTOP_STEPS' + tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger) + sz.Add(tc, pos = (3, 5)) + + k = 'PID_SCALE' + tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger) + sz.Add(tc, pos = (4, 5)) + + k = 'TEMP_HYSTERESIS' + tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlFloat) + sz.Add(tc, pos = (6, 5)) + + k = 'TEMP_RESIDENCY_TIME' + tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger) + sz.Add(tc, pos = (7, 5)) + + k = 'TEMP_EWMA' + tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlEWMA) + sz.Add(tc, pos = (8, 5)) + + self.SetSizer(sz) + self.enableAll(False) + + 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 setHeaters(self, hlist): + k = 'DC_EXTRUDER' + v = self.choices[k].GetSelection() + currentChoice = self.heaterNames[v] + self.boardHeaters = [s[0] for s in hlist] + self.heaterNames = [self.heaterNameNone] + self.boardHeaters + self.choices[k].Clear() + for h in self.heaterNames: + self.choices[k].Append(h) + self.choiceOptions[k] = self.heaterNames + + try: + v = self.heaterNames.index(currentChoice) + except: + v = 0 + dlg = wx.MessageDialog(self, + "Printer: Miscellaneous tab:\nDC Extruder heater " + "\"%s\" not defined for this board. Please check." + % currentChoice, "Warning", + wx.OK + wx.ICON_WARNING) + + dlg.ShowModal() + dlg.Destroy() + + self.choices[k].SetSelection(v) + + def setOriginalHeater(self, h): + k = 'DC_EXTRUDER' + if h and h.startswith("HEATER_"): + hname = h[len("HEATER_"):] + else: + hname = h + if len(self.boardHeaters) != 0: + if hname not in self.boardHeaters: + dlg = wx.MessageDialog(self, + "Printer: Miscellaneous tab:\nDC Extruder " + "heater \"%s\" not defined for this board. " + "Please check." + % hname, "Warning", wx.OK + wx.ICON_WARNING) + + dlg.ShowModal() + dlg.Destroy() + self.heaterNames = [self.heaterNameNone] + self.boardHeaters + else: + self.heaterNames = [self.heaterNameNone] + if h and h != self.heaterNameNone: + self.heaterNames.append(hname) + self.choices[k].Clear() + for ht in self.heaterNames: + self.choices[k].Append(ht) + self.choiceOptions[k] = self.heaterNames + if hname: + try: + v = self.heaterNames.index(hname) + except: + v = 0 + else: + v = 0 + self.choices[k].SetSelection(v) + + 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(str(cfgValues[k])) + else: + self.textControls[k].SetValue("") + + self.setChoice('REFERENCE', cfgValues, self.defaultRef) + + self.assertModified(False) + self.enableAll(True) + + def getValues(self): + result = Page.getValues(self) + + k = 'STEP_INTERRUPT_INTERRUPTIBLE' + cb = self.checkBoxes[k] + if cb.IsChecked(): + result[k] = "1" + else: + result[k] = "0" + + k = "DC_EXTRUDER" + s = self.choices[k].GetSelection() + v = self.choiceOptions[k][s] + if v == self.heaterNameNone: + result[k] = "" + else: + result[k] = "HEATER_%s" % v + + return result diff --git a/configtool/page.py b/configtool/page.py new file mode 100644 index 0000000..27a955b --- /dev/null +++ b/configtool/page.py @@ -0,0 +1,235 @@ + +import wx + +from configtool.data import pinNames, pinNamesWithBlank, reInteger, reFloat + + +class Page: + def __init__(self): + self.modified = False + self.valid = True + self.fieldValid = {} + self.textControls = {} + self.checkBoxes = {} + self.radioButtons = {} + self.choices = {} + self.choiceOptions = {} + + def enableAll(self, flag = True): + for c in self.textControls.keys(): + self.textControls[c].Enable(flag) + for c in self.checkBoxes.keys(): + self.checkBoxes[c].Enable(flag) + for c in self.radioButtons.keys(): + self.radioButtons[c].Enable(flag) + for c in self.choices.keys(): + self.choices[c].Enable(flag) + + def addTextCtrl(self, name, labelWidth, 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) + + return lsz + + def addCheckBox(self, name, 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 + + return cb + + def addRadioButton(self, name, style, validator): + rb = wx.RadioButton(self, wx.ID_ANY, self.labels[name], style = style) + self.Bind(wx.EVT_RADIOBUTTON, validator, rb) + self.radioButtons[name] = rb + + return rb + + def addChoice(self, name, choices, selection, labelWidth, 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 + self.choiceOptions[name] = choices + + return lsz + + def addPinChoice(self, name, choiceVal, allowBlank, labelWidth): + 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) + + if allowBlank: + opts = pinNamesWithBlank + else: + opts = pinNames + + ch = wx.Choice(self, wx.ID_ANY, choices = opts, name = name) + ch.Bind(wx.EVT_CHOICE, self.onChoice) + self.choices[name] = ch + self.choiceOptions[name] = opts + try: + sv = self.pinNames.index(choiceVal) + except: + sv = 0 + ch.SetSelection(sv) + lsz.Add(ch) + + return lsz + + def setChoice(self, name, cfgValues, default): + if name in cfgValues.keys(): + bv = cfgValues[name] + else: + bv = default + + try: + s = self.choiceOptions[name].index(bv) + except: + try: + s = self.choiceOptions[name].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 onCheckBox(self, evt): + self.assertModified(True) + evt.Skip() + + def setHelpText(self, ht): + for k in self.textControls.keys(): + if k in ht.keys(): + self.textControls[k].SetToolTipString(ht[k]) + + for k in self.checkBoxes.keys(): + if k in ht.keys(): + self.checkBoxes[k].SetToolTipString(ht[k]) + + for k in self.radioButtons.keys(): + if k in ht.keys(): + self.radioButtons[k].SetToolTipString(ht[k]) + + for k in self.choices.keys(): + if k in ht.keys(): + self.choices[k].SetToolTipString(ht[k]) + + def getValues(self): + self.assertModified(False) + result = {} + for k in self.checkBoxes.keys(): + cb = self.checkBoxes[k] + result[k] = cb.IsChecked() + + for k in self.textControls.keys(): + v = self.textControls[k].GetValue() + result[k] = v + + for k in self.radioButtons.keys(): + result[k] = self.radioButtons[k].GetValue() + + for k in self.choices.keys(): + v = self.choices[k].GetSelection() + result[k] = self.choiceOptions[k][v] + + return result + + 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 diff --git a/configtool/pinoutspage.py b/configtool/pinoutspage.py new file mode 100644 index 0000000..57297b4 --- /dev/null +++ b/configtool/pinoutspage.py @@ -0,0 +1,196 @@ + +import wx +from configtool.page import Page + + +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 + + pinXkeys = [('X_STEP_PIN', 2), ('X_DIR_PIN', 2), ('X_MIN_PIN', 2), + ('X_MAX_PIN', 2), ('X_ENABLE_PIN', 2), ('X_INVERT_DIR', 1), + ('X_INVERT_MIN', 1), ('X_INVERT_MAX', 1), + ('X_INVERT_ENABLE', 1)] + pinYkeys = [('Y_STEP_PIN', 2), ('Y_DIR_PIN', 2), ('Y_MIN_PIN', 2), + ('Y_MAX_PIN', 2), ('Y_ENABLE_PIN', 2), ('Y_INVERT_DIR', 1), + ('Y_INVERT_MIN', 1), ('Y_INVERT_MAX', 1), + ('Y_INVERT_ENABLE', 1)] + pinZkeys = [('Z_STEP_PIN', 2), ('Z_DIR_PIN', 2), ('Z_MIN_PIN', 2), + ('Z_MAX_PIN', 2), ('Z_ENABLE_PIN', 2), ('Z_INVERT_DIR', 1), + ('Z_INVERT_MIN', 1), ('Z_INVERT_MAX', 1), + ('Z_INVERT_ENABLE', 1)] + pinEkeys = [('E_STEP_PIN', 2), ('E_DIR_PIN', 2), ('E_ENABLE_PIN', 2), + ('E_INVERT_DIR', 1), ('E_INVERT_ENABLE', 1)] + + self.labels = {'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", + + 'DEBUG_LED_PIN': "Debug LED Pin:"} + + labelWidth = 120 + + 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 pinXkeys: + if ctype == 0: + tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlPin) + sbox.Add(tc) + elif ctype == 2: + tc = self.addPinChoice(k, "", True, labelWidth) + sbox.Add(tc) + else: + cb = self.addCheckBox(k, 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 pinYkeys: + if ctype == 0: + tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlPin) + sbox.Add(tc) + elif ctype == 2: + tc = self.addPinChoice(k, "", True, labelWidth) + sbox.Add(tc) + else: + cb = self.addCheckBox(k, 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 pinZkeys: + if ctype == 0: + tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlPin) + sbox.Add(tc) + elif ctype == 2: + tc = self.addPinChoice(k, "", True, labelWidth) + sbox.Add(tc) + else: + cb = self.addCheckBox(k, 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 pinEkeys: + if ctype == 0: + tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlPin) + sbox.Add(tc) + elif ctype == 2: + tc = self.addPinChoice(k, "", True, labelWidth) + sbox.Add(tc) + else: + cb = self.addCheckBox(k, self.onCheckBox) + sbox.Add(cb, 1, wx.LEFT, 30) + + sbox.AddSpacer((5, 5)) + + sz.Add(sbox, pos = (1, 7)) + + k = "STEPPER_ENABLE_PIN" + tc = self.addPinChoice(k, "", True, labelWidth) + sz.Add(tc, pos = (3, 1)) + + sz.AddSpacer((10, 10), pos = (4, 1)) + + k = "STEPPER_INVERT_ENABLE" + cb = self.addCheckBox(k, self.onCheckBox) + sz.Add(cb, pos = (5, 1), flag = wx.ALIGN_CENTER_HORIZONTAL) + + k = "PS_ON_PIN" + tc = self.addPinChoice(k, "", True, labelWidth) + sz.Add(tc, pos = (3, 3)) + + k = "PS_MOSFET_PIN" + tc = self.addPinChoice(k, "", True, labelWidth) + sz.Add(tc, pos = (5, 3)) + + k = "DEBUG_LED_PIN" + tc = self.addPinChoice(k, "", True, labelWidth) + sz.Add(tc, pos = (3, 7)) + + self.SetSizer(sz) + self.enableAll(False) + + def insertValues(self, cfgValues): + self.assertValid(True) + self.enableAll(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) + + for k in self.choices.keys(): + self.setChoice(k, cfgValues, "-") + + self.assertModified(False) + + def getValues(self): + result = Page.getValues(self) + + for k in self.choices.keys(): + if k in result.keys() and result[k] == "-": + result[k] = "" + + return result diff --git a/configtool/printerpanel.py b/configtool/printerpanel.py new file mode 100644 index 0000000..40a5de2 --- /dev/null +++ b/configtool/printerpanel.py @@ -0,0 +1,353 @@ + +import os +import wx +import re + +from configtool.data import (defineValueFormat, defineBoolFormat, reCommDefBL, + reCommDefBoolBL, reHelpTextStart, reHelpTextEnd, + reDefine, reDefineBL, reDefQS, reDefQSm, + reDefQSm2, reDefBool, reDefBoolBL) +from configtool.mechanicalpage import MechanicalPage +from configtool.accelerationpage import AccelerationPage +from configtool.miscellaneouspage import MiscellaneousPage + + +class PrinterPanel(wx.Panel): + def __init__(self, parent, nb, folder): + wx.Panel.__init__(self, nb, wx.ID_ANY) + self.parent = parent + + self.cfgValues = {} + self.heaters = [] + self.dir = os.path.join(folder, "config") + + sz = wx.BoxSizer(wx.HORIZONTAL) + + self.nb = wx.Notebook(self, wx.ID_ANY, size = (21, 21), + style = wx.BK_DEFAULT) + + self.pages = [] + self.titles = [] + self.pageModified = [] + self.pageValid = [] + + 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.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) + + self.SetSizer(sz) + + def onPageChange(self, evt): + print "printer notebook page changed" + print evt.GetSelection() + print evt.GetEventObject() + evt.Skip() + + def checkFocus(self): + print "check focus: ", self.nb.GetSelection() + + def assertModified(self, pg, flag = True): + self.pageModified[pg] = flag + self.modifyTab(pg) + + def isModified(self): + return (True in self.pageModified) + + def assertValid(self, pg, flag = True): + self.pageValid[pg] = flag + self.modifyTab(pg) + + if False in self.pageValid: + self.parent.enableSavePrinter(False) + else: + self.parent.enableSavePrinter(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: + pfx = "" + + self.nb.SetPageText(pg, pfx + self.titles[pg]) + + def setHeaters(self, ht): + return self.pgMiscellaneous.setHeaters(ht) + + def onClose(self, evt): + if not self.confirmLoseChanges("exit"): + return + + self.Destroy() + + def confirmLoseChanges(self, msg): + if True not in self.pageModified: + return True + + dlg = wx.MessageDialog(self, "Are you sure you want to " + msg + "?\n" + "There are changes to your printer " + "configuration 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 printer configuration"): + return + + wildcard = "Printer configuration (printer.*.h)|printer.*.h" + + dlg = wx.FileDialog(self, message = "Choose a printer config file", + defaultDir = self.dir, defaultFile = "", + wildcard = wildcard, style = wx.OPEN | wx.CHANGE_DIR) + + path = None + if dlg.ShowModal() == wx.ID_OK: + path = dlg.GetPath() + + dlg.Destroy() + if path is None: + return + + self.dir = os.path.dirname(path) + rc = self.loadConfigFile(path) + + if not rc: + dlg = wx.MessageDialog(self, "Unable to process file %s." % path, + "File error", wx.OK + wx.ICON_ERROR) + dlg.ShowModal() + dlg.Destroy() + return + + self.parent.enableSavePrinter(True) + self.parent.setPrinterTabText("Printer <%s>" % os.path.basename(path)) + + for pg in self.pages: + pg.insertValues(self.cfgValues) + pg.setHelpText(self.helpText) + + k = 'DC_EXTRUDER' + if k in self.cfgValues.keys(): + print "calling orig with (%s)" % self.cfgValues[k] + self.pgMiscellaneous.setOriginalHeater(self.cfgValues[k]) + else: + self.pgMiscellaneous.setOriginalHeater(None) + + def loadConfigFile(self, fn): + try: + self.cfgBuffer = list(open(fn)) + except: + return False + + self.configFile = fn + + self.processors = [] + gatheringHelpText = False + helpTextString = "" + helpKey = None + + self.cfgValues = {} + self.helpText = {} + + prevLines = "" + for ln in self.cfgBuffer: + if gatheringHelpText: + if reHelpTextEnd.match(ln): + gatheringHelpText = False + hk = helpKey.split() + for k in hk: + self.helpText[k] = helpTextString + helpTextString = "" + helpKey = None + continue + else: + helpTextString += ln + continue + + m = reHelpTextStart.match(ln) + if m: + t = m.groups() + gatheringHelpText = True + helpKey = t[0] + continue + + if ln.rstrip().endswith("\\"): + prevLines += ln.rstrip()[:-1] + continue + + if prevLines != "": + ln = prevLines + ln + prevLines = "" + + if ln.lstrip().startswith("//"): + continue + + if ln.lstrip().startswith("#define"): + m = reDefQS.search(ln) + if m: + t = m.groups() + if len(t) == 2: + m = reDefQSm.search(ln) + if m: + t = m.groups() + tt = re.findall(reDefQSm2, t[1]) + if len(tt) == 1: + self.cfgValues[t[0]] = tt[0] + continue + elif len(tt) > 1: + self.cfgValues[t[0]] = tt + continue + + m = reDefine.search(ln) + if m: + t = m.groups() + if len(t) == 2: + if t[0] == 'DC_EXTRUDER': print "raw value (%s)" % t[1] + self.cfgValues[t[0]] = t[1] + continue + + m = reDefBool.search(ln) + if m: + t = m.groups() + if len(t) == 1: + self.cfgValues[t[0]] = True + + return True + + def onSaveConfig(self, evt): + path = self.configFile + if self.saveConfigFile(path): + dlg = wx.MessageDialog(self, "File %s successfully written." % path, + "Save successful", wx.OK + wx.ICON_INFORMATION) + + else: + dlg = wx.MessageDialog(self, "Unable to write to file %s." % path, + "File error", wx.OK + wx.ICON_ERROR) + dlg.ShowModal() + dlg.Destroy() + + def onSaveConfigAs(self, evt): + wildcard = "Printer configuration (printer.*.h)|printer.*.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() + + if self.saveConfigFile(path): + dlg = wx.MessageDialog(self, "File %s successfully written." % path, + "Save successful", wx.OK + wx.ICON_INFORMATION) + self.parent.setPrinterTabText("Printer <%s>" % os.path.basename(path)) + + else: + dlg = wx.MessageDialog(self, "Unable to write to file %s." % path, + "File error", wx.OK + wx.ICON_ERROR) + dlg.ShowModal() + dlg.Destroy() + + def saveConfigFile(self, path): + ext = os.path.splitext(os.path.basename(path))[1] + self.dir = os.path.dirname(path) + + if ext == "": + path += ".h" + + try: + fp = file(path, 'w') + except: + return False + + self.configFile = path + + values = {} + + for pg in self.pages: + v1 = pg.getValues() + for k in v1.keys(): + values[k] = v1[k] + + for ln in self.cfgBuffer: + m = reDefineBL.match(ln) + if m: + t = m.groups() + if len(t) == 2: + if t[0] in values.keys() and values[t[0]] != "": + fp.write(defineValueFormat % (t[0], values[t[0]])) + else: + fp.write("//" + ln) + continue + + m = reDefBoolBL.match(ln) + if m: + t = m.groups() + if len(t) == 1: + if t[0] in values.keys() and values[t[0]]: + fp.write(defineBoolFormat % t[0]) + else: + fp.write("//" + ln) + continue + + m = reCommDefBL.match(ln) + if m: + t = m.groups() + if len(t) == 2: + if t[0] in values.keys() and values[t[0]] != "": + fp.write(defineValueFormat % (t[0], values[t[0]])) + else: + fp.write(ln) + continue + + m = reCommDefBoolBL.match(ln) + if m: + t = m.groups() + if len(t) == 1: + if t[0] in values.keys() and values[t[0]]: + fp.write(defineBoolFormat % t[0]) + else: + fp.write(ln) + continue + + fp.write(ln) + + fp.close() + + return True diff --git a/configtool/sensorlist.py b/configtool/sensorlist.py new file mode 100644 index 0000000..44d2c04 --- /dev/null +++ b/configtool/sensorlist.py @@ -0,0 +1,94 @@ + +import wx + + +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.sensorList = [] + + 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 setRowValidity(self, i, flag = False): + if i < 0 or i >= len(self.sensorList): + return + + self.valid[i] = flag + self.Refresh() + + def setTableValidity(self, flag = False): + for i in range(len(self.sensorList)): + self.setRowValidity(i, flag) + + 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 diff --git a/configtool/sensorpage.py b/configtool/sensorpage.py new file mode 100644 index 0000000..c0cbd5f --- /dev/null +++ b/configtool/sensorpage.py @@ -0,0 +1,190 @@ + +import wx +from configtool.page import Page +from configtool.data import pinNames, BSIZESMALL +from sensorlist import SensorList +from addsensordlg import AddSensorDlg + + +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.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_MAX6675': "MAX6675", 'TEMP_THERMISTOR': "Thermistor", + 'TEMP_AD595': "AD595", 'TEMP_PT100': "PT100", + 'TEMP_INTERCOM': "Intercom"} + + self.validPins = pinNames + + 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)) + for k in self.sensorTypeKeys: + cb = self.addCheckBox(k, self.onCheckBoxSensorType) + sbox.Add(cb, 1, wx.LEFT + wx.RIGHT, 10) + sbox.AddSpacer((5, 5)) + + sz.Add(sbox, pos = (1, 1), span = (5, 1)) + + 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) + self.bAdd.SetToolTipString("Add a sensor to the configuration.") + + 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) + self.bDelete.SetToolTipString("Remove the selected temperature sensor " + "from the configuration.") + + sz.Add(bsz, pos = (7, 4)) + + self.SetSizer(sz) + self.enableAll(False) + + 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 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.validPins) + 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.validateTable() + 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.validateTable() + 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): + self.enableAll(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) + + 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.validateTable() + self.limitSensorTypeControls() + + def setCandidatePins(self, plist): + if not plist or len(plist) == 0: + self.validPins = pinNames + else: + self.validPins = plist + + self.validateTable() + + def validateTable(self): + self.lb.setTableValidity(True) + self.setFieldValidity('SENSORLIST', True) + for i in range(len(self.sensors)): + if self.sensors[i][2] not in self.validPins: + self.lb.setRowValidity(i, False) + self.setFieldValidity('SENSORLIST', False) + + def setHelpText(self, ht): + Page.setHelpText(self, ht) + + k = 'DEFINE_TEMP_SENSOR' + if k in ht.keys(): + self.bAdd.SetToolTipString(ht[k])