Repository: opentrons/opentrons
Issue: Bug: Robot Hits Back Stop

Currently, robot hits back stop hard when homing, and does not back off.

## Issue
Expected: Robot lightly taps back limit and backs off when homing
Observed: Robot hits back limit hard and does not back off

## Environment
Robots: 
Small Smoke (commit 321538)
Bitter Wood (commit 321538)
v3.0.0-mac-2018-02-07_20-18-v3a-3210538


Changed files:
api/opentrons/drivers/smoothie_drivers/driver_3_0.py, api/tests/opentrons/drivers/test_driver.py

diff --git a/api/opentrons/drivers/smoothie_drivers/driver_3_0.py b/api/opentrons/drivers/smoothie_drivers/driver_3_0.py
index 95ffe3bb54..5475c65422 100755
--- a/api/opentrons/drivers/smoothie_drivers/driver_3_0.py
+++ b/api/opentrons/drivers/smoothie_drivers/driver_3_0.py
@@ -37,6 +37,8 @@ CURRENT_CHANGE_DELAY = 0.05
 Y_SWITCH_BACK_OFF_MM = 20
 Y_BACKOFF_LOW_CURRENT = 0.8
 Y_BACKOFF_SLOW_SPEED = 50
+Y_RETRACT_SPEED = 8
+Y_RETRACT_DISTANCE = 3
 
 DEFAULT_AXES_SPEED = 400
 
@@ -107,7 +109,8 @@ class SmoothieDriver_3_0_0:
         self._config = config
         self._saved_current_settings = config.default_current.copy()
         self._current_settings = self._saved_current_settings.copy()
-        self._max_speed_settings = config.default_max_speed
+        self._max_speed_settings = config.default_max_speed.copy()
+        self._saved_max_speed_settings = self._max_speed_settings.copy()
 
         self._combined_speed = float(DEFAULT_AXES_SPEED)
         self._saved_axes_speed = float(self._combined_speed)
@@ -207,7 +210,7 @@ class SmoothieDriver_3_0_0:
     def pop_speed(self):
         self.set_speed(self._saved_axes_speed)
 
-    def set_axis_max_speed(self, settings=None):
+    def set_axis_max_speed(self, settings):
         '''
         Sets the maximum speed (mm/sec) that a given axis will move
 
@@ -215,10 +218,7 @@ class SmoothieDriver_3_0_0:
             Dict with axes as valies (e.g.: 'X', 'Y', 'Z', 'A', 'B', or 'C')
             and floating point number for millimeters per second (mm/sec)
         '''
-        if not settings:
-            settings = self._max_speed_settings.copy()
-        else:
-            self._max_speed_settings.update(settings)
+        self._max_speed_settings.update(settings)
         values = ['{}{}'.format(axis.upper(), value)
                   for axis, value in sorted(settings.items())]
         command = '{} {}'.format(
@@ -227,6 +227,12 @@ class SmoothieDriver_3_0_0:
         )
         self._send_command(command)
 
