diff --git a/source/EV3.m b/source/EV3.m index e371dd66ab9b8d7cec88b2365f0b4fa55dd4a744..ec1a8c08dac5b71e7b49c2429afbd70ee6ca6dc4 100644 --- a/source/EV3.m +++ b/source/EV3.m @@ -108,9 +108,6 @@ classdef EV3 < handle properties (SetAccess = 'private') % Read-only properties that are set internally %isConnected - Virtual brick connected to physical one? isConnected = false; - - %commInterface - Interface to communication layer - commInterface = 0; %% Motors and Sensors @@ -125,6 +122,11 @@ classdef EV3 < handle sensor4; end + properties (Access = 'private') + %commInterface - Interface to communication layer + commInterface = 0; + end + properties (Hidden, Access = 'private') % Hidden properties for internal use only init = true; % Indicates 'init-phase' (Set to 1 as long as constructor is running) end diff --git a/source/Motor.m b/source/Motor.m index 35172e164be9aea536e951cab4cb6adc3cf1f48b..b46b1932741b7d9369fd2af1f59753f25d099f59 100644 --- a/source/Motor.m +++ b/source/Motor.m @@ -58,21 +58,21 @@ classdef Motor < handle & dynamicprops % -> Any valid boolean (0/1/'on'/'off'/true/false) speedRegulation; - %smoothStart - Degrees/Time for how far/long the motor should smoothly start (depends on tachoLimitMode) - % -> Any numeric in [0, tachoLimit] + %smoothStart - Degrees/Time for how far/long the motor should smoothly start (depends on limitMode) + % -> Any numeric in [0, limitValue] smoothStart; - %smoothStop - Degrees/Time for how far/long the motor should smoothly stop (depends on tachoLimitMode) - % -> Any numeric in [0, tachoLimit] + %smoothStop - Degrees/Time for how far/long the motor should smoothly stop (depends on limitMode) + % -> Any numeric in [0, limitValue] smoothStop; - %tachoLimit - Degrees/Time for how far/long the motor should run (depends on tachoLimitMode) - % -> Any numeric >= 0 (in ms, if tachoLimitMode = 'Time') - tachoLimit; + %limitValue- Degrees/Time for how far/long the motor should run (depends on limitMode) + % -> Any numeric >= 0 (in ms, if limitMode = 'Time') + limitValue; - %tachoLimitMode - Mode for motor limit + %limitMode - Mode for motor limit % -> 'Tacho' / 'Time' - tachoLimitMode; + limitMode; %brakeMode - Mode for braking % -> 'Brake' / 'Coast' @@ -126,7 +126,7 @@ classdef Motor < handle & dynamicprops % to the Brick right before starting it next time sendPowerOnSet = true; % Indicates whether power parameter should be sent to the Brick % immediately after setting it - limitSetToZero = false; % Indicates whether tachoLimit has been set to zero + limitSetToZero = false; % Indicates whether limitValue has been set to zero % (workaround for a bug, see motor.start, Note 2) init = true; % Indicates 'init-phase' (True as long as constructor is running) end @@ -172,6 +172,8 @@ classdef Motor < handle & dynamicprops % However, this does not even work all the time. If motor does not % start, call stop() and setPower() manually. :/ % + + % Check connection and if motor is already running if ~motor.connectedToBrick error(['Motor::start: Motor-Object not connected to comm handle.',... 'You have to call motor.connect(commInterface) first!']); @@ -182,15 +184,21 @@ classdef Motor < handle & dynamicprops error('Motor::start: Motor is already runnning!'); end + % If motor has been started synced with another, and it stopped 'itself' (when + % using a tacholimit), the sync cache has to be deleted (in general, syncedStop + % would do so) if motor.isSynced delete(motor.findprop('syncCache')); end + % Trigger warning if power = 0 (as it still sets the motor internally in a + % 'running' mode if motor.power == 0 warning('Motor::start: Motor starting with power=0.'); end - if motor.tachoLimit==0 + % Call right function in commInterface depending on limitValue and limitMode + if motor.limitValue==0 if motor.sendPowerOnNextStart if motor.limitSetToZero motor.stop(); @@ -205,10 +213,10 @@ classdef Motor < handle & dynamicprops fprintf('(DEBUG) Motor::start: Called outputStart on Port %s\n', port2str('Motor', motor.port)); end else - if strcmpi(motor.tachoLimitMode, 'Tacho') + if strcmpi(motor.limitMode, 'Tacho') if motor.speedRegulation motor.commInterface.outputStepSpeed(0, motor.port, motor.power,... - motor.smoothStart, motor.tachoLimit, motor.smoothStop,... + motor.smoothStart, motor.limitValue, motor.smoothStop,... motor.brakeMode_); if motor.debug @@ -217,7 +225,7 @@ classdef Motor < handle & dynamicprops end else motor.commInterface.outputStepPower(0, motor.port, motor.power,... - motor.smoothStart, motor.tachoLimit, motor.smoothStop,... + motor.smoothStart, motor.limitValue, motor.smoothStop,... motor.brakeMode_); if motor.debug @@ -225,10 +233,10 @@ classdef Motor < handle & dynamicprops port2str('Motor', motor.port)); end end - elseif strcmpi(motor.tachoLimitMode, 'Time') + elseif strcmpi(motor.limitMode, 'Time') if motor.speedRegulation motor.commInterface.outputTimeSpeed(0, motor.port, motor.power,... - motor.smoothStart, motor.tachoLimit, motor.smoothStop,... + motor.smoothStart, motor.limitValue, motor.smoothStop,... motor.brakeMode_); if motor.debug @@ -237,7 +245,7 @@ classdef Motor < handle & dynamicprops end else motor.commInterface.outputTimePower(0, motor.port, motor.power,... - motor.smoothStart, motor.tachoLimit, motor.smoothStop,... + motor.smoothStart, motor.limitValue, motor.smoothStop,... motor.brakeMode_); if motor.debug @@ -278,7 +286,7 @@ classdef Motor < handle & dynamicprops % brick. So, for example, changing the power on the master motor will take effect % on the slave as soon as this method is called. % The following parameters will be affected on the slave: power, brakeMode, - % tachoLimit, speedRegulation + % limitValue, speedRegulation % % Arguments % * syncMotor (the other motor object to sync with) @@ -366,22 +374,22 @@ classdef Motor < handle & dynamicprops % Keep 'slave'-motor synchronized syncMotor.speedRegulation = false; - syncMotor.tachoLimit = motor.tachoLimit; + syncMotor.limitValue= motor.limitValue; syncMotor.brakeMode = motor.brakeMode; syncMotor.power = motor.power; - if strcmpi(motor.tachoLimitMode, 'Tacho') + if strcmpi(motor.limitMode, 'Tacho') motor.commInterface.outputStepSync(0, motor.port+syncMotor.port, ... motor.power, turnRatio, ... - motor.tachoLimit, motor.brakeMode_); + motor.limitValue, motor.brakeMode_); if motor.debug fprintf(['(DEBUG) SyncMotor::syncedStart: Called outputStepSync on ' ,... 'Ports %s and %s.\n'], port2str('Motor', motor.port), port2str('Motor', syncMotor.port)); end - elseif strcmpi(motor.tachoLimitMode, 'Time') + elseif strcmpi(motor.limitMode, 'Time') motor.commInterface.outputTimeSync(0, motor.port+syncMotor.port, ... motor.power, turnRatio, ... - motor.tachoLimit, motor.brakeMode_); + motor.limitValue, motor.brakeMode_); if motor.debug fprintf('(DEBUG) SyncMotor::start: Called outputStepSync on Ports %s and %s.\n',... port2str('Motor', motor.port), port2str('Motor', syncMotor.port)); @@ -443,20 +451,21 @@ classdef Motor < handle & dynamicprops % outputReady returns in less than a second, another while-loop iterates until % the motor has stopped, this time using motor.isRunning() (this only works as % long as not both OutputTest and OutputReady are buggy). + % * Workaround: Poll isRunning (which itself return (speed>0)) until it is false + % -> No need to check if motor is connected as speed correctly + % returns 0 if it's not % if ~motor.connectedToBrick error(['Motor::waitFor: Motor-Object not connected to comm handle.',... 'You have to call motor.connect(commInterface) first!']); - elseif ~motor.physicalMotorConnected - error('Motor::waitFor: No physical motor connected to Port %s',... - port2str('Motor', motor.port)); end + pause(0.1); while motor.isRunning - pause(0.5); + pause(0.03); end -% elseif ~motor.tachoLimit +% elseif ~motor.limitValue % error(['Motor::waitFor: Motor has no tacho limit. ' ,... % 'Can''t reliably determine whether it is running or not.']); % end @@ -493,7 +502,9 @@ classdef Motor < handle & dynamicprops % motor is running with a tacho limit, internally it uses another counter than the % one read by tachoCount. This internal tacho count needs to be reset if you % physically change the motor's position. + % % See also MOTOR.RESETTACHOCOUNT + % if ~motor.connectedToBrick error(['Motor::reset: Motor-Object not connected to brick handle.',... @@ -516,7 +527,9 @@ classdef Motor < handle & dynamicprops % Compared to motor.reset, this resets the 'sensor mode' tacho count, a second % tacho counter. This counter is used for reading the tacho count with inputRead % and outputGetCount (via motor.tachoCount). + % % See also MOTOR.RESET + % if ~motor.connectedToBrick error(['Motor::resetTachoCount: Motor-Object not connected to comm handle.',... @@ -570,9 +583,9 @@ classdef Motor < handle & dynamicprops error('Motor::set.smoothStart: Smooth start steps are out of bounds.'); end - if ~isempty(motor.tachoLimit) && steps>motor.tachoLimit + if ~isempty(motor.limitValue) && steps>motor.limitValue warning(['Motor::set.smoothStart: Smooth start steps are greater than ',... - 'tachoLimit.']); + 'limitValue.']); end motor.smoothStart = steps; @@ -586,9 +599,9 @@ classdef Motor < handle & dynamicprops error('Motor::set.smoothStop: Smooth stop steps are out of bounds.'); end - if ~isempty(motor.tachoLimit) && steps>motor.tachoLimit + if ~isempty(motor.limitValue) && steps>motor.limitValue error(['Motor::set.smoothStop: Smooth stop steps are greater than ',... - 'tachoLimit.']); + 'limitValue.']); end motor.smoothStop = steps; @@ -604,48 +617,48 @@ classdef Motor < handle & dynamicprops end end - function set.tachoLimitMode(motor, tachoLimitMode) + function set.limitMode(motor, limitMode) validModes = {'Time', 'Tacho'}; - if ~ischar(tachoLimitMode) || ~ismember(tachoLimitMode, validModes) - error('Motor::set.tachoLimitMode: Given parameter is not a valid limit mode.'); + if ~ischar(limitMode) || ~ismember(limitMode, validModes) + error('Motor::set.limitMode: Given parameter is not a valid limit mode.'); else - motor.tachoLimitMode = tachoLimitMode; + motor.limitMode = limitMode; end end - function set.tachoLimit(motor, tachoLimit) - if ~isnumeric(tachoLimit) - error('Motor::set.tachoLimit: Given parameter is not a numeric.'); - elseif tachoLimit<0 - warning('Motor::set.tachoLimit: tachoLimit has to be positive!'); - error('Motor::set.tachoLimit: Given tachoLimit is out of bounds.'); - elseif any(motor.tachoLimit) - if tachoLimit==0 && motor.tachoLimit~=0 + function set.limitValue(motor, limitValue) + if ~isnumeric(limitValue) + error('Motor::set.limitValue: Given parameter is not a numeric.'); + elseif limitValue<0 + warning('Motor::set.limitValue: limitValue has to be positive!'); + error('Motor::set.limitValue: Given limitValue is out of bounds.'); + elseif any(motor.limitValue) + if limitValue==0 && motor.limitValue~=0 motor.sendPowerOnNextStart = true; motor.limitSetToZero = true; motor.sendPowerOnSet = true; - elseif tachoLimit~=0 && motor.tachoLimit==0 + elseif limitValue~=0 && motor.limitValue==0 motor.sendPowerOnSet = false; end end - motor.tachoLimit = tachoLimit; + motor.limitValue= limitValue; end function set.port(motor, port) - if ~isPortStrValid(class(motor), port) - error('Motor::set.port: Given parameter is not a valid port string.'); - else + try motor.port = str2PortParam(class(motor), port); + catch ME + error('Motor::set.port: Given parameter is not valid port string.'); end end function set.commInterface(motor, comm) if ~isCommInterfaceValid(comm) error('Motor::set.commInterface: Handle to commInterface not valid.'); - else - motor.commInterface = comm; end + + motor.commInterface = comm; end function set.debug(motor, debug) @@ -661,22 +674,22 @@ classdef Motor < handle & dynamicprops % % Arguments % * 'debug', bool - % * 'smoothStart', numeric in [0, tachoLimit] - % * 'smoothStop', numeric in [0, tachoLimit] + % * 'smoothStart', numeric in [0, limitValue] + % * 'smoothStop', numeric in [0, limitValue] % * 'speedRegulation', bool % * 'brakeMode', 'Coast'/'Brake' - % * 'tachoLimitMode', 'Time'/'Tacho' - % * 'limit', numeric > 0 + % * 'limitMode', 'Time'/'Tacho' + % * 'limitValue', numeric > 0 % * 'power', numeric in [-100,100] % * 'batteryMode', 'Voltage'/'Percentage' % % Example % b = EV3(); % b.connect('bt', 'serPort', '/dev/rfcomm0'); - % b.motorA.setProperties('debug', 'on', 'power', 50, 'limit', 720, 'speedRegulation', 'on'); + % b.motorA.setProperties('debug', 'on', 'power', 50, 'limitValue', 720, 'speedRegulation', 'on'); % % Instead of: b.motorA.debug = 'on'; % % b.motorA.power = 50; - % % b.motorA.limit = 720; + % % b.motorA.limitValue = 720; % % b.motorA.speedRegulation = 'on'; % p = inputParser(); @@ -696,8 +709,8 @@ classdef Motor < handle & dynamicprops defaultDebug = motor.debug; defaultSpeedReg = motor.speedRegulation; defaultBrakeMode = motor.brakeMode; - defaultLimitMode = motor.tachoLimitMode; - defaultLimit = motor.tachoLimit; + defaultLimitMode = motor.limitMode; + defaultLimit = motor.limitValue; defaultPower = motor.power; defaultSmoothStart = motor.smoothStart; defaultSmoothStop = motor.smoothStop; @@ -710,8 +723,8 @@ classdef Motor < handle & dynamicprops p.addOptional('debug', defaultDebug); p.addOptional('speedRegulation', defaultSpeedReg); p.addOptional('brakeMode', defaultBrakeMode) - p.addOptional('tachoLimitMode', defaultLimitMode); - p.addOptional('tachoLimit', defaultLimit); + p.addOptional('limitMode', defaultLimitMode); + p.addOptional('limitValue', defaultLimit); p.addOptional('power', defaultPower); p.addOptional('smoothStart', defaultSmoothStart); p.addOptional('smoothStop', defaultSmoothStop); @@ -723,14 +736,17 @@ classdef Motor < handle & dynamicprops if motor.init motor.port = p.Results.port; end - motor.tachoLimit = p.Results.tachoLimit; - motor.tachoLimitMode = p.Results.tachoLimitMode; - motor.power = p.Results.power; + motor.limitValue= p.Results.limitValue; + motor.limitMode = p.Results.limitMode; motor.brakeMode = p.Results.brakeMode; motor.debug = p.Results.debug; motor.speedRegulation = p.Results.speedRegulation; motor.smoothStart = p.Results.smoothStart; motor.smoothStop = p.Results.smoothStop; + + motor.sendPowerOnSet = false; + motor.power = p.Results.power; + motor.sendPowerOnSet = true; end %% Getter @@ -798,32 +814,6 @@ classdef Motor < handle & dynamicprops end methods (Access = 'private') % Private brick functions that are wrapped by dependent params - function running = getMotorStatus(motor) - %isRunning Returns whether motor is running (WITH TACHOLIMIT) or not. - % - % Notes: - % * This *mostly* works. Sometimes this falsely returns 0 if isRunning() gets - % called immediately after starting the motor. - % - - if ~motor.connectedToBrick - error(['Motor::isRunning: Motor-Object not connected to comm handle.',... - 'You have to call motor.connect(commInterface) first!']); -% elseif ~motor.physicalMotorConnected -% error('Motor::isRunning: No physical motor connected to Port %s',... -% port2str('Motor', motor.port)); - elseif ~motor.tachoLimit - warning(['Motor::isRunning: Motor has no tacho limit. ' ,... - 'Can''t reliably determine whether it is running or not.']); - end - - running = motor.commInterface.outputTest(0, motor.port); - - if motor.debug - fprintf('(DEBUG) Motor::isRunning: Called outputReady on Port %s\n', port2str('Motor', motor.port)); - end - end - function setPower(motor, power) %setPower Sets given power value on the physical Brick. % @@ -836,45 +826,41 @@ classdef Motor < handle & dynamicprops % if ~motor.connectedToBrick error(['Motor::getTachoCount: Motor-Object not connected to comm handle.',... - 'You have to call motor.connect(commInterface) first!']); -% elseif ~motor.physicalMotorConnected -% error('Motor::getTachoCount: No physical motor connected to Port %s',... -% port2str('Motor', motor.port)); + 'You have to call motor.connect(commInterface) first!']); + elseif ~motor.physicalMotorConnected + error('Motor::getTachoCount: No physical motor connected to Port %s',... + port2str('Motor', motor.port)); end - if motor.tachoLimit~=0 && motor.isRunning - warning(['Motor::setPower: Can''t set power if motor is running with a ',... - 'tacholimit. This would mess up the internal tacho state.']); - return; + assert(motor.limitValue==0); + + if motor.speedRegulation + motor.commInterface.outputSpeed(0, motor.port, power); + + if motor.debug + fprintf('(DEBUG) Motor::setPower: Called outputSpeed on Port %s\n', port2str('Motor', motor.port)); + end else - if motor.speedRegulation - motor.commInterface.outputSpeed(0, motor.port, power); - - if motor.debug - fprintf('(DEBUG) Motor::setPower: Called outputSpeed on Port %s\n', port2str('Motor', motor.port)); - end - else - motor.commInterface.outputPower(0, motor.port, power); - - if motor.debug - fprintf('(DEBUG) Motor::setPower: Called outputPower on Port %s\n', port2str('Motor', motor.port)); - end + motor.commInterface.outputPower(0, motor.port, power); + + if motor.debug + fprintf('(DEBUG) Motor::setPower: Called outputPower on Port %s\n', port2str('Motor', motor.port)); end end motor.sendPowerOnNextStart = false; end - function setMode(motor, mode) + function setMode(motor, mode) %% DEPRECATED if ~motor.connectedToBrick error(['Motor::getTachoCount: Motor-Object not connected to comm handle.',... 'You have to call motor.connect(commInterface) first!']); -% elseif ~motor.physicalMotorConnected -% error('Motor::getTachoCount: No physical motor connected to Port %s',... -% port2str('Motor', motor.port)); + elseif ~motor.physicalMotorConnected + error('Motor::getTachoCount: No physical motor connected to Port %s',... + port2str('Motor', motor.port)); end motor.commInterface.inputReadSI(0, motor.portInput, mode); % Reading a value implicitly - % sets the mode. + % sets the mode. if motor.debug fprintf('(DEBUG) Motor::setMode: Called inputReadSI on Port %s\n',... @@ -917,9 +903,6 @@ classdef Motor < handle & dynamicprops if ~motor.connectedToBrick error(['Motor::getTachoCount: Motor-Object not connected to comm handle.',... 'You have to call motor.connect(commInterface) first!']); -% elseif ~motor.physicalMotorConnected -% error('Motor::getTachoCount: No physical motor connected to Port %s',... -% port2str('Motor', motor.port)); end cnt = motor.commInterface.outputGetCount(0, motor.portNo); @@ -932,9 +915,6 @@ classdef Motor < handle & dynamicprops if ~motor.connectedToBrick error(['Motor::getSpeed: Motor-Object not connected to comm handle.',... 'You have to call motor.connect(commInterface) first!']); -% elseif ~motor.physicalMotorConnected -% error('Motor::getSpeed: No physical motor connected to Port %s',... -% port2str('Motor', motor.port)); end speed = motor.commInterface.inputReadSI(0, motor.portInput, DeviceMode.Motor.Speed); @@ -943,6 +923,29 @@ classdef Motor < handle & dynamicprops fprintf('(DEBUG) Motor::getSpeed: Called inputReadSI on Port %s\n', port2str('Motor', motor.port)); end end + + function running = getMotorStatus(motor) + %getMotorStatus Returns whether motor is running (WITH TACHOLIMIT) or not. + % + % Notes: + % * This *mostly* works. Sometimes this falsely returns 0 if isRunning() gets + % called immediately after starting the motor. + % + + if ~motor.connectedToBrick + error(['Motor::isRunning: Motor-Object not connected to comm handle.',... + 'You have to call motor.connect(commInterface) first!']); + elseif ~motor.tachoLimit + warning(['Motor::isRunning: Motor has no tacho limit. ' ,... + 'Can''t reliably determine whether it is running or not.']); + end + + running = motor.commInterface.outputTest(0, motor.port); + + if motor.debug + fprintf('(DEBUG) Motor::isRunning: Called outputReady on Port %s\n', port2str('Motor', motor.port)); + end + end end methods (Access = {?EV3}) diff --git a/source/Sensor.m b/source/Sensor.m index 33a531bebdb5c3cbb2fb79c359ab6ecb9631cd05..7861450044ff3f4eb1c44b7df6d185d416bf7c80 100644 --- a/source/Sensor.m +++ b/source/Sensor.m @@ -115,7 +115,7 @@ classdef Sensor < handle type = sensor.type; if ~isModeValid(mode, type) - error('Sensor::set.mode: Sensor mode is not valid for connected sensor type.'); + error('Sensor::set.mode: Invalid sensor mode.'); else sensor.mode = mode; @@ -245,9 +245,9 @@ classdef Sensor < handle 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); + 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