Skill Tracer
Skills › Scientific Agent Skills › opentrons-integration › modules_and_deck.md

modules_and_deck.md

429 lines · skills/opentrons-integration/references/modules_and_deck.md at 5f2bc03

Open on GitHub

Most marks come from matching words in the file, so treat them as pointers.

1# Modules and Deck Guide
2
3Module compatibility is both an API question and a physical hardware question.
4Confirm the robot model, module generation, caddy or adapter, firmware, deck
5location, and labware before authoring commands.
6

Modules and Deck Guide

Module compatibility is both an API question and a physical hardware question. Confirm the robot model, module generation, caddy or adapter, firmware, deck location, and labware before authoring commands.

Raw lines 1 to 6
7## Robot Compatibility
8
9| Hardware | Flex | OT-2 | Protocol API load name |
10| --- | --- | --- | --- |
11| Absorbance Plate Reader | Yes | No | `absorbanceReaderV1` |
12| Flex Stacker | Yes | No | `flexStackerModuleV1` |
13| Heater-Shaker GEN1 | Yes | Yes | `heaterShakerModuleV1` |
14| Magnetic Block GEN1 | Yes | No | `magneticBlockV1` |
15| Magnetic Module GEN1/GEN2 | No | Yes | `magnetic module` / `magnetic module gen2` |
16| Temperature Module GEN2 | Yes | Yes | `temperature module gen2` |
17| Thermocycler GEN2 | Yes | Yes | `thermocyclerModuleV2` |
19Notes:
21- Flex uses the passive Magnetic Block. The powered OT-2 Magnetic Module is not
22 supported on Flex.
23- Flex requires Thermocycler GEN2 for gripper-compatible operation.
24- Older module generations may be supported only on OT-2. Match the load name
25 to the exact hardware label.
26- The HEPA/UV accessory is part of Flex operations but is not loaded and
27 controlled as a Protocol API module.
29See the current
30[Flex module list](https://docs.opentrons.com/flex/modules/) and
31[OT-2 module list](https://docs.opentrons.com/ot-2/modules/) before relying on
32this snapshot.

Robot Compatibility

HardwareFlexOT-2Protocol API load name
Absorbance Plate ReaderYesNoabsorbanceReaderV1
Flex StackerYesNoflexStackerModuleV1
Heater-Shaker GEN1YesYesheaterShakerModuleV1
Magnetic Block GEN1YesNomagneticBlockV1
Magnetic Module GEN1/GEN2NoYesmagnetic module / magnetic module gen2
Temperature Module GEN2YesYestemperature module gen2
Thermocycler GEN2YesYesthermocyclerModuleV2

Notes:

  • Flex uses the passive Magnetic Block. The powered OT-2 Magnetic Module is not supported on Flex.
  • Flex requires Thermocycler GEN2 for gripper-compatible operation.
  • Older module generations may be supported only on OT-2. Match the load name to the exact hardware label.
  • The HEPA/UV accessory is part of Flex operations but is not loaded and controlled as a Protocol API module.

See the current Flex module list and OT-2 module list before relying on this snapshot.

Raw lines 7 to 33
34## Deck Models
36### Flex
38- Working deck: A1–D3.
39- Staging area: A4–D4; pipettes cannot reach it, but the Gripper can.
40- Trash bins are loaded explicitly in supported column-1 or column-3 slots.
41- The waste chute is loaded with `protocol.load_waste_chute()` and occupies D3.
42- Powered module caddies and staging slots can conflict in the same row.
43- Column-4 hardware can reserve or move through the corresponding column-3
44 position.
46```python
47trash = protocol.load_trash_bin("A3")
48chute = protocol.load_waste_chute() # D3 only; do not load both into D3
49```
51Load only the fixtures physically installed for the run.

Flex

  • Working deck: A1–D3.
  • Staging area: A4–D4; pipettes cannot reach it, but the Gripper can.
  • Trash bins are loaded explicitly in supported column-1 or column-3 slots.
  • The waste chute is loaded with protocol.load_waste_chute() and occupies D3.
  • Powered module caddies and staging slots can conflict in the same row.
  • Column-4 hardware can reserve or move through the corresponding column-3 position.
trash = protocol.load_trash_bin("A3")
chute = protocol.load_waste_chute()  # D3 only; do not load both into D3

Load only the fixtures physically installed for the run.

Raw lines 36 to 52
53### OT-2
55- User deck slots: 1–11.
56- Fixed trash: slot 12.
57- Do not call `load_trash_bin()` for the fixed trash.
58- No Gripper or staging area.
59- Labware moves are manual operator actions.

OT-2

  • User deck slots: 1–11.
  • Fixed trash: slot 12.
  • Do not call load_trash_bin() for the fixed trash.
  • No Gripper or staging area.
  • Labware moves are manual operator actions.
Raw lines 53 to 60
61## Module Loading
63```python
64heater_shaker = protocol.load_module(
65 module_name="heaterShakerModuleV1",
66 location="D1",
68```
70Loading a powered module makes it a run requirement. The App will prevent the
71run from starting unless the expected connected module is available.
73Load labware through the module:
75```python
76hs_adapter = heater_shaker.load_adapter("opentrons_96_flat_bottom_adapter")
77plate = hs_adapter.load_labware(
78 "nest_96_wellplate_200ul_flat",
79 label="Mixing Plate",
81```
83For a separate adapter:
85```python
86temperature_module = protocol.load_module(
87 "temperature module gen2",
88 "D3",
90adapter = temperature_module.load_adapter(
91 "opentrons_96_well_aluminum_block"
93plate = adapter.load_labware(
94 "opentrons_96_wellplate_200ul_pcr_full_skirt"
96```
98The API generally cannot prove that every unusual module, adapter, and labware
99combination is physically valid. Check the official compatibility list.

Module Loading

heater_shaker = protocol.load_module(
    module_name="heaterShakerModuleV1",
    location="D1",
)

Loading a powered module makes it a run requirement. The App will prevent the run from starting unless the expected connected module is available.

Load labware through the module:

hs_adapter = heater_shaker.load_adapter("opentrons_96_flat_bottom_adapter")
plate = hs_adapter.load_labware(
    "nest_96_wellplate_200ul_flat",
    label="Mixing Plate",
)

For a separate adapter:

temperature_module = protocol.load_module(
    "temperature module gen2",
    "D3",
)
adapter = temperature_module.load_adapter(
    "opentrons_96_well_aluminum_block"
)
plate = adapter.load_labware(
    "opentrons_96_wellplate_200ul_pcr_full_skirt"
)

The API generally cannot prove that every unusual module, adapter, and labware combination is physically valid. Check the official compatibility list.

Raw lines 61 to 100
101## Temperature Module
103```python
104temperature_module.set_temperature(celsius=4)
105# Pipetting or incubation commands...
106temperature_module.deactivate()
107```
109`set_temperature()` blocks until the target is reached. In API 2.27+,
110`start_set_temperature()` can run concurrently and returns a task. Wait for the
111task before relying on the target temperature.
113Operational checks:
115- Use the correct aluminum block or adapter.
116- Include condensation and cold-surface effects in the wet-lab method.
117- Deactivate when the method no longer requires control.
118- Do not assume the liquid itself instantly reaches the module temperature.

Temperature Module

temperature_module.set_temperature(celsius=4)
# Pipetting or incubation commands...
temperature_module.deactivate()

set_temperature() blocks until the target is reached. In API 2.27+, start_set_temperature() can run concurrently and returns a task. Wait for the task before relying on the target temperature.

Operational checks:

  • Use the correct aluminum block or adapter.
  • Include condensation and cold-surface effects in the wet-lab method.
  • Deactivate when the method no longer requires control.
  • Do not assume the liquid itself instantly reaches the module temperature.
Raw lines 101 to 119
120## Heater-Shaker
122Typical sequence:
124```python
125heater_shaker.close_labware_latch()
126heater_shaker.set_and_wait_for_temperature(37)
127heater_shaker.set_and_wait_for_shake_speed(1000)
128protocol.delay(minutes=5)
129heater_shaker.deactivate_shaker()
130heater_shaker.deactivate_heater()
131```
133Current context methods include `set_target_temperature()`,
134`set_and_wait_for_temperature()`, `wait_for_temperature()`,
135`set_and_wait_for_shake_speed()`, `deactivate_shaker()`, and
136`deactivate_heater()`.
138Always:
140- Close the latch before shaking.
141- Stop shaking before opening the latch or moving labware.
142- Check maximum speed for the exact labware and fill volume.
143- Account for clearance restrictions in neighboring slots.
145API 2.27+ provides nonblocking shake and temperature operations for concurrent
146work. Keep task handles and wait before a dependent step.

Heater-Shaker

Typical sequence:

heater_shaker.close_labware_latch()
heater_shaker.set_and_wait_for_temperature(37)
heater_shaker.set_and_wait_for_shake_speed(1000)
protocol.delay(minutes=5)
heater_shaker.deactivate_shaker()
heater_shaker.deactivate_heater()

Current context methods include set_target_temperature(), set_and_wait_for_temperature(), wait_for_temperature(), set_and_wait_for_shake_speed(), deactivate_shaker(), and deactivate_heater().

Always:

  • Close the latch before shaking.
  • Stop shaking before opening the latch or moving labware.
  • Check maximum speed for the exact labware and fill volume.
  • Account for clearance restrictions in neighboring slots.

API 2.27+ provides nonblocking shake and temperature operations for concurrent work. Keep task handles and wait before a dependent step.

Raw lines 120 to 147
Involves a person and wait :146
148## Thermocycler
150```python
151thermocycler = protocol.load_module("thermocyclerModuleV2")
152plate = thermocycler.load_labware(
153 "opentrons_96_wellplate_200ul_pcr_full_skirt"
156thermocycler.open_lid()
157# Pipette into plate.
158thermocycler.close_lid()
159thermocycler.set_lid_temperature(temperature=105)
160thermocycler.set_block_temperature(
161 temperature=95,
162 hold_time_seconds=180,
163 block_max_volume=25,
165thermocycler.execute_profile(
166 steps=[
167 {"temperature": 95, "hold_time_seconds": 15},
168 {"temperature": 60, "hold_time_seconds": 30},
169 {"temperature": 72, "hold_time_seconds": 30},
170 ],
171 repetitions=35,
172 block_max_volume=25,
174thermocycler.set_block_temperature(
175 temperature=4,
176 block_max_volume=25,
178thermocycler.deactivate_lid()
179```
181API 2.28 adds an optional `ramp_rate` to block-temperature commands.
183Checks:
185- Correct plate and seal for the thermocycler.
186- Before cycling, pause to confirm the compatible sealing setup. A reusable
187 GEN2 automation seal attaches to the module lid, while a disposable Tough
188 auto-sealing lid covers a compatible PCR plate. Never combine those two;
189 follow the [lid-seal instructions](https://docs.opentrons.com/thermocycler/lid-seals/).
190 Closing the heated lid does not itself establish correct seal installation.
191- Lid closed before heating and cycling.
192- `block_max_volume` matches the actual per-well reaction volume.
193- Final hold behavior is intentional.
194- Lid and block are deactivated or left active according to the method.
195- No deck item conflicts with the thermocycler footprint.

Thermocycler

thermocycler = protocol.load_module("thermocyclerModuleV2")
plate = thermocycler.load_labware(
    "opentrons_96_wellplate_200ul_pcr_full_skirt"
)

thermocycler.open_lid()
# Pipette into plate.
thermocycler.close_lid()
thermocycler.set_lid_temperature(temperature=105)
thermocycler.set_block_temperature(
    temperature=95,
    hold_time_seconds=180,
    block_max_volume=25,
)
thermocycler.execute_profile(
    steps=[
        {"temperature": 95, "hold_time_seconds": 15},
        {"temperature": 60, "hold_time_seconds": 30},
        {"temperature": 72, "hold_time_seconds": 30},
    ],
    repetitions=35,
    block_max_volume=25,
)
thermocycler.set_block_temperature(
    temperature=4,
    block_max_volume=25,
)
thermocycler.deactivate_lid()

API 2.28 adds an optional ramp_rate to block-temperature commands.

Checks:

  • Correct plate and seal for the thermocycler.
  • Before cycling, pause to confirm the compatible sealing setup. A reusable GEN2 automation seal attaches to the module lid, while a disposable Tough auto-sealing lid covers a compatible PCR plate. Never combine those two; follow the lid-seal instructions. Closing the heated lid does not itself establish correct seal installation.
  • Lid closed before heating and cycling.
  • block_max_volume matches the actual per-well reaction volume.
  • Final hold behavior is intentional.
  • Lid and block are deactivated or left active according to the method.
  • No deck item conflicts with the thermocycler footprint.
Raw lines 148 to 196
197## Magnetic Block, Flex
199The Magnetic Block is passive. It has no engage or disengage methods.
200Separation happens by moving compatible labware onto and off the block:
202```python
203magnetic_block = protocol.load_module("magneticBlockV1", "D1")
205protocol.move_labware(
206 labware=plate,
207 new_location=magnetic_block,
208 use_gripper=True,
210protocol.delay(minutes=5)
211protocol.move_labware(
212 labware=plate,
213 new_location="C1",
214 use_gripper=True,
216```
218Confirm gripper compatibility and plate orientation. Avoid aspirating beads by
219validating settle time, aspiration side, bottom clearance, and flow rate.

Magnetic Block, Flex

The Magnetic Block is passive. It has no engage or disengage methods. Separation happens by moving compatible labware onto and off the block:

magnetic_block = protocol.load_module("magneticBlockV1", "D1")

protocol.move_labware(
    labware=plate,
    new_location=magnetic_block,
    use_gripper=True,
)
protocol.delay(minutes=5)
protocol.move_labware(
    labware=plate,
    new_location="C1",
    use_gripper=True,
)

Confirm gripper compatibility and plate orientation. Avoid aspirating beads by validating settle time, aspiration side, bottom clearance, and flow rate.

Raw lines 197 to 220
221## Magnetic Module, OT-2
223The powered Magnetic Module is OT-2-only:
225```python
226magnetic_module = protocol.load_module(
227 "magnetic module gen2",
228 "1",
230plate = magnetic_module.load_labware(
231 "nest_96_wellplate_2ml_deep"
234magnetic_module.engage(height_from_base=6.5)
235protocol.delay(minutes=5)
236magnetic_module.disengage()
237```
239Engage height is labware- and assay-specific. Do not copy an arbitrary height
240from another plate. The Magnetic Module is discontinued but remains supported
241for existing OT-2 hardware.

Magnetic Module, OT-2

The powered Magnetic Module is OT-2-only:

magnetic_module = protocol.load_module(
    "magnetic module gen2",
    "1",
)
plate = magnetic_module.load_labware(
    "nest_96_wellplate_2ml_deep"
)

magnetic_module.engage(height_from_base=6.5)
protocol.delay(minutes=5)
magnetic_module.disengage()

Engage height is labware- and assay-specific. Do not copy an arbitrary height from another plate. The Magnetic Module is discontinued but remains supported for existing OT-2 hardware.

Raw lines 221 to 242
243## Absorbance Plate Reader, Flex API 2.21+
245The reader is Flex-only and loads in A3–D3. Its caddy also uses the
246corresponding column-4 location for lid travel.
248Required workflow:
2501. Load the module.
2512. Close the lid with no plate inside.
2523. Initialize the reader.
2534. Open the lid.
2545. Move a compatible plate onto the module with the Gripper.
2556. Close the lid.
2567. Read.
258```python
259reader = protocol.load_module(
260 module_name="absorbanceReaderV1",
261 location="D3",
263plate = protocol.load_labware(
264 "corning_96_wellplate_360ul_flat",
265 "C2",
268reader.close_lid()
269reader.initialize(
270 mode="multi",
271 wavelengths=[450, 562, 600],
273reader.open_lid()
274protocol.move_labware(
275 labware=plate,
276 new_location=reader,
277 use_gripper=True,
279reader.close_lid()
280data = reader.read(export_filename="plate_data")
281```
283Default hardware wavelengths are 450, 562, 600, and 650 nm.
285`read()` returns:
287```python
288dict[int, dict[str, float]]
289```
291The first key is wavelength; the second is well name. In simulation every
292measurement is zero, and no output file is written. Guard calculations that
293would divide by a reading:
295```python
296if not protocol.is_simulating():
297 normalized = data[450]["A1"] / data[450]["H12"]
298```
300Do not move the plate-reader lid manually.

Absorbance Plate Reader, Flex API 2.21+

The reader is Flex-only and loads in A3–D3. Its caddy also uses the corresponding column-4 location for lid travel.

Required workflow:

  1. Load the module.
  2. Close the lid with no plate inside.
  3. Initialize the reader.
  4. Open the lid.
  5. Move a compatible plate onto the module with the Gripper.
  6. Close the lid.
  7. Read.
reader = protocol.load_module(
    module_name="absorbanceReaderV1",
    location="D3",
)
plate = protocol.load_labware(
    "corning_96_wellplate_360ul_flat",
    "C2",
)

reader.close_lid()
reader.initialize(
    mode="multi",
    wavelengths=[450, 562, 600],
)
reader.open_lid()
protocol.move_labware(
    labware=plate,
    new_location=reader,
    use_gripper=True,
)
reader.close_lid()
data = reader.read(export_filename="plate_data")

Default hardware wavelengths are 450, 562, 600, and 650 nm.

read() returns:

dict[int, dict[str, float]]

The first key is wavelength; the second is well name. In simulation every measurement is zero, and no output file is written. Guard calculations that would divide by a reading:

if not protocol.is_simulating():
    normalized = data[450]["A1"] / data[450]["H12"]

Do not move the plate-reader lid manually.

Raw lines 243 to 301
302## Flex Stacker, API 2.25+
304Up to four Stackers attach to the right side of Flex. Each shuttle is addressed
305as a column-4 location:
307```python
308stacker = protocol.load_module(
309 module_name="flexStackerModuleV1",
310 location="A4",
312```
314Configure one labware type per Stacker:
316```python
317stacker.set_stored_labware(
318 load_name="opentrons_flex_96_tiprack_200ul",
319 count=5,
320 lid="opentrons_flex_tiprack_lid",
322```
324Retrieve and move:
326```python
327tip_rack = stacker.retrieve()
328protocol.move_labware(
329 labware=tip_rack,
330 new_location="B2",
331 use_gripper=True,
333```
335Return the same configured type to storage (with its required lid restored):
337```python
338protocol.move_labware(
339 labware=tip_rack,
340 new_location=stacker,
341 use_gripper=True,
343stacker.store()
344```
346`fill()` and `empty()` pause for manual loading or unloading.
348Constraints:
350- Configure stored labware before `retrieve()` or `store()`.
351- Only one labware type per Stacker at a time.
352- Flex tip racks need compatible lids to stack.
353- Remove the tip-rack lid with a validated lid move before pipetting; restore
354 it before storage. A retrieved lidded rack is not yet ready for tip pickup.
355- The Stacker does not identify what an operator physically loaded.
356- Reserve the corresponding row's shuttle path and check column-3 conflicts.
357- Use capacity helper methods for the exact labware height.

Flex Stacker, API 2.25+

Up to four Stackers attach to the right side of Flex. Each shuttle is addressed as a column-4 location:

stacker = protocol.load_module(
    module_name="flexStackerModuleV1",
    location="A4",
)

Configure one labware type per Stacker:

stacker.set_stored_labware(
    load_name="opentrons_flex_96_tiprack_200ul",
    count=5,
    lid="opentrons_flex_tiprack_lid",
)

Retrieve and move:

tip_rack = stacker.retrieve()
protocol.move_labware(
    labware=tip_rack,
    new_location="B2",
    use_gripper=True,
)

Return the same configured type to storage (with its required lid restored):

protocol.move_labware(
    labware=tip_rack,
    new_location=stacker,
    use_gripper=True,
)
stacker.store()

fill() and empty() pause for manual loading or unloading.

Constraints:

  • Configure stored labware before retrieve() or store().
  • Only one labware type per Stacker at a time.
  • Flex tip racks need compatible lids to stack.
  • Remove the tip-rack lid with a validated lid move before pipetting; restore it before storage. A retrieved lidded rack is not yet ready for tip pickup.
  • The Stacker does not identify what an operator physically loaded.
  • Reserve the corresponding row's shuttle path and check column-3 conflicts.
  • Use capacity helper methods for the exact labware height.
Raw lines 302 to 358
359## Moving Labware and Lids
361Flex automated move:
363```python
364protocol.move_labware(
365 labware=plate,
366 new_location="C2",
367 use_gripper=True,
369```
371Manual move:
373```python
374protocol.move_labware(
375 labware=plate,
376 new_location="C2",
377 use_gripper=False,
379```
381A manual move pauses for the operator. Make sure the message and run setup make
382the source and destination unambiguous.
384API 2.23+ supports lid stacks and `move_lid()`. Confirm lid and labware
385compatibility, orientation, stack quantity, and disposal location.

Moving Labware and Lids

Flex automated move:

protocol.move_labware(
    labware=plate,
    new_location="C2",
    use_gripper=True,
)

Manual move:

protocol.move_labware(
    labware=plate,
    new_location="C2",
    use_gripper=False,
)

A manual move pauses for the operator. Make sure the message and run setup make the source and destination unambiguous.

API 2.23+ supports lid stacks and move_lid(). Confirm lid and labware compatibility, orientation, stack quantity, and disposal location.

Raw lines 359 to 386
387## Concurrent Module Actions, API 2.27+
389Concurrent methods can overlap long module actions with independent pipetting:
391- Temperature: `start_set_temperature()`.
392- Heater-Shaker: `set_target_temperature()` and `set_shake_speed()`.
393- Thermocycler: `start_set_block_temperature()`,
394 `start_set_lid_temperature()`, and `start_execute_profile()`.
396Pattern:
3981. Start the operation and keep the returned task.
3992. Perform only physically independent commands.
4003. Wait for the task before any step that assumes completion.
402```python
403heat_task = heater_shaker.set_target_temperature(celsius=37)
404# Independent work can run here.
405protocol.wait_for_tasks([heat_task])
406incubation = protocol.create_timer(seconds=60)
407protocol.wait_for_tasks([incubation])
408heater_shaker.deactivate_heater()
409```
411Use `set_target_temperature()` for the Heater-Shaker; it does not have the
412Temperature Module's `start_set_temperature()` method. Wait through the protocol
413context with `wait_for_tasks([task])`, not an invented `task.wait()` method.
415Do not create concurrency merely to shorten runtime. Check deck access,
416vibration, thermal dependencies, and collision risk.

Concurrent Module Actions, API 2.27+

Concurrent methods can overlap long module actions with independent pipetting:

  • Temperature: start_set_temperature().
  • Heater-Shaker: set_target_temperature() and set_shake_speed().
  • Thermocycler: start_set_block_temperature(), start_set_lid_temperature(), and start_execute_profile().

Pattern:

  1. Start the operation and keep the returned task.
  2. Perform only physically independent commands.
  3. Wait for the task before any step that assumes completion.
heat_task = heater_shaker.set_target_temperature(celsius=37)
# Independent work can run here.
protocol.wait_for_tasks([heat_task])
incubation = protocol.create_timer(seconds=60)
protocol.wait_for_tasks([incubation])
heater_shaker.deactivate_heater()

Use set_target_temperature() for the Heater-Shaker; it does not have the Temperature Module's start_set_temperature() method. Wait through the protocol context with wait_for_tasks([task]), not an invented task.wait() method.

Do not create concurrency merely to shorten runtime. Check deck access, vibration, thermal dependencies, and collision risk.

Raw lines 387 to 417
418## Module State Checklist
420- Correct robot and module generation.
421- Correct load name and API level.
422- Valid deck slot and no footprint conflict.
423- Correct caddy, adapter, and labware.
424- Latch or lid state is safe before motion.
425- Temperature, speed, and timing are validated for the method.
426- Every started concurrent task is awaited.
427- Gripper moves use compatible labware and clear paths.
428- Modules are deactivated or intentionally held at protocol end.
429- App analysis and a physical dry run cover the exact configuration.

Module State Checklist

  • Correct robot and module generation.
  • Correct load name and API level.
  • Valid deck slot and no footprint conflict.
  • Correct caddy, adapter, and labware.
  • Latch or lid state is safe before motion.
  • Temperature, speed, and timing are validated for the method.
  • Every started concurrent task is awaited.
  • Gripper moves use compatible labware and clear paths.
  • Modules are deactivated or intentionally held at protocol end.
  • App analysis and a physical dry run cover the exact configuration.
Raw lines 418 to 429