Code covered by the BSD License  

Highlights from
CO2gui - lab control and automation

CO2gui - lab control and automation



06 Jan 2010 (Updated )

Software used for controlling and data logging lab equipment.

gilsonpumpobjcomm(type, serialObject, deviceID, command)
function response = gilsonpumpobjcomm(type, serialObject, deviceID, command)
% GILSONPUMPOBJCOMM Reads and writes data from Gilson pumps
% gilsonpumpobjcomm(type, serialObject, deviceID, command) writes or reads
% information from Gilson pumps (specifically designed for the 305, but
% probably works the same for other pumps), connected via the Gilson 506C
% interface through RS232 (the pumps connect to the interface via RS422).
% type is 'read' or 'write', serialObject is the serial port object,
% deviceID the ID of the Gilson pump (they are set to 1 by default, but may
% be between 1 and 63 depending on the system - the interface is usually
% 63), command is the command to be read/written.  If reading a value,
% function responds with the requested value.  If writing a value, the
% response is [1 value] if successful.  For both read and write operations,
% the response is -1 if the controller returns an unexpected message, -2 if
% there was no response, or -3 if any of the arguments were invalid.

% Normal usage:

% e.g. 1 response = gilsonpumpobjcomm('read', serialObject, 1, 'W') - reads
% the version number of the pump e.g. '305V3.0'.

% e.g. 2 response = gilsonpumpobjcomm('write', serialObject, 1, 's0100')
% writes a flow rate of 1 mLmin-1 to the pump, returning a 1 if successful.

% Range:

% type = 'read' or 'write'

% serialObject = serial object

% deviceID = unsigned integer: 1-63

% command = string

% checks the number of arguments
error(nargchk(4, 4, nargin))

% error handling
if ~ischar(type) || all(~strcmp(type, {'read', 'write'}))
    % errors
    error('type must be "read" or "write"')
elseif ~isserial(serialObject) || (~isfield(serialObject.UserData, 'realTermHandle') && ~isrunning(serialObject))
    % the serial object must be valid and open to send or receive data
    % (unless there is a realterm handle available)
    error('serialObject must be a valid open serial object')
elseif ~isserial(serialObject) || (isfield(serialObject.UserData, 'realTermHandle') && ~isrunning(serialObject.UserData.realTermHandle))
    % if the handle is there, the object must be running (PortOpen is 1)    
    error('If the Realterm handle is present, the object must be connected via Realterm.')
elseif isfield(serialObject.UserData, 'realTermHandle') && (~isfield(serialObject.UserData, 'captureFileID') || ~isfid(serialObject.UserData.captureFileID))
    % if using realterm, the capture file ID must be there
    error('If using Realterm, the capture file handle must be in the serial object''s UserData.')
elseif ~isnumberbetween(deviceID, 1, 63) || ~isainteger(deviceID)
    % errors
    error('deviceID must be an unsigned integer from 1 to 63')
elseif ~isstring(command)
    % errors
    error('command must be a string')
elseif strcmp(type, 'read') && numel(command) ~= 1
    % errors
    error('Read commands must only be a single character')

% flushes anything in the buffer

% all Gilson commands must be sent byte by byte, so are sent and read using
% fwrite and fread, respectively

% parses the deviceID
deviceID = deviceID + 128;

% alerts device that this command is for that device
fwritetc(serialObject, deviceID)

% device ALWAYS responds with the deviceID back if it heard it
freadmatchbyte(serialObject, deviceID, 'Gilson device did not respond to ID check')

% defines line feed and carriage return characters for readability
LF = char(10);
CR = char(13);

% inserted a pause BEFORE the command to give it time in case timers
% interrupt the usual course of action (which they will) - MATLAB is being
% extremely strange with this - a "40ms" pause is turning into ~400 ms
%if ~isfield(serialObject.UserData, 'realTermHandle')
    % pause it

% different protocols required for different commands
if strcmp(type, 'write')
    % if the command is to be written ("Buffered" in Gilson documentation
    % parlance), an LF is sent to the device, to which the device always
    % respond with an LF - but it MAY not immediately do so - should try at
    % least twice to make sure it does behave
        % attempts to match the byte to LF - if it doesn't work...
        fwritetc(serialObject, LF)
        % reads it back
        freadmatchbyte(serialObject, LF, 'Gilson device did not respond to the command to start writing a value')
        % it'll try again, only this time, if it errors the program will
        % stop completely
        fwritetc(serialObject, LF)
        % reads it back
        freadmatchbyte(serialObject, LF, 'Gilson device did not respond to the command to start writing a value')

    % the command is then written byte by byte, with the device echoing
    % back the same command byte
    for m = 1:numel(command)
        % writes the command
        fwritetc(serialObject, command(m))

        % device ALWAYS responds with the command byte back if it heard it
        freadmatchbyte(serialObject, command(m), 'Gilson device did not respond to one of the command bytes')
    % at the end of the write, the last character must be a carriage
    % return
    fwritetc(serialObject, CR)
    % device then responds with CR
    freadmatchbyte(serialObject, CR, 'Gilson device did not respond to signal to finish writing command')

    % if its reading...('Immediate' in Gilson parlance - is able to
    % interrupt 'Buffered' commands - useful if something isn't working
    % correctly, although calling the deviceID also does the same thing)
    % defines ack character for convenience and readability
    ack = char(6);
    % defines the read error message
    readError = 'Gilson device did not respond to a request for the next byte to be read';

    % sends the command
    fwritetc(serialObject, command)

    % device responds with the first byte
    responseNum(1) = freadbyte(serialObject, readError);
    % initialises the ticker
    ticker = 1;
    % if data is not empty (covered by freadbyte) or not larger than 128,
    % read more data
    while responseNum(ticker) < 128
        % requests the next byte of data
        fwritetc(serialObject, ack)

        % reads the next byte
        responseNum(ticker + 1) = freadbyte(serialObject, readError);
        % increments ticker
        ticker = ticker + 1;

    % the last byte to be sent is the ASCII number for the last character,
    % but + 128 to signify that this is the last character.  GSIOC does not
    % appear to transmit any data larger than 128 unless it is a deviceID,
    % or it is the last byte of data.
    % modifies the last byte and converts it into ASCII
    response = char([responseNum(1:end - 1), responseNum(end) - 128]);

% the pump does not seem to behave properly unless a pause is left after
% the command has finished (despite it responding to some commands very
% quickly
%if ~isfield(serialObject.UserData.realTermHandle)
    % pause it

function fwritetc(serialObject, data)
% FWRITETC writes data to a serialObject using fwrite, but in a try-catch loop
% in case it errors

% sends data
serialwrite(serialObject, data, 'async')

function freadmatchbyte(serialObject, matchByte, errorMessage)
% FREADBYTE reads a single byte from a serialObject using fwrite, seeing if it
% matches matchByte, and erroring with the errorMessage if it does not

% reads a byte out and compares it    
if serialread(serialObject, 1) ~= matchByte
    % errors

function byte = freadbyte(serialObject, errorMessage)
% FREADBYTE reads a single byte from a serialObject using fwrite, seeing if it
% is empty (i.e. it timed out), and erroring with the errorMessage if it
% is.

% fetches byte
byte = serialread(serialObject, 1);

% if its empty (timed out), error
if isempty(byte)
    % errors

Contact us