Select Git revision
greyscale.cpp
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