Week 3 HW: Lab Automations
Python Script for Opentrons Artwork
from opentrons import types import math
metadata = { ‘author’: ‘Shubhadip Saha’, ‘protocolName’: ‘H+S’, ‘description’: ‘A heart with the letters H+S inside of it.’, ‘source’: ‘HTGAA 2026 Opentrons Lab’, ‘apiLevel’: ‘2.20’ }
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): tips_20ul = protocol.load_labware(‘opentrons_96_tiprack_20ul’, TIP_RACK_DECK_SLOT, ‘Opentrons 20uL Tips’) pipette_20ul = protocol.load_instrument(“p20_single_gen2”, “right”, [tips_20ul]) temperature_module = protocol.load_module(’temperature module gen2’, COLORS_DECK_SLOT) temperature_plate = temperature_module.load_labware(‘opentrons_96_aluminumblock_generic_pcr_strip_200ul’, ‘Cold Plate’) color_plate = temperature_plate agar_plate = protocol.load_labware(‘htgaa_agar_plate’, AGAR_DECK_SLOT, ‘Agar Plate’) center_location = agar_plate[‘A1’].top() pipette_20ul.starting_tip = tips_20ul.well(PIPETTE_STARTING_TIP_WELL)
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}")
def dispense_and_detach(pipette, volume, location): assert(isinstance(volume, (int, float))) above_location = location.move(types.Point(z=5)) pipette.move_to(above_location) pipette.dispense(volume, location) pipette.move_to(above_location)
1. Draw the Heart (Red) and the Plus Sign (+)
pipette_20ul.pick_up_tip() red_points = [] t = 0 while t < 2 * math.pi: x = 16 * math.sin(t)**3 y = 13 * math.cos(t) - 5 * math.cos(2t) - 2 * math.cos(3t) - math.cos(4*t) red_points.append((x * 2.0, y * 2.0)) t += 0.05
Plus Sign (+) moved to Red
curr_x = -2.0 while curr_x <= 2.0: red_points.append((curr_x, 0.0)) curr_x += 0.8 curr_y = -2.0 while curr_y <= 2.0: red_points.append((0.0, curr_y)) curr_y += 0.8
max_vol = 20 for i in range(0, len(red_points), max_vol): batch = red_points[i:i + max_vol] pipette_20ul.aspirate(len(batch), location_of_color(‘Red’)) for x, y in batch: loc = center_location.move(types.Point(x=x, y=y)) dispense_and_detach(pipette_20ul, 1, loc) pipette_20ul.drop_tip()
2. Draw ‘H’ (Green)
pipette_20ul.pick_up_tip() h_coords = []
H Left Bar
curr_y = -7.0 while curr_y <= 7.0: h_coords.append((-15.0, curr_y)) curr_y += 1.0
H Right Bar
curr_y = -7.0 while curr_y <= 7.0: h_coords.append((-7.0, curr_y)) curr_y += 1.0
H Middle Bar
curr_x = -14.0 while curr_x <= -8.0: h_coords.append((curr_x, 0.0)) curr_x += 1.0
for i in range(0, len(h_coords), max_vol): batch = h_coords[i:i + max_vol] pipette_20ul.aspirate(len(batch), location_of_color(‘Green’)) for x, y in batch: loc = center_location.move(types.Point(x=x, y=y)) dispense_and_detach(pipette_20ul, 1, loc) pipette_20ul.drop_tip()
3. Draw ‘S’ (Orange) - Connected
pipette_20ul.pick_up_tip() s_coords = []
S top bar
curr_x = 14.0 while curr_x >= 7.0: s_coords.append((curr_x, 7.0)) curr_x -= 0.8
S upper vertical
curr_y = 7.0 while curr_y >= 0.0: s_coords.append((7.0, curr_y)) curr_y -= 0.8
S middle bar
curr_x = 7.0 while curr_x <= 14.0: s_coords.append((curr_x, 0.0)) curr_x += 0.8
S lower vertical
curr_y = 0.0 while curr_y >= -7.0: s_coords.append((14.0, curr_y)) curr_y -= 0.8
S bottom bar
curr_x = 14.0 while curr_x >= 7.0: s_coords.append((curr_x, -7.0)) curr_x -= 0.8
for i in range(0, len(s_coords), max_vol): batch = s_coords[i:i + max_vol] pipette_20ul.aspirate(len(batch), location_of_color(‘Orange’)) for x, y in batch: loc = center_location.move(types.Point(x=x, y=y)) dispense_and_detach(pipette_20ul, 1, loc) pipette_20ul.drop_tip()

