Switch the motors off when the designs are done or stopped
After the last design (or a Stop), the player sends $SLP (GRBL and FluidNC sleep), so the motors stop holding and the table is quiet. The page shows "Motors off"; the next design wakes the controller (reset + unlock) and homes first, since the arm can be moved by hand while the motors are off. The jog panel wakes it too. Setting: motion.motors_off_when_done (default true). Checked on the real Uno: Idle -> $SLP -> Sleep -> reset -> Alarm -> $X -> Idle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8967271e90
commit
7afe562124
10 files changed
+104
-10
No files matched your search
@@ -353,6 +353,18 @@ def cmd_play(cfg, args):
|
||||
|
||||
|
||||
def _play(c, cfg, args, items):
|
||||
try:
|
||||
_play_list(c, cfg, args, items)
|
||||
finally:
|
||||
if cfg['motion'].get('motors_off_when_done', True):
|
||||
try:
|
||||
c.motors_off() # done, stopped or failed: let the motors rest
|
||||
print(' motors off')
|
||||
except Exception:
|
||||
pass # e.g. the USB link has gone
|
||||
|
||||
|
||||
def _play_list(c, cfg, args, items):
|
||||
if not args.no_home:
|
||||
print(' homing rho ...', flush=True)
|
||||
c.home_rho(cfg)
|
||||
|
||||
@@ -26,6 +26,8 @@ DEFAULTS = {
|
||||
},
|
||||
'motion': {
|
||||
'ball_speed_mm_s': 25.0, # drawing speed of the ball across the sand
|
||||
'motors_off_when_done': True, # switch the motors off when the designs are done or stopped
|
||||
# (quiet, cool); the next design homes first
|
||||
'max_theta_deg_s': 60.0, # arm spin limit (10 rpm)
|
||||
'max_rho_mm_s': 25.0, # carriage limit
|
||||
'theta_accel_deg_s2': 120.0,
|
||||
|
||||
@@ -346,7 +346,33 @@ class Controller:
|
||||
self._presses = 0
|
||||
return self.query()
|
||||
|
||||
def motors_off(self):
|
||||
"""Switch the motors off with the sleep command ($SLP; FluidNC has it too). They stop
|
||||
holding, so the arm turns by hand and the position is lost: before moving again the
|
||||
controller needs waking (a reset, see wake_up) and the table homing."""
|
||||
self.guard = None
|
||||
if self.query().state != 'Sleep':
|
||||
self.command('$SLP')
|
||||
self.query()
|
||||
|
||||
def wake_up(self):
|
||||
"""Out of sleep: only a reset wakes it, and it then waits in ALARM until unlocked."""
|
||||
if self.query().state != 'Sleep':
|
||||
return
|
||||
self.realtime(RT_RESET)
|
||||
t_end, seen = time.monotonic() + 3, None
|
||||
while time.monotonic() < t_end:
|
||||
for line in self._lines():
|
||||
if 'grbl' in line.lower() or 'fluidnc' in line.lower():
|
||||
seen = time.monotonic()
|
||||
if seen and time.monotonic() - seen > 0.2:
|
||||
break
|
||||
time.sleep(0.02)
|
||||
self.pending.clear()
|
||||
self.query()
|
||||
|
||||
def unlock(self):
|
||||
self.wake_up()
|
||||
if self.query().state == 'Alarm':
|
||||
self.command('$X')
|
||||
|
||||
|
||||
@@ -212,7 +212,8 @@ class Panel:
|
||||
|
||||
def status(self):
|
||||
st = self.c.status
|
||||
return {'state': 'Disconnected' if not self.connected else self.busy or (st.state + (':' + st.sub if st.sub else '')),
|
||||
state = 'Motors off' if st.state == 'Sleep' else st.state + (':' + st.sub if st.sub else '')
|
||||
return {'state': 'Disconnected' if not self.connected else self.busy or state,
|
||||
'connected': self.connected,
|
||||
'theta': st.wpos[0], 'rho': st.wpos[1], 'switch': 'Y' in st.pins, 'pins': st.pins,
|
||||
'homed': self.homed, 'steps': self.steps, 'error': self.error}
|
||||
@@ -224,6 +225,9 @@ class Panel:
|
||||
feed = 1200 if axis == 'X' else 600 # 20 deg/s, 10 mm/s
|
||||
with self.lock:
|
||||
st = self.c.query()
|
||||
if st.state == 'Sleep': # motors switched off after a design: wake them
|
||||
self.c.unlock()
|
||||
st = self.c.query()
|
||||
if st.state == 'Alarm':
|
||||
return False, 'alarm: press Unlock first'
|
||||
inward = axis == 'Y' and d < 0
|
||||
@@ -263,6 +267,7 @@ class Panel:
|
||||
|
||||
def unlock(self):
|
||||
with self.lock:
|
||||
self.c.unlock() # also wakes the motors after a design
|
||||
self.c.command('$X')
|
||||
return True, 'unlocked'
|
||||
|
||||
|
||||
@@ -275,6 +275,13 @@ class Player:
|
||||
finally:
|
||||
c.guard = None
|
||||
c.abort = False
|
||||
if self.cfg['motion'].get('motors_off_when_done', True) and self.p.connected:
|
||||
try:
|
||||
c.motors_off() # done or stopped: let the motors rest
|
||||
self.p.homed = False # the arm can drift now, so home before the next one
|
||||
log('motors off')
|
||||
except Exception as e:
|
||||
log(f'could not switch the motors off: {e!r}')
|
||||
self.now = None
|
||||
self.state = 'idle'
|
||||
|
||||
|
||||
Reference in new issue
Block a user