In an effort to improve the functionality and reliability of the OT-2, we have rolled out a new version of the API: API Version 2! This version has all of the features available in version 1, with added support for new products.
Here’s a side-by-side comparison of the protocols written in both versions. We will dive into more details below.
Metadata and Version Selection
You must include a metadata dictionary at the top of the protocol. The only required element is apiLevel
, which defines the major and minor version of the Python Protocol API for which your protocol is designed. You can also specify the protocolName
, author
, and description
in the metadata, and you would be able to see these information in the App once you upload the protocol.
from opentrons import protocol_api
metadata = {'apiLevel': '2.0',
'protocolName': 'Your Protocol Name',
'author': 'Your Name',
'description': 'Your protocol description'}
def run(protocol: protocol_api.ProtocolContext):
pass
Run Function and Import Statement
One of the most obvious differences between the versions is the introduction of the Run function in APIv2. This function must be present in your protocol and take exactly one mandatory argument.
from opentrons import protocol_api
def run(protocol: protocol_api.ProtocolContext):
# the rest of your code goes here
The argument that is passed in the Run function is an instance of the ProtocolContext class. You can name the argument anything you would like, but we suggest protocol
because it actually represents the protocol that would be executed by the robot.
Note the import statement is optional. The addition of the import statement could allow your text editor to provide you with autocompletion. But if this is not a concern, you can simply write your protocols this way:
def run(protocol):
# your code
Labware
As always, you still need to define the labware required for your protocol. In APIv2, you do this by calling the method on your protocol
object, such as
protocol.load_labware('labware_name', slot_number)
Do you know about our Labware Library? It holds information of all of our default and validated labware. Just copy and paste the API name of the appropriate labware in your protocol.
Pipettes
Similar to labware, pipettes are defined using a new method:
protocol.load_instrument('pipette_name', mount, tip_racks)
Here is a list of valid pipette names:
p10_single
p10_multi
p300_single
p300_multi
p1000_single
p20_single_gen2
p20_multi_gen2
p300_single_gen2
p300_multi_gen2
p1000_single_gen2
Were you ever frustrated because you could not easily assign the same tip rack for your two pipettes? You can do that now in APIv2! The tip tracking function is more powerful than ever and can keep track of how many tips are left in each column of the tip rack.
Commands
The protocol commands are mostly very similar to that of APIv1. Here are the two main categories of our protocol-related commands:
- Building Block Commands
- Complex Commands
They are parallel to the Atomic and Complex Liquid Handling Commands in version 1 for the most part. Though you might notice some behavioral changes for certain commands:
No More Optional Arguments!
In APIv1, you could use positional arguments to specify either volume, location (and repetitions for mix). We have removed this feature in APIv2. This means you must add in all of the missing arguments for each command, or else it would raise an error.
Mix
The pipette now moves above liquid level and resets the plunger at the top of the well between each dispense and the following aspirate during mix. This helps prevent the pipette from over-aspirating when mixing.
Move To
In APIv1, pipette.move_to(well)
would move the pipette to the top of the specified well. In APIv2, you must state the location of the well, such as pipette.move_to(well.top())
or it will cause an error.
Utility Commands
Previously in APIv1, we used pipette.delay()
and robot.pause()
, which could cause some confusion. We have fixed this inconsistency in APIv2 to make our API more intuitive by making these utility commands ProtocolContext
methods.
protocol.pause("Pause the protocol and await for user to resume.")
protocol.delay(minutes=1, seconds=3) # pause for 1 minute, 3 seconds
Read more about other Utility Commands in our docs here here.
Hardware Modules
Rather than passing the argument share=True
in labware.load()
, labware are loaded directly to the module object using load_labware()
.
Here are some examples on how to use the hardware modules with the new API:
Magnetic Module
mag_mod = protocol.load_module('Magnetic Module', 4)
mag_plate = mag_mod.load_labware(
'nest_96_wellplate_100ul_pcr_full_skirt')
mag_mod.engage() # raise magnetic stage
mag_mod.disengage() # lower magnetic stage
Temperature Module
temp_mod = protocol.load_module('Temperature Module', 7)
temp_plate = temp_mod.load_labware(
'opentrons_96_aluminumblock_nest_wellplate_100ul')
temp_mod.set_temperature(4) # set target temperature to 4°C
temp_mod.wait_for_temp() # halt protocol until 4°C is reached
temp_mod.deactivate() # stop cooling
You can read the target and current real-temperature of the module by the following:
temp_mod.target
temp_mod.temperature
Thermocycler Module
Our Thermocycler Module is only supported in APIv2. If your workflow requires a thermocycler, you now have the option to automate the thermocycling steps:
tc_mod = protocol.load_module('Thermocycler Module')
tc_plate = tc_mod.load_labware(
'nest_96_wellplate_100ul_pcr_full_skirt')
# lid motion control
tc_mod.open_lid()
tc_mod.close_lid()
tc_mod.set_lid_temperature(105) # set lid temperature
tc_mod.set_block_temperature(4) # set aluminum block temperature
tc_mod.set_block_temperature(10, # inside the thermocycler
hold_time_minutes=5,
hold_time_seconds=30)
tc.deactivate()
Read more about Thermocycler Module commands here.