Select Git revision
usbBrickIO.m
Maximilian Schnabel authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
usbBrickIO.m 12.33 KiB
classdef usbBrickIO < BrickIO
% USB interface between MATLAB and the brick
%
% Notes:
% * Uses the hid library implementation in hidapi.m
% * The default parameters should always work when you try to connect to an EV3 brick,
% so in nearly all use-cases, the constructor does not need any parameters (besides
% 'debug' eventually).
%
% Attributes:
% debug (bool): If true, each open/close/read/write-call will be noted in the console.
% Defaults to false.
% vendorID (numeric): Vendor-ID of the USB device. Defaults to 0x694 (EV3 vendor ID).
% productID (numeric): Product-ID of the USB device. Defaults to 0x0005 (EV3 product ID).
% nReadBuffer (numeric): Read-buffer size in bytes. Defaults to 1024.
% nWriteBuffer (numeric): Write-buffer size in bytes. Needs to be 1 Byte bigger than
% actual packet. Defaults to 1025 (EV3 USB maximum packet size = 1024).
% timeOut (numeric >= 0): Milliseconds after which a timeout-error occurs if no packet could be
% read. Defaults to 10000.
%
% ::
%
% Examples:
% % Connecting via USB
% commHandle = usbBrickIO();
% % Connecting via USB with enabled debug output
% commHandle = usbBrickIO('debug', true);
%
properties
% debug (bool): If true, each open/close/read/write-call will be noted in the console.
% Defaults to false.
debug;
% vendorID (numeric): Vendor-ID of the USB device. Defaults to 0x694 (EV3 vendor ID).
vendorID;
% productID (numeric): Product-ID of the USB device. Defaults to 0x0005 (EV3 product ID).
productID;
% nReadBuffer (numeric): Read-buffer size in bytes. Defaults to 1024.
nReadBuffer;
% nWriteBuffer (numeric): Write-buffer size in bytes. Defaults to 1025 (EV3 USB maximum packet size = 1024).
% Needs to be 1 Byte bigger than actual packet.
nWriteBuffer;
% timeOut (numeric >= 0): Milliseconds after which a timeout-error occurs if no packet could be read. Defaults to 10000.
timeOut;
end
properties (Access = protected)
% handle: Connection handle to the device
handle
end
methods
function brickIO = usbBrickIO(varargin)
% Create an usbBrickIO object
%
% Arguments:
% varargin: Any number of property names as strings, each followed by the
% desired value.
%
% ::
%
% Examples:
% % Connecting via USB
% commHandle = usbBrickIO();
% % Connecting via USB with enabled debug output
% commHandle = usbBrickIO('debug', true);
%
% See also USBBRICKIO.SETPROPERTIES
brickIO.setProperties(varargin{:});
if brickIO.debug > 0
fprintf('\t(DEBUG) (USB init)\n');
end
% Set the connection handle
try
brickIO.handle = hidapi(brickIO.vendorID,brickIO.productID, ...
brickIO.nReadBuffer,brickIO.nWriteBuffer);
catch ME
if ~isempty(strfind(ME.identifier, 'InvalidParameterOrFileMissing'))
% Throw a clean InvalidParameterOrFileMissing to avoid confusion in upper layers
msg = ['Couldn''t load hidapi-library for USB connection due to a ' ...
'missing file. Make sure the correct hidapi-library and its ' ...
'corresponding thunk- and proto-files are available.'];
id = [ID(), ':', 'InvalidParameterOrFileMissing'];
throw(MException(id, msg));
elseif ~isempty(strfind(ME.identifier, 'LoadingLibraryError'))
% Throw a clean LoadingLibraryError to avoid confusion in upper layers
msg = 'Failed to load hidapi-library for USB connection.';
id = [ID(), ':', 'LoadingLibraryError'];
throw(MException(id, msg));
else
% Throw combined error because error did not happen due to known reasons...
msg = 'Unknown error occurred while trying to load the HIDAPI-lib for USB.';
id = [ID(), ':', 'UnknownError'];
newException = MException(id, msg);
newException = addCause(newException, ME);
throw(newException);
end
end
% Open the connection
brickIO.open;
end
function delete(brickIO)
% Delete the usbBrickIO object and closes the connection
if brickIO.debug > 0
fprintf('\t(DEBUG) (USB delete)\n');
end
% Disconnect
try
brickIO.close;
catch
% Connection already closed (probably due to an error) - do nothing
end
end
function open(brickIO)
% Opens the usb connection to the brick through the hidapi interface.
if brickIO.debug > 0
fprintf('\t(DEBUG) (USB open)\n');
end
% Open the usb handle
try
brickIO.handle.open;
catch ME
if ~isempty(strfind(ME.identifier, 'CommError'))
% Throw a clean CommError to avoid confusion in upper layers
msg = 'Failed to open connection to Brick via USB.';
id = [ID(), ':', 'CommError'];
throw(MException(id, msg));
else
% Throw combined error because error did not happen due to known reasons...
msg = 'Unknown error occurred while trying to connect to the Brick via USB.';
id = [ID(), ':', 'UnknownError'];
newException = MException(id, msg);
newException = addCause(newException, ME);
throw(newException);
end
end
end
function close(brickIO)
% Closes the usb connection the brick through the hidapi interface.
if brickIO.debug > 0
fprintf('\t(DEBUG) (USB close) \n');
end
try
% Close the usb handle
brickIO.handle.close;
catch ME
% Throw combined error because error did not happen due to known reasons...
msg = 'Unknown error occurred while closing the USB connection.';
id = [ID(), ':', 'UnknownError'];
newException = MException(id, msg);
newException = addCause(newException, ME);
throw(newException);
end
end
function rmsg = read(brickIO)
% Reads data from the brick through usb using the hidapi interface and returns the data in uint8 format.
if brickIO.debug > 0
fprintf('\t(DEBUG) (USB read) ');
end
% Read from the usb handle
try
if brickIO.timeOut ~= 0
rmsg = brickIO.handle.read_timeout(brickIO.timeOut);
else
rmsg = brickIO.handle.read();
end
catch ME
if ~isempty(strfind(ME.identifier, 'CommError'))
% Throw a clean CommError to avoid confusion in upper layers
msg = 'Failed to read data from the Brick via USB due to connection-error.';
id = [ID(), ':', 'CommError'];
throw(MException(id, msg));
elseif ~isempty(strfind(ME.identifier, 'InvalidHandle'))
% Throw a clean InvalidHandle to avoid confusion in upper layers
msg = 'Failed to read data from the Brick via USB due to invalid handle to USB-device.';
id = [ID(), ':', 'InvalidHandle'];
throw(MException(id, msg));
else
% Throw combined error because error did not happen due to known reasons...
msg = 'Unknown error occurred while reading data from the Brick via USB.';
id = [ID(), ':', 'UnknownError'];
newException = MException(id, msg);
newException = addCause(newException, ME);
throw(newException);
end
end
% Get the number of read bytes
pLength = double(typecast(uint8(rmsg(1:2)),'uint16')) + 2;
% Format the read message (2 byte length plus message)
if pLength < length(rmsg)
rmsg = rmsg(1:pLength);
end
end
function write(brickIO,wmsg)
% Writes data to the brick through usb using the hidapi interface.
%
% Arguments:
% wmsg (uint8 array): Data to be written to the brick via usb
if brickIO.debug > 0
fprintf('\t(DEBUG) (USB write) ');
end
% Write to the usb handle using report ID 0
try
brickIO.handle.write(wmsg,0);
catch ME
if ~isempty(strfind(ME.identifier, 'CommError'))
% Throw a clean CommError to avoid confusion in upper layers
msg = 'Failed to send data to Brick via USB due to connection-error.';
id = 'RWTHMindstormsEV3:usbBrickIO:write:CommError';
throw(MException(id, msg));
elseif ~isempty(strfind(ME.identifier, 'InvalidHandle'))
% Throw a clean InvalidHandle to avoid confusion in upper layers
msg = 'Failed to send data to Brick via USB due to invalid handle to USB-device.';
id = [ID(), ':', 'InvalidHandle'];
throw(MException(id, msg));
else
% Throw combined error because error did not happen due to known reasons...
msg = 'Unknown error occurred while sending data to the Brick via USB.';
id = [ID(), ':', 'UnknownError'];
newException = MException(id, msg);
newException = addCause(newException, ME);
throw(newException);
end
end
end
function set.timeOut(brickIO, timeOut)
if ~isnumeric(timeOut) || timeOut < 0
error(ID(), 'timeOut-period of USB-handle can only be set to positive numerical values.');
end
brickIO.timeOut = timeOut*1000;
end
function setProperties(brickIO, varargin)
% Sets multiple usbBrickIO properties at once using MATLAB's inputParser.
%
% The syntax is as follows: commHandle.setProperties('propertyName1',
% propertyValue1, 'propertyName2', propertyValue2, ...). Valid, optional properties
% are: debug, vendorID, productID, nReadBuffer, nWriteBuffer, timeOut.
%
% See also USBBRICKIO.DEBUG, USBBRICKIO.VENDORID, USBBRICKIO.PRODUCTID,
% USBBRICKIO.NREADBUFFER, USBBRICKIO.NWRITEBUFFER, USBBRICKIO.TIMEOUT
p = inputParser();
p.addOptional('debug', false);
p.addOptional('vendorID', 1684);
p.addOptional('productID', 5);
p.addOptional('nReadBuffer', 1024);
p.addOptional('nWriteBuffer', 1025);
p.addOptional('timeOut', 10000);
p.parse(varargin{:});
brickIO.debug = p.Results.debug;
brickIO.vendorID = p.Results.vendorID;
brickIO.productID = p.Results.productID;
brickIO.nReadBuffer = p.Results.nReadBuffer;
brickIO.nWriteBuffer = p.Results.nWriteBuffer;
brickIO.timeOut = p.Results.timeOut;
end
end
end