Automating experiments using ESAAC

Please note: This article is a WIP and will be updates in the coming days

Automation can be the ultimate time-saver for experiments and preparations that require many repetitions of the same actions, but can take a lot of time to develop. ESAAC is a Matlab based interface that allows for the automation of measurements and preparation of samples. The system is based on two main components: a protocol reader and interfaces to devices. The protocol reader reads humanly readable protocol files that are easy to write and develop. The device interfaces handle the communication with the devices. ESAAC is open source (not for profit) and can be easily extended to include other hardware.

ESAAC (Experimental Streamlining And Automation Code) was developed during my Marie Curie Global Fellowship as a way to perform automated TPM measurements for the study of interactions between proteins and DNA sequences. Soon I realized that my work on the interface could by used by my colleagues with a few small modifications and I set out to update the code to be easily used by most people.

Currently ESAAC interfaces with AMF microfluidic pumps and valves, FLIR BlackFly cameras, Matlab based TPM tracking software and external software tools. These interfaces can be easily adapted to interface with other hardware devices. The protocol reader and the devices are implemented in the code as objects. To using the object you simply create an instance of the object, configure it, connect it to the required devices and hardware and it is ready to go!

Now let’s get started…

Get the code

The base code is available through GitHub.

Note: Keep in mind that some code requires certain Matlab toolboxes to function. There is no comprehensive list, but I’d always recommend the parallel computing toolboxes

Now: let’s start with an overview of some basic components!

The protocol reader

The protocol reader is the central hub that controls the timing of and sensing of commands to other components. The protocol reader will read a plain text recipe file, verify that all required devices are interfaced and checks the command syntax.

Initializing the protocol reader and add device interfaces

The protocol reader should be instantiated in Matlab before you can use it. To use the protocol reader with any hardware you need to register instances of hardware interfaces with the protocol:

% Instantiate the ProtocolReader under the name Reader
Reader = ProtocolReader; 

% Instantiate the interface to an AMF LSPOne Pump/Valve combination
Pump = LSPOneInterface;

% Register the interface under the devicename "PUMP"
Reader.addDevice(Pump, "PUMP"); 

% Load the protocol file
Reader.loadProtocolFile('your protocol file.txt');

% Start executing the protocol
Reader.start()

Pay attention to the command window. If any errors occur you’ll find them there.

Recipe file

The syntax for the recipe file is straightforward. Each command is placed on a new line.

DEVICE|Command  ➝  parameter 1 ➝ parameter 2 ➝ …; Comments
  • ‘DEVICE’ indicates the device name to which the command is send.
  • If no device is specified the command is interpreted by the protocol reader. (for example ‘protocol’, ‘time’, ‘pause’ and ‘waitfor’)
  • ‘Command’ is the command that is send to the command. Commands are device specific
  • Parameters are passed on the same line after the command
  • The command and all parameters are separated by tabs (shown with ➝ in the code block above for clarity)
  • Comments can be placed after an (optional) semicolon.
  • A block of commands can be grouped using ‘>>’ with as optional parameter a description of the commands

Tip: the correct placement of tabs is essential. Use a text editor that shows the tab placement. I’d recommend Notepad++ (available online for free) for editing protocols and turn on showing whitespaces and tabs (View>Show symbol>Show white space and TAB).

For example: a protocol file can look something like this:

protocol	Example code

PUMP|syringevolume	250; You can add comments here. Sets syringe volume to 250 uL
PUMP|sampledispensespeed	20; Unit is set by syringe volume, thus 20 uL
PUMP|mixingspeed	5; Unit: ul/s
PUMP|mixingcycles	3
PUMP|airplugvolume	2
PUMP|samplevolume	100

>>	Sample settings
PUMP|samplevolume	40

>>	Measurement 1
time	0:00:00
REC|name	10 pM
PUMP|pickupsample	4 5	ratio	0.5 0.5
PUMP|dispensesample
waitfor	PUMP; Wait for the pump to complete all actions
pause	0:05; Allow the fluid to settle
time	0:04:00
REC|startmeasurement
waitfor	REC

>>	Measurement 2
time	0:05:00
PUMP|pickupsample	4 5	ratio	0.2929 0.7071
PUMP|dispensesample
waitfor	PUMP
pause	0:05; Allow the fluid to settle
time	0:09:00
REC|startmeasurement
waitfor	REC

Device interfaces

The device interfaces contains all the code that is required to communicate with the device itself and the code that allows it to work with the protocol reader. The first code will vary significantly with the hardware you connect to, while the latter part is uniform to ensure compatibility with the protocol reader. More on that in a future post.

The device interfaces are instantiated from the interface object in Matlab. All further interactions occur through the created instance:

% Create an instance of the interface to the device
Pump = LSPOneInterface;
 
% Show a list of available communication ports
disp(strjoin(["Available COM ports:" serialportlist]));
 
% Connect to the pump at COM5 (will be different on different systems and for different devices)
Pump.connect("COM5");
 
% Check that pump is connected
disp("Connection: "+Pump.Connected);
 
% Change some settings
Pump.WastePort = 1;
Pump.OutputPort = 2;
Pump.MixPort = 3;
Pump.BufferPort = 4;
Pump.SyringeVolume = 250; % uL
 
% Verify unit
disp("Unit for speed settings: "+Pump.SpeedUnit);  % should be ul/s
 
% Change some settings
Pump.MixingSpeed = 10;
Pump.MixingCycles = 3;
Pump.SamplePickupSpeed = 10; 
 
% Start interacting with the pump through direct commands
Pump.initialize();
Pump.action('primesamples');
Pump.action('primebuffer');
Pump.PickupSample([5 6], [0.5 0.5]);
Pump.DispenseSampleToChamber();

A key step for the LSPOne interface is to establish the connection to the pump hardware. In the code above we do this using the command: Pump.connect(“COM5”), which attempts to establish a serial communication connection over a USB connection. The command window will show a success/failure message and some useful hints.

It is possible to interact directly with the pump through the pump interface, as you can see in the example above.

This line in the protocol file:

PUMP|pickupsample	4 5	ratio	0.5 0.5

Is very similar to the line in Matlab:

Pump.PickupSample([4 5], [0.5 0.5]);

Indeed, the protocol reader will execute these commands when the protocol calls for it. You could in theory program your entire protocol using Matlab calls to the device interfaces. The main advantages of the protocol reader are that you can easily interact with multiple devices, that it handles the timing of the protocol, can wait for devices to finish all actions, and that multiple protocols can be setup using just a few simple text files.

Device specific commands

As every device and hardware is different, the commands will be different. I cannot provide a comprehensive list of commands here. A good way to check which commands are available in the Matlab interface

Leave a Reply

Your email address will not be published. Required fields are marked *