Skip to content

Commit

Permalink
Merge pull request #1724 from avanwinkle/servo-relative-positions-hom…
Browse files Browse the repository at this point in the history
…e-events

Steppers: relative positions and home events
  • Loading branch information
avanwinkle authored Feb 4, 2024
2 parents 813a775 + 22b2d83 commit 161bdac
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 11 deletions.
3 changes: 3 additions & 0 deletions mpf/config_spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1923,6 +1923,7 @@ steppers:
__valid_in__: machine
__type__: device
named_positions: dict|float:str|None
home_events: event_handler|event_handler:ms|None
homing_mode: single|enum(hardware,switch)|hardware
homing_switch: single|machine(switches)|None
homing_direction: single|enum(clockwise,counterclockwise)|clockwise
Expand All @@ -1932,6 +1933,7 @@ steppers:
ball_search_max: single|int|1
ball_search_wait: single|ms|5s
include_in_ball_search: single|bool|true
relative_positions: dict|float:str|None
reset_position: single|int|0
reset_events: event_handler|event_handler:ms|machine_reset_phase_3, ball_starting, ball_will_end, service_mode_entered
number: single|str|
Expand Down Expand Up @@ -2319,6 +2321,7 @@ widgets:
force_complete_event: single|event_handler|None
block_events: list|event_handler|None
release_events: list|event_handler|None
bitmap_font: single|bool_or_token|false
bold: single|bool_or_token|false
italic: single|bool_or_token|false
number_grouping: single|bool_or_token|false
Expand Down
38 changes: 30 additions & 8 deletions mpf/devices/stepper.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Implements a servo in MPF."""
"""Implements a stepper in MPF."""
import asyncio

from typing import Optional
Expand Down Expand Up @@ -55,6 +55,12 @@ async def _initialize(self):
self.event_move_to_position,
position=position)

for position in self.config['relative_positions']:
self.machine.events.add_handler(self.config['relative_positions'][position],
self.event_move_to_position,
position=position,
is_relative=True)

if not self.platform.features['allow_empty_numbers'] and self.config['number'] is None:
self.raise_config_error("Stepper must have a number.", 2)

Expand Down Expand Up @@ -97,11 +103,18 @@ async def _run(self):

while True:
# wait until we should be moving
self.debug_log("Waiting for stepper to move...")
await self._is_moving.wait()
if not self._is_homed:
await self._home()
self._post_ready_event()
continue
self.debug_log("Moving the stepper!")
self._is_moving.clear()
# store target position in local variable since it may change in the meantime
target_position = self._target_position
delta = target_position - self._current_position
self.debug_log("Stepper moving relative %s to hit target %s from %s", delta, target_position, self._current_position)
if delta != 0:
self.debug_log("Got move command. Current position: %s Target position: %s Delta: %s",
self._current_position, target_position, delta)
Expand All @@ -118,8 +131,8 @@ async def _run(self):
self.debug_log("Move completed")

def _move_to_absolute_position(self, position):
"""Move servo to position."""
self.debug_log("Moving to position %s", position)
"""Move stepper to position."""
self.debug_log("Moving stepper %s to absolute position %s", self.hw_stepper, position)
if self.config['pos_min'] <= position <= self.config['pos_max']:
self._target_position = position
self._is_moving.set()
Expand Down Expand Up @@ -168,6 +181,14 @@ def stop_device(self):
self._move_task.cancel()
self._move_task = None

@event_handler(1)
def event_home(self, **kwargs):
"""Event handler for home event."""
del kwargs
self._target_position = 0
self._is_homed = False
self._is_moving.set()

@event_handler(1)
def event_reset(self, **kwargs):
"""Event handler for reset event."""
Expand All @@ -179,20 +200,21 @@ def reset(self):
self._move_to_absolute_position(self.config['reset_position'])

@event_handler(5)
def event_move_to_position(self, position=None, **kwargs):
def event_move_to_position(self, position=None, is_relative=False, **kwargs):
"""Event handler for move_to_position event."""
del kwargs
if position is None:
raise AssertionError("move_to_position event is missing a position.")

self.move_to_position(position)
self.move_to_position(position, is_relative)

def move_to_position(self, position):
def move_to_position(self, position, is_relative=False):
"""Move stepper to a position."""
self._target_position = position
self.debug_log("Stepper at %s moving to %s position %s", self._current_position, "relative" if is_relative else "absolute", position)
self._target_position = (self._current_position + position) if is_relative else position
if self._ball_search_started:
return
self._move_to_absolute_position(position)
self._move_to_absolute_position(self._target_position)

def _ball_search_start(self, **kwargs):
del kwargs
Expand Down
5 changes: 5 additions & 0 deletions mpf/tests/machine_files/stepper/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ steppers:
number: 1
pos_min: -5 #user units (negative is behind home flag)
pos_max: 1000 #user units
home_events: test_home
homing_direction: clockwise
homing_mode: hardware
reset_position: 0
Expand All @@ -14,6 +15,10 @@ steppers:
-5: test_00
999: test_01
500: test_10
relative_positions:
-2: test_rel_00
25: test_rel_01
100: test_rel_02


# this is needed to test ball search
Expand Down
31 changes: 28 additions & 3 deletions mpf/tests/test_Stepper.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,24 +96,49 @@ def test_stepper_events(self):
# should go to reset position
self.assertEqual(0.0, stepper._current_position)

# post another defined event
# post a named_position defined event
event_future = self.machine.events.wait_for_event("stepper_linearAxis_stepper_ready")
self.post_event("test_00")
self.machine.clock.loop.run_until_complete(event_future)
self.assertEqual(-5.0, stepper._current_position, 0)

# post another defined event
# post a named_position defined event
event_future = self.machine.events.wait_for_event("stepper_linearAxis_stepper_ready")
self.post_event("test_01")
self.machine.clock.loop.run_until_complete(event_future)
self.assertEqual(999.0, stepper._current_position, 0)

# post another defined event
# post a named_position defined event
event_future = self.machine.events.wait_for_event("stepper_linearAxis_stepper_ready")
self.post_event("test_10")
self.machine.clock.loop.run_until_complete(event_future)
self.assertEqual(500.0, stepper._current_position, 0)

# post a home event
event_future = self.machine.events.wait_for_event("stepper_linearAxis_stepper_ready")
self.post_event("test_home")
self.machine.clock.loop.run_until_complete(event_future)
# should go to reset position
self.assertEqual(0.0, stepper._current_position)

# post a relative_position defined event
event_future = self.machine.events.wait_for_event("stepper_linearAxis_stepper_ready")
self.post_event("test_rel_00")
self.machine.clock.loop.run_until_complete(event_future)
self.assertEqual(-2.0, stepper._current_position, 0)

# post a relative_position defined event
event_future = self.machine.events.wait_for_event("stepper_linearAxis_stepper_ready")
self.post_event("test_rel_01")
self.machine.clock.loop.run_until_complete(event_future)
self.assertEqual(23.0, stepper._current_position, 0)

# post a relative_position defined event
event_future = self.machine.events.wait_for_event("stepper_linearAxis_stepper_ready")
self.post_event("test_rel_02")
self.machine.clock.loop.run_until_complete(event_future)
self.assertEqual(123.0, stepper._current_position, 0)

def test_ball_search(self):
stepper = self.machine.steppers["linearAxis_stepper"]

Expand Down

0 comments on commit 161bdac

Please sign in to comment.