---
the issue i've been running into is that when i set the feed rate to
something nonzero, the printer seems to wait forever saying it is
busy. i seem to need experience sending successful g-code.
i've learned enough about g-code now that i can probably read a
premade file, so it would make sense to find one of those files and
bum it down, like bisecting code when there isn't a debugger, to
figure out how to correctly use the extruder without freezing the
printer up.
i was hoping to be able to interrupt the printer if it is in a frozen
state, but so far i haven't figured out how to do this in this
particular state yet (it looks like disconnecting the usb device would
add some options, as well as learning more gcode).
attached are the files i have, which don't quite function but show my
attempt to prototype something that could parameterize stuff. i
copypasted some documentation into _parser.py which contains code to
parse my copypasting into structures. meanwhile test.py uses these
structures to send code to the printer.
-------
another thing i've discovered is that i'm actually using a very old
version of mecode that happened to have a fork recently updated. the
mainline gcode had two major version updates prior to the fork i've
been engaging. this gives me a desire to merge the two together which
is unfortunately inefficient. it would be more efficient to try to run
the newer code, porting my fixes over if they become needed (this
could take me some time from my spasms, which i've recently been
finding a couple possible avenues that could help reduce). i might
also want to look for forks that are based off the newer tip, to see
what the actual latest codebase is.
# this could read gcode firmware docs straight from their files
# but is presently copypaste
def parse_cmd(name, example, params, prefix='', suffix=''):
cmd, name = name.split(' - ')
exline, *comment = example.split(';',1)
exline = exline.strip()
exparts = exline.split(' ')
assert exparts[0] == cmd
exparts.pop(0)
values = {
value[0]: value[1:]
for value in exparts
}
for param in params.split('\n\n'):
param, pname = param.split('\n', 1)
param = param.strip()
if param[-1] in '0123456789':
param, _ = param.rsplit(' ',1)
param = param.strip()
if param[0] == '[':
param = param[1:-1]
pchar = param[0]
if pchar in values:
if len(param) > 1 and param[1] == '<':
pshort = param.strip()[2:-1]
values[pchar] = f'PrinterValue({repr(pchar)}, {repr(pname)}, {repr(pshort)}, typical={values[pchar]})'
else:
values[pchar] = f'PrinterFlag({repr(pchar)}, {repr(pname)})'
output = prefix+f'PrinterCmd({repr(name)}, {repr(cmd)}, doc={repr(comment[0].strip())},\n'
for pname, value in values.items():
output += prefix+f'\t{pname} = {value},\n'
output += prefix+f')'+suffix + '\n'
return output
def parse(varname, *texts):
output = varname + ' = [\n'
for text in texts:
output += parse_cmd(*text.strip().split('\n',2), prefix='\t', suffix=',')
output += ']\n'
return output
def immed_cmds_code():
return parse('immed_cmds', '''
M201 - Print Move Limits
M201 X500 Y500 Z100 E5000 ; sets maximum accelerations, mm/sec^2
[E<accel>]
E axis max acceleration
[F<Hz>]
Planner frequency limit (Requires XY_FREQUENCY_LIMIT)
[S<percent>]
Planner XY frequency minimum speed percentage (Requires XY_FREQUENCY_LIMIT)
[T<index>]
Target extruder (Requires DISTINCT_E_FACTORS)
[X<accel>]
X axis max acceleration
[Y<accel>]
Y axis max acceleration
[Z<accel>]
Z axis max acceleration
''','''
M203 - Set Max Feedrate
M203 X500 Y500 Z10 E60 ; sets maximum feedrates, mm / sec
[E<units/s>]
E axis max feedrate
[T<index>]
Target extruder (Requires DISTINCT_E_FACTORS)
[X<units/s>]
X axis max feedrate
[Y<units/s>]
Y axis max feedrate
[Z<units/s>]
Z axis max feedrate
''','''
M204 - Set Starting Acceleration
M204 P500 R1000 T500 ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2
[P<accel>]
Printing acceleration. Used for moves that include extrusion (i.e., which employ the current tool).
[R<accel>]
Retract acceleration. Used for extruder retraction moves.
[S<accel>]
Legacy parameter for move acceleration. Set both printing and travel acceleration.
[T<accel>]
Travel acceleration. Used for moves that include no extrusion.
''','''
M205 - Set Advanced Settings
M205 S0 T0 X8.00 Y8.00 Z0.40 E5.00 ; sets the minimum extruding and travel feed rate, mm/sec ; sets the jerk limits, mm/sec
[B<µs>]
Minimum segment time (µs)
[E<jerk>]
E max jerk (units/s)
[J<deviation>]
Junction deviation (requires JUNCTION_DEVIATION)
[S<units/s>]
Minimum feedrate for print moves (units/s)
[T<units/s>]
Minimum feedrate for travel moves (units/s)
[X<jerk>]
X max jerk (units/s)
[Y<jerk>]
Y max jerk (units/s)
[Z<jerk>]
Z max jerk (units/s)
''')
def delay_cmds_code():
return parse('delay_cmds', '''
M106 - Set Fan Speed
M106 S128 ; fan duty cycle
[I<index>] 2.0.6
Material preset index. Overrides S.
[P<index>]
Fan index
[S<speed>]
Speed, from 0 to 255. S255 provides 100% duty cycle; S128 produces 50%.
[T<secondary>]
Secondary speed. Added in Marlin 1.1.7. (Requires EXTRA_FAN_SPEED)
M106 P<fan> T3-255 sets a secondary speed for <fan>.
M106 P<fan> T2 uses the set secondary speed.
M106 P<fan> T1 restores the previous fan speed.
''','''
M104 - Set Hotend Temperature
M104 S225 ; set temporary nozzle temp
[B<temp>]
AUTOTEMP: The max auto-temperature.
[F<flag>]
AUTOTEMP: Autotemp flag. Omit to disable autotemp.
[I<index>] 2.0.6
Material preset index. Overrides S.
[S<temp>]
Target temperature.
AUTOTEMP: the min auto-temperature.
[T<index>]
Hotend index. If omitted, the currently active hotend will be used.
''','''
M140 - Set Bed Temperature
M140 S60 ; set final bed temp
[I<index>] 2.0.6
Material preset index. Overrides S.
[S<temp>]
Target temperature
''')
def wait_cmds_code():
return parse('wait_cmds', '''
G4 - Dwell
G4 S30 ; allow partial nozzle warmup
[P<time(ms)>]
Amount of time to dwell
[S<time(sec)>]
Amount of time to dwell
''','''
M190 - Wait for Bed Temperature
M190 R60 ; wait for bed temp to stabilize
[I<index>] 2.0.6
Material preset index. Overrides S.
[R<temp>]
Target temperature (wait for cooling or heating).
[S<temp>]
Target temperature (wait only when heating).
''','''
M109 - Wait for Hotend Temperature
M109 R210 ; wait for nozzle temp to stabilize
[B<temp>]
With AUTOTEMP, the max auto-temperature.
[F<flag>]
Autotemp flag. Omit to disable autotemp.
[I<index>] 2.0.6
Material preset index. Overrides S.
[R<temp>]
Target temperature (wait for cooling or heating).
[S<temp>]
Target temperature (wait only when heating). Also AUTOTEMP: The min auto-temperature.
[T<index>]
Hotend index. If omitted, the currently active hotend will be used.
''')
import mecode
class BaseValue:
def __init__(self, name, typical, min = None, max = None, long_name = None, descr = None):
self.name = name
self.typical = typical
self.min = min
self.max = max
self.long_name = long_name
self.descr = descr
class BaseCmd:
def __init__(self, name, validator=None, **values):
self.name = name
if validator is None:
validator = lambda g, **kwparams: True
self.validator = validator
self.values = values
class PrinterValue(BaseValue):
def __init__(self, char, name, short, typical, min = None, max = None):
super().__init__(name = char, typical = typical, min = min, max = max, long_name = short, descr = name)
class PrinterFlag:
pass
class PrinterCmd(BaseCmd):
def __init__(self, name, cmd, doc=None, validator=None, **values):
super().__init__(name, validator=validator, **values)
self.cmd = cmd
self.doc = doc
def __call__(self, g):
output = self.cmd
kwparams = {
char: value.typical if isinstance(value, BaseValue) else True
for char, value in self.values.items()
}
assert(self.validator(g, **kwparams))
for char, typical in kwparams.items():
output += ' ' + char
if typical is not True:
output += str(typical)
if self.doc is not None:
output += ' ; ' + self.doc.replace('\n', '; ')
g.write(output)
class MecodeValue(BaseValue):
pass
class MecodeCmd(BaseCmd):
def __call__(self, g):
kwparams = {
name: value.typical
for name, value in self.values.items()
}
assert(self.validator(g, **kwparams))
return getattr(g, self.name)(**kwparams)
# adds "*_cmds[]" of PrinterCmd to globals based on some copypasted text
import _parser
exec(_parser.immed_cmds_code(), globals(), locals())
exec(_parser.delay_cmds_code(), globals(), locals())
exec(_parser.wait_cmds_code(), globals(), locals())
def sufficient_temperature_for_feed(gcode, min_temp=200):
temps = gcode._p.current_temperature()
if temps['T'] < min_temp:
if temps['T/'] < min_temp:
return False
while gcode._p.current_temperature()['T'] < min_temp:
print('waiting for temperature')
sleep(0.5)
return True
immed_cmds.extend([
MecodeCmd('feed',
rate=MecodeValue(
'rate', typical=5.0, min=0.0,
descr='The speed to move the tool head (feed) in mm/s.'
),
validator=lambda gcode, rate: rate == 0 or sufficient_temperature_for_feed(g)
)
])
with mecode.G(
direct_write=True,
direct_write_mode='serial',
printer_port='/dev/ttyUSB0',
baudrate=115200,
two_way_comm=True,
setup=True,
) as g:
g.break_and_continue()
#print(g._p.current_position())
#print(g._p.current_temperature())
#print(g.write('M105', resp_needed=True))
#g.break_and_continue()
g.auto_home()
#g.finish_moves()
for cmd in delay_cmds:
cmd(g)
for cmd in immed_cmds:
cmd(g)
g.finish_moves()
#g.move(10, 10)
#g.finish_moves()
#g.arc(x=10, y=5, radius=20, direction='CCW')
#g.finish_moves()
g.meander(5, 10, spacing=1)
g.finish_moves()
#g.abs_move(x=1, y=1)
#g.finish_moves()
#g.home()
#g.finish_moves()