
from opentrons import types
metadata = {
'protocolName': 'Stitch Red',
'author': 'Jorge Luis Moreano',
'source': 'HTGAA25 agar',
'apiLevel': '2.20'
}
# Definición de color (rojo para Stitch)
well_colors = {
'A1': 'Red', # Color rojo para el cuerpo
}
def run(protocol):
TIP_RACK_DECK_SLOT = 9
COLORS_DECK_SLOT = 6
AGAR_DECK_SLOT = 5
PIPETTE_STARTING_TIP_WELL = 'A1'
# Cargar los equipos
tips_20ul = protocol.load_labware('opentrons_96_tiprack_20ul', TIP_RACK_DECK_SLOT, '20ul Tip Rack')
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', '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)
# Función para obtener la ubicación de color
def location_of_color(color_string):
for well, color in well_colors.items():
if color.lower() == color_string.lower():
return temperature_plate[well]
raise ValueError(f"No se encontró el pozo con el color {color_string}")
# Función para dispensar el volumen sin error
def dispense_and_jog(pipette, volume, location):
if pipette.current_volume < volume:
raise ValueError(f"No hay suficiente volumen en la pipeta. Intentas dispensar {volume}uL, pero solo quedan {pipette.current_volume}uL.")
pipette.dispense(volume, location)
currLoc = pipette._get_last_location_by_api_version()
pipette.move_to(currLoc.move(types.Point(z=5))) # Mover ligeramente
pipette.move_to(currLoc) # Regresar a la posición original
# Comienza el dibujo de Stitch
pipette_20ul.pick_up_tip()
# Definir los puntos del contorno de Stitch, asegurando que estén dentro de los límites de -40 a 40 mm
stitch_points = [(12, 33), (-18, 30), (-15, 30), (-12, 30), (-9, 30), (-6, 30), (9, 30), (12, 30),
(-24, 27), (-21, 27), (-18, 27), (-15, 27), (-12, 27), (-9, 27), (-6, 27),
(-3, 27), (0, 27), (6, 27), (9, 27), (12, 27), (-24, 24), (-21, 24),
(-9, 24), (-6, 24), (-3, 24), (0, 24), (3, 24), (9, 24), (24, 24),
(27, 24), (-30, 21), (-27, 21), (-24, 21), (-21, 21), (-18, 21),
(-15, 21), (-6, 21), (-3, 21), (0, 21), (3, 21), (21, 21),
(24, 21), (27, 21), (-30, 18), (-27, 18), (-24, 18), (-15, 18),
(-12, 18), (-6, 18), (-3, 18), (0, 18), (3, 18), (6, 18),
(18, 18), (21, 18), (24, 18), (-30, 15), (-27, 15), (-12, 15),
(-6, 15), (-3, 15), (0, 15), (3, 15), (6, 15), (12, 15),
(15, 15), (18, 15), (21, 15), (-30, 12), (-12, 12), (-9, 12),
(-6, 12), (-3, 12), (0, 12), (3, 12), (6, 12), (9, 12),
(12, 12), (15, 12), (18, 12), (-30, 9), (-15, 9), (-12, 9),
(-9, 9), (-6, 9), (-3, 9), (0, 9), (3, 9), (6, 9), (9, 9),
(12, 9), (15, 9), (-33, 6), (-30, 6), (-27, 6), (-24, 6),
(-21, 6), (-18, 6), (-15, 6), (-12, 6), (-9, 6), (-6, 6),
(-3, 6), (0, 6), (3, 6), (9, 6), (-27, 3), (-24, 3),
(-21, 3), (6, 3), (-3, 0), (0, 0), (3, 0), (6, 0),
(-6, -3), (-3, -3), (0, -3), (3, -3), (6, -3), (9, -3),
(12, -3), (-6, -6), (-3, -6), (0, -6), (6, -6),
(9, -6), (12, -6), (15, -6), (-15, -9), (-9, -9),
(-6, -9), (-3, -9), (9, -9), (12, -9), (15, -9),
(-24, -12), (-15, -12), (-9, -12), (-6, -12),
(12, -12), (15, -12), (18, -12), (-24, -15),
(-21, -15), (-15, -15), (-9, -15), (-3, -15),
(0, -15), (9, -15), (15, -15), (18, -15),
(-24, -18), (-21, -18), (-9, -18), (-3, -18),
(0, -18), (3, -18), (9, -18), (12, -18), (15, -18),
(18, -18), (21, -18), (-24, -21), (-18, -21),
(-12, -21), (-9, -21), (-3, -21), (0, -21),
(3, -21), (6, -21), (9, -21), (12, -21),
(15, -21), (18, -21), (21, -21), (-21, -24),
(-15, -24), (-12, -24), (-9, -24), (-6, -24),
(0, -24), (3, -24), (9, -24), (12, -24), (15, -24)]
for x, y in stitch_points:
adjusted_location = center_location.move(types.Point(x=x, y=y))
# Comprobar si la ubicación ajustada está dentro del área segura
distance_from_center = (adjusted_location.point.x ** 2 + adjusted_location.point.y ** 2) ** 0.5
if distance_from_center > 40: # 40 mm de radio
print(f"Advertencia: Ubicación ajustada fuera de límites: ({adjusted_location.point.x}, {adjusted_location.point.y})")
continue # Saltar a la siguiente iteración si está fuera de límites
pipette_20ul.aspirate(1, location_of_color('Red')) # Aspirar 1 µL de color rojo
dispense_and_jog(pipette_20ul, 1, adjusted_location)
pipette_20ul.drop_tip()
=== VOLUME TOTALS BY COLOR === Red: aspirated 185 dispensed 185 [all colors]: [aspirated 185] [dispensed 185]
=== TIP COUNT === Used 1 tip(s) (ideally exactly one per unique color)

For my final project, I intend to use automation tools, specifically the Opentrons OT-2 liquid-handling robot, to streamline key experimental procedures related to biosensor-based early diagnostics of acetaminophen (APAP)-induced hepatotoxicity. Automation will enhance reproducibility, reduce human error, and increase throughput for biomarker quantification.
Automated pipetting of biological samples (e.g., serum or cell culture supernatant) into 96-well plates for analysis.
Dispensing reagents such as antibody-functionalized nanoparticles and enzymatic substrates for biomarker detection.
Standardizing dilution series for biomarker calibration curves.
Ensuring accurate mixing and volume handling for fluorescence or colorimetric assays.
Automating washing cycles to remove unbound antibodies or reagents from ELISA-type assays.
Integrating automated sample loading into a plate reader or fluorescence microscope for biomarker signal detection.