+    def push_axis_max_speed(self):
+        self._saved_max_speed_settings = self._max_speed_settings.copy()
+
+    def pop_axis_max_speed(self):
+        self.set_axis_max_speed(self._saved_max_speed_settings)
+
     def set_current(self, settings):
         '''
         Sets the current in mA by axis.
@@ -324,6 +330,23 @@ class SmoothieDriver_3_0_0:
         # now it is safe to home the X axis
         self._send_command(GCODES['HOME'] + 'X')
 
+    def _home_y(self):
+        # home the Y at normal speed (fast)
+        self._send_command(GCODES['HOME'] + 'Y')
+        self.update_position(default={'Y': HOMED_POSITION.get('Y')})
+        # slow the maximum allowed speed on Y axis
+        self.push_axis_max_speed()
+        self.set_axis_max_speed({'Y': Y_RETRACT_SPEED})
+        # move away from the switch
+        self.move({'Y': self.position['Y'] - Y_RETRACT_DISTANCE})
+        # home again, but slower now
+        self._send_command(GCODES['HOME'] + 'Y')
+        self.update_position()
+        # move away from the switch again
+        self.move({'Y': self.position['Y'] - Y_RETRACT_DISTANCE})
+        # bring max speeds back to normal
+        self.pop_axis_max_speed()
+
     def _setup(self):
         self._reset_from_error()
         self._send_command(self._config.acceleration)
@@ -331,7 +354,7 @@ class SmoothieDriver_3_0_0:
         self._send_command(self._config.steps_per_mm)
         self._send_command(GCODES['ABSOLUTE_COORDS'])
         self.update_position(default=HOMED_POSITION)
-        self.set_axis_max_speed()
+        self.pop_axis_max_speed()
         self.pop_speed()
     # ----------- END Private functions ----------- #
 
@@ -406,7 +429,10 @@ class SmoothieDriver_3_0_0:
         for axes in home_sequence:
             if 'X' in axes:
                 self._home_x()
+            elif 'Y' in axes:
+                self._home_y()
             else:
+                # if we are homing neither the X nor Y axes, simple home
                 command = GCODES['HOME'] + axes
                 self._send_command(command, timeout=30)
 
diff --git a/api/tests/opentrons/drivers/test_driver.py b/api/tests/opentrons/drivers/test_driver.py
index 43fe49f19c..b69a470b23 100755
--- a/api/tests/opentrons/drivers/test_driver.py
+++ b/api/tests/opentrons/drivers/test_driver.py
@@ -25,14 +25,6 @@ def test_plunger_commands(smoothie, monkeypatch):
     monkeypatch.setattr(driver_3_0, '_parse_axis_values', _parse_axis_values)
 
     smoothie.home()
-    smoothie.move({'X': 0, 'Y': 1.123456, 'Z': 2, 'A': 3})
-    smoothie.move({
-        'X': 10.987654321,
-        'Y': 1.12345678,
-        'Z': 2,
-        'A': 3,
-        'B': 4,
-        'C': 5})
     expected = [
         ['M907 B0.5 C0.5 M400'],               # Set plunger current high
         ['G4P0.05 M400'],                      # Dwell
@@ -47,9 +39,38 @@ def test_plunger_commands(smoothie, monkeypatch):
         ['G4P0.05 M400'],                      # delay for current
         ['G0F24000 M400'],                      # set back to default speed
         ['G28.2X M400'],                       # home X
-        ['G28.2Y M400'],                       # home Y
+        ['G28.2Y M400'],                        # home Y
         ['M114.2 M400'],                       # Get position
-        ['G0.+ M400'],                         # Move (non-plunger)
+        ['M203.1 Y8 M400'],                     # lower speed on Y for retract
+        ['G0.+ M400'],                          # retract Y
+        ['G28.2Y M400'],                        # home Y
+        ['M114.2 M400'],                       # Get position
+        ['G0.+ M400'],                          # retract Y
+        ['M203.1 A100 B70 C70 X600 Y400 Z100 M400'],  # return to norm current
+        ['M114.2 M400']                       # Get position
+    ]
+    # from pprint import pprint
+    # pprint(command_log)
+    fuzzy_assert(result=command_log, expected=expected)
+    command_log = []
+
+    smoothie.move({'X': 0, 'Y': 1.123456, 'Z': 2, 'A': 3})
+    expected = [
+        ['G0.+ M400']                         # Move (non-plunger)
+    ]
+    # from pprint import pprint
+    # pprint(command_log)
+    fuzzy_assert(result=command_log, expected=expected)
+    command_log = []
+
+    smoothie.move({
+        'X': 10.987654321,
+        'Y': 1.12345678,
+        'Z': 2,
+        'A': 3,
+        'B': 4,
+        'C': 5})
+    expected = [
         ['M907 B0.5 C0.5 M400'],               # Set plunger current high
         ['G4P0.05 M400'],                      # Dwell
         ['G0.+[BC].+ M400'],                   # Move (including BC)