message: adds Z calibration as first step
diff: @@ -32,7 +32,7 @@ current_position = (0, 0, 0)
  TIP_LENGTH = 51.7
  # Reference point being calibrated
  -point_number = 0
  +point_number = -1
  # (0, 0) is in bottom-left corner
  # Expected reference points
  @@ -59,10 +59,6 @@ test_points = [(x, y, TIP_LENGTH) for x, y in expected] + \
  T = robot.config.gantry_calibration
  XY = None
  -# 3rd row, 4th column corresponds to Z-shift in
  -# heterogeneous 4x4 matrix T
  -Z_OFFSET = T[2][3]
  -
  # Add fixed Z offset which is known so we don't have to calibrate for height
  # during calibration process
  @@ -70,7 +66,7 @@ def add_z(XY):
  return insert(
  insert(XY, 2, [0, 0, 0], axis=1),
  2,
  - [0, 0, 1, Z_OFFSET],
  + [0, 0, 1, T[2][3]],
  axis=0)
  @@ -165,7 +161,6 @@ def key_pressed(key):
  status('skipped #{0}'.format(point_number))
  # run tip probe
  elif key == 'p':
  - robot.reset()
  pipette = instruments.Pipette(mount='right', channels=1)
  probe_center = tuple(probe_instrument(pipette, robot))
  robot.config = robot.config._replace(
  @@ -174,14 +169,23 @@ def key_pressed(key):
  status('Tip probe')
  # save calibration point and move to next
  elif key == 'enter':
  - if (point_number < len(actual)):
  - actual[point_number] = position()[:-1]
  + if (point_number >= len(actual)):
  + return
  +
  + if point_number < 0:
  + actual_z = position()[-1]
  + expected_z = T[2][3] + TIP_LENGTH
  + T[2][3] += actual_z - expected_z
  point_number += 1
  + status('saved Z-Offset: {0}'.format(T[2][3]))
  + return
  + actual[point_number] = position()[:-1]
  + point_number += 1
  status('saved #{0}: {1}'.format(point_number, actual[point_number-1]))
  # On last point update gantry calibration
  - if point_number == 3:
  + if point_number == len(actual):
  XY = solve(expected, actual)
  T = add_z(XY)
  robot.config = robot.config._replace(
  