Skip to content

Commit

Permalink
Merge pull request #1621 from jpirnay/grbl-tuning
Browse files Browse the repository at this point in the history
Grbl tuning #2
  • Loading branch information
tatarize committed Jan 2, 2023
2 parents 1022d12 + 1e8dae4 commit 6c9f7a1
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 4 deletions.
111 changes: 110 additions & 1 deletion meerk40t/grbl/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
Defines the interactions between the device service and the meerk40t's viewport.
Registers relevant commands and options.
"""

from time import sleep
from meerk40t.kernel import CommandSyntaxError, Service

from ..core.laserjob import LaserJob
Expand All @@ -23,6 +23,7 @@ def __init__(self, kernel, path, *args, **kwargs):
Service.__init__(self, kernel, path)
self.name = "GRBLDevice"
self.extension = "gcode"
# self.redlight_preferred = False

self.setting(str, "label", path)
_ = self._
Expand Down Expand Up @@ -260,6 +261,33 @@ def __init__(self, kernel, path, *args, **kwargs):
"If the device has endstops, then the laser can home itself to this position = physical home ($H)"
),
},
{
"attr": "use_red_dot",
"object": self,
"default": False,
"type": bool,
"label": _("Simulate reddot"),
"tip": _(
"If active then you can turn on the laser at a very low power to get a visual representation " +
"of the current position to help with focusing and positioning. Use with care!"
),
"signals": "icons", # Update ribbonbar if needed
},
{
"attr": "red_dot_level",
"object": self,
"default": 3,
"type": int,
"style": "slider",
"min": 0,
"max": 50,
"label": _("Reddot Laser strength"),
"trailer": "%",
"tip": _(
"Provide the power level of the red dot indicator, needs to be under the critical laser strength to not burn the material"
),
"conditional": (self, "use_red_dot"),
},
]
self.register_choices("grbl-global", choices)

Expand Down Expand Up @@ -371,6 +399,87 @@ def codes_update(**kwargs):
self.show_origin_y = self.origin_y
self.realize()

@self.console_option("strength", "s", type=int, help="Set the dot laser strength.")
@self.console_argument("off", type=str)
@self.console_command(
"red",
help=_("Turns redlight on/off"),
)
def red_dot_on(command, channel, _, off=None, strength=None, remainder=None, **kwgs):
if not self.use_red_dot:
channel ("Red Dot feature is not enabled, see config")
# self.redlight_preferred = False
return
if not self.spooler.is_idle:
channel ("Won't interfere with a running job, abort...")
return
if strength is not None:
if strength >= 0 and strength <= 100:
self.red_dot_level = strength
channel(f"Laser strength for red dot is now: {self.red_dot_level}%")
if off == "off":
self.driver.laser_off()
# self.driver.grbl("G0")
self.driver.move_mode = 0
# self.redlight_preferred = False
channel("Turning off redlight.")
self.signal("grbl_red_dot", True)
else:
# self.redlight_preferred = True
# self.driver.set("power", int(self.red_dot_level / 100 * 1000))
self.driver._clean()
self.driver.laser_on(power=int(self.red_dot_level / 100 * 1000), speed=1000)
# By default any move is a G0 move which will not activate the laser,
# so we need to switch to G1 mode:
self.driver.move_mode = 1
# An arbitrary move to turn the laser really on!
# self.driver.grbl("G1")
channel("Turning on redlight.")
self.signal("grbl_red_dot", False)

@self.console_option(
"idonotlovemyhouse",
type=bool,
action="store_true",
help=_("override one second laser fire pulse duration"),
)
@self.console_argument("time", type=float, help=_("laser fire pulse duration"))
@self.console_command(
"pulse",
help=_("pulse <time>: Pulse the laser in place."),
)
def pulse(command, channel, _, time=None, idonotlovemyhouse=False, **kwargs):
if time is None:
channel(_("Must specify a pulse time in milliseconds."))
return
if time > 1000.0:
channel(
_(
'"{time}ms" exceeds 1 second limit to fire a standing laser.'
).format(time=time)
)
try:
if not idonotlovemyhouse:
return
except IndexError:
return

def timed_fire():
yield "wait_finish"
yield "laser_on"
yield "wait", time
yield "laser_off"

if self.spooler.is_idle:
self.driver.laser_on(power = 1000, speed=1000)
sleep(time/1000)
self.driver.laser_off()
label = _("Pulse laser for {time}ms").format(time=time)
channel(label)
else:
channel(_("Pulse laser failed: Busy"))
return

@self.console_argument("filename", type=str)
@self.console_command("save_job", help=_("save job export"), input_type="plan")
def gcode_save(channel, _, filename, data=None, **kwargs):
Expand Down
17 changes: 14 additions & 3 deletions meerk40t/grbl/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,16 +159,27 @@ def laser_off(self, *values):
@param values:
@return:
"""
self.grbl("M3\r")
self.grbl("M5\r")

