Matlab: Serial Communication with Pfeiffer MaxiGauge

Connect the MaxGauge to the PC using a simple (3-wire) cross-over serial cable.

The communication protocol (viewed from the PC) is roughly:

--> Mnemonic <CR>
<-- <ACK> <CR> <LF>
--> <ENQ>
<-- Data <CR> <LF>

The mnemonics are described in the user manual (page 85ff). CR (carriage return), ACK (acknowledge), LF (line-feed), ENQ (enquire) are standard ASCII control chararacters.

A simple script for first interactions (almost no error checks!):

maxigauge = serial('COM1');
fopen(maxigauge);

% ASCII control characters
ENQ = int8(5);
LF = int8(10);
CR = int8(13);
ACK = int8(6);
NAK = int8(21);

% Send command: I want to know the part number.
message = [int8('PNR'), CR];
fwrite(maxigauge, message)

% Device the "command received" answer from the device
answer = fscanf(maxigauge);
if all(answer == [ACK CR LF])
    % disp('Command received by MaxiGauge')
else
    error('Command not received correctly by MaxiGauge')
end
% Send the ENQ signal
fwrite(maxigauge, ENQ)

% Receive Answer
answer = fscanf(maxigauge);
disp(answer(1:end-2))

% End communication and cleanup
fclose(maxigauge);
delete(maxigauge)
clear maxigauge

Here is somewhat equivalent code as a Matlab class. I don’t do much OOP, so take it with a grain of salt.

classdef PfeifferMaxiGauge < handleAllHidden
    properties (Hidden, SetAccess = immutable)
        ENQ @ int8 scalar = 5;  % Undocumented @ syntax: value is of type doulbe, a scalar, with initinal value = 0
        LF @ int8 scalar = 10;
        CR @ int8 scalar = 13;
        ACK @ int8 scalar = 6;
        NAK @ int8 scalar = 21;
        com_port @ char vector;
    end
    properties (SetAccess = immutable)
        serial_handle @ serial scalar;
    end
    
    methods (Hidden) % Low-level methods that the user should not (but can) call
        function obj = PfeifferMaxiGauge(com_port) % constructor
            obj.com_port = com_port;
            obj.serial_handle = serial(obj.com_port);
            fopen(obj.serial_handle); 
        end
      
        function delete(obj) % destructor
            fclose(obj.serial_handle);
        end
        
        function write(obj, str)
            % Adds CR to str. 
            % Low-level function. Use send_command(), if appropriate.
            message = [int8(str), obj.CR];
            fwrite(obj.serial_handle, message)
        end
        
        function output = read(obj)
            % Removes CR LF from output
            output = fscanf(obj.serial_handle);
            if output(end-1) ~= obj.CR || output(end) ~= obj.LF
                error('MaxiGauge Read Operation: Unexpected data format - no CR LF at end!')
            end
            output = output(1:end-2);
        end
    end
    
    methods % High-level methods that the user should use.
        function send_command(obj, str)
            obj.write(str)
            ack_msg = obj.read();
            if length(ack_msg) > 1 || ack_msg ~= obj.ACK;
                error(['Command "' str '" not acknowledged by device'])
            end
        end
        
        function output = query(obj, str)
            if nargin > 1
                obj.send_command(str)
            end
            fwrite(obj.serial_handle, obj.ENQ);
            output = obj.read();
        end
        
        function pnr = get_pnr(obj)
            pnr = obj.query('PNR');
        end

    end
end

This class derives from handleAllHidden, which is a trick to hide the standard methods when deriving from the handle class. I only implemented one helper method in my class (get_pnr), the rest will come later, as I need it.