root/branches/behrens/mfiles/COM_OpenNXTEx.m @ 231

Revision 231, 40.7 KB (checked in by behrens, 5 years ago)

add libusb_win_proto.m, modified COM_OpenNXTEx.m

Line 
1function handle = COM_OpenNXTEx(ConnectionMode, UseThisNXTMAC, varargin)
2% Opens a Bluetooth or USB connection to an NXT device and returns a handle for future use
3%
4% Syntax
5%   |handle = COM_OpenNXTEx('USB', UseThisNXTMAC)|
6%
7%   |handle = COM_OpenNXTEx('Bluetooth', UseThisNXTMAC, inifilename, 'check')|
8%
9%   |handle = COM_OpenNXTEx('Any', UseThisNXTMAC, inifilename, 'check')|
10%
11% Description
12%   This function establishes a connection to an NXT brick and returns the
13%   handle structure that has to be used with NXT-functions (you can call
14%   |COM_SetDefaultNXT(handle)| afterwards for easier use).
15%
16%   For a more convenient way to open an NXT handle with less parameters, the
17%   function |COM_OpenNXT| is provided.
18%
19%   Different types of connection modes are supported. In all modes, you can
20%   set |UseThisNXTMAC| to a string with the NXT's MAC address (serial number).
21%   A connection will then only be estabslished to a matching NXT brick. This can be
22%   useful for programs with multiple NXT devices. Set it to an empty string
23%   |''| to use any NXT available (usually the first one found).
24%   
25%
26%   |handle = COM_OpenNXTEx('USB', UseThisNXTMAC)|
27%   This will try to open a connection via USB. Device drivers (Fantom on
28%   Windows, libusb on Linux) have to be installed.
29%
30%
31%   |handle = COM_OpenNXTEx('Bluetooth', UseThisNXTMAC, inifilename, 'check')|
32%   Uses Bluetooth as communication method. A valid inifile containing
33%   parameters like the COM-Port has to be specified in |inifilename|. The
34%   optional paramter |'check'| can be omitted (it will nake sure that the
35%   new Bluetooth connection is working bi-directional). Leave it out if
36%   your hardware does only support sending data (depends on the Bluetooth
37%   hardware, drivers and stack used). In this case you will not be able to
38%   receive any data from the brick, but can still send commands (like e.g.
39%   motor control).
40%   To create an inifile with Bluetooth settings, the function
41%   |COM_MakeBTConfigFile| is available.
42%   
43%   Note that as of right now, the parameter |UseThisNXTMAC| will be
44%   ignored for Bluetooth connections until implemented in a future version.
45%
46%
47%   |handle = COM_OpenNXTEx('Any', UseThisNXTMAC, inifilename, 'check')|
48%   This syntax combines the two parameter settings from above.
49%   |inifilename| has to be given, the optional |'check'| can be omitted.
50%   The function will try to locate an NXT device on the USB bus first. If
51%   this fails for some reason (no USB connection to the NXT available, no
52%   device drivers installed, or NXT device is busy), the function will
53%   silently try to establish a connection via Bluetooth.
54%
55%   The advantage is that this version works with both Bluetooth and USB
56%   connections _without changing_ any code. Plug or unplug the USB cable
57%   to switch between connection types...
58%   
59%
60% Limitations of |COM_CloseNXT|
61%   If you call |COM_CloseNXT('all')| after a |clear all| command has been
62%   issued, the function will not be able to close all remaining open USB
63%   handles, since they have been cleared out of memory. This is a problem
64%   on Linux systems. You will not be able to use the NXT device without
65%   rebooting it.
66%   Solution: Either use only |clear| in your programs, or you use the
67%   |COM_CloseNXT('all')| statement before |clear all|.
68%   The best way however is to track your handles carefully and close them
69%   manually (|COM_CloseNXT(handle)|) before exiting whenever possible!%
70%
71%
72% Example
73%+   myNXT = COM_OpenNXTEx('Any', '001612345678', 'bluetooth.ini', 'check');
74%+   % This will connect to an NXT device with the MAC/serial number 001612345678,
75%+   % first trying via USB. If this fails (no drivers installed or no matching USB
76%+   % device found), a connection via Bluetooth will be established, using
77%+   % the paramters found in the given config file.
78%
79%
80% See also: COM_OpenNXT, COM_CloseNXT, COM_MakeBTConfigFile, COM_SetDefaultNXT
81%
82% Signature
83%   Author: Linus Atorf (see AUTHORS)
84%   Date: 2008/06/11
85%   Copyright: 2007-2008, RWTH Aachen University
86%
87; %#ok<NOSEM>
88%
89% ***********************************************************************************************
90% *  This file is part of the RWTH - Mindstorms NXT Toolbox.                                    *
91% *                                                                                             *
92% *  The RWTH - Mindstorms NXT Toolbox is free software: you can redistribute it and/or modify  *
93% *  it under the terms of the GNU General Public License as published by the Free Software     *
94% *  Foundation, either version 3 of the License, or (at your option) any later version.        *
95% *                                                                                             *
96% *  The RWTH - Mindstorms NXT Toolbox is distributed in the hope that it will be useful,       *
97% *  but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS  *
98% *  FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
99% *                                                                                             *
100% *  You should have received a copy of the GNU General Public License along with the           *
101% *  RWTH - Mindstorms NXT Toolbox. If not, see <http://www.gnu.org/licenses/>.                 *
102% ***********************************************************************************************
103 
104
105%% *** A few guidelines ***
106% This function supports both Linux and Windows with both USB and Bluetooth
107% connections. So we have 4 cases to distinguish.
108% For Bluetooth, the old function BT_OpenHandle was adapted and used. It works for
109% Linux and Windows (it is split internally where necessary).
110% The function USB_OpenHandle is just a wrapper (the counterpart to
111% BT_OpenHandle), the actual work is done by USB_OpenHandle_Windows and
112% USB_OpenHandleLinux (which are very different!).
113%
114% The principle is always the same: We use createHandleStruct() to create an
115% "empty" default handle, that is already initialized to some empty strings and
116% NaNs. We then fill in the required information wherever appropriate.
117%
118% A handle is not considered valid until we call .Connected(true). So the top
119% level wrappers can track back if creation was successful or not.
120%
121% The USB functions have an additional parameter "SuppressErrors". If true, they
122% will "ignore" errors, so later on we can still establish a Bluetooth connection.
123% This way, if USB drivers/libraries are not present, the simple COM_OpenNXT
124% (which is essentially COM_OpenNXTEx('Any', ...) won't fail with USB.
125
126
127
128
129%% Actual code
130
131    % needed to track handles for later COM_CloseNXT('all')
132    global NXTHANDLE_Array
133   
134
135%% Check input parameters...
136
137    % correct connection mode string?
138    if isempty(ConnectionMode) || (~strcmpi(ConnectionMode, 'any') && ~strcmpi(ConnectionMode, 'USB') && ~strcmpi(ConnectionMode, 'Bluetooth'))
139        error('MATLAB:RWTHMindstormsNXT:invalidStringParameter', 'Input argument ConnectionMode has to be either ''USB'', ''Bluetooth'', or ''any''!')
140    end%if
141
142    % nxt name or mac must be string
143    if ~ischar(UseThisNXTMAC)
144        error('MATLAB:RWTHMindstormsNXT:invalidStringParameter', 'Input argument UseThisNXTMAC has to be a string containing the NXT''s name or MAC address, or an empty string '''' for no restrictions!')
145    end%if
146
147    if strcmpi(ConnectionMode, 'USB')
148        % for usb, only 2 params needed
149        if (nargin > 2)
150            error('MATLAB:RWTHMindstormsNXT:tooManyInputArguments', 'Too many input arguments: For connection mode ''USB'', there is only 1 more input argument needed!')
151        end%if
152    else % Note: Nodes BT and any!!! So we require an inifile for any-mode as well!
153        % check varargins
154        if (nargin <= 2)
155            error('MATLAB:RWTHMindstormsNXT:notEnoughInputArguments', 'Not enough input arguments: For connection modes ''any'' and ''Bluetooth'', argument inifilename is required!')
156        else
157            if ~ischar(varargin{1})
158                error('MATLAB:RWTHMindstormsNXT:invalidStringParameter', 'Optional input argument Inifilename has to be a string!')
159            end%if
160        end%if
161        if nargin > 3
162            if ~(ischar(varargin{2}) && strcmpi(varargin{2}, 'check'))
163                error('MATLAB:RWTHMindstormsNXT:invalidVararginParameter','2nd optional input parameter after inifilename has to be ''check'' or nothing.');
164            end%if
165        end%if
166
167    end%if
168
169
170%% Get empty handle, fill with infos
171
172    h = createHandleStruct();
173    h.CreationTime = clock;
174    % The next steps are necessary, we need some sort of timestamp. We take the
175    % current date minus 1 year, so we get a point defenitely in the past! This
176    % way, COM_Send and COM_Collect will start sending / receiving at once.
177    timestamp = clock();
178    timestamp(1) = timestamp(1) - 1;
179    h.LastSendTime(timestamp);   
180    h.LastReceiveTime(timestamp);
181   
182    if ispc
183        h.OSName    = 'Windows';
184        h.OSValue   = 2;
185    else % no support for Mac yet...
186        h.OSName    = 'Linux';
187        h.OSValue   = 2;
188    end%if
189
190    % now set the MAC parameter, the functions later down will evaluate and
191    % overwrite it!
192        h.NXTMAC = UseThisNXTMAC;
193   
194   
195%% Now proceed with OS / mode specific handle creation etc.
196
197    if strcmpi(ConnectionMode, 'any')
198        % for any, try USB first...
199        h = USB_OpenHandle(h, true);
200        if ~h.Connected()
201            % we should have all needed parameters, so lets go
202            h = BT_OpenHandle(h, varargin{1}, varargin{2:end});
203        end%if
204       
205    elseif strcmpi(ConnectionMode, 'USB')
206        h = USB_OpenHandle(h, false);
207
208    elseif strcmpi(ConnectionMode, 'Bluetooth')
209        % pass inifile params and optional check this easy way, works as long
210        % as we dont introduce new varargins for other reasons...
211        h = BT_OpenHandle(h, varargin{1}, varargin{2:end});
212
213    end%if
214   
215
216%% Actual work is done, acquire a slot in global array
217
218    % although we should've a valid handle at this point (or already errored out),
219    % only add it to global list if valid!
220    if h.Connected()
221        h.Index = length(NXTHANDLE_Array) + 1;
222        NXTHANDLE_Array{h.Index} = h;
223    end%if
224   
225
226%% Return handle
227% TODO Actually, we wanted to return only the most important static part
228% of a full handle, to make the struct more compact and don't confuse the
229% user. The problem however is, that if someone retrieves a handle, doesn't
230% set it as default handle and then calls an NXT_ function with the handle
231% as varargin, this handle is "too short" to be used, as it doesn't contain
232% the functions. What do we do?
233% Workaround: We make a "sub-field" of the handle, another struct, called
234% "functions". A handle would then be used like this:
235% h.functions.BytesSent(), etc.
236%
237%
238%     handle.OSName                = h.OSName;
239%     handle.OSValue               = h.OSValue;
240%     handle.ConnectionTypeName    = h.ConnectionTypeName;
241%     handle.ConnectionTypeValue   = h.ConnectionTypeValue;
242%
243%     handle.Handle                = h.Handle;
244%
245%     handle.IniFilename           = h.IniFilename;
246%     handle.ComPort               = h.ComPort;
247%     handle.BaudRate                    = h.BaudRate;
248%     handle.DataBits                    = h.DataBits;
249%     handle.Timeout                     = h.Timeout;
250%
251%     handle.SendSendPause               = h.SendSendPause;
252%     handle.SendReceivePause      = h.SendReceivePause;
253%
254%     handle.NXTMAC                = h.NXTMAC;
255%     handle.CreationTime                = h.CreationTime; 
256%     handle.Index                 = h.Index;
257   
258    handle = h;
259
260   
261end%function
262
263
264%% --- FUNCTION BT_OpenHandle -- the original :-)
265function hOut = BT_OpenHandle(hIn, inifilename, varargin)
266
267%% copy struct info, initialize
268
269    hOut = hIn;
270    hOut.Connected(false);
271
272   
273%% Read parameter from ini file
274    inisection = 'Bluetooth';
275
276    ComPort             = readFromIniFile(           inisection, 'SerialPort',       inifilename);
277    if ispc % WINDOWS serial port version....
278        BaudRate            = str2double(readFromIniFile(inisection, 'BaudRate',         inifilename));
279        DataBits            = str2double(readFromIniFile(inisection, 'DataBits',         inifilename));
280        Timeout             = str2double(readFromIniFile(inisection, 'Timeout',          inifilename));
281        if isempty(Timeout) || isnan(Timeout)
282            Timeout = 2;
283            warning('MATLAB:RWTHMindstormsNXT:Bluetooth:missingInifileParameters','Timeout in bluetooth settings inifile not set. Using default value (2 seconds).')
284        end%if
285    else % LINUX file handle version
286        % on linux, there is no baudrate etc to set, so we ignore these ini
287        % settings, as they are not necessary, and we don't want to display
288        % error messages for ini settings that are not needed in linux.
289        %
290        % we set the variables to default values anyway to avoid
291        % potential errors or is-empty-failures, just in case some functions
292        % checks this...
293        BaudRate = 9600;
294        DataBits = 8;
295        Timeout  = 2;
296    end%if
297    BT_SendSendPause    = str2double(readFromIniFile(inisection, 'SendSendPause',    inifilename));
298    BT_SendReceivePause = str2double(readFromIniFile(inisection, 'SendReceivePause', inifilename));
299
300
301%% Set default sending/receiving delay times if they are not set manually
302    if isempty(BT_SendSendPause) || isnan(BT_SendSendPause) || isempty(BT_SendReceivePause) || isnan(BT_SendReceivePause)
303        warning('MATLAB:RWTHMindstormsNXT:Bluetooth:missingInifileParameters','SendSendPause or SendReceivePause in bluetooth settings inifile not set. Using default values.')
304        BT_SendSendPause    =  5; %ms
305        BT_SendReceivePause =  30; %ms
306    end%if
307
308
309
310%% Open bluetooth connection
311    textOut(sprintf('Opening Bluetooth connection on port %s... ', ComPort));
312
313    try
314        if ispc
315            handle = serial(ComPort,'BaudRate', BaudRate, 'DataBits', DataBits, 'Timeout', Timeout);
316            fopen(handle);
317        else
318            % is unix
319            handle = fopen(ComPort, 'r+');
320        end
321    catch
322        textOut(sprintf('failed.\n'));
323        error('MATLAB:RWTHMindstormsNXT:Bluetooth:couldNotOpenConnection','Could not open bluetooth connection using port %s, BaudRate=%d, DataBits=%d.\nTry using BT_CloseAllHandles() first.', ComPort, BaudRate, DataBits);
324    end%try
325
326    textOut(sprintf('done.\n'));
327
328
329   
330%% Fill handle structure with information
331
332   
333
334    hOut.ConnectionTypeName     = 'Bluetooth';
335    hOut.ConnectionTypeValue    = 2;
336    hOut.Handle                 = handle;
337    hOut.IniFilename            = inifilename;
338    hOut.ComPort                = ComPort;
339    hOut.BaudRate               = BaudRate;
340    hOut.DataBits               = DataBits;
341    hOut.Timeout                = Timeout;
342    hOut.SendSendPause          = BT_SendSendPause;
343    hOut.SendReceivePause       = BT_SendReceivePause;
344    hOut.Connected(true);
345
346   
347%% If required, send test (keep alive) packet
348    if nargin > 2
349        if ischar(varargin{1}) && strcmpi(varargin{1}, 'check')
350
351            try
352                status = NXT_SendKeepAlive('reply', hOut);
353            catch
354                errordlg('*** Bluetooth connection not working ***. Please make sure that the NXT is turned on and your bluetooth adapter is connected.');
355                error('MATLAB:RWTHMindstormsNXT:Bluetooth:connectionNotWorking', ...
356                     ['*** Bluetooth connection not working ***.' char(10) ...
357                      'Please make sure that the NXT is turned on and your bluetooth adapter is connected.']);
358            end%if
359
360            %NOTE should we really display the same message here as above?
361            % Apparently, transfer succeeded, but only the status reply is
362            % wrong. This is interesting: Malformed packet etc? But probably
363            % never happens wnyway...
364            if status ~= 0
365                errordlg('*** Bluetooth connection not working ***. Please make sure that the NXT is turned on and your bluetooth adapter is connected.');
366                error('MATLAB:RWTHMindstormsNXT:Bluetooth:connectionNotWorking', ...
367                     ['*** Bluetooth connection not working ***.' char(10) ...
368                      'Please make sure that the NXT is turned on and your bluetooth adapter is connected.']);
369            end%if
370
371        else
372
373            error('MATLAB:RWTHMindstormsNXT:invalidVararginParameter','Only possible varargin is ''check''');
374        end%if
375    end%if
376
377%% Get NXT's MAC, and see if it matches constraints
378    if ~isempty(hOut.NXTMAC)
379
380        %FIXME get NXT's name & mac in here, and compare...
381       
382    end%if
383
384end%function
385
386
387%% --- FUNCTION USB_OpenHandle
388function hOut = USB_OpenHandle(hIn, SuppressErrors)
389   
390    textOut(sprintf('Opening USB connection...\n'));
391   
392    if hIn.OSValue == 1
393        hOut = USB_OpenHandle_Windows(hIn, SuppressErrors);
394    elseif hIn.OSValue == 2
395        hOut = USB_OpenHandle_Linux(hIn, SuppressErrors);
396    end%if
397   
398end%function
399
400
401%% --- FUNCTION USB_OpenHandle_Windows
402function hOut = USB_OpenHandle_Windows(hIn, SuppressErrors)
403% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % %
404% Temporary USB construction site for the RWTH - Mindstorms NXT Toolbox
405%           http://www.mindstorms.rwth-aachen.de
406%
407% Based on the code by Vital van Reeven
408%           http://forums.nxtasy.org/index.php?showtopic=2018
409%           http://www.vitalvanreeven.nl/page156/fantomNXT.zip
410%
411% Linus Atorf, 29.3.2008
412% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % %
413
414
415% Basic structure of this function: Load fantom library, create NXT iterator (if
416% it fails, no NXT on USB present), loop through all NXT on this iterator
417% (important, otherwise it won't work), take the NXT we want, open it, and done.
418
419
420    %TODO we dont want to suppress all errors! but we do want to avoid errors
421    %caused by missing libraries and errors of the kind "no nxt was found"
422
423
424    % copy struct info
425    hOut = hIn;
426    hOut.Connected(false); % should be false already, why am I so paranoid and set it anyway?
427   
428    hOut.ConnectionTypeValue    = 1;
429    hOut.ConnectionTypeName     = 'USB';
430   
431   
432%% Load Fantom library (only if necessary)
433    if ~libisloaded('fantom')
434        textOut(sprintf('  - Loading library "fantom"... '));
435        try
436            % use "our" wrapper file...
437            loadlibrary('fantom', @fantom_proto);
438            textOut(sprintf('done.\n'));
439        catch
440            textOut(sprintf('failed.\n'));
441            if ~SuppressErrors
442                error('MATLAB:RWTHMindstormsNXT:USB:Windows:couldNotLoadLibraryFantom', 'The "fantom" library could not be loaded. Make sure the official Mindstorms NXT driver is installed correctly!')
443            else
444                return
445            end%if
446        end%try
447    else
448        textOut(sprintf('  - Library "fantom" already loaded.\n'));
449    end%if
450       
451   
452   
453%% Create NXT iterator
454   
455    textOut(sprintf('  - Creating NXT iterator... '))
456
457    % last 3 params are:                        searchbluetooth, searchtimeout, status;
458    [hNXTIterator status] = calllib('fantom', 'nFANTOM100_createNXTIterator', 0, 0, 0);
459    displayUSBWinStatus(status)
460   
461    % if there is no NXT connected, we don't get an iterator...
462    if status
463        if ~SuppressErrors
464            errordlg('No NXT found on USB bus! Make sure the NXT is turned on and device drivers ("Fantom") are properly installed. Rebooting your NXT might help!')
465            error('MATLAB:RWTHMindstormsNXT:USB:Windows:noNXTfound', 'No NXT found on USB bus! Make sure the NXT is turned on and device drivers ("Fantom") are properly installed. Rebooting your NXT might help!')
466        else
467            % don't do anything but exit
468            textOut(sprintf('Could not open USB connection.\n'));
469            return
470        end%if
471    end%if
472
473
474   
475%% Loop through all NXTs
476    gotNXT = false;
477   
478    textOut(sprintf('  - Enumerating NXT devices on USB bus\n'));
479    status = 0;
480    while(~status) % successful if == 0
481
482        % the first time we're in this loop, the iterator already points to the
483        % first NXT, so we can retrieve it's device id straight away. At this
484        % point we already have an NXT, otherwise we wouldn't have gotten an
485        % iterator in the first place!
486       
487        textOut(sprintf('    . getting current NXT''s device id... '));
488        [deviceID stat] = calllib('fantom', 'nFANTOM100_iNXTIterator_getName', hNXTIterator, blanks(255), 0); %deviceid, status; ok
489        displayUSBWinStatus(stat)
490        if ~stat
491            textOut(sprintf(['      deviceID = ' deviceID '\n']));
492        end%if
493
494        % replace :: with spaces, get 4th item
495        MAC = sscanf(strrep(deviceID, '::', ' '), '%*s %*s %*s %s %*s');
496       
497        % only get NXT if MAC matches and if we don't already have one
498        if ~gotNXT && (isempty(hIn.NXTMAC) || strcmpi(hIn.NXTMAC, MAC))
499            textOut(sprintf('    . opening current NXT... '));
500            [hNXT stat]= calllib('fantom', 'nFANTOM100_iNXTIterator_getNXT', hNXTIterator, 0); %status; ok           
501            displayUSBWinStatus(stat)
502            if stat
503                if ~SuppressErrors
504                    errordlg('Could not open desired device! Is it already in use? Rebooting your NXT might help!')
505                    error('MATLAB:RWTHMindstormsNXT:USB:Windows:couldNotOpenNXT', 'Could not open desired device! Is it already in use? Rebooting your NXT might help!')
506                else
507                    textOut(sprintf('Could not open USB connection./n'));
508                    return
509                end%if
510            end%if
511           
512            % remember for later!
513            hOut.NXTMAC = MAC;
514            hOut.Handle = hNXT;
515            hOut.Connected(true);
516
517            gotNXT = true; % set flag to mark our great success
518
519        end%if
520           
521        % Note that we HAVE to advance to the last device of the iterator,
522        % until the call fails! If we don't do so, the next time we create
523        % a fresh iterator after properly destroying the current, we would
524        % get an invalid one. Really annoying, took me ages to find out,
525        % but since we now know, everything is fine.
526       
527        textOut(sprintf('    . advancing to next NXT... '));
528        status = calllib('fantom', 'nFANTOM100_iNXTIterator_advance', hNXTIterator, 0);
529        if status
530            % here we've got our error, just as expected (and desired, see comment above)
531            % the while-loop won't iterate now, so we don't need a return here
532            textOut(sprintf('done. (this was the last one)\n'));
533        else
534           textOut(sprintf('done.\n'));
535        end%if
536       
537    end%while - proceed to next NXT
538
539    textOut(sprintf('  - Destroying NXT iterator... '));
540    status = calllib('fantom', 'nFANTOM100_destroyNXTIterator', hNXTIterator, 0);
541    displayUSBWinStatus(status)
542   
543    % did we have success?
544    if ~gotNXT
545        if ~SuppressErrors
546            errordlg('No NXT found that matches your parameters (MAC address or NXT''s name)! Please check the constraints. (Rebooting your NXT might help!)')
547            error('MATLAB:RWTHMindstormsNXT:USB:Windows:noMatchingNXTfound', 'No NXT found that matches your parameters (MAC address or NXT''s name)! Please check the constraints. (Rebooting your NXT might help!)')
548        else
549            textOut(sprintf('Could not open USB connection (no matching NXT found)./n'));
550            return
551        end%if
552    end%if
553     
554
555end%function
556
557
558%% --- FUNCTION displayUSBWinStatus(status)
559function displayUSBWinStatus(status)
560% little convenient helper to save some lines of code with textOut
561    if status
562        textOut(sprintf('failed.\n'));
563        textOut(sprintf(['VISA error ' num2str(status) ': ' getVISAErrorString(status) '\n']))
564    else
565        textOut(sprintf('done.\n'));
566    end%if
567end%function
568
569
570%% --- FUNCTION displayLibusbStatus(status)
571% little convenient helper to save some lines of code with textOut
572function displayLibusbStatus(status)
573% little convenient helper to save some lines of code with textOut
574    if isnumeric(status) && (status < 0)
575        textOut(sprintf('failed.\n'));
576        textOut(sprintf(['Libusb error ' num2str(status) ': ' getLibusbErrorString(status) '\n']))
577    else
578        textOut(sprintf('done.\n'));
579    end%if
580end%function
581
582
583%% --- FUNCTION USB_OpenHandle_Linux
584function hOut = USB_OpenHandle_Linux(hIn, SuppressErrors)
585% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % %
586% Temporary USB construction site for the RWTH - Mindstorms NXT Toolbox
587%           http://www.mindstorms.rwth-aachen.de
588%
589% Linus Atorf, 29.4.2008
590% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % %
591
592% Basic structure of this function:
593% Load library libusb, initialize it, get root bus object, loop through all
594% busses, for each bus loop through all devices, see if vendor and product id
595% match with lego NXT, try to open device, read serial no (which is = MAC), if it
596% matches, keep it open. then do basic USB device initialization (set config,
597% claim interface, reset device). dev should be ready then.
598
599%TODO There is still one big problem with Multi-NXT: If the first one that is
600%connected is also the first that was opened (and is now in use), the usb_open_dev
601%might fail when looping through all devs (since the dev is busy). Careful testing
602%is necessary to see wether opening an "NXT in use" fails, or if it is possible
603%and only fails when claiming its interface (I think that's the case actually, so
604%this would not be an issue after all).
605
606
607
608%% initialize etc...
609    ID_VENDOR_LEGO = 1684; % hex2dec('0694');
610    ID_PRODUCT_NXT = 2;    % hex2dec('0002');
611
612    LIBUSB_Configuration = 1;
613    LIBUSB_Interface = 0;
614   
615   
616    hOut = hIn;
617    hOut.Connected(false);
618
619    hOut.ConnectionTypeValue    = 1;
620    hOut.ConnectionTypeName     = 'USB';
621   
622%% Load libusb library (only if necessary)
623    if ~libisloaded('libusb0')
624        textOut(sprintf('  - Loading library "libusb0"... '));
625        try
626            % use "our" wrapper file...
627            loadlibrary('libusb0', @libusb_win_proto)
628            textOut(sprintf('done.\n'));
629        catch
630            textOut(sprintf('failed.\n'));
631            if ~SuppressErrors
632                error('MATLAB:RWTHMindstormsNXT:USB:Linux:couldNotLoadLibraryLibusb', 'The "libusb" library could not be loaded (file "libusb" or "libusb.so"). Make sure it is installed and paths are set correctly!')
633            else
634                return
635            end%if
636        end%try
637    else
638        textOut(sprintf('  - Library "libusb0" already loaded.\n'));
639    end%if
640
641
642    textOut(sprintf('  - Initializing and browsing USB busses\n'));
643
644%% Init libusb
645    textOut(sprintf('    . initializing libusb0.\n'))
646    calllib('libusb0', 'usb_init');
647   
648    % > - insert usb_set_debug(255) in pyusb.c right behind usb_init()
649    % > - recompile and reinstall the module
650    % > - hook up DebugView from www.sysinternals.com
651    % > - rerun your application
652    % so if we want debug mode, this needs to be uncommented!
653    %calllib('libusb', 'usb_set_debug', 255);
654
655
656    % these functions return the number of changes made to the busses and
657    % devices since last call, don't neet it...
658    textOut(sprintf('    . finding busses... '))
659    ret = calllib('libusb0', 'usb_find_busses');
660    displayLibusbStatus(ret);
661   
662    textOut(sprintf('    . finding devices... '))
663    ret = calllib('libusb0', 'usb_find_devices');
664    displayLibusbStatus(ret);
665
666%% Get main root bus
667   
668    textOut(sprintf('    . getting root bus object... '))
669    p = calllib('libusb0', 'usb_get_busses');
670    bus = libstruct('usb_bus', p);
671    displayLibusbStatus(p);
672   
673
674    textOut(sprintf('  - Enumerating busses and devices\n'));
675
676%% Cycle through all busses and devices: enumerate...
677    foundNXT = false;
678    while ~foundNXT % for all busses
679       
680        % we want to save some debug-lines (already got too many), so we don't
681        % care what USB devices the user has. remove comment for advanced debug
682        % mode
683        %textOut(sprintf('    . current bus: %s\n', strtrim(char(bus.dirname))))
684
685        dev = libstruct('usb_device', bus.devices);
686
687        % -------------------------------------
688        while true % for all devices! sorry for endless-loop, but we break later down
689
690            % if no devs at all
691            if isempty(dev)
692                break
693            end%if
694
695
696            % we want to save some debug-lines (already got too many), so we don't
697            % care what USB devices the user has. remove comment for advanced debug
698            % mode
699            textOut(sprintf('    . current device: %s\n', strtrim(char(dev.filename))))
700
701
702            % try a little string reading....
703
704            % THIS DOESN'T WORK UNDER LINUX MOST OF THE TIME
705            % APPARENTLY, WE CANNOT OPEN ALL PRESENT DEVICES
706            % Since we don't want to try to open a device now, we keep this
707            % working code (at least for public devices on Linux and for
708            % alle devices on Windows) commented, maybe someone else can
709            % use it to improve this function later on...
710            % It's debug-info only anyway
711
712           % open device to get a handle
713            DevHandle = calllib('libusb0', 'usb_open', dev);
714     
715            % now the string stuff
716            buffer = blanks(255);
717            % we don't need a real buffer or pointer, matlab seems to do this
718            % for us, so we pass that buffer variable, without really needing
719            % it. it seems like matlab "knows" how strings get written by
720            % reference and returns the new value from the function. if you
721            % compare the matlab-returnvalues of usb_get_string_simple using
722            % libfunctionsview libusb, you'll find that they don't match whats
723            % written inside usb.h. very nice and handy, thank you matlab :-)
724            %pBuffer = libpointer('cstring', buffer); not needed, see above
725           
726            % now the actual call:
727            [bytesRead newHandleOrWhat ManufacturerName] = calllib('libusb0', 'usb_get_string_simple', ...
728                       DevHandle, dev.descriptor.iManufacturer,  buffer, length(buffer));
729            % again:
730            [bytesRead newHandleOrWhat ProductName] = calllib('libusb0', 'usb_get_string_simple', ...
731                       DevHandle, dev.descriptor.iProduct,  buffer, length(buffer));
732                   
733            disp(sprintf('        Manufacturer: %s', ManufacturerName))
734            disp(sprintf('        Product: %s', ProductName))
735           
736            % close device again
737            ret = calllib('libusb0', 'usb_close', DevHandle);
738            clear DevHandle %better doing it now than forgetting it later
739
740
741            % note at this point that we only try to open a device if it's one
742            % from LEGO!
743   
744            % check if it's LEGO and NXT
745            if (dev.descriptor.idVendor == ID_VENDOR_LEGO) && (dev.descriptor.idProduct == ID_PRODUCT_NXT)
746                textOut(sprintf('    . found NXT device\n'));
747
748%                 textOut(sprintf('    . opening device... '));
749%                 DevHandle = calllib('libusb', 'usb_open', dev);
750%                 displayLibusbStatus(DevHandle);
751%
752%                 textOut(sprintf('    . reading serial number... '));
753%                 % c-syntax from documentation so we know whats going on:
754%                 % int usb_get_string_simple(usb_dev_handle *dev, int index, char *buf, size_t buflen);
755%                 buffer = blanks(255);
756%                 [bytesRead newHandleOrWhat SerialNo] = calllib('libusb', 'usb_get_string_simple', DevHandle, dev.descriptor.iSerialNumber,  buffer, length(buffer));
757%                 displayLibusbStatus(bytesRead);
758%                                 
759%                 SerialNo = strtrim(SerialNo); % just to be safe
760%                 textOut(sprintf('    . MAC = %s ', SerialNo))
761%                 
762%                 % is it the right one?
763%                 if isempty(hIn.NXTMAC) || strcmpi(hIn.NXTMAC, SerialNo)
764%                     foundNXT = true;
765%                     textOut(sprintf('(MAC matches, this is our desired NXT)\n'));
766%                     break % search came to an end
767%                 else
768%                     foundNXT = false;
769%                     textOut(sprintf('(no match, not using this NXT)\n'));
770%                     % don't forget to close the opened NXT that didnt match...
771%                     textOut(sprintf('    . closing device... '));
772%                     status = calllib('libusb', 'usb_close', DevHandle);
773%                     displayLibusbStatus(status);
774%                 end%if
775               
776            end%if
777
778
779            % we are at the end of our "pointer-queue"
780            if isempty(dev.next)
781                break
782            else
783                % get next device, not that easy, but it works now \o/
784                ptr = dev.next;
785                ptr.setdatatype('usb_device');
786                dev = ptr.Value;
787                clear ptr % to be sure, don't want to risk memory leaks
788            end%if
789           
790        end%while (for all devices)
791        % -------------------------------------
792
793       
794        % no need to scan other busses if already found
795        if foundNXT
796            break
797        end%if
798
799        % jump to next bus - same procedure as for devices, see above
800        if isempty(bus.next)
801            break
802        else
803            ptr = bus.next;
804            ptr.setdatatype('usb_bus');
805            bus = ptr.Value;
806            clear ptr
807        end%if
808       
809    end%while (for all busses)
810
811
812%% check if NXT present
813    if ~foundNXT
814        if ~SuppressErrors
815            errordlg('No NXT found on USB bus! Make sure the NXT is turned on and access rights in /dev/ are properly set. Rebooting your NXT might help!')
816            error('MATLAB:RWTHMindstormsNXT:USB:Linux:noNXTfound', 'No NXT found on USB bus! Make sure the NXT is turned on and access rights in /dev/ are properly set. Rebooting your NXT might help!')
817        else
818            % well, no NXT, exit silently
819            return
820        end%if
821    end%if
822
823
824% %% NXT found, open connection
825%
826%     % DevHandle is now the NXT we want
827%     
828%     % the following commands are standard procedure for USB devices, similar usage
829%     % can be found in the open source packages Python_NXT and LEGO::NXT (Perl)
830%
831%     % flag to remember
832%     ErrorWhileOpening = false;
833%     
834%     % if the following fails with error -16, the NXT is probably already in
835%     % use (opened in another handle!)
836%     textOut(sprintf('    . setting active configuration... '));
837%     % somehow this doesn't work, so we use the hardcoded configuration 1!
838%     %calllib('libusb', 'usb_set_configuration', DevHandle, dev.config.bConfigurationValue);
839%     ret = calllib('libusb', 'usb_set_configuration', DevHandle, LIBUSB_Configuration);
840%     displayLibusbStatus(ret);
841%     if (ret < 0)
842%         ErrorWhileOpening = true;
843%     end%if
844%
845%     textOut(sprintf('    . claiming interface... '));
846%     % again, interface is hardcoded (compare implementations in python and
847%     % perl, they do it the same way)
848%     %calllib('libusb', 'usb_claim_interface', DevHandle, dev.config.interface.altsetting.bInterfaceNumber);
849%     ret = calllib('libusb', 'usb_claim_interface', DevHandle, LIBUSB_Interface);
850%     displayLibusbStatus(ret);
851%     if (ret < 0)
852%         ErrorWhileOpening = true;
853%     end%if
854%     
855%     
856%     % we don't need an alternative interface, whatever that is.
857%     % but from debugging experience (also with Windows), you never know
858%     % when you might need this, so we keep it in here!
859%     
860%     % disp('    . set altinterface')
861%     % %  %[int32, usb_dev_handlePtr] usb_set_altinterface(usb_dev_handlePtr, int32)
862%     % ret = calllib('libusb', 'usb_set_altinterface', DevHandle, dev.config.interface.altsetting.bInterfaceNumber);
863%     % disp(sprintf('usb_strerror: %s', calllib('libusb', 'usb_strerror')))
864%
865%     % is this necessary? but found it in perl and python versions...
866%     % the main point: never touch a running system
867%     textOut(sprintf('    . resetting device... '));
868%     ret = calllib('libusb', 'usb_reset', DevHandle);
869%     displayLibusbStatus(ret);
870%     if (ret < 0)
871%         ErrorWhileOpening = true;
872%     end%if
873%     
874%     % now it's time to decide:
875%     if ErrorWhileOpening || isnumeric(DevHandle)
876%         if ~SuppressErrors
877%             errordlg('Something went wrong while opening the NXT device via USB (is it already open in another handle?). Please try to reboot the NXT or call COM_CloseNXT(''all'')!')
878%             error('MATLAB:RWTHMindstormsNXT:USB:Linux:couldNotOpenNXT', 'Something went wrong while opening the NXT device via USB (is it already open in another handle?). Please try to reboot the NXT or call COM_CloseNXT(''all'')!')
879%         else
880%             % again, exit silently when no success
881%             return
882%         end%if
883%     end%if
884%     
885%     % finally, we're good to go!
886%     hOut.Handle = DevHandle;
887%     hOut.NXTMAC = SerialNo;
888%
889%     % and, important:
890%     hOut.Connected(true);
891%     
892%     
893% %% clean up
894%
895%     % is this needed? or will matlab destroy this private vars anyway after
896%     % finishing this function? just to be sure with pointers...
897%     clear p v bus dev newHandleOrWhat DevHandle
898%% NXT found, open connection
899
900% dev should now be our NXT
901
902disp('*** Opening USB connection')
903
904disp('    . getting device handle')
905DevHandle = calllib('libusb0', 'usb_open', dev);
906if isnumeric(DevHandle)
907    ret = DevHandle;
908    if ret < 0
909        disp(getLibusbErrorString(ret))
910    end%if
911end%if
912
913disp('    . reading serial number:')
914% int usb_get_string_simple(usb_dev_handle *dev, int index, char *buf, size_t buflen);
915buffer = blanks(255);
916[bytesRead newHandleOrWhat SerialNo] = calllib('libusb0', 'usb_get_string_simple', DevHandle, dev.descriptor.iSerialNumber,  buffer, length(buffer));
917disp(sprintf('      %s', SerialNo))
918
919disp('    . setting active configuration')
920%calllib(USBLIB, 'usb_set_configuration', DevHandle, dev.config.bConfigurationValue);
921ret = calllib('libusb0', 'usb_set_configuration', DevHandle, 1);
922if ret < 0
923    disp(getLibusbErrorString(ret))
924end%if
925
926disp('    . claiming interface')
927%calllib(USBLIB, 'usb_claim_interface', DevHandle, dev.config.interface.altsetting.bInterfaceNumber);
928ret = calllib('libusb0', 'usb_claim_interface', DevHandle, 0);
929if ret < 0
930    disp(getLibusbErrorString(ret))
931end%if
932
933
934% disp('    . set altinterface')
935% %  %[int32, usb_dev_handlePtr] usb_set_altinterface(usb_dev_handlePtr, int32)
936% ret = calllib('libusb0', 'usb_set_altinterface', DevHandle, dev.config.interface.altsetting.bInterfaceNumber);
937% disp(sprintf('usb_strerror: %s', calllib('libusb0', 'usb_strerror')))
938%
939
940% disp('    . resetting device')
941% ret = calllib(USBLIB, 'usb_reset', DevHandle);
942% if ret < 0
943%     disp(getLibusbErrorString(ret))
944% end%if
945
946%   // Discard any data that is left in the buffer
947%   while (usb_bulk_read(nxt->hdl, 0x82, buf, sizeof(buf), 1) > 0)
948%     ;
949disp('    . discarding any data')
950buffer = char(uint8(blanks(64)));
951buffer = uint8(zeros(64,1));
952ret = 1;
953while ret > 0
954   ret = calllib('libusb0', 'usb_bulk_read', DevHandle, uint8(130), buffer, 5, 1);
955end
956
957%   // try to set the stream I/O feature
958%   ret = usb_control_msg(nxt->hdl, 0x41, 0x3, 0, 0, NULL, 0, 1000);
959%   if (ret >= 0)
960%   {
961%     nxt->stream_mode = 1;
962%   }
963disp('    . open stream modus')
964buffer = char(uint8(blanks(64)));
965ret = calllib('libusb0', 'usb_control_msg', DevHandle, 65, 3, 0, 0, buffer, 0, 1000);
966if ret < 0
967  msg = ['Libusb error ' num2str(ret) ' while open stream mode: ' getLibusbErrorString(ret)];
968  warning('MATLAB:RWTHMindstormsNXT:USB:Linux:libusbErrorWhileOpenStreamMode', msg);
969end%if
970
971hNXT = DevHandle;
972
973% finally, we're good to go!
974    hOut.Handle = DevHandle;
975    hOut.NXTMAC = SerialNo;
976
977    % and, important:
978    hOut.Connected(true);
979
980
981%% clean up
982
983% needed? or will matlab destroy this private vars anyway after finishing
984% this function? probably...
985clear p v bus dev newHandleOrWhat
986
987if ~isnumeric(hNXT)
988    disp('Successfully established USB connection to NXT')
989else
990    disp('Something went wrong, handle doesn''t seem valid')
991end%if
992
993end%function
Note: See TracBrowser for help on using the browser.