def laser_on(self, *values):
def laser_on(self, power=None, speed=None, *values):
"""
Turn laser on in place.
@param values:
@return:
"""
self.grbl("M5\r")
spower = ""
sspeed = ""
if power is not None:
spower = f" S{power:.1f}"
# We already established power, so no need for power_dirty
self.power = power
self.power_dirty = False
if speed is not None:
sspeed = f"G1 F{speed}\r"
self.speed = speed
self.speed_dirty = False
self.grbl(f"M3{spower}\r{sspeed}")

def plot(self, plot):
"""
Expand Down
27 changes: 27 additions & 0 deletions meerk40t/grbl/gui/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ def plugin(service, lifecycle):
icons8_emergency_stop_button_50,
icons8_info_50,
icons8_pause_50,
icons8_quick_mode_on_50,
icons8_flash_off_50,
)

service.register("window/Serial-Controller", SerialController)
Expand Down Expand Up @@ -64,6 +66,31 @@ def plugin(service, lifecycle):
},
)

def has_red_dot_enabled():
# Does the current device have an active use_red_dot?
res = False
if hasattr(service, "use_red_dot"):
if service.use_red_dot:
res = True
return res

service.register(
"button/control/Redlight",
{
"label": _("Red Dot On"),
"icon": icons8_quick_mode_on_50,
"tip": _("Turn Redlight On"),
"action": lambda v: service("red on\n"),
"toggle": {
"label": _("Red Dot Off"),
"action": lambda v: service("red off\n"),
"icon": icons8_flash_off_50,
"signal": "grbl_red_dot",
},
"rule_enabled": lambda v: has_red_dot_enabled(),
},
)

service.register(
"button/control/ClearAlarm",
{
Expand Down
64 changes: 64 additions & 0 deletions meerk40t/grbl/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,70 @@ def process(self, data):
return 0
else:
return 5 # Homing cycle not enabled by settings.
elif data.startswith("$J="):
"""
$Jx=line - Run jogging motion
New to Grbl v1.1, this command will execute a special jogging motion. There are three main
differences between a jogging motion and a motion commanded by a g-code line.
Like normal g-code commands, several jog motions may be queued into the planner buffer,
but the jogging can be easily canceled by a jog-cancel or feed-hold real-time command.
Grbl will immediately hold the current jog and then automatically purge the buffers
of any remaining commands.
Jog commands are completely independent of the g-code parser state. It will not change
any modes like G91 incremental distance mode. So, you no longer have to make sure
that you change it back to G90 absolute distance mode afterwards. This helps reduce
the chance of starting with the wrong g-code modes enabled.
If soft-limits are enabled, any jog command that exceeds a soft-limit will simply
return an error. It will not throw an alarm as it would with a normal g-code command.
This allows for a much more enjoyable and fluid GUI or joystick interaction.
Executing a jog requires a specific command structure, as described below:
The first three characters must be '$J=' to indicate the jog.
The jog command follows immediate after the '=' and works like a normal G1 command.
Feed rate is only interpreted in G94 units per minute. A prior G93 state is
ignored during jog.
Required words:
XYZ: One or more axis words with target value.
F - Feed rate value. NOTE: Each jog requires this value and is not treated as modal.
Optional words: Jog executes based on current G20/G21 and G90/G91 g-code parser state.
If one of the following optional words is passed, that state is overridden for one command only.
G20 or G21 - Inch and millimeter mode
G90 or G91 - Absolute and incremental distances
G53 - Move in machine coordinates
All other g-codes, m-codes, and value words are not accepted in the jog command.
Spaces and comments are allowed in the command. These are removed by the pre-parser.
Example: G21 and G90 are active modal states prior to jogging. These are sequential commands.
$J=X10.0 Y-1.5 will move to X=10.0mm and Y=-1.5mm in work coordinate frame (WPos).
$J=G91 G20 X0.5 will move +0.5 inches (12.7mm) to X=22.7mm (WPos).
Note that G91 and G20 are only applied to this jog command.
$J=G53 Y5.0 will move the machine to Y=5.0mm in the machine coordinate frame (MPos).
If the work coordinate offset for the y-axis is 2.0mm, then Y is 3.0mm in (WPos).
Jog commands behave almost identically to normal g-code streaming. Every jog command
will return an 'ok' when the jogging motion has been parsed and is setup for execution.
If a command is not valid or exceeds a soft-limit, Grbl will return an 'error:'.
Multiple jogging commands may be queued in sequence.
"""
commands = {}
for c in _tokenize_code(data):
g = c[0]
if g not in commands:
commands[g] = []
if len(c) >= 2:
commands[g].append(c[1])
else:
commands[g].append(None)
return 3 # not yet supported
elif data.startswith("$"):
return 3 # GRBL '$' system command was not recognized or supported.

Expand Down

0 comments on commit 6c9f7a1

Please sign in to comment.