CAM: snapmaker toolheads and spindle speeds
* Removed generic snapmaker machine and toolhead configuration. The idea of running a CNC code that is not matched to the machine's abilities and work area seems dangerous. * --machine argument is required * --toolhead argument is required when the selected machine is compatible with more than one toolhead. When the selected machine only supports one toolhead, it is selected as the default. * --spindle-percent defaults according to the selected toolhead capabilities. If the toolhead can do RPM, then RPM is used, otherwise falls back to percent. This option now functions as an override. * fixed a bug in convert_spindle() when RPM is selected. The gcode was not returned.
This commit is contained in:
@@ -51,47 +51,79 @@ SNAPMAKER_MACHINES = dict(
|
||||
key="Original",
|
||||
name="Snapmaker Original",
|
||||
boundaries=dict(X=90, Y=90, Z=50),
|
||||
compatible_toolheads={"Original_CNC"},
|
||||
),
|
||||
Original_Z_Extension=dict(
|
||||
key="Original_Z_Extension",
|
||||
name="Snapmaker Original with Z extension",
|
||||
boundaries=dict(X=90, Y=90, Z=146),
|
||||
compatible_toolheads={"Original_CNC"},
|
||||
),
|
||||
A150=dict(
|
||||
key="A150",
|
||||
name="Snapmaker 2 A150",
|
||||
boundaries=dict(X=160, Y=160, Z=90),
|
||||
compatible_toolheads={"50W_CNC"},
|
||||
),
|
||||
A250=dict(
|
||||
key="A250",
|
||||
name="Snapmaker 2 A250",
|
||||
boundaries=dict(X=230, Y=250, Z=180),
|
||||
compatible_toolheads={"50W_CNC", "200W_CNC"},
|
||||
),
|
||||
A250T=dict(
|
||||
key="A250T",
|
||||
name="Snapmaker 2 A250T",
|
||||
boundaries=dict(X=230, Y=250, Z=180),
|
||||
compatible_toolheads={"50W_CNC", "200W_CNC"},
|
||||
),
|
||||
A350=dict(
|
||||
key="A350",
|
||||
name="Snapmaker 2 A350",
|
||||
boundaries=dict(X=320, Y=350, Z=275),
|
||||
compatible_toolheads={"50W_CNC", "200W_CNC"},
|
||||
),
|
||||
A350T=dict(
|
||||
key="A350T",
|
||||
name="Snapmaker 2 A350T",
|
||||
boundaries=dict(X=320, Y=350, Z=275),
|
||||
compatible_toolheads={"50W_CNC", "200W_CNC"},
|
||||
),
|
||||
Artisan=dict(
|
||||
key="Artisan",
|
||||
name="Snapmaker Artisan",
|
||||
boundaries=dict(X=400, Y=400, Z=400),
|
||||
boundaries=dict(X=400, Y=413, Z=400),
|
||||
compatible_toolheads={"200W_CNC"},
|
||||
),
|
||||
)
|
||||
|
||||
# Could support other types of toolheads (laser, drag knife, 3DP, ...) in the future
|
||||
# https://wiki.snapmaker.com/en/Snapmaker_Luban/manual/2_supported_gcode_references#m3m4-modified-cnclaser-on
|
||||
SNAPMAKER_TOOLHEADS = {
|
||||
"50W": dict(name="50W CNC module", min=0, max=12000, percent=True),
|
||||
"200W": dict(name="200W CNC module", min=8000, max=18000, percent=False),
|
||||
"Original_CNC": dict(
|
||||
key="Original_CNC",
|
||||
name="Original CNC module",
|
||||
speed_rpm=dict(min=0, max=7000),
|
||||
boundaries_delta=dict(X=0, Y=0, Z=0),
|
||||
has_percent=True,
|
||||
has_speed_s=False,
|
||||
),
|
||||
"50W_CNC": dict(
|
||||
key="50W_CNC",
|
||||
name="50W CNC module",
|
||||
speed_rpm=dict(min=0, max=12000),
|
||||
boundaries_delta=dict(X=0, Y=0, Z=0),
|
||||
has_percent=True,
|
||||
has_speed_s=False,
|
||||
),
|
||||
"200W_CNC": dict(
|
||||
key="200W_CNC",
|
||||
name="200W CNC module",
|
||||
speed_rpm=dict(min=0, max=18000),
|
||||
boundaries_delta=dict(X=0, Y=-13, Z=0),
|
||||
has_percent=True,
|
||||
has_speed_s=True,
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@@ -223,16 +255,9 @@ class Snapmaker(Path.Post.Processor.PostProcessor):
|
||||
self.values["BOUNDARIES_CHECK"] = False
|
||||
self.values["MACHINES"] = SNAPMAKER_MACHINES
|
||||
self.values["TOOLHEADS"] = SNAPMAKER_TOOLHEADS
|
||||
# default toolhead is 50W (the weakest one)
|
||||
self.values["DEFAULT_TOOLHEAD"] = "50W"
|
||||
self.values["TOOLHEAD_NAME"] = SNAPMAKER_TOOLHEADS[self.values["DEFAULT_TOOLHEAD"]]["name"]
|
||||
self.values["SPINDLE_SPEEDS"] = dict(
|
||||
min=SNAPMAKER_TOOLHEADS[self.values["DEFAULT_TOOLHEAD"]]["min"],
|
||||
max=SNAPMAKER_TOOLHEADS[self.values["DEFAULT_TOOLHEAD"]]["max"],
|
||||
)
|
||||
self.values["SPINDLE_PERCENT"] = SNAPMAKER_TOOLHEADS[self.values["DEFAULT_TOOLHEAD"]][
|
||||
"percent"
|
||||
]
|
||||
self.values["TOOLHEAD_NAME"] = None
|
||||
self.values["SPINDLE_SPEEDS"] = dict()
|
||||
self.values["SPINDLE_PERCENT"] = None
|
||||
|
||||
def snapmaker_init_argument_defaults(self) -> None:
|
||||
"""Initialize which arguments (in a pair) are shown as the default argument."""
|
||||
@@ -245,7 +270,6 @@ class Snapmaker(Path.Post.Processor.PostProcessor):
|
||||
self.argument_defaults["thumbnail"] = True
|
||||
self.argument_defaults["gui"] = True
|
||||
self.argument_defaults["boundaries-check"] = True
|
||||
self.argument_defaults["spindle-percent"] = True
|
||||
|
||||
def snapmaker_init_arguments_visible(self) -> None:
|
||||
"""Initialize which argument pairs are visible in TOOLTIP_ARGS."""
|
||||
@@ -349,14 +373,8 @@ class Snapmaker(Path.Post.Processor.PostProcessor):
|
||||
group.add_argument(
|
||||
"--spindle-percent",
|
||||
action="store_true",
|
||||
default=argument_defaults["spindle-percent"],
|
||||
help="use percent as toolhead spindle speed unit",
|
||||
)
|
||||
group.add_argument(
|
||||
"--spindle-rpm",
|
||||
action="store_false",
|
||||
dest="spindle_percent",
|
||||
help="Use RPM as toolhead spindle speed unit",
|
||||
default=None,
|
||||
help="use percent as toolhead spindle speed unit (default: use RPM if supported by toolhead, otherwise percent)",
|
||||
)
|
||||
|
||||
group.add_argument(
|
||||
@@ -390,33 +408,63 @@ class Snapmaker(Path.Post.Processor.PostProcessor):
|
||||
# The deepcopy is necessary to avoid modifying the boundaries in the MACHINES dict.
|
||||
self.values["BOUNDARIES"] = copy.deepcopy(machine["boundaries"])
|
||||
|
||||
if args.boundaries: # may override machine boundaries, which is expected
|
||||
self.values["BOUNDARIES"] = args.boundaries
|
||||
|
||||
if args.toolhead:
|
||||
if args.toolhead not in machine["compatible_toolheads"]:
|
||||
FreeCAD.Console.PrintError(
|
||||
f"Selected --toolhead={args.toolhead} is not compatible with machine {machine['name']}."
|
||||
+f" Choose from [{machine['compatible_toolheads']}]\n")
|
||||
flag = False
|
||||
return (flag, args)
|
||||
toolhead = self.values["TOOLHEADS"][args.toolhead]
|
||||
self.values["TOOLHEAD_NAME"] = toolhead["name"]
|
||||
elif len(machine["compatible_toolheads"]) == 1:
|
||||
toolhead_key = next(iter(machine["compatible_toolheads"]))
|
||||
toolhead = self.values["TOOLHEADS"][toolhead_key]
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning(
|
||||
f'No toolhead selected, using default ({self.values["TOOLHEAD_NAME"]}). '
|
||||
f"Consider adding --toolhead\n"
|
||||
FreeCAD.Console.PrintError(
|
||||
f"Machine {machine['name']} has multiple compatible toolheads:\n"
|
||||
f"{machine['compatible_toolheads']}\n"
|
||||
"Please add --toolhead argument.\n"
|
||||
)
|
||||
toolhead = self.values["TOOLHEADS"][self.values["DEFAULT_TOOLHEAD"]]
|
||||
flag = False
|
||||
return (flag, args)
|
||||
self.values["TOOLHEAD_KEY"] = toolhead["key"]
|
||||
self.values["TOOLHEAD_NAME"] = toolhead["name"]
|
||||
|
||||
self.values["SPINDLE_SPEEDS"] = {key: toolhead[key] for key in ("min", "max")}
|
||||
self.values["SPINDLE_SPEEDS"] = toolhead["speed_rpm"]
|
||||
|
||||
if args.spindle_speeds: # may override toolhead value, which is expected
|
||||
self.values["SPINDLE_SPEEDS"] = args.spindle_speeds
|
||||
|
||||
if args.spindle_percent is not None:
|
||||
if toolhead["percent"] is True:
|
||||
if toolhead["has_percent"]:
|
||||
self.values["SPINDLE_PERCENT"] = True
|
||||
if args.spindle_percent is False:
|
||||
FreeCAD.Console.PrintWarning(
|
||||
"Toolhead does not handle RPM spindle speed, using percents instead.\n"
|
||||
)
|
||||
else:
|
||||
self.values["SPINDLE_PERCENT"] = args.spindle_percent
|
||||
FreeCAD.Console.PrintError(
|
||||
f"Requested spindle speed in percent, but toolhead {toolhead['name']}"
|
||||
+" does not support speed as percent.\n"
|
||||
)
|
||||
flag = False
|
||||
return (flag, args)
|
||||
else:
|
||||
# Prefer speed S over percent P
|
||||
self.values["SPINDLE_PERCENT"] = toolhead['has_percent'] and not toolhead['has_speed_s']
|
||||
if self.values["SPINDLE_PERCENT"]:
|
||||
FreeCAD.Console.PrintWarning(
|
||||
"Spindle speed will be controlled using using percentages.\n"
|
||||
)
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning(
|
||||
"Spindle speed will be controlled using using RPM.\n"
|
||||
)
|
||||
|
||||
if args.boundaries: # may override machine boundaries, which is expected
|
||||
self.values["BOUNDARIES"] = args.boundaries
|
||||
self.values["MACHINE_NAME"] += " Boundaries overide=" + str(args.boundaries)
|
||||
else:
|
||||
# Update machine dimensions based on installed toolhead
|
||||
for axis in toolhead["boundaries_delta"].keys():
|
||||
self.values["BOUNDARIES"][axis] += toolhead["boundaries_delta"][axis]
|
||||
self.values["MACHINE_NAME"] += " " + toolhead["name"]
|
||||
|
||||
self.values["THUMBNAIL"] = args.thumbnail
|
||||
self.values["ALLOW_GUI"] = args.gui
|
||||
@@ -539,9 +587,10 @@ class Snapmaker(Path.Post.Processor.PostProcessor):
|
||||
def convert_spindle(self, gcode: List[str]) -> List[str]:
|
||||
"""convert spindle speed values from RPM to percent (%) (M3/M4 commands)"""
|
||||
if self.values["SPINDLE_PERCENT"] is False:
|
||||
return
|
||||
return gcode
|
||||
|
||||
# TODO: check if percentage covers range 0-max (most probable) or min-max (200W has a documented min speed)
|
||||
# https://wiki.snapmaker.com/en/Snapmaker_Luban/manual/2_supported_gcode_references#m3m4-modified-cnclaser-on
|
||||
# Speed as percentage in [0,100]% range
|
||||
for index, commandline in enumerate(
|
||||
gcode
|
||||
): # .split(self.values["END_OF_LINE_CHARACTERS"]):
|
||||
|
||||
Reference in New Issue
Block a user