I submitted my Opentrons .ipynb in fulfilment of the lab component.
https://colab.research.google.com/drive/12QWy9XQPNYZR-H8hs9YzWnw9YsxsSDH3?usp=sharing
from opentrons import types
metadata = { # see https://docs.opentrons.com/v2/tutorial.html#tutorial-metadata
'author': 'shrimay_saikia',
'protocolName': 'green_red_diamonds',
'description': 'Creates a criss-crossed diamond pattern',
'source': 'HTGAA 2026 Opentrons Lab',
'apiLevel': '2.20'
}
##############################################################################
### Robot deck setup constants - don't change these
##############################################################################
TIP_RACK_DECK_SLOT = 9
COLORS_DECK_SLOT = 6
AGAR_DECK_SLOT = 5
PIPETTE_STARTING_TIP_WELL = 'A1'
well_colors = {
'A1' : 'Red',
'B1' : 'Green',
'C1' : 'Orange'
}
def run(protocol):
##############################################################################
### Load labware, modules and pipettes
##############################################################################
# Tips
tips_20ul = protocol.load_labware('opentrons_96_tiprack_20ul', TIP_RACK_DECK_SLOT, 'Opentrons 20uL Tips')
# Pipettes
pipette_20ul = protocol.load_instrument("p20_single_gen2", "right", [tips_20ul])
# Modules
temperature_module = protocol.load_module('temperature module gen2', COLORS_DECK_SLOT)
# Temperature Module Plate
temperature_plate = temperature_module.load_labware('opentrons_96_aluminumblock_generic_pcr_strip_200ul',
'Cold Plate')
# Choose where to take the colors from
color_plate = temperature_plate
# Agar Plate
agar_plate = protocol.load_labware('htgaa_agar_plate', AGAR_DECK_SLOT, 'Agar Plate') ## TA MUST CALIBRATE EACH PLATE!
# Get the top-center of the plate, make sure the plate was calibrated before running this
center_location = agar_plate['A1'].top()
pipette_20ul.starting_tip = tips_20ul.well(PIPETTE_STARTING_TIP_WELL)
##############################################################################
### Patterning
##############################################################################
###
### Helper functions for this lab
###
# pass this e.g. 'Red' and get back a Location which can be passed to aspirate()
def location_of_color(color_string):
for well,color in well_colors.items():
if color.lower() == color_string.lower():
return color_plate[well]
raise ValueError(f"No well found with color {color_string}")
# For this lab, instead of calling pipette.dispense(1, loc) use this: dispense_and_detach(pipette, 1, loc)
def dispense_and_detach(pipette, volume, location):
"""
Move laterally 5mm above the plate (to avoid smearing a drop); then drop down to the plate,
dispense, move back up 5mm to detach drop, and stay high to be ready for next lateral move.
5mm because a 4uL drop is 2mm diameter; and a 2deg tilt in the agar pour is >3mm difference across a plate.
"""
assert(isinstance(volume, (int, float)))
above_location = location.move(types.Point(z=location.point.z + 5)) # 5mm above
pipette.move_to(above_location) # Go to 5mm above the dispensing location
pipette.dispense(volume, location) # Go straight downwards and dispense
pipette.move_to(above_location) # Go straight up to detach drop and stay high
###
### YOUR CODE HERE to create your design
###
'''Algorithm Design: I use lazy logic instead of optimizing the travel path
or doing streaks since I prioritize reliability over efficiency. With greater
familiarity, I'll be more confident in optimizing for pattern art print times.
'''
#Pre-generated coordinate arrays
sfgfp_points = [(-2.2, 39.6),(0, 39.6),(2.2, 39.6),(-4.4, 37.4),(0, 37.4),(4.4, 37.4),(-6.6, 35.2),(0, 35.2),(6.6, 35.2),(-8.8, 33),(0, 33),(8.8, 33),(-11, 30.8),(0, 30.8),(11, 30.8),(-13.2, 28.6),(0, 28.6),(13.2, 28.6),(-15.4, 26.4),(0, 26.4),(15.4, 26.4),(-17.6, 24.2),(0, 24.2),(17.6, 24.2),(-19.8, 22),(0, 22),(19.8, 22),(-22, 19.8),(0, 19.8),(22, 19.8),(-24.2, 17.6),(0, 17.6),(24.2, 17.6),(-26.4, 15.4),(0, 15.4),(26.4, 15.4),(-28.6, 13.2),(0, 13.2),(28.6, 13.2),(-30.8, 11),(0, 11),(30.8, 11),(-33, 8.8),(0, 8.8),(33, 8.8),(-35.2, 6.6),(0, 6.6),(35.2, 6.6),(-37.4, 4.4),(0, 4.4),(37.4, 4.4),(-39.6, 2.2),(0, 2.2),(39.6, 2.2),(-39.6, 0),(-37.4, 0),(-35.2, 0),(-33, 0),(-30.8, 0),(-28.6, 0),(-26.4, 0),(-24.2, 0),(-22, 0),(-19.8, 0),(-17.6, 0),(-15.4, 0),(-13.2, 0),(-11, 0),(-8.8, 0),(-6.6, 0),(-4.4, 0),(-2.2, 0),(0, 0),(2.2, 0),(4.4, 0),(6.6, 0),(8.8, 0),(11, 0),(13.2, 0),(15.4, 0),(17.6, 0),(19.8, 0),(22, 0),(24.2, 0),(26.4, 0),(28.6, 0),(30.8, 0),(33, 0),(35.2, 0),(37.4, 0),(39.6, 0),(-39.6, -2.2),(0, -2.2),(39.6, -2.2),(-37.4, -4.4),(0, -4.4),(37.4, -4.4),(-35.2, -6.6),(0, -6.6),(35.2, -6.6),(-33, -8.8),(0, -8.8),(33, -8.8),(-30.8, -11),(0, -11),(30.8, -11),(-28.6, -13.2),(0, -13.2),(28.6, -13.2),(-26.4, -15.4),(0, -15.4),(26.4, -15.4),(-24.2, -17.6),(0, -17.6),(24.2, -17.6),(-22, -19.8),(0, -19.8),(22, -19.8),(-19.8, -22),(0, -22),(19.8, -22),(-17.6, -24.2),(0, -24.2),(17.6, -24.2),(-15.4, -26.4),(0, -26.4),(15.4, -26.4),(-13.2, -28.6),(0, -28.6),(13.2, -28.6),(-11, -30.8),(0, -30.8),(11, -30.8),(-8.8, -33),(0, -33),(8.8, -33),(-6.6, -35.2),(0, -35.2),(6.6, -35.2),(-4.4, -37.4),(0, -37.4),(4.4, -37.4),(-2.2, -39.6),(0, -39.6),(2.2, -39.6)]
mrfp1_points = [(-26.4, 26.4),(-24.2, 26.4),(-22, 26.4),(-19.8, 26.4),(-17.6, 26.4),(-13.2, 26.4),(-11, 26.4),(-8.8, 26.4),(-6.6, 26.4),(-4.4, 26.4),(-2.2, 26.4),(2.2, 26.4),(4.4, 26.4),(6.6, 26.4),(8.8, 26.4),(11, 26.4),(13.2, 26.4),(17.6, 26.4),(19.8, 26.4),(22, 26.4),(24.2, 26.4),(26.4, 26.4),(-26.4, 24.2),(-24.2, 24.2),(24.2, 24.2),(26.4, 24.2),(-26.4, 22),(-22, 22),(22, 22),(26.4, 22),(-26.4, 19.8),(-19.8, 19.8),(19.8, 19.8),(26.4, 19.8),(-26.4, 17.6),(-17.6, 17.6),(17.6, 17.6),(26.4, 17.6),(-15.4, 15.4),(15.4, 15.4),(-26.4, 13.2),(-13.2, 13.2),(13.2, 13.2),(26.4, 13.2),(-26.4, 11),(-11, 11),(11, 11),(26.4, 11),(-26.4, 8.8),(-8.8, 8.8),(8.8, 8.8),(26.4, 8.8),(-26.4, 6.6),(-6.6, 6.6),(6.6, 6.6),(26.4, 6.6),(-26.4, 4.4),(-4.4, 4.4),(4.4, 4.4),(26.4, 4.4),(-26.4, 2.2),(-2.2, 2.2),(2.2, 2.2),(26.4, 2.2),(-26.4, -2.2),(-2.2, -2.2),(2.2, -2.2),(26.4, -2.2),(-26.4, -4.4),(-4.4, -4.4),(4.4, -4.4),(26.4, -4.4),(-26.4, -6.6),(-6.6, -6.6),(6.6, -6.6),(26.4, -6.6),(-26.4, -8.8),(-8.8, -8.8),(8.8, -8.8),(26.4, -8.8),(-26.4, -11),(-11, -11),(11, -11),(26.4, -11),(-26.4, -13.2),(-13.2, -13.2),(13.2, -13.2),(26.4, -13.2),(-15.4, -15.4),(15.4, -15.4),(-26.4, -17.6),(-17.6, -17.6),(17.6, -17.6),(26.4, -17.6),(-26.4, -19.8),(-19.8, -19.8),(19.8, -19.8),(26.4, -19.8),(-26.4, -22),(-22, -22),(22, -22),(26.4, -22),(-26.4, -24.2),(-24.2, -24.2),(24.2, -24.2),(26.4, -24.2),(-26.4, -26.4),(-24.2, -26.4),(-22, -26.4),(-19.8, -26.4),(-17.6, -26.4),(-13.2, -26.4),(-11, -26.4),(-8.8, -26.4),(-6.6, -26.4),(-4.4, -26.4),(-2.2, -26.4),(2.2, -26.4),(4.4, -26.4),(6.6, -26.4),(8.8, -26.4),(11, -26.4),(13.2, -26.4),(17.6, -26.4),(19.8, -26.4),(22, -26.4),(24.2, -26.4),(26.4, -26.4)]
#Process lists into a single dictionary for code modularity
sfgfp_points.sort()
mrfp1_points.sort()
all_dots = {
'Green': sfgfp_points,
'Red': mrfp1_points
}
#For each color, pick up tip once
for color in all_dots:
pipette_20ul.pick_up_tip()
for i in range(len(all_dots[color])):
if i%15==0:
pipette_20ul.aspirate(min(15, len(all_dots[color])-i), location_of_color(color))
dispense_and_detach(pipette_20ul, 1, center_location.move(types.Point(x=all_dots[color][i][0], y=all_dots[color][i][1])))
pipette_20ul.drop_tip()