Skip to content
Snippets Groups Projects
Select Git revision
  • master default protected
  • dev_2022
  • patch-1
  • develop
  • 50-use-ubuntus-libhidapi
  • issue-highLevelDispatch
  • issue-highLevelDesign
  • issue-motorStartBug
  • issue-commandLayerDesign
  • v1.0
  • v0.4-rc.13
  • v0.4-rc.12
  • v0.4-rc.11
  • v0.4-rc.10
  • v0.4-rc.9
  • v0.3-rc.8
  • v0.3-rc.7
  • v0.3-rc.6
  • v0.3-rc.5
  • v0.3-rc.4
  • v0.3-rc.3
  • v0.3-rc.2
  • v0.3-rc.1
  • v0.3-rc
  • v0.2
  • v0.1.1
  • v0.1
27 results

EV3.m

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    EV3.m 17.81 KiB
    classdef EV3 < MaskedHandle
        % High-level class to work with physical bricks.
        %
        % This is the 'central' class (from user's view) when working with this toolbox. It
        % delivers a convenient interface for creating a connection to the brick and sending
        % commands to it. 
        %
        % Notes
        %  * Creating multiple EV3 objects and connecting them to different physical bricks has not
        %    been thoroughly tested yet, but seems to work on a first glance.
        %  * When referring to an instance of this class the term 'virtual brick' is used from now
        %    on. The LEGO EV3 brick itself is referred to as 'physical brick'.
        %
        % Signature
        %  Author: Tim Stadtmann
        %  Date: 2016/05/19
        %  Updated: 2016/08/15
        
        properties  % Standard properties 
            %debug -  [Writable] Debug turned on or off (0/1/off/on/'false'/'true'/2)
            % In debug mode 1 (debug=1/'on'/true), everytime a command is passed to the sublayer 
            % ('communication layer'), there is feedback in the console about what command has been 
            % called. Debug mode 2 (debug=2) enables debug mode in the lower layers. Each packet
            % sent and received is printed to the console.
            debug; 
            
            %batteryMode -  [Writable] Mode for reading battery charge ('Percentage'/'Voltage')
            % See also EV3.BATTERYVALUE
            batteryMode;
        end
    
        properties (Dependent)  % Parameters to be read directly from physical brick
            %batteryValue -  [Read-only] Current battery charge
            % Depending on batteryMode, the reading is either in percentage or voltage.
            % See also EV3.BATTERYMODE
            batteryValue; 
        end
        
        properties (SetAccess = 'private')  % Read-only properties that are set internally
            %isConnected -  [Read-only] Virtual brick connected to physical one?
            % See also EV3.CONNECT, EV3.DISCONNECT
            isConnected = false; 
            
            %motorA -  [Read-only] 
            % See also MOTOR
            motorA;
            %motorB -  [Read-only] 
            % See also MOTOR
            motorB;
            %motorC -  [Read-only] 
            % See also MOTOR
            motorC;
            %motorD -  [Read-only] 
            % See also MOTOR
            motorD;
            
            %sensor1 -  [Read-only]
            % See also SENSOR
            sensor1;
            %sensor2 -  [Read-only]
            % See also SENSOR
            sensor2;
            %sensor3 -  [Read-only]
            % See also SENSOR
            sensor3;
            %sensor4 -  [Read-only]
            % See also SENSOR
            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
        
        methods  % Standard methods
            %% Constructor
            function ev3 = EV3(varargin)
                %EV3 Sets properties of EV3-object and creates Motor- and Sensor-objects with default
                % parameters.
                %
                % Arguments
                %  * varargin: see EV3::setProperties(ev3, varargin)
                %
                
                ev3.setProperties(varargin{:});
                
                ev3.motorA = Motor('A', 'Debug', ev3.debug>=1);
                ev3.motorB = Motor('B', 'Debug', ev3.debug>=1);
                ev3.motorC = Motor('C', 'Debug', ev3.debug>=1);
                ev3.motorD = Motor('D', 'Debug', ev3.debug>=1);
                
                
                ev3.sensor1 = Sensor('1', 'Debug', ev3.debug>=1);
                ev3.sensor2 = Sensor('2', 'Debug', ev3.debug>=1);
                ev3.sensor3 = Sensor('3', 'Debug', ev3.debug>=1);
                ev3.sensor4 = Sensor('4', 'Debug', ev3.debug>=1);
                
                ev3.init = false;
            end
            
            function delete(ev3)
                %delete Disconnects from physical brick and deletes this instance
                
                if ev3.isConnected
                    ev3.disconnect();
                end
            end
            
            %% Connection 
            function connect(ev3, varargin)
                %connect Connects EV3-object and its Motors and Sensors to physical brick.
                %
                % Arguments
                %  * 'bt'/'usb': Connection type
                %  * 'serPort', '/dev/rfcommx': Path to serial port (if 'bt'), where x = 0...9
                %  * 'beep', bool: EV3 beeps if connection has been established.
                %
                % Examples
                %  b = EV3(); b.connect('bt', 'serPort', '/dev/rfcomm0');
                %  b = EV3(); b.connect('usb', 'beep', 'on', ); 
                %
                
                % Check connection
                if ev3.isConnected
                    if isCommInterfaceValid(ev3.commInterface)
                        error('EV3::connect: Already connected.');
                    else
                        warning(['EV3::connect: EV3.isConnected is set to ''True'', but ',...
                                 'comm handle is invalid. Deleting invalid handle and ' ,...
                                 'resetting EV3.isConnected now...']);
                             
                        ev3.commInterface = 0;
                        ev3.isConnected = false;
                    end
                end
                
                if nargin < 2
                     error('EV3::connect: Wrong number of input arguments.');
                end
                
                idxes = strcmpi('beep', varargin);
                idx = find([0, idxes(1:end-1)]);
                if ~isempty(idx)
                    beep = varargin{idx}; %#ok<FNDSB>
                    if ~isBool(beep)
                        error('EV3::connect: Argument after ''beep'' has to be a bool.');
                    end
                else
                    beep = false;
                end
                
                % Try to connect
                try 
                    % Connect to physical brick
                    % -> Creating communication-handle implicitly establishes connection
                    ev3.commInterface = CommunicationInterface(varargin{:}, 'debug', ev3.debug>=2);
                    ev3.isConnected = true;
                    
                    if beep
                        ev3.beep();
                    end
                    
                    % Connect motors
                    ev3.motorA.connect(ev3.commInterface);
                    ev3.motorB.connect(ev3.commInterface);
                    ev3.motorC.connect(ev3.commInterface);
                    ev3.motorD.connect(ev3.commInterface);
                    
                    % Connect sensors
                    ev3.sensor1.connect(ev3.commInterface);
                    ev3.sensor2.connect(ev3.commInterface);
                    ev3.sensor3.connect(ev3.commInterface);
                    ev3.sensor4.connect(ev3.commInterface);
                catch ME
                    % Something went wrong...
                    ev3.isConnected = false;
                    if isCommInterfaceValid(ev3.commInterface) && ev3.commInterface ~= 0
                        ev3.commInterface.delete();
                        ev3.commInterface = 0;
                    end
                    
                    rethrow(ME);
                end
            end
            
            function disconnect(ev3)
                %disconnect Disconnects EV3-object and its Motors and Sensors from physical brick.
                %
                % Example
                %  b = EV3(); 
                %  b.connect('bt', 'serPort', '/dev/rfcomm0');
                %  % do stuff
                %  b.disconnect();
                %
                
                % Reset motors and sensors before disconnecting
                ev3.resetPhysicalBrick();
                
                % Disconnect motors and sensors
                % -> set references to comm handle to 0
                ev3.motorA.disconnect();
                ev3.motorB.disconnect();
                ev3.motorC.disconnect();
                ev3.motorD.disconnect();
                
                ev3.sensor1.disconnect();
                ev3.sensor2.disconnect();
                ev3.sensor3.disconnect();
                ev3.sensor4.disconnect();
                
                % Delete handle to comm-interface
                if isCommInterfaceValid(ev3.commInterface) && ev3.commInterface ~= 0
                    ev3.commInterface.delete();
                end
                ev3.commInterface = 0;
                
                ev3.isConnected = false;
            end
            
            %% Device functions
            function stopAllMotors(ev3)
                %stopAllMotors Sends a stop-command to all motor-ports
                
                if ~ev3.isConnected
                    stopAllMotors(['EV3::beep: Brick-Object not connected physical brick. ',...
                           'You have to call ev3.connect(...) first!']);
                end
                
                ev3.commInterface.outputStopAll();
            end
            
            %% Sound functions
            function beep(ev3)
                %beep Plays a 'beep' tone on brick.
                %
                % Notes
                %  * This equals playTone(10, 1000, 100) (Wraps the same opCode in comm-layer)
                %
                % Example
                %  b = EV3(); 
                %  b.connect('bt', 'serPort', '/dev/rfcomm0');
                %  b.beep(); 
                %
                
                if ~ev3.isConnected
                    error(['EV3::beep: Brick-Object not connected physical brick. ',...
                           'You have to call ev3.connect(...) first!']);
                end
                
                ev3.commInterface.beep();
                
                if ev3.debug
    				fprintf('(DEBUG) EV3::beep: Called beep on brick\n');
                end
            end
            
            function playTone(ev3, volume, frequency, duration)
                %playTone Plays tone on brick.
                %
                % Arguments
                %  * volume (0...100)
                %  * frequency (250...10000)
                %  * duration (>0, in milliseconds)
                %
                % Example
                %  b = EV3(); 
                %  b.connect('bt', 'serPort', '/dev/rfcomm0');
                %  b.playTone(50, 5000, 1000);  % Plays tone with 50% volume and 5000Hz for 1
                %                               % second.
                %
                
                if ~ev3.isConnected
                    error(['EV3::isConnected: Brick-Object not connected physical brick. ',...
                           'You have to call ev3.connect(...) first!']);
                end
                
                ev3.commInterface.soundPlayTone(volume, frequency, duration);
                
                if ev3.debug
    				fprintf('(DEBUG) EV3::beep: Called soundPlayTone on brick\n');
                end
            end
            
            function stopTone(ev3)
                %stopTone Stops tone currently played.
                %
                % Example
                %  b = EV3(); 
                %  b.connect('bt', 'serPort', '/dev/rfcomm0');
                %  b.playTone(10,100,100000000);  % Accidentally given wrong tone duration.
                %  b.stopTone();  % Stops tone immediately.
                %
                
                if ~ev3.isConnected
                    error(['EV3::stopTone: Brick-Object not connected physical brick. ',...
                           'You have to call ev3.connect(...) first!']);
                end
                
                ev3.commInterface.soundStopTone();
                
                if ev3.debug
    				fprintf('(DEBUG) EV3::beep: Called soundStopTone on brick\n');
                end
            end
            
            function status = tonePlayed(ev3)
                %tonePlayed Tests if tone is currently played.
                %
                % Output
                %  * status: True for a tone being played
                %
                % Example
                %  b = EV3(); 
                %  b.connect('bt', 'serPort', '/dev/rfcomm0');
                %  b.playTone(10, 100, 1000);
                %  pause(0.5);
                %  b.tonePlayed() -> Outputs 1 to console.
                %
                
                if ~ev3.isConnected
                    error(['EV3::tonePlayed: Brick-Object not connected physical brick. ',...
                           'You have to call ev3.connect(...) first!']);
                end
                
                status = ev3.commInterface.soundTest;
                
                if ev3.debug
    				fprintf('(DEBUG) EV3::beep: Called soundTest on brick\n');
                end
            end
            
            %% Setter
            function set.commInterface(ev3, comm)
                if ~isCommInterfaceValid(comm)
                    error('EV3::set.commInterface: Handle to Brick-object not valid.');
                else
                    ev3.commInterface = comm;
                end
            end
            
            function set.batteryMode(ev3, batteryMode)
                validModes = {'Voltage', 'Percentage'};
                if ~ischar(batteryMode) || ~ismember(batteryMode, validModes)
                    error('EV3::set.batteryMode: Given parameter is not a valid battery mode.');
                else 
                    ev3.batteryMode = batteryMode;
                end
            end
            
            function set.debug(ev3, debug)
                if ~isBool(debug) && debug ~= 2
                    error('EV3::set.debug: Given parameter is not a bool.');
                end
                
                ev3.debug = str2bool(debug);
                
                if ev3.isConnected
                    ev3.commInterface.debug = (ev3.debug >= 2); 
                end
            end
            
            function setProperties(ev3, varargin)
                %setProperties Set multiple EV3 properties at once using MATLAB's inputParser.
                %
                % Arguments
                %  * 'debug', 0/1/'on'/'off'/'true'/'false'/2
                %  * 'batteryMode', 'Voltage'/'Percentage': Mode in which batteryValue will be read.
                %
                % Example
                %  b = EV3(); 
                %  b.connect('bt', 'serPort', '/dev/rfcomm0');
                %  b.setProperties('debug', 'on', 'batteryMode', 'Voltage');
                %  % Instead of: b.debug = 'on'; b.batteryMode = 'Voltage';
                %
                % See also EV3.DEBUG, EV3.BATTERYMODE
                % 
                
                p = inputParser();
                
                % Set default values
                if ev3.init
                    defaultDebug = false;
                    defaultBatteryMode = 'Percentage';
                else
                    defaultDebug = ev3.debug;
                    defaultBatteryMode = ev3.batteryMode;
                end
                
                % Add parameter
                p.addOptional('debug', defaultDebug);
                p.addOptional('batteryMode', defaultBatteryMode);
                
                % Parse...
                p.parse(varargin{:});
                
                % Set properties
                ev3.batteryMode = p.Results.batteryMode;
                ev3.debug = p.Results.debug;
            end
            
            %% Getter
            function bat = get.batteryValue(ev3)
                if ~ev3.isConnected
                    warning('EV3::getBattery: EV3-Object not connected to physical EV3.');
                    
                    bat = 0;
                    return;
                end
                
                bat = ev3.getBattery();
            end
            
            function display(ev3)
                displayProperties(ev3); 
                
                fprintf('\n\tDevices\n');
                props = properties(ev3);
                
                warning('off', 'all');  % Turn off warnings while reading values
                for i = 1:length(props)
                    p = props{i};
                    
                    if strcmp(class(ev3.(p)),'Sensor') || strcmp(class(ev3.(p)), 'Motor')
                        fprintf('\t%15s [Type: %s]\n', p, char(ev3.(p).type));
                    end
                end
                warning('on', 'all');
            end
    %         function display(ev3)
    %             warning('off','all');
    %             %builtin('disp', ev3);
    %             
    %             
    %             props = properties(ev3);  % Save list with property names as strings
    %             fprintf('   <a href="matlab:helpPopup EV3">EV3</a> with properties:\n\n');
    %             for i = 1:length(props)
    %                 p = props{i};
    %                 
    %                 if isa(ev3.(p), 'handle') && ev3.(p).isvalid  % Valid handle?
    %                     if isa(ev3.(p), 'Brick')  % Communication interface?
    %                         fprintf('\t%15s:   Brick-object (Interface for comm-layer)\n');
    %                     else  % Motor or Sensor object?
    %                         fprintf('\t%15s:   %s-object for port %s\n', p, class(ev3.(p)), ...
    %                                 port2str(class(ev3.(p)), ev3.(p).port));
    %                     end
    %                 elseif isnumeric(ev3.(p))
    %                     fprintf('\t%15s:   %d\n', p, ev3.(p));
    %                 elseif ischar(ev3.(p))
    %                     fprintf('\t%15s:   %s\n', p, ev3.(p));
    %                 else
    %                     warning('EV3::display: Unsuspected parameter type.');  % Not really useful
    %                 end
    %             end
    %             
    %             warning('on','all');
    %             
    %         end
        end
        
        methods (Access = 'private')  % Private brick functions that are wrapped by dependent params
            function bat = getBattery(ev3)
                if ~ev3.isConnected
                    error(['EV3::getBattery: EV3-Object not connected to physical EV3. You have ',...
                        'to call ev3.connect(properties) first!']);
                end
                
                if strcmpi(ev3.batteryMode, 'Percentage')
                    bat = ev3.commInterface.uiReadLbatt();
                    
                    if ev3.debug
                        fprintf('(DEBUG) EV3::getBattery: Called uiReadLBatt.\n');
                    end
                elseif strcmpi(ev3.batteryMode, 'Voltage')
                    bat = ev3.commInterface.uiReadVbatt();
                    
                    if ev3.debug
                        fprintf('(DEBUG) EV3::getBattery: Called uiReadVBatt.\n');
                    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