opentrons-absorbance-reader
Skill42 starsChanged 18 days agoMITView on GitHub
Opentrons Absorbance Plate Reader Module for Flex - on-deck microplate spectrophotometry with single/multi-wavelength reading (380-740nm in 10nm increments), automated lid control, and CSV data export for ELISA, cell growth, and colorimetric assays
In this repository44 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- phd-qualifierSkill
- citation-verifierSkill
- scientific-writingSkill
- brainstormingSkill
- elevenlabsSkill
- image-generationSkill
- presentationsSkill
- video-storytellingSkill
- code-reviewerSkill
- ralph-wiggumSkill
- tddSkill
- version-controlSkill
- opentrons-gripperSkill
- opentrons-heater-shakerSkill
- opentrons-magnetic-blockSkill
- opentronsSkill
- opentrons-temperature-moduleSkill
- opentrons-thermocyclerSkill
- planningSkill
- claude-lightSkill
- emacs-lispSkill
- fairchemSkill
- idaesSkill
- materials-propertiesSkill
- pycalphadSkill
- pymatgenSkill
- python-aseSkill
- python-best-practicesSkill
- python-jaxSkill
- python-multiobjective-optimizationSkill
- python-optimizationSkill
- python-plottingSkill
- python-regression-statisticsSkill
- vaspSkill
- pycseSkill
- elnSkill
- literature-reviewSkill
- materials-databasesSkill
- scientific-data-extractionSkill
- scientific-reviewerSkill
- scientific-workflowsSkill
- design-of-experimentsSkill
- troubleshootingSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
20 rules. About 0 are common, 14 are distinctive, and 6 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈14
Distinctive · estimate
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 19
Use the Absorbance Reader skill when:
- Very specific: line 106
Critical: Always call close_lid() before initialize(), even if lid is already closed.
- Distinctive: line 144
Read up to 6 wavelengths simultaneously:
- Distinctive: line 480
Protect column 4: - Never attempt to load labware in staging area
- Distinctive: line 482
Check gripper compatibility: - Ensure plates have grippable geometry
- Distinctive: line 552
Verify API version is 2.21 or later
- Very specific: line 553
Check robotType: "Flex" in requirements
- Distinctive: line 554
Ensure module is in column 3 slot (A3-D3)
- Very specific: line 557
Always call close_lid() before initialize()
- Distinctive: line 558
Ensure column 4 staging area is clear
- Distinctive: line 559
Check gripper is functioning properly
- Very specific: line 562
Verify lid is closed before read()
- Distinctive: line 563
Check plate is on module
- Very specific: line 564
Ensure wavelengths match those set in initialize()
- Distinctive: line 568
Use correct well names ("A1" not "a1")
- Distinctive: line 569
Check data type (dict, not list)
- Distinctive: line 572
Verify plate has grippable geometry
- Distinctive: line 573
Ensure reader lid is open before moving plate onto/off module
- Distinctive: line 574
Check plate is compatible with reader (96-well plates)
- Very specific: line 633
File naming: Use descriptive export_filename parameter for easy identification
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Opentrons Absorbance Plate Reader Module
Overview
The Absorbance Plate Reader Module is an on-deck microplate spectrophotometer exclusively for Opentrons Flex. It measures light absorbance in 96-well plates at up to 6 wavelengths simultaneously, enabling automated ELISA, cell density measurements, colorimetric assays, and kinetic readings without manual plate transfers.
Core value: Eliminate manual plate reader transfers. Read absorbance mid-protocol to make real-time decisions or collect kinetic data with automated timing and liquid handling.
Platform: Opentrons Flex only (not compatible with OT-2)
When to Use
Use the Absorbance Reader skill when:
- Running ELISA or other colorimetric assays requiring absorbance reading
- Measuring bacterial or cell culture growth (OD600)
- Performing kinetic assays with timed readings
- Collecting multi-wavelength absorbance data
- Protocols requiring real-time decision-making based on absorbance values
- Automating plate-based biochemical assays
Don't use when:
- Working with OT-2 robot (module is Flex-only)
- Need wavelengths outside 380-740nm range
- Require fluorescence or luminescence measurements
Quick Reference
| Operation | Method | Key Parameters |
| Load module | protocol.load_module() | "absorbanceReaderV1", "D3" (or A3-C3) |
| Close lid | close_lid() | - |
| Open lid | open_lid() | - |
| Check lid status | is_lid_on() | Returns bool |
| Initialize reader | initialize() | mode, wavelengths, reference_wavelength |
| Read plate | read() | export_filename (optional) |
Available wavelengths: 380-740nm in 10nm increments (e.g., 450nm, 562nm, 600nm, 650nm)
Platform Requirements
Opentrons Flex only
- API version: 2.21 or later
- Compatible deck slots: A3, B3, C3, D3 (column 3 only)
- Staging area: Column 4 (entire column reserved for lid storage)
Important: Cannot load any labware in column 4 (A4, B4, C4, D4) when absorbance reader is present.
Deck Layout
Column 3 (Reader) Column 4 (Staging - Reserved)
┌─────────────┐ ┌─────────────┐
│ A3 │───▶│ A4 (Lid) │
│ Reader │ │ Reserved │
│ Module │ │ │
└─────────────┘ └─────────────┘Reader occupies column 3 - Detection unit and plate holder Column 4 reserved - Lid staging area (gripper moves lid here when open)
Loading the Module
from opentrons import protocol_api
metadata = {'apiLevel': '2.21'}
requirements = {"robotType": "Flex", "apiLevel": "2.21"}
def run(protocol: protocol_api.ProtocolContext):
# Load Absorbance Reader in column 3
reader = protocol.load_module("absorbanceReaderV1", "D3")
# Note: Column 4 is now unavailable for labwareCompatible slots: A3, B3, C3, D3 Recommended: D3 (bottom-right position for easy gripper access)
Lid Control
The gripper manages lid position automatically:
# Close lid (required before initialization)
reader.close_lid()
# Check lid status
if reader.is_lid_on():
protocol.comment("Lid is on detection unit")
# Open lid (moves to staging area in column 4)
reader.open_lid()Lid positions:
- Closed: Lid covers detection unit on module
- Open: Lid stored in staging area (column 4)
Critical: Always call close_lid() before initialize(), even if lid is already closed.
Initialization
The initialize() method configures the reader with measurement parameters.
Single Wavelength Reading
Simplest mode - read at one wavelength:
# Initialize for single wavelength
reader.close_lid()
reader.initialize(mode="single", wavelengths=[450])
reader.open_lid()Use case: OD600 bacterial growth, single-color ELISA
Single Wavelength with Reference
Normalize readings against a reference wavelength:
# Initialize with reference wavelength
reader.close_lid()
reader.initialize(
mode="single",
wavelengths=[450],
reference_wavelength=562
)
reader.open_lid()Use case: Reduce background noise, normalize for plate artifacts
Multi-Wavelength Reading
Read up to 6 wavelengths simultaneously:
# Initialize for multiple wavelengths
reader.close_lid()
reader.initialize(
mode="multi",
wavelengths=[450, 562, 600, 650]
)
reader.open_lid()Use case: Multi-analyte assays, spectral analysis, dual-wavelength ELISA
Available wavelengths: 380-740nm in 10nm increments. Common choices:
- 450nm - Blue (common ELISA substrate TMB)
- 562nm - Green (BCA protein assay)
- 600nm - Orange (bacterial growth OD600)
- 650nm - Red (alternative reference wavelength)
Maximum: 6 wavelengths per reading
Reading Plates
Basic Reading
# Move plate to reader with gripper
protocol.move_labware(assay_plate, reader, use_gripper=True)
# Close lid
reader.close_lid()
# Read plate
absorbance_data = reader.read()
# Open lid
reader.open_lid()
# Move plate off reader
protocol.move_labware(assay_plate, "C1", use_gripper=True)Reading with CSV Export
# Read and export data
absorbance_data = reader.read(export_filename="experiment_001_plate1")
# CSV file saved to robot and accessible via Opentrons AppExport format: CSV file with plate layout, metadata (wavelengths, serial number, timestamps)
Accessing Reading Results
Results are returned as nested dictionary: {wavelength: {well: absorbance}}
# Read plate at 450nm
data = reader.read()
# Access specific well at 450nm
absorbance_a1 = data[450]["A1"]
protocol.comment(f"Well A1 absorbance at 450nm: {absorbance_a1}")
# Iterate through all wells at 600nm
for well_name, absorbance in data[600].items():
protocol.comment(f"{well_name}: {absorbance}")Value range: 0.0 - 4.0 OD (optical density)
Multi-Wavelength Data Access
# Initialize with multiple wavelengths
reader.initialize(mode="multi", wavelengths=[450, 600])
# Read plate
data = reader.read()
# Access different wavelengths
od450_a1 = data[450]["A1"]
od600_a1 = data[600]["A1"]
protocol.comment(f"A1: OD450={od450_a1}, OD600={od600_a1}")
# Calculate ratio
if od600_a1 > 0:
ratio = od450_a1 / od600_a1
protocol.comment(f"450/600 ratio: {ratio}")Data Formats
In-Protocol Dictionary Format
{
450: {
"A1": 0.123,
"A2": 0.145,
"A3": 0.167,
# ... all 96 wells
"H12": 0.089
},
600: {
"A1": 0.456,
# ... all 96 wells
}
}Structure:
- Top level: Wavelengths (450, 562, 600, 650)
- Second level: Well names ("A1" - "H12")
- Values: Absorbance (0.0 - 4.0 OD)
CSV Export Format
Opentrons Flex Absorbance Plate Reader
Serial: ABC123456
Wavelength: 450nm
Date: 2024-11-09
Time: 14:32:01
1 2 3 4 5 6 ... 12
A 0.123 0.145 0.167 0.189 0.201 0.223 ... 0.089
B 0.234 0.256 0.278 0.290 0.312 0.334 ... 0.190
C 0.345 0.367 0.389 0.401 0.423 0.445 ... 0.291
...
H 0.456 0.478 0.490 0.512 0.534 0.556 ... 0.392Metadata included:
- Module serial number
- Wavelength(s) measured
- Timestamp
- Plate layout with row/column labels
Complete Workflow Pattern
Standard Reading Workflow
# 1. Load and setup
reader = protocol.load_module("absorbanceReaderV1", "D3")
plate = protocol.load_labware("corning_96_wellplate_360ul_flat", "B1")
pipette = protocol.load_instrument("flex_8channel_1000", "left")
# 2. Initialize reader (empty)
reader.close_lid()
reader.initialize(mode="single", wavelengths=[450])
reader.open_lid()
# 3. Prepare samples
# ... pipetting operations ...
# 4. Move plate to reader
protocol.move_labware(plate, reader, use_gripper=True)
# 5. Read plate
reader.close_lid()
data = reader.read(export_filename="my_assay_data")
reader.open_lid()
# 6. Process results or continue protocol
for well in plate.wells():
if data[450][well.well_name] > 1.0:
protocol.comment(f"{well.well_name} is positive")
# 7. Move plate off reader
protocol.move_labware(plate, "C1", use_gripper=True)Common Patterns
ELISA Protocol
# Setup
reader = protocol.load_module("absorbanceReaderV1", "D3")
elisa_plate = protocol.load_labware("corning_96_wellplate_360ul_flat", "C2")
pipette = protocol.load_instrument("flex_8channel_1000", "left")
# Initialize reader
reader.close_lid()
reader.initialize(mode="single", wavelengths=[450], reference_wavelength=562)
reader.open_lid()
# 1. Coat plate, incubate, wash (not shown)
# 2. Add samples
pipette.transfer(100, samples, elisa_plate.wells()[:24])
# 3. Incubate
protocol.delay(minutes=60)
# 4. Wash steps
for _ in range(3):
# Wash logic (not shown)
pass
# 5. Add substrate
pipette.transfer(100, substrate, elisa_plate.wells()[:24])
# 6. Incubate
protocol.delay(minutes=15)
# 7. Add stop solution
pipette.transfer(50, stop_solution, elisa_plate.wells()[:24])
# 8. Read plate
protocol.move_labware(elisa_plate, reader, use_gripper=True)
reader.close_lid()
elisa_data = reader.read(export_filename="elisa_450nm_results")
reader.open_lid()
# 9. Analyze results
for well in elisa_plate.wells()[:24]:
od450 = elisa_data[450][well.well_name]
if od450 > 0.5:
protocol.comment(f"{well.well_name}: POSITIVE (OD450={od450:.3f})")
else:
protocol.comment(f"{well.well_name}: NEGATIVE (OD450={od450:.3f})")Bacterial Growth Kinetics
# Setup
reader = protocol.load_module("absorbanceReaderV1", "D3")
culture_plate = protocol.load_labware("corning_96_wellplate_360ul_flat", "C2")
hs_mod = protocol.load_module("heaterShakerModuleV1", "D1")
# Initialize reader for OD600
reader.close_lid()
reader.initialize(mode="single", wavelengths=[600])
reader.open_lid()
# Inoculate cultures (not shown)
# Kinetic reading loop
for timepoint in range(8): # 8 timepoints
# Incubate on heater-shaker
protocol.move_labware(culture_plate, hs_mod, use_gripper=True)
hs_mod.set_and_wait_for_temperature(37)
hs_mod.close_labware_latch()
hs_mod.set_and_wait_for_shake_speed(300)
protocol.delay(hours=1)
hs_mod.deactivate_shaker()
hs_mod.deactivate_heater()
hs_mod.open_labware_latch()
# Read OD600
protocol.move_labware(culture_plate, reader, use_gripper=True)
reader.close_lid()
od_data = reader.read(export_filename=f"growth_curve_t{timepoint}")
reader.open_lid()
# Log growth
for well in culture_plate.wells()[:8]:
od600 = od_data[600][well.well_name]
protocol.comment(f"T{timepoint}h {well.well_name}: OD600={od600:.3f}")
# Return plate to deck
protocol.move_labware(culture_plate, "B1", use_gripper=True)Multi-Wavelength Protein Assay
# Setup for BCA or Bradford assay
reader = protocol.load_module("absorbanceReaderV1", "D3")
assay_plate = protocol.load_labware("corning_96_wellplate_360ul_flat", "C2")
# Initialize for multi-wavelength
reader.close_lid()
reader.initialize(mode="multi", wavelengths=[562, 600])
reader.open_lid()
# Prepare standard curve + samples (not shown)
# Incubate
protocol.delay(minutes=30)
# Read at multiple wavelengths
protocol.move_labware(assay_plate, reader, use_gripper=True)
reader.close_lid()
absorbance = reader.read(export_filename="protein_assay_multi_wl")
reader.open_lid()
# Calculate protein concentrations using standard curve
for well in assay_plate.wells()[:24]:
od562 = absorbance[562][well.well_name]
od600 = absorbance[600][well.well_name]
# Example: simple linear standard curve
protein_conc = od562 * 100 # µg/mL (simplified)
protocol.comment(f"{well.well_name}: {protein_conc:.1f} µg/mL")Real-Time Decision Making
# Read plate and perform conditional operations
protocol.move_labware(screening_plate, reader, use_gripper=True)
reader.close_lid()
screen_data = reader.read()
reader.open_lid()
protocol.move_labware(screening_plate, "C1", use_gripper=True)
# Identify hits and cherry-pick
hit_wells = []
for well_name, absorbance in screen_data[450].items():
if absorbance > 2.0: # Hit threshold
hit_wells.append(screening_plate.wells_by_name()[well_name])
protocol.comment(f"HIT: {well_name} (OD450={absorbance:.3f})")
# Transfer hits to new plate for follow-up
if hit_wells:
pipette.transfer(50, hit_wells, hit_plate.wells()[:len(hit_wells)])
protocol.comment(f"Found {len(hit_wells)} hits - transferred for validation")Best Practices
- Always close lid before initialization - Even if already closed
- Export data with descriptive filenames - Include experiment ID, date, plate number
- Use reference wavelengths - Reduces background and plate artifacts
- Read blank wells - Establish baseline for background subtraction
- Include standard curves - For quantitative assays
- Plan deck layout carefully - Column 4 is reserved for lid storage
- Protect column 4 - Never attempt to load labware in staging area
- Allow plate equilibration - Brief delay after moving to reader prevents condensation issues
- Check gripper compatibility - Ensure plates have grippable geometry
- Document wavelength selection - Comment why specific wavelengths were chosen
Common Mistakes
❌ Initializing with lid open:
reader.initialize(mode="single", wavelengths=[450]) # Error: lid must be closed✅ Correct:
reader.close_lid()
reader.initialize(mode="single", wavelengths=[450])
reader.open_lid()❌ Loading labware in column 4:
reader = protocol.load_module("absorbanceReaderV1", "D3")
tips = protocol.load_labware("opentrons_flex_96_tiprack_1000ul", "D4") # Error: reserved✅ Correct:
reader = protocol.load_module("absorbanceReaderV1", "D3")
tips = protocol.load_labware("opentrons_flex_96_tiprack_1000ul", "D2") # Use different column❌ Reading without closing lid:
protocol.move_labware(plate, reader, use_gripper=True)
data = reader.read() # Error: lid must be closed for reading✅ Correct:
protocol.move_labware(plate, reader, use_gripper=True)
reader.close_lid()
data = reader.read()
reader.open_lid()❌ Using unsupported wavelength:
reader.initialize(mode="single", wavelengths=[550]) # Error: not supported✅ Correct:
reader.initialize(mode="single", wavelengths=[562]) # Use any wavelength 380-740nm in 10nm steps❌ Accessing wrong wavelength in data:
reader.initialize(mode="single", wavelengths=[450])
data = reader.read()
od600 = data[600]["A1"] # Error: only 450nm was measured✅ Correct:
reader.initialize(mode="single", wavelengths=[450])
data = reader.read()
od450 = data[450]["A1"] # Access wavelength that was measuredTroubleshooting
Module not loading:
- Verify API version is 2.21 or later
- Check
robotType: "Flex"in requirements - Ensure module is in column 3 slot (A3-D3)
Lid errors:
- Always call
close_lid()beforeinitialize() - Ensure column 4 staging area is clear
- Check gripper is functioning properly
Reading errors:
- Verify lid is closed before
read() - Check plate is on module
- Ensure wavelengths match those set in
initialize()
Data access errors:
- Access wavelengths that were initialized
- Use correct well names ("A1" not "a1")
- Check data type (dict, not list)
Gripper cannot move plate:
- Verify plate has grippable geometry
- Ensure reader lid is open before moving plate onto/off module
- Check plate is compatible with reader (96-well plates)
Integration with Other Modules
With Heater-Shaker Module
# Incubate with shaking, read absorbance
hs_mod = protocol.load_module("heaterShakerModuleV1", "D1")
reader = protocol.load_module("absorbanceReaderV1", "D3")
# Setup plate on heater-shaker
protocol.move_labware(assay_plate, hs_mod, use_gripper=True)
hs_mod.set_and_wait_for_temperature(37)
hs_mod.close_labware_latch()
hs_mod.set_and_wait_for_shake_speed(400)
protocol.delay(minutes=30)
hs_mod.deactivate_shaker()
hs_mod.deactivate_heater()
hs_mod.open_labware_latch()
# Move to reader
protocol.move_labware(assay_plate, reader, use_gripper=True)
reader.close_lid()
data = reader.read()
reader.open_lid()With Temperature Module
# Cool samples, then read
temp_mod = protocol.load_module("temperature module gen2", "D1")
reader = protocol.load_module("absorbanceReaderV1", "C3")
# Cool plate
protocol.move_labware(assay_plate, temp_mod, use_gripper=True)
temp_mod.set_temperature(4)
protocol.delay(minutes=5)
# Read cooled plate
protocol.move_labware(assay_plate, reader, use_gripper=True)
reader.close_lid()
data = reader.read()
reader.open_lid()
temp_mod.deactivate()CSV Data Export
Exported CSV files are stored on the robot and accessible via the Opentrons App:
- Complete protocol run
- Open Opentrons App
- Navigate to completed protocol run
- Download exported CSV files
- Analyze in Excel, Python, R, or other tools
File naming: Use descriptive export_filename parameter for easy identification
data = reader.read(export_filename=f"experiment_{exp_id}_plate_{plate_num}_timepoint_{t}")API Version Requirements
- Minimum API version: 2.21
- Robot type: Opentrons Flex only
- Recommended: Latest API version for full feature support
Additional Resources
- Module Documentation: https://docs.opentrons.com/v2/modules/absorbance_plate_reader.html
- Opentrons Support: https://support.opentrons.com/
- Protocol Examples: https://protocols.opentrons.com/
Related Skills
opentrons- Main Opentrons Python API skillopentrons-gripper- Automated labware movement (required for plate reader)opentrons-heater-shaker- Temperature control with mixing (common integration)opentrons-temperature-module- Temperature control for assays
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

