Configtool: first steps for a user friendly configuration tool.
Contribution by jbernardis. This attempt uses a rarther simple tree-type dialog. Also only half done, which is entirely sufficient for evaluation.
This commit is contained in:
parent
86f1fc418f
commit
0d956511f9
|
|
@ -1,3 +1,4 @@
|
|||
*.pyc
|
||||
*.hex
|
||||
build
|
||||
*~
|
||||
|
|
|
|||
|
|
@ -0,0 +1,371 @@
|
|||
#!/bin/env python
|
||||
|
||||
import wx
|
||||
import re
|
||||
|
||||
try:
|
||||
from agw import customtreectrl as CT
|
||||
except ImportError:
|
||||
import wx.lib.agw.customtreectrl as CT
|
||||
|
||||
from config_helptext import helpText
|
||||
|
||||
BSIZE = (90, 60)
|
||||
|
||||
|
||||
class MyFrame(wx.Frame):
|
||||
|
||||
def __init__(self):
|
||||
self.reDefine = re.compile("\s*#define\s+(\S+)\s+(\S+)")
|
||||
self.reDefQS = re.compile("\s*#define\s+(\S+)\s+(\"[^\"]*\")")
|
||||
self.reDefTS = re.compile("\s*#define\s+(DEFINE_TEMP_SENSOR\\([^)]*\\))")
|
||||
self.reDefHT = re.compile("\s*#define\s+(DEFINE_HEATER\\([^)]*\\))")
|
||||
self.reDefBool = re.compile("\s*#define\s+(\S+)\s+")
|
||||
|
||||
self.t = 0
|
||||
self.seq = 1
|
||||
wx.Frame.__init__(self, None, -1, "Teacup Firmware Configurator",
|
||||
size = (450, 600))
|
||||
self.Bind(wx.EVT_CLOSE, self.onClose)
|
||||
|
||||
self.cfgValues = {}
|
||||
|
||||
panel = wx.Panel(self, -1)
|
||||
|
||||
sz = wx.BoxSizer(wx.HORIZONTAL)
|
||||
bsz = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
b = wx.Button(panel, wx.ID_ANY, "Load\nConfig", size = BSIZE)
|
||||
panel.Bind(wx.EVT_BUTTON, self.onLoadConfig, b)
|
||||
bsz.Add(b)
|
||||
|
||||
style = wx.SUNKEN_BORDER | wx.WANTS_CHARS
|
||||
agwStyle = CT.TR_HAS_BUTTONS | CT.TR_HAS_VARIABLE_ROW_HEIGHT | CT.TR_ALIGN_WINDOWS
|
||||
|
||||
self.tree = CT.CustomTreeCtrl(panel, wx.ID_ANY, wx.DefaultPosition,
|
||||
(100, 100), style, agwStyle)
|
||||
self.tree.Bind(CT.EVT_TREE_ITEM_CHECKED, self.onItemChecked)
|
||||
self.tree.Bind(CT.EVT_TREE_ITEM_GETTOOLTIP, self.onToolTip)
|
||||
self.root = self.tree.AddRoot("Teacup Configuration")
|
||||
|
||||
self.stMech = self.mechSubTree(self.tree, self.root)
|
||||
self.stAcc = self.accSubTree(self.tree, self.root)
|
||||
|
||||
pins = self.tree.AppendItem(self.root, "3. Pinouts",
|
||||
data = "Pinouts")
|
||||
sensors = self.tree.AppendItem(self.root, "4. Temperature Sensors",
|
||||
data = "Sensors")
|
||||
heaters = self.tree.AppendItem(self.root, "5. Heaters",
|
||||
data = "Heaters")
|
||||
comm = self.tree.AppendItem(self.root, "6. Communications",
|
||||
data = "Communications")
|
||||
misc = self.tree.AppendItem(self.root, "7. Miscellaneous",
|
||||
data = "Miscellaneous")
|
||||
|
||||
sz.Add(self.tree, 1, wx.EXPAND + wx.ALL, 5)
|
||||
sz.Add(bsz, 0, wx.ALL, 5)
|
||||
|
||||
panel.SetSizer(sz)
|
||||
|
||||
def mechSubTree(self, tree, root):
|
||||
spmKeys = ['SMX', 'SMY', 'SMZ', 'SME']
|
||||
|
||||
mfrKeys = ['MFRX', 'MFRY', 'MFRZ', 'MFRE']
|
||||
|
||||
msrKeys = ['MSRX', 'MSRY', 'MSRZ']
|
||||
|
||||
eclKeys = ['ECX', 'ECY', 'ECZ']
|
||||
|
||||
minmaxKeys = ['MINX', 'MAXX', 'MINY', 'MAXY', 'MINZ', 'MAXZ']
|
||||
|
||||
mechLabels = {'SPM': "Steps/Meter",
|
||||
'SMX': "X:", 'SMY': "Y:", 'SMZ': "Z:", 'SME' : "E:",
|
||||
'MFR': "Max Feed Rate",
|
||||
'MFRX': "X:", 'MFRY': "Y:", 'MFRZ': "Z:", 'MFRE': "E:",
|
||||
'MSR': "Search Feed Rate",
|
||||
'MSRX': "X:", 'MSRY': "Y:", 'MSRZ': "Z:",
|
||||
'ECL': "Endstop Clearance",
|
||||
'ECX': "X:", 'ECY': "Y:", 'ECZ': "Z:",
|
||||
'MINMAX': "Minimum/Maximum X/Y/Z",
|
||||
'MINX': "Min X:", 'MAXX': "Max X:", 'MINY': "Min Y:",
|
||||
'MAXY': "Max Y:", 'MINZ': "Min Z:", 'MAXZ': "Max Z:",
|
||||
'ABSE': "Absolute E Coordinates"}
|
||||
|
||||
self.mechCfgKeys = {'SMX': "STEPS_PER_M_X", 'SMY': "STEPS_PER_M_Y",
|
||||
'SMZ': "STEPS_PER_M_Z", 'SME' : "STEPS_PER_M_E",
|
||||
'MFRX': "MAXIMUM_FEEDRATE_X",
|
||||
'MFRY': "MAXIMUM_FEEDRATE_Y",
|
||||
'MFRZ': "MAXIMUM_FEEDRATE_Z",
|
||||
'MFRE': "MAXIMUM_FEEDRATE_E",
|
||||
'MSRX': "SEARCH_FEEDRATE_X",
|
||||
'MSRY': "SEARCH_FEEDRATE_Y",
|
||||
'MSRZ': "SEARCH_FEEDRATE_Z",
|
||||
'ECX': "ENDSTOP_CLEARANCE_X",
|
||||
'ECY': "ENDSTOP_CLEARANCE_Y",
|
||||
'ECZ': "ENDSTOP_CLEARANCE_Z",
|
||||
'MINX': "X_MIN", 'MAXX': "X_MAX", 'MINY': "Y_MIN",
|
||||
'MAXY': "Y_MAX", 'MINZ': "Z_MIN", 'MAXZ': "Z_MAX",
|
||||
'ABSE': "E_ABSOLUTE"}
|
||||
|
||||
self.tcMech = {}
|
||||
self.brMech = {}
|
||||
mech = tree.AppendItem(root, "1. Mechanical/Hardware", data = "Mechanical")
|
||||
|
||||
for tag, tbl, cttype in [('SPM', spmKeys, 0), ('MFR', mfrKeys, 0),
|
||||
('MSR', msrKeys, 0), ('ECL', eclKeys, 0),
|
||||
('MINMAX', minmaxKeys, 1)]:
|
||||
st = tree.AppendItem(mech, mechLabels[tag], ct_type = cttype, data = tag)
|
||||
self.brMech[tag] = st
|
||||
for k in tbl:
|
||||
ck = self.mechCfgKeys[k]
|
||||
if ck in self.cfgValues.keys():
|
||||
v = self.cfgValues[ck]
|
||||
else:
|
||||
v = ""
|
||||
tc = wx.TextCtrl(tree, wx.ID_ANY, v, style = wx.TE_RIGHT)
|
||||
if k in helpText.keys():
|
||||
tc.SetToolTipString(helpText[k])
|
||||
tc.Bind(wx.EVT_CHAR, self.onTextCtrl)
|
||||
self.tcMech[k] = tc
|
||||
self.brMech[k] = tree.AppendItem(st, mechLabels[k], ct_type = 0,
|
||||
wnd = tc, data = k)
|
||||
|
||||
allAbsent = True
|
||||
for i in minmaxKeys:
|
||||
k = self.mechCfgKeys[i]
|
||||
if k in self.cfgValues.keys():
|
||||
allAbsent = False
|
||||
break
|
||||
|
||||
if allAbsent:
|
||||
self.tree.CheckItem(self.brMech['MINMAX'], False)
|
||||
else:
|
||||
self.tree.CheckItem(self.brMech['MINMAX'], True)
|
||||
|
||||
br = tree.AppendItem(mech, mechLabels['ABSE'], ct_type = 1, data = "ABSE")
|
||||
ck = self.mechCfgKeys['ABSE']
|
||||
if ck in self.cfgValues.keys() and self.cfgValues[ck]:
|
||||
self.tree.CheckItem(br, True)
|
||||
else:
|
||||
self.tree.CheckItem(br, False)
|
||||
self.brMech['ABSE'] = br
|
||||
return mech
|
||||
|
||||
def insertMechValues(self):
|
||||
for k in self.mechCfgKeys.keys():
|
||||
if k in self.tcMech.keys():
|
||||
ck = self.mechCfgKeys[k]
|
||||
if ck in self.cfgValues.keys():
|
||||
self.tcMech[k].SetValue(self.cfgValues[ck])
|
||||
|
||||
allAbsent = True
|
||||
for i in ['MINX', 'MAXX', 'MINY', 'MAXY', 'MINZ', 'MAXZ']:
|
||||
k = self.mechCfgKeys[i]
|
||||
if k in self.cfgValues.keys():
|
||||
allAbsent = False
|
||||
break
|
||||
|
||||
if allAbsent:
|
||||
self.tree.CheckItem(self.brMech['MINMAX'], False)
|
||||
else:
|
||||
self.tree.CheckItem(self.brMech['MINMAX'], True)
|
||||
|
||||
br = self.brMech['ABSE']
|
||||
ck = self.mechCfgKeys['ABSE']
|
||||
if ck in self.cfgValues.keys() and self.cfgValues[ck]:
|
||||
self.tree.CheckItem(br, True)
|
||||
else:
|
||||
self.tree.CheckItem(br, False)
|
||||
|
||||
def accSubTree(self, tree, root):
|
||||
accLabels = {'ACTYPE': "Acceleration Type:",
|
||||
'ACRR': "RepRap", 'ACRP': "Ramping", 'ACTP': "Temporal",
|
||||
'ACCEL' : "Acceleration", 'LKAH': "Look Ahead",
|
||||
'JERK': "Maximum Jerk:",
|
||||
'JERKX': "X", 'JERKY': "Y", 'JERKZ': "Z", 'JERKE': "E"}
|
||||
|
||||
self.accCfgKeys = {'ACRR': "ACCELERATION_REPRAP",
|
||||
'ACRP': "ACCELERATION_RAMPING",
|
||||
'ACTP': "ACCELERATION_TEMPORAL",
|
||||
'ACCEL': "ACCELERATION", 'LKAH': "LOOKAHEAD",
|
||||
'JERKX': "MAX_JERK_X", 'JERKY': "MAX_JERK_Y",
|
||||
'JERKZ': "MAX_JERK_Z", 'JERKE': "MAX_JERK_E"}
|
||||
|
||||
self.tcAcc = {}
|
||||
self.brAcc = {}
|
||||
|
||||
acc = tree.AppendItem(root, "2. Acceleration", data = "Acceleration")
|
||||
|
||||
st = tree.AppendItem(acc, accLabels['ACTYPE'], data = "ACTYPE")
|
||||
self.brAcc['ACTYPE'] = st
|
||||
|
||||
for tag in ['ACRR', 'ACRP', 'ACTP']:
|
||||
br = tree.AppendItem(st, accLabels[tag], ct_type = 2, data = tag)
|
||||
self.brAcc[tag] = br
|
||||
ck = self.accCfgKeys[tag]
|
||||
if ck in self.cfgValues.keys() and self.cfgValues[ck]:
|
||||
self.tree.CheckItem(br, True)
|
||||
|
||||
tag = 'ACRP'
|
||||
ck = self.accCfgKeys['ACCEL']
|
||||
if ck in self.cfgValues.keys():
|
||||
v = self.cfgValues[ck]
|
||||
else:
|
||||
v = ""
|
||||
tc = wx.TextCtrl(tree, wx.ID_ANY, v, style = wx.TE_RIGHT)
|
||||
tc.SetToolTipString(helpText['ACCEL'])
|
||||
tc.Bind(wx.EVT_CHAR, self.onTextCtrl)
|
||||
self.tcAcc['ACCEL'] = tc
|
||||
|
||||
br = self.brAcc[tag]
|
||||
self.brAcc['ACCEL'] = tree.AppendItem(br, accLabels['ACCEL'], ct_type = 0,
|
||||
wnd = self.tcAcc['ACCEL'],
|
||||
data = 'ACCEL')
|
||||
self.brAcc['LKAH'] = tree.AppendItem(br, accLabels['LKAH'], ct_type = 1,
|
||||
data = 'LKAH')
|
||||
|
||||
br = self.brAcc['LKAH']
|
||||
ck = self.accCfgKeys['LKAH']
|
||||
if ck in self.cfgValues.keys() and self.cfgValues[ck]:
|
||||
self.tree.CheckItem(br, True)
|
||||
else:
|
||||
self.tree.CheckItem(br, False)
|
||||
|
||||
st = tree.AppendItem(acc, accLabels['JERK'], data = 'JERK')
|
||||
self.brAcc['JERK'] = st
|
||||
|
||||
for k in ['JERKX', 'JERKY', 'JERKZ', 'JERKE']:
|
||||
ck = self.accCfgKeys[k]
|
||||
if ck in self.cfgValues.keys():
|
||||
v = self.cfgValues[ck]
|
||||
else:
|
||||
v = ""
|
||||
tc = wx.TextCtrl(tree, wx.ID_ANY, v, style = wx.TE_RIGHT)
|
||||
if k in helpText.keys():
|
||||
tc.SetToolTipString(helpText[k])
|
||||
tc.Bind(wx.EVT_CHAR, self.onTextCtrl)
|
||||
self.tcAcc[k] = tc
|
||||
self.brAcc[k] = tree.AppendItem(st, accLabels[k], ct_type = 0,
|
||||
wnd = tc, data = k)
|
||||
|
||||
return acc
|
||||
|
||||
def insertAccValues(self):
|
||||
for k in self.accCfgKeys.keys():
|
||||
if k in self.tcAcc.keys():
|
||||
ck = self.accCfgKeys[k]
|
||||
if ck in self.cfgValues.keys():
|
||||
self.tcAcc[k].SetValue(self.cfgValues[ck])
|
||||
|
||||
for tag in ['ACRR', 'ACRP', 'ACTP']:
|
||||
br = self.brAcc[tag]
|
||||
ck = self.accCfgKeys[tag]
|
||||
if ck in self.cfgValues.keys() and self.cfgValues[ck]:
|
||||
self.tree.CheckItem(br, True)
|
||||
|
||||
br = self.brAcc['LKAH']
|
||||
ck = self.accCfgKeys['LKAH']
|
||||
if ck in self.cfgValues.keys() and self.cfgValues[ck]:
|
||||
self.tree.CheckItem(br, True)
|
||||
else:
|
||||
self.tree.CheckItem(br, False)
|
||||
|
||||
def onItemChecked(self, evt):
|
||||
item = evt.GetItem()
|
||||
match = None
|
||||
for k in self.brMech.keys():
|
||||
if item == self.brMech[k]:
|
||||
match = k
|
||||
break
|
||||
|
||||
if match is None:
|
||||
return
|
||||
|
||||
if match == 'MINMAX':
|
||||
self.tree.EnableChildren(item, self.tree.IsItemChecked(item))
|
||||
|
||||
def onToolTip(self, evt):
|
||||
item = evt.GetItem()
|
||||
if item:
|
||||
data = self.tree.GetPyData(item)
|
||||
if data and data in helpText.keys():
|
||||
evt.SetToolTip(wx.ToolTip(helpText[data]))
|
||||
|
||||
def onClose(self, evt):
|
||||
self.Destroy()
|
||||
|
||||
def onTextCtrl(self, event):
|
||||
char = chr(event.GetKeyCode())
|
||||
event.Skip()
|
||||
|
||||
def onLoadConfig(self, evt):
|
||||
wildcard = "C Header files (*.h)|*.h"
|
||||
|
||||
dlg = wx.FileDialog(self, message = "Choose a Config file",
|
||||
defaultDir = ".", 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.loadConfigFile(path)
|
||||
self.insertMechValues()
|
||||
self.insertAccValues()
|
||||
|
||||
def loadConfigFile(self, fn):
|
||||
try:
|
||||
lst = list(open(fn))
|
||||
except:
|
||||
return False
|
||||
|
||||
self.cfgValues = {}
|
||||
self.cfgValues['E_ABSOLUTE'] = False
|
||||
for ln in lst:
|
||||
if ln.lstrip().startswith("#define"):
|
||||
m = self.reDefTS.search(ln)
|
||||
if m:
|
||||
t = m.groups()
|
||||
if len(t) == 1:
|
||||
print "TSkey (%s)" % t[0]
|
||||
continue
|
||||
|
||||
m = self.reDefHT.search(ln)
|
||||
if m:
|
||||
t = m.groups()
|
||||
if len(t) == 1:
|
||||
print "HTkey (%s)" % t[0]
|
||||
continue
|
||||
|
||||
m = self.reDefQS.search(ln)
|
||||
if m:
|
||||
t = m.groups()
|
||||
if len(t) == 2:
|
||||
self.cfgValues[t[0]] = t[1]
|
||||
continue
|
||||
|
||||
m = self.reDefine.search(ln)
|
||||
if m:
|
||||
t = m.groups()
|
||||
if len(t) == 2:
|
||||
self.cfgValues[t[0]] = t[1]
|
||||
continue
|
||||
|
||||
m = self.reDefBool.search(ln)
|
||||
if m:
|
||||
t = m.groups()
|
||||
if len(t) == 1:
|
||||
self.cfgValues[t[0]] = True
|
||||
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = wx.PySimpleApp()
|
||||
frame = MyFrame()
|
||||
frame.Show(True)
|
||||
app.MainLoop()
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
helpText = {
|
||||
'SPM': "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",
|
||||
'SMX': "steps per meter for the X axis",
|
||||
'SMY': "steps per meter for the Y axis",
|
||||
'SMZ': "steps per meter for the Z axis",
|
||||
'SME': "steps per meter for the E axis",
|
||||
|
||||
'MFR': "maximum feed rate - in mm/min - for G0 rapid moves and as a cap for \
|
||||
all other feedrates",
|
||||
'MFRX': "maximum feed rate for the X axis (mm/min)",
|
||||
'MFRY': "maximum feed rate for the Y axis (mm/min)",
|
||||
'MFRZ': "maximum feed rate for the Z axis (mm/min)",
|
||||
'MFRE': "maximum feed rate for the E axis (mm/min)",
|
||||
|
||||
'MSR': "search feed rate - in mm/min - used when doing precision endstop \
|
||||
search and as a default feed rate",
|
||||
'MSRX': "search feed rate for the X axis (mm/min)",
|
||||
'MSRY': "search feed rate for the Y axis (mm/min)",
|
||||
'MSRZ': "search feed rate for the Z axis (mm/min)",
|
||||
|
||||
'ECL': "When hitting an endstop, Teacup properly decelerates instead of \
|
||||
doing an abrupt stop\nto save your mechanics. Ineviteably, this means it \
|
||||
overshoots the endstop trigger point by some distance.\n\n\
|
||||
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",
|
||||
'ECX': "endstop clearance for the X axis (mm * 1000)",
|
||||
'ECY': "endstop clearance for the Y axis (mm * 1000)",
|
||||
'ECZ': "endstop clearance for the Z axis (mm * 1000)",
|
||||
|
||||
'MINMAX': "soft axis limits, in mm.\n\ndefine them to your machine's size \
|
||||
relative to what your host considers to be the origin.",
|
||||
'MINX': "Minimum limit for the X axis:",
|
||||
'MAXX': "maximum limit for the X axis:",
|
||||
'MINY': "minimum limit for the Y axis:",
|
||||
'MAXY': "maximum limit for the Y axis:",
|
||||
'MINZ': "minimum limit for the Z axis",
|
||||
'MAXZ': "maximum limit for the Z axis",
|
||||
|
||||
'ABSE': "some G-code creators produce relative length commands for the \
|
||||
extruder,\nothers absolute ones. G-code using absolute lengths can be \
|
||||
recognized when there\nare G92 E0 commands from time to time. if you have \
|
||||
G92 E0 in your G-code, check this box.",
|
||||
|
||||
'ACTYPE': "Acceleration algorithm",
|
||||
'ACRR': "acceleration, reprap style.\n\n\
|
||||
Each movement starts at the speed of the previous command and accelerates or \
|
||||
decelerates\nlinearly to reach target speed at the end of the movement.",
|
||||
'ACRP': "acceleration and deceleration ramping.\n\n\
|
||||
Each movement starts at (almost) no speed, linearly accelerates to target \
|
||||
speed and decelerates\njust in time to smoothly stop at the target.",
|
||||
'ACTP': "This algorithm causes the timer to fire when any axis needs to step, \
|
||||
instead of\nsynchronising to the axis with the most steps ala bresenham",
|
||||
'ACCEL' : "how fast to accelerate when using acceleration ramping.\n\n\
|
||||
given in mm/s^2, decimal allowed, useful range 1. to 10,000.\n\
|
||||
Start with 10. for milling (high precision) or 1000. for printing",
|
||||
'LKAH': "Define this to enable look-ahead during *ramping* acceleration to \
|
||||
smoothly transition\nbetween moves instead of performing a dead stop every \
|
||||
move. Enabling look-ahead requires about\n3600 bytes of flash memory.",
|
||||
'JERK': "When performing look-ahead, we need to decide what an acceptable \
|
||||
jerk to the\nmechanics is. Look-ahead attempts to instantly change direction \
|
||||
at movement\ncrossings, which means instant changes in the speed of the axes \
|
||||
participating\nin the movement. Define here how big the speed bumps on each \
|
||||
of the axes is\nallowed to be.\n\n\
|
||||
If you want a full stop before and after moving a specific axis, define\n\
|
||||
maximum jerk of this axis to 0. This is often wanted for the Z axis. If you want\n\
|
||||
to ignore jerk on an axis, define it to twice the maximum feedrate of this axis.\n\n\
|
||||
Having these values too low results in more than neccessary slowdown at\n\
|
||||
movement crossings, but is otherwise harmless. Too high values can result\n\
|
||||
in stepper motors suddenly stalling. If angles between movements in your\n\
|
||||
G-code are small and your printer runs through entire curves full speed,\n\
|
||||
there's no point in raising the values.\n\n\
|
||||
Units: mm/min\n\
|
||||
Sane values: 0 to 400\n\
|
||||
Valid range: 0 to 65535",
|
||||
'JERKX': "maximum jerk for the X axis",
|
||||
'JERKY': "maximum jerk for the Y axis",
|
||||
'JERKZ': "maximum jerk for the Z axis",
|
||||
'JERKE': "maximum jerk for the E axis",
|
||||
}
|
||||
Loading…
Reference in New Issue