diff --git a/source/BrickIO.m b/source/BrickIO.m index 585eb6d9f54c32615c5331124bad6c1109142956..ebdb054b682f28d8dc5562de9e9dad224011b9ae 100755 --- a/source/BrickIO.m +++ b/source/BrickIO.m @@ -12,6 +12,11 @@ % - The write function should be given a uint8 datatype as a parameter classdef BrickIO < handle + properties (Abstract) + % time-out period (if 0, no time-out) + timeOut + end + properties (Abstract, Access = 'protected') % connection handle handle diff --git a/source/CommunicationInterface.m b/source/CommunicationInterface.m index f1ed918fab8e988db33fd9b22b0f22c2031b1405..66758207ec91db80556f6ae2fc48912e16b81275 100755 --- a/source/CommunicationInterface.m +++ b/source/CommunicationInterface.m @@ -105,6 +105,11 @@ classdef CommunicationInterface < handle debug; end + properties (Dependent) + % Time-out period in seconds (if 0, no time-out) + timeOut; + end + properties (SetAccess = private) % IO connection type ioType; @@ -128,6 +133,7 @@ classdef CommunicationInterface < handle end methods + %% Constructor, Destructor, Setter... function commInterface = CommunicationInterface(varargin) % Brick.Brick Create a Brick object % @@ -217,6 +223,18 @@ classdef CommunicationInterface < handle brick.conn.debug = debug; end + function set.timeOut(brick, timeOut) + if ~isnumeric(timeOut) || timeOut < 0 + error(ID(), 'timeOut-period of USB-handle can only be set to positive numerical values.'); + end + + brick.conn.timeOut = timeOut; + end + + function timeOut = get.timeOut(brick) + timeOut = brick.conn.timeOut; + end + function setProperties(brick, varargin) p = inputParser(); p.KeepUnmatched = true; @@ -245,120 +263,9 @@ classdef CommunicationInterface < handle brick.debug = p.Results.debug; end - function send(brick, cmd) - % Brick.send Send data to the brick - % - % Brick.send(cmd) sends a command to the brick through the - % connection handle. - % - % Notes:: - % - cmd is a command object. - % - % Example:: - % b.send(cmd) - - % Send the message through the brickIO write function - brick.conn.write(cmd.msg); - - % (MMI) When spamming the brick with commands, at some point, it will start - % behaving 'strange'. Sometimes, commands will be executed only with - % a delay, some commands may even be bypassed. - % (Maybe too many commands screw up the brick's internal command queue?..) - % Temporary workaround: Wait 5ms after each sent packet. - pause(0.005); - - % Verbose output - if brick.debug > 0 - fprintf('sent (hex): [ '); - for ii=1:length(cmd.msg) - fprintf('%s ',dec2hex(cmd.msg(ii))) - end - fprintf(']\n'); - fprintf('sent (dec): [ '); - for ii=1:length(cmd.msg) - fprintf('%d ',cmd.msg(ii)) - end - fprintf(']\n'); - end - end - - function rmsg = receive(brick) - % Brick.receive Receive data from the brick - % - % rmsg = Brick.receive() receives data from the brick through - % the connection handle. - % - % Notes: - % - If received packet is corrupt, up to five new packets are read (if all are - % corrupt, an error is thrown) - % - % Example:: - % rmsg = b.receive() - % - - % Read the message through the brickIO read function - rmsg = brick.conn.read(); - - % Check if reply is corrupt or error byte is set - try - reply = Command(rmsg); - catch ME - corrupt = 1; - if ~isempty(strfind(ME.identifier, 'CorruptPacket')) - % Read packet was corrupt - retry - %id = [ID(), ':', 'CorruptPacket']; - %warning(id, 'Read corrupt packet. Retrying...'); - if brick.debug - fprintf('received (corrupt) (hex): [ '); - for ii=1:length(rmsg) - fprintf('%s ',dec2hex(rmsg(ii))) - end - fprintf(']\n'); - fprintf('received (corrupt) (dec): [ '); - for ii=1:length(rmsg) - fprintf('%d ',rmsg(ii)) - end - fprintf(']\n'); - end - - retries = 5; - while corrupt && retries - rmsg = brick.conn.read(); - try - reply = Command(rmsg); - corrupt = 0; - catch - retries = retries-1; - end - end - end - - if corrupt - rethrow(ME); - end - end - - if reply.checkForError() - msg = 'Error byte is set. The Brick couldn''t handle the last packet'; - id = [ID(), ':', 'CommandError']; - warning(id, msg); - end - - % Verbose output - if brick.debug > 0 - fprintf('received (hex): [ '); - for ii=1:length(rmsg) - fprintf('%s ',dec2hex(rmsg(ii))) - end - fprintf(']\n'); - fprintf('received (dec): [ '); - for ii=1:length(rmsg) - fprintf('%d ',rmsg(ii)) - end - fprintf(']\n'); - end - end - + %% Commands + % Methods in this block each correspond to a certain opCode which is implemented in the + % EV3 firmware -> each method creates one packet and sends it. function voltage = uiReadVbatt(brick) % Brick.uiReadVbatt Return battery level (voltage) % @@ -373,7 +280,8 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive the command - msg = brick.receive()'; + reply = brick.receive()'; + msg = reply.msg; voltage = typecast(uint8(msg(6:9)),'single'); if brick.debug > 0 fprintf('Battery voltage: %.02fV\n', voltage); @@ -395,81 +303,14 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive the command - msg = brick.receive()'; + reply = brick.receive()'; + msg = reply.msg; level = msg(6); if brick.debug > 0 fprintf('Battery level: %d%%\n', level); end end - function drawTest(brick) - % Brick.drawTest Draw test shapes - % - % Brick.drawTest() shows the drawing capabilities of the brick. - % - % Example:: - % b.drawTest() - - cmd = Command(); - cmd.addHeaderDirect(42,4,1); - % save the UI screen - cmd.opUI_DRAW_STORE(0); - % change the led pattern - cmd.opUI_WRITE_LED(Device.LedGreenFlash); - % clear the screen (top line still remains with remote cmds) - cmd.opUI_DRAW_FILLWINDOW(0,0,0); - % draw four pixels - cmd.opUI_DRAW_PIXEL(vmCodes.vmFGColor,12,15); - cmd.opUI_DRAW_PIXEL(vmCodes.vmFGColor,12,20); - cmd.opUI_DRAW_PIXEL(vmCodes.vmFGColor,18,15); - cmd.opUI_DRAW_PIXEL(vmCodes.vmFGColor,18,20); - % draw line - cmd.opUI_DRAW_LINE(vmCodes.vmFGColor,0,25,vmCodes.vmLCDWidth,25); - cmd.opUI_DRAW_LINE(vmCodes.vmFGColor,15,25,15,127); - % draw circle - cmd.opUI_DRAW_CIRCLE(1,40,40,10); - % draw rectangle - cmd.opUI_DRAW_RECT(vmCodes.vmFGColor,70,30,20,20); - % draw filled cricle - cmd.opUI_DRAW_FILLCIRCLE(vmCodes.vmFGColor,40,70,10); - % draw filled rectangle - cmd.opUI_DRAW_FILLRECT(vmCodes.vmFGColor,70,60,20,20); - % draw inverse rectangle - cmd.opUI_DRAW_INVERSERECT(30,90,60,20); - % change font - cmd.opUI_DRAW_SELECT_FONT(2); - % draw text - cmd.opUI_DRAW_TEXT(vmCodes.vmFGColor,100,40,'EV3'); - % change font - cmd.opUI_DRAW_SELECT_FONT(1); - % reprint - cmd.opUI_DRAW_TEXT(vmCodes.vmFGColor,100,70,'EV3'); - % change font - cmd.opUI_DRAW_SELECT_FONT(0); - % reprint - cmd.opUI_DRAW_TEXT(vmCodes.vmFGColor,100,90,'EV3'); - % voltage string - cmd.opUI_DRAW_TEXT(vmCodes.vmFGColor,100,110,'v ='); - % store voltage - cmd.opUI_READ_GET_VBATT(0); - % print the voltage value (global) - cmd.opUI_DRAW_VALUE(vmCodes.vmFGColor,130,110,0,5,3); - % update the window - cmd.opUI_DRAW_UPDATE; - % 5 second timer (so you can see the changing LED pattern) - cmd.opTIMER_WAIT(5000,0); - % wait for timer - cmd.opTIMER_READY(0); - % reset the LED - cmd.opUI_WRITE_LED(Device.LedGreen); - % return UI screen - cmd.opUI_DRAW_RESTORE(0); - % return - cmd.opUI_DRAW_UPDATE; - cmd.addLength(); - brick.send(cmd) - end - % Implemented @ MMI function state = soundTest(brick) % Brick.soundTest Test speaker @@ -490,7 +331,8 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive the state - msg = brick.receive()'; + reply = brick.receive()'; + msg = reply.msg; % speaker state is the 6th byte state = msg(6); end @@ -511,7 +353,7 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive reply - brick.receive(); + brick.waitForReply(); end function soundPlayTone(brick, volume, frequency, duration) @@ -550,29 +392,7 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); end - - function beep(brick,volume,duration) - % Brick.beep Play a beep on the brick - % - % Brick.beep(volume,duration) plays a beep tone with volume and - % duration. - % - % Notes:: - % - volume is the beep volume from 0 to 100, by default 10. (DATA8) - % - duration is the beep duration in ms, by default 100. (DATA16) - % - % Example:: - % b.beep(5,500) - if nargin < 2 - volume = 10; - end - if nargin < 3 - duration = 100; - end - brick.soundPlayTone(volume, 1000, duration); - end - function playThreeTones(brick) % Brick.playThreeTones Play three tones on the brick % @@ -616,7 +436,8 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive the command - msg = brick.receive()'; + reply = brick.receive()'; + msg = reply.msg; % return the type array types = [msg(6), msg(7), msg(8), msg(9)]; end @@ -642,7 +463,8 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive the command - msg = brick.receive()'; + reply = brick.receive()'; + msg = reply.msg; % return the device name name = sscanf(char(msg(6:end)),'%s'); end @@ -670,7 +492,8 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive the command - msg = brick.receive()'; + reply = brick.receive()'; + msg = reply.msg; % return the type and mode type = msg(6); mode = msg(7); @@ -710,7 +533,8 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive the command - msg = brick.receive()'; + reply = brick.receive()'; + msg = reply.msg; % return the mode name mode = sscanf(char(msg(6:end)),'%s'); end @@ -740,7 +564,8 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive the command - msg = brick.receive()'; + reply = brick.receive()'; + msg = reply.msg; % return the connection type conn = msg(6); end @@ -767,7 +592,8 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive the command - msg = brick.receive()'; + reply = brick.receive()'; + msg = reply.msg; % return the values min = typecast(uint8(msg(6:9)),'single'); max = typecast(uint8(msg(10:13)),'single'); @@ -799,7 +625,8 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive the command - msg = brick.receive()'; + reply = brick.receive()'; + msg = reply.msg; % return the changes changes = typecast(uint8(msg(6:9)),'single'); if brick.debug > 0 @@ -835,7 +662,8 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive the command - msg = brick.receive()'; + reply = brick.receive()'; + msg = reply.msg; % return the data datasets = msg(6); format = msg(7); @@ -865,7 +693,8 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive the command - msg = brick.receive()'; + reply = brick.receive()'; + msg = reply.msg; % return the bumps bumps = typecast(uint8(msg(6:9)),'single'); if brick.debug > 0 @@ -895,7 +724,8 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive the command - msg = brick.receive()'; + reply = brick.receive()'; + msg = reply.msg; % return the symbol name name = sscanf(char(msg(6:end)),'%s'); end @@ -963,7 +793,7 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive reply - brick.receive(); + brick.waitForReply(); end % Implemented @ MMI @@ -988,7 +818,8 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive the state - msg = brick.receive()'; + reply = brick.receive()'; + msg = reply.msg; % device state is the 6th (final) byte state = msg(6); end @@ -1022,7 +853,8 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive the command - msg = brick.receive()'; + reply = brick.receive()'; + msg = reply.msg; reading = msg(6); if brick.debug > 0 fprintf('Sensor reading: %.02f\n', reading); @@ -1052,7 +884,8 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive the command - msg = brick.receive()'; + reply = brick.receive()'; + msg = reply.msg; reading = typecast(uint8(msg(6:9)),'single'); if brick.debug > 0 fprintf('Sensor reading: %.02f\n', reading); @@ -1082,126 +915,27 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive the command - msg = brick.receive()'; + reply = brick.receive()'; + msg = reply.msg; reading = typecast(uint8(msg(6:9)),'single'); if brick.debug > 0 fprintf('Sensor reading: %.02f\n', reading); end end - function plotSensor(brick,layer,no,mode) - % Brick.plotSensor plot the sensor output + function outputStop(brick,layer,nos,brake) + % Brick.outputPower Stops a motor % - % Brick.plotSensor(layer,no,mode) plots the sensor output - % to MATLAB. + % Brick.outputPower(layer,nos,brake) stops motor at a layer + % NOS and brake. % % Notes:: % - layer is the usb chain layer (usually 0). - % - NO is the output port number from [0..3] or sensor port - % number minus 1. - % - mode is the sensor mode from types.html. (-1=don't change) + % - NOS is a bit field representing output 1 to 4 (0x01, 0x02, 0x04, 0x08). + % - brake is [0..1] (0=Coast, 1=Brake). % % Example:: - % b.plotSensor(0,SensorPort.Sensor1,Device.USDistCM) - % b.plotSensor(0,SensorPort.Sensor1,Device.GyroAng) - - % start timing - tic; - % create figure - hfig = figure('name','EV3 Sensor'); - % init the the data - t = 0; - x = 0; - hplot = plot(t,x); - % one read to set the mode - reading = brick.inputReadSI(layer,no,mode); - % set the title - name = brick.inputDeviceGetName(layer,no); - title(['Device name: ' name]); - % set the y label - name = brick.inputDeviceSymbol(layer,no); - ylabel(['Sensor value (' name(1:end-1) ')']); - % set the x label - xlabel('Time (s)'); - % set the x axis - xlim([0 10]); - % wait until the figure is closed - while(findobj('name','EV3 Sensor') == 1) - % get the reading - reading = brick.inputReadSI(layer,no,mode); - t = [t toc]; - x = [x reading]; - set(hplot,'Xdata',t) - set(hplot,'Ydata',x) - drawnow - % reset after 10 seconds - if (toc > 10) - % reset - t = 0; - x = x(end); - tic - end - end - end - - function displayColor(brick,layer,no) - % Brick.displayColor display sensor color - % - % Brick.displayColor(layer,no) displays the color read from the - % color sensor in a MATLAB figure. - % - % Notes:: - % - layer is the usb chain layer (usually 0). - % - NO is the output port number from [0..3] or sensor port - % number minus 1. - % - % Example:: - % b.displayColor(0,SensorPort.Sensor1) - - % create figure - hfig = figure('name','EV3 Color Sensor'); - % wait until the figure is closed - while(findobj('name','EV3 Color Sensor') == 1) - % read the color sensor in color detection mode - color = brick.inputReadSI(layer,no,Device.ColColor); - % change the figure background according to the color - switch color - case Device.NoColor - set(hfig,'Color',[0.8,0.8,0.8]) - case Device.BlackColor - set(hfig,'Color',[0,0,0]) - case Device.BlueColor - set(hfig,'Color',[0,0,1]) - case Device.GreenColor - set(hfig,'Color',[0,1,0]) - case Device.YellowColor - set(hfig,'Color',[1,1,0]) - case Device.RedColor - set(hfig,'Color',[1,0,0]) - case Device.WhiteColor - set(hfig,'Color',[1,1,1]) - case Device.BrownColor - set(hfig,'Color',[0.6,0.3,0]) - otherwise - set(hfig,'Color',[0.8,0.8,0.8]) - end - drawnow - end - end - - function outputStop(brick,layer,nos,brake) - % Brick.outputPower Stops a motor - % - % Brick.outputPower(layer,nos,brake) stops motor at a layer - % NOS and brake. - % - % Notes:: - % - layer is the usb chain layer (usually 0). - % - NOS is a bit field representing output 1 to 4 (0x01, 0x02, 0x04, 0x08). - % - brake is [0..1] (0=Coast, 1=Brake). - % - % Example:: - % b.outputStop(0,MotorBitfield.MotorA,BrakeMode.Brake) + % b.outputStop(0,MotorBitfield.MotorA,BrakeMode.Brake) cmd = Command(); cmd.addHeaderDirect(42,0,0); @@ -1311,7 +1045,8 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive the command - msg = brick.receive(); + reply = brick.receive()'; + msg = reply.msg; % motor state is the final byte state = msg(end); end @@ -1336,7 +1071,7 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive reply - brick.receive(); + brick.waitForReply(); end % Implemented @ MMI @@ -1607,7 +1342,8 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive the command - msg = brick.receive()'; + reply = brick.receive()'; + msg = reply.msg; tacho = typecast(uint8(msg(6:9)),'int32'); if brick.debug > 0 fprintf('Tacho: %d degrees\n', tacho); @@ -1643,7 +1379,8 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive the command - msg = brick.receive()'; + reply = brick.receive()'; + msg = reply.msg; % Current thoughts: the bug could be in the firmware (DUNDUNDUN). % The docu states: "Offset in the response buffer (global variables) must be % aligned (float/32bits first and 8 bits last)." [/lms2012/doc/html/directcommands.html] @@ -1691,7 +1428,8 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive the state - msg = brick.receive()'; + reply = brick.receive()'; + msg = reply.msg; % return the state state = msg(end); end @@ -1717,7 +1455,7 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive reply - brick.receive(); + brick.waitForReply(); end function name = comGetBrickName(brick) @@ -1734,7 +1472,8 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive the command - msg = brick.receive()'; + reply = brick.receive()'; + msg = reply.msg; % return the brick name name = sscanf(char(msg(6:end)),'%s'); end @@ -1768,7 +1507,8 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive the command - msg = brick.receive()'; + reply = brick.receive()'; + msg = reply.msg; % return the brick name mac = sscanf(char(msg(4:10)),'%s'); end @@ -1787,7 +1527,8 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive the command - msg = brick.receive()'; + reply = brick.receive()'; + msg = reply.msg; % return the brick name id = sscanf(char(msg(6:end)),'%s'); end @@ -1843,7 +1584,8 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive the sent response - rmsg = brick.receive(); + reply = brick.receive()'; + rmsg = reply.msg; handle = rmsg(end); pause(1) % send the file @@ -1853,7 +1595,8 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive the sent response - rmsg = brick.receive(); + reply = brick.receive()'; + rmsg = reply.msg; % print message fprintf('%s uploaded\n',filename); end @@ -1881,7 +1624,8 @@ classdef CommunicationInterface < handle cmd.addLength(); brick.send(cmd); % receive the sent response - rmsg = brick.receive(); + reply = brick.receive()'; + rmsg = reply.msg; % extract payload payload = rmsg(13:end); % print to file @@ -1913,7 +1657,8 @@ classdef CommunicationInterface < handle cmd.LIST_FILES(maxlength,pathname); cmd.addLength(); brick.send(cmd); - rmsg = brick.receive(); + reply = brick.receive()'; + rmsg = reply.msg; % print fprintf('%s',rmsg(13:end)); end @@ -1935,7 +1680,7 @@ classdef CommunicationInterface < handle cmd.CREATE_DIR(pathname); cmd.addLength(); brick.send(cmd); - rmsg = brick.receive(); + brick.waitForReply(); end function deleteFile(brick,pathname) @@ -1956,7 +1701,7 @@ classdef CommunicationInterface < handle cmd.DELETE_FILE(pathname); cmd.addLength(); brick.send(cmd); - rmsg = brick.receive(); + brick.waitForReply(); end function writeMailBox(brick,title,type,msg) @@ -1996,7 +1741,8 @@ classdef CommunicationInterface < handle % Example:: % [title,msg] = b.readMailBox('text') - mailmsg = brick.receive(); + reply = brick.receive()'; + mailmsg = reply.msg; % extract message title (starts at pos 8, pos 7 is the size) title = char(mailmsg(8:7+mailmsg(7))); % parse message according to type @@ -2013,6 +1759,7 @@ classdef CommunicationInterface < handle end end + %% Bytecode test function threeToneByteCode(brick,filename) % Brick.threeToneByteCode Create three tone byte code % @@ -2044,5 +1791,322 @@ classdef CommunicationInterface < handle % generate the byte code cmd.GenerateByteCode(filename); end + + %% Utility + % Methods in this block are composed of multiple commands and more logic for convenience + function drawTest(brick) + % Brick.drawTest Draw test shapes + % + % Brick.drawTest() shows the drawing capabilities of the brick. + % + % Example:: + % b.drawTest() + + cmd = Command(); + cmd.addHeaderDirect(42,4,1); + % save the UI screen + cmd.opUI_DRAW_STORE(0); + % change the led pattern + cmd.opUI_WRITE_LED(Device.LedGreenFlash); + % clear the screen (top line still remains with remote cmds) + cmd.opUI_DRAW_FILLWINDOW(0,0,0); + % draw four pixels + cmd.opUI_DRAW_PIXEL(vmCodes.vmFGColor,12,15); + cmd.opUI_DRAW_PIXEL(vmCodes.vmFGColor,12,20); + cmd.opUI_DRAW_PIXEL(vmCodes.vmFGColor,18,15); + cmd.opUI_DRAW_PIXEL(vmCodes.vmFGColor,18,20); + % draw line + cmd.opUI_DRAW_LINE(vmCodes.vmFGColor,0,25,vmCodes.vmLCDWidth,25); + cmd.opUI_DRAW_LINE(vmCodes.vmFGColor,15,25,15,127); + % draw circle + cmd.opUI_DRAW_CIRCLE(1,40,40,10); + % draw rectangle + cmd.opUI_DRAW_RECT(vmCodes.vmFGColor,70,30,20,20); + % draw filled cricle + cmd.opUI_DRAW_FILLCIRCLE(vmCodes.vmFGColor,40,70,10); + % draw filled rectangle + cmd.opUI_DRAW_FILLRECT(vmCodes.vmFGColor,70,60,20,20); + % draw inverse rectangle + cmd.opUI_DRAW_INVERSERECT(30,90,60,20); + % change font + cmd.opUI_DRAW_SELECT_FONT(2); + % draw text + cmd.opUI_DRAW_TEXT(vmCodes.vmFGColor,100,40,'EV3'); + % change font + cmd.opUI_DRAW_SELECT_FONT(1); + % reprint + cmd.opUI_DRAW_TEXT(vmCodes.vmFGColor,100,70,'EV3'); + % change font + cmd.opUI_DRAW_SELECT_FONT(0); + % reprint + cmd.opUI_DRAW_TEXT(vmCodes.vmFGColor,100,90,'EV3'); + % voltage string + cmd.opUI_DRAW_TEXT(vmCodes.vmFGColor,100,110,'v ='); + % store voltage + cmd.opUI_READ_GET_VBATT(0); + % print the voltage value (global) + cmd.opUI_DRAW_VALUE(vmCodes.vmFGColor,130,110,0,5,3); + % update the window + cmd.opUI_DRAW_UPDATE; + % 5 second timer (so you can see the changing LED pattern) + cmd.opTIMER_WAIT(5000,0); + % wait for timer + cmd.opTIMER_READY(0); + % reset the LED + cmd.opUI_WRITE_LED(Device.LedGreen); + % return UI screen + cmd.opUI_DRAW_RESTORE(0); + % return + cmd.opUI_DRAW_UPDATE; + cmd.addLength(); + brick.send(cmd) + end + + function beep(brick,volume,duration) + % Brick.beep Play a beep on the brick + % + % Brick.beep(volume,duration) plays a beep tone with volume and + % duration. + % + % Notes:: + % - volume is the beep volume from 0 to 100, by default 10. (DATA8) + % - duration is the beep duration in ms, by default 100. (DATA16) + % + % Example:: + % b.beep(5,500) + + if nargin < 2 + volume = 10; + end + if nargin < 3 + duration = 100; + end + brick.soundPlayTone(volume, 1000, duration); + end + + function plotSensor(brick,layer,no,mode) + % Brick.plotSensor plot the sensor output + % + % Brick.plotSensor(layer,no,mode) plots the sensor output + % to MATLAB. + % + % Notes:: + % - layer is the usb chain layer (usually 0). + % - NO is the output port number from [0..3] or sensor port + % number minus 1. + % - mode is the sensor mode from types.html. (-1=don't change) + % + % Example:: + % b.plotSensor(0,SensorPort.Sensor1,Device.USDistCM) + % b.plotSensor(0,SensorPort.Sensor1,Device.GyroAng) + + % start timing + tic; + % create figure + hfig = figure('name','EV3 Sensor'); + % init the the data + t = 0; + x = 0; + hplot = plot(t,x); + % one read to set the mode + reading = brick.inputReadSI(layer,no,mode); + % set the title + name = brick.inputDeviceGetName(layer,no); + title(['Device name: ' name]); + % set the y label + name = brick.inputDeviceSymbol(layer,no); + ylabel(['Sensor value (' name(1:end-1) ')']); + % set the x label + xlabel('Time (s)'); + % set the x axis + xlim([0 10]); + % wait until the figure is closed + while(findobj('name','EV3 Sensor') == 1) + % get the reading + reading = brick.inputReadSI(layer,no,mode); + t = [t toc]; + x = [x reading]; + set(hplot,'Xdata',t) + set(hplot,'Ydata',x) + drawnow + % reset after 10 seconds + if (toc > 10) + % reset + t = 0; + x = x(end); + tic + end + end + end + + function displayColor(brick,layer,no) + % Brick.displayColor display sensor color + % + % Brick.displayColor(layer,no) displays the color read from the + % color sensor in a MATLAB figure. + % + % Notes:: + % - layer is the usb chain layer (usually 0). + % - NO is the output port number from [0..3] or sensor port + % number minus 1. + % + % Example:: + % b.displayColor(0,SensorPort.Sensor1) + + % create figure + hfig = figure('name','EV3 Color Sensor'); + % wait until the figure is closed + while(findobj('name','EV3 Color Sensor') == 1) + % read the color sensor in color detection mode + color = brick.inputReadSI(layer,no,Device.ColColor); + % change the figure background according to the color + switch color + case Device.NoColor + set(hfig,'Color',[0.8,0.8,0.8]) + case Device.BlackColor + set(hfig,'Color',[0,0,0]) + case Device.BlueColor + set(hfig,'Color',[0,0,1]) + case Device.GreenColor + set(hfig,'Color',[0,1,0]) + case Device.YellowColor + set(hfig,'Color',[1,1,0]) + case Device.RedColor + set(hfig,'Color',[1,0,0]) + case Device.WhiteColor + set(hfig,'Color',[1,1,1]) + case Device.BrownColor + set(hfig,'Color',[0.6,0.3,0]) + otherwise + set(hfig,'Color',[0.8,0.8,0.8]) + end + drawnow + end + end + end + + methods (Access = private) + function send(brick, cmd) + % Brick.send Send data to the brick + % + % Brick.send(cmd) sends a command to the brick through the + % connection handle. + % + % Notes:: + % - cmd is a command object. + % + % Example:: + % b.send(cmd) + + % Send the message through the brickIO write function + brick.conn.write(cmd.msg); + + % (MMI) When spamming the brick with commands, at some point, it will start + % behaving 'strange'. Sometimes, commands will be executed only with + % a delay, some commands may even be bypassed. + % (Maybe too many commands screw up the brick's internal command queue?..) + % Temporary workaround: Wait 5ms after each sent packet. + pause(0.005); + + % Verbose output + if brick.debug > 0 + fprintf('sent (hex): [ '); + for ii=1:length(cmd.msg) + fprintf('%s ',dec2hex(cmd.msg(ii))) + end + fprintf(']\n'); + fprintf('sent (dec): [ '); + for ii=1:length(cmd.msg) + fprintf('%d ',cmd.msg(ii)) + end + fprintf(']\n'); + end + end + + function reply = receive(brick) + % Brick.receive Receive data from the brick + % + % rmsg = Brick.receive() receives data from the brick through + % the connection handle. + % + % Notes:: + % - If received packet is corrupt, up to five new packets are read (if all are + % corrupt, an error is thrown) + % + % Example:: + % rmsg = b.receive() + % + + % Read the message through the brickIO read function + rmsg = brick.conn.read(); + + % Check if reply is corrupt or error byte is set + try + reply = Command(rmsg); + catch ME + corrupt = 1; + if ~isempty(strfind(ME.identifier, 'CorruptPacket')) + % Read packet was corrupt - retry + %id = [ID(), ':', 'CorruptPacket']; + %warning(id, 'Read corrupt packet. Retrying...'); + if brick.debug + fprintf('received (corrupt) (hex): [ '); + for ii=1:length(rmsg) + fprintf('%s ',dec2hex(rmsg(ii))) + end + fprintf(']\n'); + fprintf('received (corrupt) (dec): [ '); + for ii=1:length(rmsg) + fprintf('%d ',rmsg(ii)) + end + fprintf(']\n'); + end + + retries = 5; + while corrupt && retries + rmsg = brick.conn.read(); + try + reply = Command(rmsg); + corrupt = 0; + catch + retries = retries-1; + end + end + end + + if corrupt + rethrow(ME); + end + end + + if reply.checkForError() + msg = 'Error byte is set. The Brick couldn''t handle the last packet'; + id = [ID(), ':', 'CommandError']; + warning(id, msg); + end + + % Verbose output + if brick.debug > 0 + fprintf('received (hex): [ '); + for ii=1:length(rmsg) + fprintf('%s ',dec2hex(rmsg(ii))) + end + fprintf(']\n'); + fprintf('received (dec): [ '); + for ii=1:length(rmsg) + fprintf('%d ',rmsg(ii)) + end + fprintf(']\n'); + end + end + + function waitForReply(brick) + oldTimeOut = brick.timeOut; + brick.timeOut = 0; + + brick.receive(); + + brick.timeOut = oldTimeOut; + end end end diff --git a/source/btBrickIO.m b/source/btBrickIO.m index 02946f677e153d650af1a0195670a0e426e59df0..e5a5bfb1628e59ea513a5d5c9e10d1db4f84187f 100755 --- a/source/btBrickIO.m +++ b/source/btBrickIO.m @@ -30,6 +30,8 @@ classdef btBrickIO < BrickIO debug = 0; % bluetooth serial port serialPort = '/dev/rfcomm0' + % time-out period in seconds (if 0, no time-out) + timeOut = 10; end properties (Access = 'protected') @@ -221,5 +223,17 @@ classdef btBrickIO < BrickIO end end end + + function set.timeOut(brickIO, timeOut) + if ~isnumeric(timeOut) || timeOut < 0 + error(ID(), 'timeOut-period of Bluetooth-handle can only be set to positive numerical values.'); + end + + brickIO.timeOut = timeOut; + + if timeOut == 0 + brickIO.handle.Timeout = 9999999; % MATLAB.serial seems to have no option to disable timeout + end + end end end diff --git a/source/hidapi.m b/source/hidapi.m index 4542eea7c0501aafb2b6d358c84cadc1a2e1423e..a8803b46dea4d1baaeaba85db5ec3737da8741ff 100755 --- a/source/hidapi.m +++ b/source/hidapi.m @@ -253,8 +253,63 @@ classdef hidapi < handle hid.handle = []; end + % @ MMI + function rmsg = read_timeout(hid, timeOut) + %hidapi.read_timeout Read from hid object with a time-out + % + % rmsg = hid.read_timeout() reads from a hid device and returns the + % read bytes. Will print an error if no data was read during the time-out period. + % + % Arguments:: + % timeOut time-out period in milliseconds + % + % Throws:: + % CommError Error during communication with device + % InvalidHandle Handle to USB-device not valid + % + + if hid.debug > 0 + fprintf('hidapi read_timeout\n'); + end + + % Read buffer of nReadBuffer length + buffer = zeros(1,hid.nReadBuffer); + % Create a uint8 pointer + pbuffer = libpointer('uint8Ptr', uint8(buffer)); + + % Check if pointer is (unexpectedly) already invalidated + assert(isLibPointerValid(hid.handle)==1, ... + [hid.slib, ':', 'InvalidHandle'], ... + 'Failed to read USB-data because pointer to USB-device is invalidated.'); + + % Read data from HID device + [res,h] = calllib(hid.slib,'hid_read_timeout',hid.handle,pbuffer,uint64(length(buffer)),timeOut); + + % Check the response (No assert as there are multiple cases) + if res < 1 + % Error occurred + id = [hid.slib, ':', 'CommError']; + % Narrow error down + if res == -1 + msg = 'Connection error (probably lost connection to device)'; + elseif res == 0 + msg = ['Could not read data from device (device is still connected, ',... + 'but does not react)']; + else + msg = 'Unexpected connection error'; + end + causeException = MException(id, msg); + ME = MException(id, 'Failed to read data via USB.'); + addCause(ME, causeException); + throw(ME); + end + + % Return the string value + rmsg = pbuffer.Value; + end + function rmsg = read(hid) - %hidapi.rmsg Read from hid object + %hidapi.read Read from hid object % % rmsg = hid.read() reads from a hid device and returns the % read bytes. Will print an error if no data was read. diff --git a/source/usbBrickIO.m b/source/usbBrickIO.m index 06fd285c036c5086feb854475603e1da532de990..5854cc57a775c682caf3ab41dca62ae819a78bd8 100755 --- a/source/usbBrickIO.m +++ b/source/usbBrickIO.m @@ -28,6 +28,8 @@ classdef usbBrickIO < BrickIO nReadBuffer = 1024; % write buffer size nWriteBuffer = 1024; + % time-out period in milliseconds (if 0, no time-out) + timeOut = 10000; end properties (Access = 'protected') @@ -173,7 +175,11 @@ classdef usbBrickIO < BrickIO % Read from the usb handle try - rmsg = brickIO.handle.read; + if brickIO.timeOut ~= 0 + rmsg = brickIO.handle.read_timeout(brickIO.timeOut); + else + rmsg = brickIO.handle.read(); + end catch ME if ~isempty(strfind(ME.identifier, 'CommError')) % Throw a clean CommError to avoid confusion in upper layers @@ -241,5 +247,13 @@ classdef usbBrickIO < BrickIO end end end + + function set.timeOut(brickIO, timeOut) + if ~isnumeric(timeOut) || timeOut < 0 + error(ID(), 'timeOut-period of USB-handle can only be set to positive numerical values.'); + end + + brickIO.timeOut = timeOut*1000; + end end end