Skip to content
Snippets Groups Projects
Commit c0b7eca9 authored by Tim Stadtmann's avatar Tim Stadtmann
Browse files

Merge branch 'develop'

parents 38cfc40b eb79f263
Branches
Tags v0.3-rc.4
No related merge requests found
......@@ -198,6 +198,9 @@ classdef EV3 < MaskedHandle
% b.disconnect();
%
% Reset motors and sensors before disconnecting
ev3.resetPhysicalBrick();
% Disconnect motors and sensors
% -> set references to comm handle to 0
ev3.motorA.disconnect();
......@@ -480,5 +483,20 @@ classdef EV3 < MaskedHandle
end
end
end
function resetPhysicalBrick(ev3)
sensors = {'sensor1', 'sensor2', 'sensor3', 'sensor4'};
motors = {'motorA', 'motorB', 'motorC', 'motorD'};
for i = 1:4
motor = motors{i};
ev3.(motor).resetPhysicalMotor();
end
for i = 1:4
sensor = sensors{i};
ev3.(sensor).resetPhysicalSensor();
end
end
end
end
......@@ -95,16 +95,9 @@ classdef Motor < MaskedHandle & dynamicprops
brakeMode_;
%% Flags
connectedToBrick = false; % Connection to physical Brick?
init = true; % Indicates 'init-phase' (True as long as constructor is running)
sendPowerOnSet = false; % If true, OUTPUT_POWER is sent when setting power
% Bitfield representing which opCodes should be sent on Motor.start()
% The corresponding opCodes for each bit are (in Big Endian):
% * 1st Bit: OUTPUT_POWER (sets power on physical Brick)
% * 2nd Bit: OUTPUT_STOP (stops Brick; workaround for a bug, see motor.start, Note 2)
sendOnStart = 0;
connectedToBrick = false; % Connection to physical Brick?
state = MotorState(); % State consisting of several special Motor-flags
end
properties (Hidden, Dependent, Access = 'private') % Hidden, dependent properties for internal use only
......@@ -162,35 +155,59 @@ classdef Motor < MaskedHandle & dynamicprops
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
% using a tacholimit), the sync cache has to be deleted (otherwise, syncedStop
% would do so)
if motor.isSynced
delete(motor.findprop('syncCache'));
motor.internalReset(); % Better safe than sorry
% Retrieve and delete former slave
if length(findprop(motor, 'slave'))==1
syncMotor = motor.slave;
delete(motor.findprop('slave'));
delete(syncMotor.findprop('master'));
else
syncMotor = motor.master;
delete(motor.findprop('master'));
delete(syncMotor.findprop('slave'));
end
% Reset state
motor.applyState();
syncMotor.applyState();
% Send power on next set
if motor.state.sendPowerOnSet
motor.state.sendOnStart = bitset(motor.state.sendOnStart, SendOnStart.Power, 1);
end
if syncMotor.state.sendPowerOnSet
syncMotor.state.sendOnStart = bitset(syncMotor.state.sendOnStart, SendOnStart.Power, 1);
end
% Better safe than sorry
motor.internalReset();
syncMotor.internalReset();
end
% If the motor coasts into its stops, the internal tachocount has to be reset
% before each start for it to behave predictable
if motor.brakeMode_ == BrakeMode.Coast
if motor.brakeMode_ == BrakeMode.Coast || motor.internalTachoCount ~= 0
motor.internalReset();
end
% Call appropriate function in commInterface depending on limitValue and limitMode
if motor.limitValue==0
if motor.sendOnStart > 0
if motor.state.sendOnStart > 0
% If stop-flag is set: call stop() and reset flag
if bitget(motor.sendOnStart, SendOnStart.Stop)
if bitget(motor.state.sendOnStart, SendOnStart.Stop)
motor.stop();
motor.sendOnStart = bitset(motor.sendOnStart, SendOnStart.Stop, 0);
motor.state.sendOnStart = bitset(motor.state.sendOnStart, SendOnStart.Stop, 0);
end
% If power-flag is set: call setPower() and reset flag if successful
if bitget(motor.sendOnStart, SendOnStart.Power)
if bitget(motor.state.sendOnStart, SendOnStart.Power)
success = motor.setPower(motor.power);
if ~success
motor.sendOnStart = bitset(motor.sendOnStart, SendOnStart.Power, 1);
motor.state.sendOnStart = bitset(motor.state.sendOnStart, SendOnStart.Power, 1);
else
motor.sendOnStart = bitset(motor.sendOnStart, SendOnStart.Power, 0);
motor.state.sendOnStart = bitset(motor.state.sendOnStart, SendOnStart.Power, 0);
end
end
......@@ -201,6 +218,7 @@ classdef Motor < MaskedHandle & dynamicprops
if motor.debug
fprintf('(DEBUG) Motor::start: Called outputStart on Port %s\n', port2str('Motor', motor.port));
end
motor.state.startedNotBusy = true;
else
limit = motor.limitValue - (motor.smoothStart + motor.smoothStop);
if limit < 0
......@@ -270,6 +288,8 @@ classdef Motor < MaskedHandle & dynamicprops
if motor.debug
fprintf('(DEBUG) Motor::stop: Called outputStop on Port %s\n', port2str('Motor', motor.port));
end
motor.state.startedNotBusy = false;
end
function syncedStart(motor, syncMotor, varargin)
......@@ -342,42 +362,37 @@ classdef Motor < MaskedHandle & dynamicprops
error('Motor::syncedStart: One of the motors is already running!');
end
if motor.power == 0
warning('Motor::syncedStart: Synchronized motors starting with power=0.');
end
% if motor.power == 0
% warning('Motor::syncedStart: Synchronized motors starting with power=0.');
% end
% If the motor coasts into its stops, the internal tachocount has to be reset
% before each start for it to behave predictable
if motor.brakeMode_ == BrakeMode.Coast
if motor.brakeMode_ == BrakeMode.Coast || motor.internalTachoCount ~= 0
motor.internalReset();
syncMotor.internalReset();
end
if motor.sendOnStart > 0
if motor.state.sendOnStart > 0
% If stop-flag is set: call stop() and reset flag
if bitget(motor.sendOnStart, SendOnStart.Stop)
if bitget(motor.state.sendOnStart, SendOnStart.Stop)
motor.stop();
motor.sendOnStart = bitset(motor.sendOnStart, SendOnStart.Stop, 0);
motor.state.sendOnStart = bitset(motor.state.sendOnStart, SendOnStart.Stop, 0);
end
end
% Cache old values to make it possible to reset them on stopSynced
% Note: the existence of 'syncCache' is also used to determine whether motor is
% Cache old values to make it possible to reset them on syncedStop
% Note: the existence of 'slave' is also used to determine whether motor is
% running synchronized or not, see get.isSynced()
if motor.isSynced
meta = motor.findprop('syncCache');
else
meta = motor.addprop('syncCache');
meta.Hidden = true;
meta.Access = 'private';
end
motor.syncCache.master_oldSendPowerOnSet = motor.sendPowerOnSet;
motor.syncCache.slave_oldSendPowerOnSet = syncMotor.sendPowerOnSet;
motor.syncCache.slave = syncMotor;
motor.addProperty(syncMotor, 'slave', true);
syncMotor.addProperty(motor, 'master', true);
motor.saveState();
syncMotor.saveState();
% Disable immediate sending of new power values
motor.sendPowerOnSet = false;
syncMotor.sendPowerOnSet = false;
motor.state.sendPowerOnSet = false;
syncMotor.state.sendPowerOnSet = false;
% Keep 'slave'-motor synchronized
syncMotor.speedRegulation = false;
......@@ -413,11 +428,19 @@ classdef Motor < MaskedHandle & dynamicprops
if ~motor.isSynced
error('Motor::syncedStop: Motor has not been started synchronized with another.');
else
% Retrieve synced motor from cache
if length(findprop(motor, 'slave'))==1
syncMotor = motor.slave;
delete(motor.findprop('slave'));
delete(syncMotor.findprop('master'));
else
syncMotor = motor.master;
delete(motor.findprop('master'));
delete(syncMotor.findprop('slave'));
end
end
% Retrieve synced motor from cache
syncMotor = motor.syncCache.slave;
if ~motor.connectedToBrick || ~syncMotor.connectedToBrick
error('Motor::syncedStop: Motor-Object not connected to comm handle.');
elseif ~motor.physicalMotorConnected || ~syncMotor.physicalMotorConnected
......@@ -425,21 +448,19 @@ classdef Motor < MaskedHandle & dynamicprops
port2str('Motor', motor.port), port2str('Motor', syncMotor.port));
end
% Retrieve other values from cache and delete it
motor.sendPowerOnSet = motor.syncCache.master_oldSendPowerOnSet;
%syncMotor.sendPowerOnSet = motor.syncCache.slave_oldSendPowerOnSet;
syncMotor.sendPowerOnSet = motor.syncCache.master_oldSendPowerOnSet;
delete(motor.findprop('syncCache'));
% Reset state
motor.applyState();
syncMotor.applyState();
% Synced stopping
motor.commInterface.outputStop(0, motor.port+syncMotor.port, motor.brakeMode_);
% On next start, both motors have to send power-opcode again
if motor.sendPowerOnSet
motor.sendOnStart = bitset(motor.sendOnStart, SendOnStart.Power, 1);
if motor.state.sendPowerOnSet
motor.state.sendOnStart = bitset(motor.state.sendOnStart, SendOnStart.Power, 1);
end
if syncMotor.sendPowerOnSet
syncMotor.sendOnStart = bitset(syncMotor.sendOnStart, SendOnStart.Power, 1);
if syncMotor.state.sendPowerOnSet
syncMotor.state.sendOnStart = bitset(syncMotor.state.sendOnStart, SendOnStart.Power, 1);
end
......@@ -572,7 +593,7 @@ classdef Motor < MaskedHandle & dynamicprops
motor.releaseBrake();
end
motor.sendOnStart = SendOnStart.Power + SendOnStart.Stop;
motor.state.sendOnStart = SendOnStart.Power + SendOnStart.Stop;
end
%% Setter
......@@ -586,12 +607,12 @@ classdef Motor < MaskedHandle & dynamicprops
motor.power = power; % Set power parameter.
if motor.sendPowerOnSet
if motor.state.sendPowerOnSet
success = motor.setPower(power);
if ~success
motor.sendOnStart = bitset(motor.sendOnStart, SendOnStart.Power, 1);
motor.state.sendOnStart = bitset(motor.state.sendOnStart, SendOnStart.Power, 1);
else
motor.sendOnStart = bitset(motor.sendOnStart, SendOnStart.Power, 0);
motor.state.sendOnStart = bitset(motor.state.sendOnStart, SendOnStart.Power, 0);
end
end
end
......@@ -610,8 +631,8 @@ classdef Motor < MaskedHandle & dynamicprops
speedRegulation = str2bool(speedRegulation);
if ~isempty(motor.speedRegulation) && (speedRegulation ~= motor.speedRegulation)
if motor.sendPowerOnSet
motor.sendOnStart = bitset(motor.sendOnStart, SendOnStart.Power, 1);
if motor.state.sendPowerOnSet
motor.state.sendOnStart = bitset(motor.state.sendOnStart, SendOnStart.Power, 1);
end
end
......@@ -660,12 +681,12 @@ classdef Motor < MaskedHandle & dynamicprops
end
function set.limitMode(motor, limitMode)
validModes = {'Time', 'Tacho'};
if ~ischar(limitMode) || ~ismember(limitMode, validModes)
if ~ischar(limitMode) || ...
(~strcmpi(limitMode, 'tacho') && ~strcmpi(limitMode, 'time'))
error('Motor::set.limitMode: Given parameter is not a valid limit mode.');
else
motor.limitMode = limitMode;
end
end
motor.limitMode = limitMode;
end
function set.limitValue(motor, limitValue)
......@@ -677,14 +698,14 @@ classdef Motor < MaskedHandle & dynamicprops
end
if limitValue == 0
motor.sendOnStart = SendOnStart.Power;
motor.state.sendOnStart = SendOnStart.Power;
if ~isempty(motor.limitValue) && motor.limitValue > 0
motor.sendOnStart = motor.sendOnStart + SendOnStart.Stop;
motor.state.sendOnStart = motor.state.sendOnStart + SendOnStart.Stop;
end
motor.sendPowerOnSet = true;
motor.state.sendPowerOnSet = true;
else
motor.sendOnStart = 0;
motor.sendPowerOnSet = false;
motor.state.sendOnStart = 0;
motor.state.sendPowerOnSet = false;
end
motor.limitValue= limitValue;
......@@ -789,10 +810,6 @@ classdef Motor < MaskedHandle & dynamicprops
motor.speedRegulation = p.Results.speedRegulation;
motor.smoothStart = p.Results.smoothStart;
motor.smoothStop = p.Results.smoothStop;
% if motor.limitValue == 0
% motor.sendPowerOnSet = true;
% end
end
%% Getter
......@@ -842,13 +859,20 @@ classdef Motor < MaskedHandle & dynamicprops
function running = get.isRunning(motor)
running = 0;
if motor.connectedToBrick
running = motor.getBusyFlag();
busyFlag = motor.getBusyFlag();
else
busyFlag = 0;
end
assert(~(motor.state.startedNotBusy && busyFlag));
running = motor.state.startedNotBusy || busyFlag;
end
function synced = get.isSynced(motor)
synced = (length(findprop(motor, 'syncCache'))==1);
synced = (length(findprop(motor, 'slave'))==1 || ...
length(findprop(motor, 'master'))==1);
end
function motorType = get.type(motor)
......@@ -1053,6 +1077,47 @@ classdef Motor < MaskedHandle & dynamicprops
end
end
methods (Access = 'private', Hidden = true)
function saveState(motor)
%saveState Saves current motor state in dynamic property
meta = motor.findprop('savedState');
if isempty(meta)
meta = motor.addprop('savedState');
meta.Hidden = true;
meta.Access = 'private';
end
motor.savedState = motor.state;
end
function applyState(motor)
%applyState Sets motor state to saved state and deletes the dynamic property in
%which the latter is stored
assert(length(motor.findprop('savedState')) ~= 0);
motor.state = motor.savedState;
delete(motor.findprop('savedState'))
end
function addProperty(motor, propValue, propName, override)
override = str2bool(override);
meta = motor.findprop(propName);
if isempty(meta)
meta = motor.addprop(propName);
meta.Hidden = true;
meta.Access = 'private';
elseif ~override
error('Motor::addProperty: Motor already has this property.');
end
motor.(propName) = propValue;
end
end
methods (Access = {?EV3})
function connect(motor,commInterface)
%connect Connects Motor-object to physical brick.
......@@ -1112,5 +1177,16 @@ classdef Motor < MaskedHandle & dynamicprops
motor.commInterface = 0; % Note: actual deleting is done in EV3::disconnect.
motor.connectedToBrick = false;
end
function resetPhysicalMotor(motor)
if ~motor.connectedToBrick || ~motor.physicalMotorConnected
return;
end
motor.resetTachoCount();
motor.internalReset();
motor.setBrake(0);
%motor.stop();
end
end
end
classdef MotorState
properties
startedNotBusy = false; % Set to true if motor started w/o tacholimit and unsynced
sendPowerOnSet = false; % If true, OUTPUT_POWER is sent when setting power
% Bitfield representing which opCodes should be sent on Motor.start()
% The corresponding opCodes for each bit are (in Big Endian):
% * 1st Bit: OUTPUT_POWER (sets power on physical Brick)
% * 2nd Bit: OUTPUT_STOP (stops Brick; workaround for a bug, see motor.start, Note 2)
sendOnStart = 0;
end
methods
function display(state)
fprintf('#### Motor State ####\n');
props = properties(state);
for i = 1:length(props)
p = props{i};
fprintf('%16s: %d\n', p, state.(p));
end
fprintf('#####################\n');
end
function isequal = eq(state1, state2)
props = properties(state1);
isequal = 1;
for i = 1:length(props)
p = props{i};
if state1.(p) ~= state2.(p)
isequal = 0;
break;
end
end
end
end
end
......@@ -357,6 +357,15 @@ classdef Sensor < MaskedHandle
sensor.commInterface = 0; % Note: actual deleting is done in EV3::disconnect.
sensor.connectedToBrick = false;
end
function resetPhysicalSensor(sensor)
if ~sensor.connectedToBrick || ~sensor.physicalSensorConnected
return
end
sensor.mode = DeviceMode(sensor.type, uint8(0));
sensor.reset;
end
end
end
function [] = connectWires()
%Connect motors to ports
disp(sprintf('Please connect middle motor to port A, large motors to port B and C. Then press any key to continue.\n\n'));
pause();
%Connect sensors to ports
disp(sprintf('Please connect touch sensor to port 1, gyro sensor to port 2, color sensor to port 3,\nultrasonic sensor to port 4. Then press any key to continue.\n\n'));
pause();
end
function ret = motorTest(EV3, mode)
ret = 0;
%Start test
disp(sprintf(['--------------------------------------------------------------\nStart motor test ', mode]));
%Test motor A
EV3.motorA.power=20;
EV3.motorA.limitValue=1000;
try EV3.motorA.start
catch all
end;
answerA = input('Did motor A move? [y/n]', 's');
if ~strcmp(answerA,'y') && ~strcmp(answerA,'n')
while true
answerA = input('Incorrect input. Did motor A move? [y/n]', 's');
if strcmp(answerA,'y') || strcmp(answerA,'n')
break;
end
end
end
%Test motor B
EV3.motorB.power=20;
EV3.motorB.limitValue=1000;
try EV3.motorB.start
catch all
end;
answerB = input('Did motor B move? [y/n]', 's');
if ~strcmp(answerB,'y') && ~strcmp(answerB,'n')
while true
answerB = input('Incorrect input. Did motor B move? [y/n]', 's');
if strcmp(answerB,'y') || strcmp(answerB,'n')
break;
end
end
end
%Test motor C
EV3.motorC.power=20;
EV3.motorC.limitValue=1000;
try EV3.motorC.start
catch all
end;
answerC = input('Did motor C move? [y/n]', 's');
if ~strcmp(answerC,'y') && ~strcmp(answerC,'n')
while true
answerC = input('Incorrect input. Did motor C move? [y/n]', 's');
if strcmp(answerC,'y') || strcmp(answerC,'n')
break;
end
end
end
%Warnings for defect motors
if (~isempty(strfind(answerA, 'n')))
warning('Test for motor A failed');
end;
if (~isempty(strfind(answerB, 'n')))
warning('Test for motor B failed');
end;
if (~isempty(strfind(answerC, 'n')))
warning('Test for motor C failed');
end;
if (~isempty(strfind(answerA, 'y'))) && (~isempty(strfind(answerB, 'y'))) && (~isempty(strfind(answerC, 'y')))
disp(sprintf('All motors work correctly.\n'));
ret = 1;
end;
end
function ret = sensorTest(EV3, mode)
ret = 0;
%Start test
disp(sprintf(['--------------------------------------------------------------\nStart sensor test ', mode]));
%Test sensor 1
if EV3.sensor1.type == DeviceType.Touch
answer1 = 1;
else
answer1 = 0;
end;
%Test sensor 2
if EV3.sensor2.type == DeviceType.Gyro
answer2 = 1;
else
answer2 = 0;
end;
%Test sensor 3
if EV3.sensor3.type == DeviceType.Color
answer3 = 1;
else
answer3 = 0;
end;
%Test sensor 4
if EV3.sensor4.type == DeviceType.UltraSonic
answer4 = 1;
else
answer4 = 0;
end;
%Warnings for defect motors
if answer1 == 0
warning('Test for sensor 1 failed. Expected touch sensor.');
end;
if answer2 == 0
warning('Test for sensor 2 failed. Expected gyro sensor.');
end;
if answer3 == 0
warning('Test for sensor 3 failed. Expected color sensor.');
end;
if answer4 == 0
warning('Test for sensor 4 failed. Expected ultrasonic sensor');
end;
if answer1==1 && answer2==1 && answer3==1 && answer4==1
disp(sprintf('All sensors work correctly.\n'));
ret = 1;
end;
end
function [] = testAllMotorsAndSensors()
% Connect the Brick via USB
clear all;
b=EV3;
connected = 0;
i=0;
while connected == 0
disp(sprintf('Please connect the brick via USB and press any key to continue.\n\n'));
pause();
try b.connect('usb')
catch disp('Connection failed, try again...')
%discard errors
end;
connected = b.isConnected();
i=i+1;
if i==3
error('USB connection failed. You can not continue the test. Restart Matlab and the brick, then try again. If the error occurs again, please contact us.');
end;
end;
% Connect motors and sensors to ports
connectWires();
% Test motors (USB)
USBresultMotor = motorTest(b, 'USB');
if USBresultMotor == 0
warning('The USB motor test failed. Please contact us.');
end;
% Test sensors (USB)
USBresultSensor = sensorTest(b, 'USB');
if USBresultSensor == 0
warning('The USB sensor test failed. Please contact us.');
end;
if (USBresultMotor == 1) && (USBresultSensor == 1)
disp(sprintf('\n<strong>--------------All USB tests passed successfully---------------</strong>\n'));
else
disp(sprintf('\n<strong>-----End of USB tests. Some tests failed, notice the warnings.-----</strong>\n'));
end;
b.disconnect;
pause(1);
%Connect the Brick via BT
b=EV3;
connected = 0;
i=0;
while connected==0
disp(sprintf('Please remove the USB wire and use btconnect in the terminal, so that the brick can be\nautomatically connected via BT and press any key to continue.\n\n'));
pause();
try b.connect('bt', 'serPort', '/dev/rfcomm0')
catch all
end;
try b.connect('bt', 'serPort', '/dev/rfcomm1')
catch all
end;
pause(1);
connected = b.isConnected();
i=i+1;
if i==3
error('BT connection failed. You can not continue the test. Did you connect the brick with btconnect? If no, start btconnect in the terminal. If yes, restart Matlab and try again. If the error occurs again, please contact us.');
end;
end;
%Test motors (BT)
BTresultMotor = motorTest(b, 'BT');
if BTresultMotor == 0
warning('The BT motor test failed. Please contact us.');
end;
%Test sensors (BT)
BTresultSensor = sensorTest(b, 'BT');
if BTresultSensor == 0
warning('The BT sensor test failed. Please contact us.');
end;
if (BTresultMotor == 1) && (BTresultSensor == 1)
disp(sprintf('\n<strong>---------------All BT tests passed successfully---------------</strong>\n'));
else
disp(sprintf('\n<strong>-----End of BT tests. Some tests failed, notice the warnings.-----</strong>\n'));
end;
disp(sprintf('\n------------------------End of the test------------------------\n'));
clear all;
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment