Select Git revision

Tim Stadtmann authored
Renamed tachoLimit back to limitValue, fixed comments and minor bugs, and made commInterface private.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
Sensor.m 13.66 KiB
classdef Sensor < handle
% Sensor High-level class to work with sensors.
%
% Properties:
% Standard
% debug - Debug mode turned on or off
% mode - Sensor mode in which the values will be read
% Dependent
% value - Value read from sensor
% type - Type of connected sensor if any
%
% Methods:
% Standard
% EV3
% connect - Connects Sensor-object to physical brick
% disconnect - Disconnects Sensor-object from physical brick
% reset - Resets value on sensor
% setProperties - Sets multiple Sensor properties at once using MATLAB's inputParser
%
% Example
% % This small example should only roughly demonstrate how to work with sensors.
% % Establish connection, set properties on sensor1 and wait, until user has pressed the
% % touch sensor ten times. Then beep and disconnect.
%
% b = EV3();
% b = EV3('debug', 'on', 'batteryMode', 'Voltage');
% b.connect('bt', 'serPort', '/dev/rfcomm0');
% b.sensor1.mode = DeviceMode.Touch.Bumps;
% while value < 10
% pause(0.1);
% end
% b.beep();
% b.disconnect();
% b.delete()
%
%
% Signature
% Author: Tim Stadtmann
% Date: 2016/05/20
% Updated: 2016/08/15
properties % Standard properties to be set by user
%mode - Sensor mode in which the value will be read
% -> DeviceMode.[...] (except for DeviceMode.Motor.[...])
mode;
%debug - Debug turned on or off
% In debug mode, everytime a command is passed to the sublayer ('communication layer'),
% there is feedback in the console about what command has been called, etc.
% -> Any valid boolean (0/1/'on'/'off'/'true'/'false')
debug;
end
properties (SetAccess = 'private')
%port - Sensor port
% This is only the string representation of the sensor port to work with.
% Internally, SensorPort-enums are used.
% -> '1' / '2' / '3' / '4'
port;
end
properties (Dependent) % Parameters to be read directly from physical brick
value; % Value read from sensor
type; % Physical sensor connected to this port?
end
properties (Hidden, Access = 'private') % Hidden properties for internal use only
commInterface = 0; % Communication interface
%% Miscallenous flags
connectedToBrick = false; % Does (virtual) sensor-object have a valid brick-handle?
init = true; % Indicates 'init-phase' (True as long as constructor is running)
end
properties (Hidden, Dependent, Access = 'private')
physicalSensorConnected; % Physical sensor connected to this port?
end
methods % Standard methods
%% Constructor
function sensor = Sensor(varargin)
sensor.setProperties(varargin{1:end});
sensor.init = false;
end
%% Brick functions
function reset(sensor)
%reset Resets value on sensor
% Note: This clears ALL the sensors right now, no other Op-Code available... :(
if ~sensor.connectedToBrick
error(['Sensor::reset: Sensor-Object not connected to comm handle.',...
'You have to call sensor.connect(commInterface) first!']);
elseif ~sensor.physicalSensorConnected
error('Sensor::reset: No physical sensor connected to Port %d.',...
sensor.port+1);
end
warning(['Sensor::reset: Current version of reset resets ALL devices, that is, ',...
'all motor tacho counts and all other sensor counters!']);
sensor.commInterface.inputDeviceClrAll(0);
if sensor.debug
fprintf('(DEBUG) Sensor::reset: Called inputReadSI on Port %d.\n',...
sensor.port+1);
end
end
%% Setter
function set.mode(sensor, mode)
if strcmp(class(mode),'DeviceMode.Default') && ~sensor.physicalSensorConnected
sensor.mode = mode;
return;
end
type = sensor.type;
if ~isModeValid(mode, type)
error('Sensor::set.mode: Invalid sensor mode.');
else
sensor.mode = mode;
if ~strcmp(class(mode),'DeviceMode.Default') && sensor.connectedToBrick
try
sensor.setMode(mode); % Update physical brick's mode parameter
catch
% Ignore
end
end
end
end
function set.debug(sensor, debug)
% Check if debug is valid and set sensor.debug if it is.
if ~isBool(debug)
error('Sensor::set.debug: Given parameter is not a bool.');
end
sensor.debug = str2bool(debug);
end
function set.port(sensor, port)
if ~isPortStrValid(class(sensor),port)
error('Sensor::set.port: Given port is not a valid port.');
else
sensor.port = str2PortParam(class(sensor), port);
end
end
function set.commInterface(sensor, comm)
if ~isCommInterfaceValid(comm)
error('Sensor::set.commInterface: Handle to commInterface not valid.');
else
sensor.commInterface = comm;
end
end
function setProperties(sensor, varargin)
%setProperties Sets multiple Sensor properties at once using MATLAB's inputParser.
%
% Arguments
% * 'debug', 0/1/'on'/'off'/'true'/'false'
% * 'mode', DeviceMode.[...]
%
% Example
% b = EV3();
% b.connect('bt', 'serPort', '/dev/rfcomm0');
% b.sensor1.setProperties('debug', 'on', 'mode', DeviceMode.Color.Ambient);
% % Instead of: b.sensor1.debug = 'on';
% % b.sensor1.mode = DeviceMode.Color.Ambient;
%
p = inputParser();
% Set default values
if sensor.init
defaultDebug = 0;
defaultMode = DeviceMode.Default.Undefined;
else
defaultDebug = sensor.debug;
defaultMode = sensor.mode;
end
% Add parameter
if sensor.init
p.addRequired('port');
end
p.addOptional('debug', defaultDebug);
p.addOptional('mode', defaultMode);
% Parse...
p.parse(varargin{:});
% Set properties
if sensor.init
sensor.port = p.Results.port;
end
sensor.mode = p.Results.mode;
sensor.debug = p.Results.debug;
end
%% Getter
function value = get.value(sensor)
value = 0;
if strcmp(class(sensor.mode), 'DeviceMode.Default') || ...
~isModeValid(sensor.mode, sensor.type)
warning('Sensor::get.value: Cannot read sensor values in current mode.');
return;
end
if sensor.connectedToBrick
try
value = sensor.getValue();
catch
warning('Sensor::get.value: Could not detect sensor at port %d.', ...
sensor.port+1);
return;
end
end
end
function conn = get.physicalSensorConnected(sensor)
currentType = sensor.type;
conn = (currentType<DeviceType.Unknown && ...
(currentType~=DeviceType.MediumMotor && currentType~=DeviceType.LargeMotor));
end
function sensorType = get.type(sensor)
if sensor.connectedToBrick
[sensorType, ~] = sensor.getTypeMode();
else
sensorType = DeviceType.Unknown;
end
end
%% Display
function display(sensor)
warning('off','all');
builtin('disp', sensor);
warning('on','all');
end
end
methods (Access = 'private') % Private brick functions that are wrapped by dependent params
function setMode(sensor, mode)
if ~sensor.connectedToBrick
error(['Sensor::getTachoCount: Sensor-Object not connected to comm handle.',...
'You have to call sensor.connect(commInterface) first!']);
elseif ~sensor.physicalSensorConnected
error('Sensor::getTachoCount: No physical sensor connected to Port %d',...
sensor.port+1);
end
sensor.commInterface.inputReadSI(0, sensor.port, mode); % Reading a value implicitly
% sets the mode.
if sensor.debug
fprintf('(DEBUG) Sensor::setMode: Called inputReadSI on Port %d.\n',...
sensor.port+1);
end
end
function val = getValue(sensor)
%getValue Reads value from sensor
%
% Notes
% * After changing the mode, sensors initially always send an invalid value. In
% this case, the inputReadSI-opCode is sent again to get the correct value.
%
if ~sensor.connectedToBrick
error(['Sensor::getValue: Sensor-Object not connected to comm handle.',...
'You have to call sensor.connect(commInterface) first!']);
% elseif ~sensor.physicalSensorConnected
% error('Sensor::getValue: No physical sensor connected to Port %d.',...
% sensor.port+1);
end
val = sensor.commInterface.inputReadSI(0, sensor.port, sensor.mode);
if strcmp(class(sensor.mode), 'DeviceMode.Color')
if sensor.mode == DeviceMode.Color.Col
val = Color(val);
end
end
if isnan(val)
pause(0.01);
val = sensor.commInterface.inputReadSI(0, sensor.port, sensor.mode);
if isnan(val)
warning('Sensor::getValue: Brick returned invalid value (NaN). Try again...');
end
end
if sensor.debug
fprintf('(DEBUG) Sensor::getValue: Called inputReadSI on Port %d.\n',...
sensor.port+1);
end
end
function status = getStatus(sensor)
if ~sensor.connectedToBrick
error(['Sensor::getStatus: Sensor-Object not connected to comm handle.',...
'You have to call sensor.connect(commInterface) first!']);
end
statusNo = sensor.commInterface.inputDeviceGetConnection(0, sensor.port);
status = ConnectionType(statusNo);
if sensor.debug
fprintf(['(DEBUG) Sensor::getStatus: Called inputDeviceGetConnection on ' ,...
'Port %d.\n'], sensor.port+1);
end
end
function [type,mode] = getTypeMode(sensor)
if ~sensor.connectedToBrick
error(['Sensor::getTypeMode: Sensor-Object not connected to comm handle.',...
'You have to call sensor.connect(commInterface) first!']);
end
[typeNo,modeNo] = sensor.commInterface.inputDeviceGetTypeMode(0, sensor.port);
type = DeviceType(typeNo);
try
mode = DeviceMode(type,modeNo);
catch ME
mode = DeviceMode.Default.Undefined;
end
if sensor.debug
fprintf(['(DEBUG) Sensor::getStatus: Called inputDeviceGetConnection on ' ,...
'Port %d.\n'], sensor.port+1);
end
end
end
methods (Access = {?EV3})
function connect(sensor,commInterface)
%connect Connects Sensor-object to physical brick
if sensor.connectedToBrick
if isCommInterfaceValid(sensor.commInterface)
error('Sensor::connect: Sensor-Object already has a comm handle.');
else
warning(['Sensor::connect: Sensor.connectedToBrick is set to ''True'', but ',...
'comm handle is invalid. Deleting invalid handle and ' ,...
'resetting Sensor.connectedToBrick now...']);
sensor.commInterface = 0;
sensor.connectedToBrick = false;
error('Sensor::connect: Disconnected due to internal error.');
end
end
sensor.commInterface = commInterface;
sensor.connectedToBrick = true;
if sensor.debug
fprintf('(DEBUG) Sensor-Object connected to comm handle.\n');
end
end
function disconnect(sensor)
%disconnect Disconnects Sensor-object from physical brick
sensor.commInterface = 0; % Note: actual deleting is done in EV3::disconnect.
sensor.connectedToBrick = false;
end
end
end