Gemini Prompts
I want to create a heart with the letters H+S inside of it I’ve added the code to generate the heart shape with ‘H+S’ inside. I used simple geometric calculations for the heart and coordinates for the letters, ensuring each color (Red for the heart, Green and Orange for the letters) is handled correctly with tip management.
Please explain this error:
The error ‘Aspirating 32uL … is more than the pipette can hold (20uL)’ happens because your code tries to pull up 32 microliters of red liquid for the 32 points of the heart, but the pipette only holds 20 microliters. I have updated the code to handle the heart in two smaller batches so it fits the pipette’s capacity.
can you make the border of the heart more defined and close together, also the H+S is not centered inside of the heart, you can make the heart bigger if needed, but make sure that there is no space between the dots of the border of the heart I’ve refined the heart to make the border dots touch, creating a solid-looking outline. I also scaled up the heart and adjusted the ‘H+S’ coordinates to ensure they are perfectly centered within the shape.
the heart looks good, now the letters inside of the heart have to be fixed. the dots inside of the H are very spaced out. the plus sign doesnt look like a plus sign but instead looks like a diamond, and the dots are also very spaced out, and the S doesnt look proper I’ve increased the number of dots for the letters ‘H’, ‘+’, and ‘S’ to make them look solid and well-defined, just like the heart border. I also reshaped the ‘+’ to be a clear cross and refined the ‘S’ curves.
I have updated the coordinates for the letters to be much denser so they appear as solid lines. I also refined the ‘+’ sign to be a clear cross and adjusted the ‘S’ shape to ensure it looks proper and fits nicely inside the heart.
ok now the H looks fine, and the plus looks fine, its just that the S now has a disconnect between the top half and the bottom half. also can you make the plus sign red instead of green I’ve fixed the disconnect in the letter ‘S’ by adjusting the start and end points of the curves. I also moved the plus sign to the Red color section so it matches the heart border.
Post-Lab Questions
1. DeRoo, J. B., Jones, A. A., Slaughter, C. K., Ahr, T. W., Stroup, S. M., Thompson, G. B., & Snow, C. D. (2025). Automation of protein crystallization scaleup via Opentrons-2 liquid handling. SLAS technology, 32, 100268.
The paper talks about how how a low-cost Opentrons OT-2 liquid handling robot can automate the setup of protein crystallization experiments. Protein crystallization is essential for determining molecular structures but traditionally requires careful, repetitive manual pipetting. The authors developed Python scripts to automate the preparation of sitting-drop crystallization trials across multi-well plates. They found that the automated workflow reduced labor, improved reproducibility, and produced crystallization results comparable to manual methods.
Part 2:
For my final project, I intend to develop a multimodal assistive communication and physiological monitoring system that integrates biological sensing with digital interfaces. Specifically, I aim to explore the use of a biological biosensor (e.g., a cell-free or microbial system responsive to sweat metabolites such as lactate or pH) as an auxiliary communication channel. The long-term goal is to investigate how biological signals can support symptom monitoring during medical treatment or provide physiological feedback for applications such as athletic performance tracking.
Automation would play a critical role in ensuring the reliability and reproducibility of the biological sensing component. Using the Opentrons OT-2, I would automate the preparation and calibration of the biosensor assays. This could include automated serial dilutions to generate standard curves, precise dispensing of cell-free reaction mixtures, and batch preparation of replicate assay wells to evaluate sensitivity and variability. Automation would allow systematic testing of different analyte concentrations and environmental conditions, ensuring consistent reagent volumes and minimizing human error.
For example, an automated workflow might involve dispensing varying concentrations of lactate standards into reaction wells, followed by consistent addition of reporter reagents to quantify fluorescence or colorimetric output. Conceptually, this could be structured as:
for concentration in standard_curve: pipette.aspirate(volume, analyte_stock) pipette.dispense(volume, reaction_well)
Beyond assay calibration, I would also design a 3D-printed cartridge holder compatible with the Opentrons deck to standardize placement of biosensor strips during testing. This would improve spatial consistency and enable repeatable experimental conditions.
Overall, automation would allow me to focus on the human-centered aspects of the project. Specifically, how users interpret and respond to biological feedback — while ensuring that the underlying biological measurements are consistent and scalable. By integrating bioengineering workflows with automated liquid handling, this project would bridge synthetic biology, assistive technology, and human-computer interaction.