Code covered by the BSD License  

Highlights from
ECRobotInstaller – Simplified installation of tools for Embedded Coder Robot

image thumbnail

ECRobotInstaller – Simplified installation of tools for Embedded Coder Robot

by

 

02 Sep 2009 (Updated )

Simplifies the installation of ECRobot, a Simulink platform for LEGO Mindstorms NXT code generation

install_ecrobot_tools
function install_ecrobot_tools
% INSTALL_ECROBOT_TOOLS
%
%   INSTALL_ECROBOT_TOOLS automatically installs all the programs necessary
%   for running ECRobot (the embedded coder robot for LEGO Mindstorms NXT).
%   It assumes that all the required files have been downloaded already
%   (by running DOWNLOAD_ECROBOT_TOOLS or by copying them from elsewhere).
% 
%   Notes on usage:
%
%   1) INSTALL_ECROBOT_TOOLS reads the configuration information (install
%   directories, download filenames, etc.) from ECROBOT_INSTALL_CONFIG.M,
%   so before executing this function, ensure that the information in
%   ECROBOT_INSTALL_CONFIG is valid.  
%
%   2) It is safe to run INSTALL_ECROBOT_TOOLS multiple times. At worst, it
%   causes some unecessary copying or unzipping of files. 
%
%   3) The install information is logged to C:\TEMP\ECRobot_tools_install_log.txt

%   Copyright 2009 The MathWorks, Inc.

% ECRobotInstaller version 1.3: (April-02-2010)
%   * Updated Cygwin install to use the new --packages and --categories 
%     options (for cygwin setup.exe version 2.693)
%   * Updated ECRobot install to accomodate ECRobot ver 3.18
%
% ECRobotInstaller version 1.37: (Jan-01-2011)
%   * Added "libintl3" to the list of cygwin packages to be installed
%    (this is required by GNU ARM Compiler ver 4.0.2, which in turn is 
%     required by ECRobot). 
%   * "Install status being logged to ..." message hyperlinks to logfile
%
% % ECRobotInstaller version 1.38: (Dec-01-2011)
%   * Post-install check of GNU ARM tools modified. The cmdstr adds cygwin 
%     to the path before invoking arm-elf-gcc
%   * configure_ecrobot checks for stale ver_nxtOSEK.p & deletes it 
%
% % ECRobotInstaller version 1.381: (Dec-07-2011)
%   * configure_ecrobot now patches ecrobotnxtsetup.m to handle make 3.82 
%     as well as make 3.81.  

    if ~ispc
        fprintf('This installer runs only on Windows PCs\n');
        return;
    end

    if ~(exist('ecrobot_install_config', 'file') == 2)
        fprintf('Unable to find ecrobot_install_config.m\n');
        return;
    end
    
    logfileName = fullfile(tempdir, 'ECRobot_tools_install_log.txt');
    fid = fopen(logfileName, 'a');
    if fid < 0
        fprintf('Unable to create log file (%s)\n', logfileName);
        return;
    else
        fprintf('Install status being logged to <a href="matlab:edit(''%s'')">%s</a>\n', logfileName, logfileName);
    end
    
    % initialize logging functions
    visibleLog(fid);
    silentLog(fid);
    
    visibleLogSection('install_ecrobot_tools invoked');
    errorinfo = [];
    try
        config = ecrobot_install_config;
        verifyConfig(config, 'installation_dirs_create', @visibleLog);
                
        % note: install nxtosek after installing ecrobot        
        % (nxtosek has to go in ecrobot's environment subdirectory)
        
       install_nxt_firmware(config);
       install_nxt_tools(config);
       install_ecrobot(config);
       install_nxtosek(config);        
       install_cygwin(config);
       install_gnutools(config);
       install_lego_usb_driver(config);
        
        % automatically configure ecrobot
        configure_ecrobot(config);
    catch  %#ok<CTCH>
        visibleLog('Install cancelled\n');
        % save error information only if it isn't generated by us
        le = lasterror; %#ok<LERR>
        if ~strncmp(le.identifier, 'ecrobot_installer', 17)
            errorinfo = le;
        end        
    end
    
    fclose(fid);    
    if ~isempty(errorinfo)
        rethrow(errorinfo);
    end
    
    % reset logging functions 
    visibleLog([]);
    silentLog([]);    
end

%% Logging functions
function visibleLog(varargin)
    persistent fileID
    if isnumeric(varargin{1})
        fileID = varargin{1};
        return;
    end
    fprintf(1, varargin{:});
    fprintf(fileID, varargin{:});
end

function silentLog(varargin)
    persistent fileID
    if isnumeric(varargin{1})
        fileID = varargin{1};
        return;
    end    
    fprintf(fileID, varargin{:});
end

function visibleLogSection(sectionname)    
    visibleLog('\n================ %s %s\n', datestr(now), sectionname);
end

%%
function install_cygwin(config)
    % Step 1: CYGWIN
    % setup.exe is in the cygwin_packages directory as well
    % setup.exe --local-install c:\cygwin_packages --root c:\cygwin
    % --local-package-dir c:\cygwin_packages --no-shortcuts --quiet-mode
    % this page lists how to check for cygwin registry keys
    % http://www1.maths.lth.se/help/windows/cygwin/
    % winqueryreg('name', 'HKEY_CURRENT_USER', 'Software\Cygnus Solutions')
    visibleLogSection('Starting Cygwin Install');

    if isempty(config.Cygwin_PackageDirectory) || isempty(config.Cygwin_InstallationDirectory)
        visibleLog('  Skipping installation\n');
        return;
    end        
    
    fprintf('\n   If the Cygwin install seems "stuck", there may be an open Cygwin dialog box in the background\n\n');
    
    packageDir = fullfile(config.ArchiveDir, config.Cygwin_PackageDirectory);
    setupExe = fullfile(packageDir, 'setup.exe');

    % Note: setup.exe cannot handle -root <dirname> when 
    % the dirname is in quote quotes
    switch config.InstallType        
        case 'RequireClickthrough'
            cmdstr =  ['"' setupExe '"' ...
                ' --local-install '     ...
                ' --no-shortcuts ' ...
                ' --categories Base ' ...
                ' --packages make,libintl3 ' ...                
                ' --root '              config.Cygwin_InstallationDirectory ...
                ' --local-package-dir ' '"' packageDir '"'  ...
                ]; 
            
        case 'Automated'
            cmdstr =  ['"' setupExe '"' ...
                ' --local-install '     ...
                ' --no-shortcuts ' ...
                ' --categories Base ' ...
                ' --packages make,libintl3 ' ...                
                ' --quiet-mode '  ...
                ' --root '              config.Cygwin_InstallationDirectory ...
                ' --local-package-dir ' '"' packageDir '"'  ...
                ];
            
        otherwise
            error('Invalid InstallType option. Aborting Cygwin install');
    end

   visibleLog('  setup file = %s\n  install dir = %s\n',  setupExe, ...
             config.Cygwin_InstallationDirectory);
           
    [status1,result]=system(cmdstr);
    silentLog('\n%s\n%s\n', cmdstr, result);
    addedPackages = regexpi(result, '\nAdded manual package');
     if status1 || (numel(addedPackages) == 0)
        visibleLog('Cygwin does not seem to be installed correctly!\n');
     end
    
    cygcheck = fullfile(config.Cygwin_InstallationDirectory, 'bin', 'cygcheck');
    cmdstr = ['"' cygcheck '"' ' --version'];
    [status2,result]=system(cmdstr);
    silentLog('\n%s\n%s\n', cmdstr, result);
    if status2
        visibleLog('Cygwin does not seem to be installed correctly!\n');
    end                
    
    cygwin_make = fullfile(config.Cygwin_InstallationDirectory, 'bin', 'make');
    cmdstr = ['"' cygwin_make '"' ' --version'];
    [status,result]=system(cmdstr);
    silentLog('\n%s\n%s\n', cmdstr, result);
    if status
        visibleLog('Problem executing Cygwin MAKE command!\n');
    else
        indices = find(result == 10); % search for newlines
        if isempty(indices), indices = numel(result); end
        visibleLog('Cygwin MAKE correctly installed (%s)\n', result(1:indices(1)-1));
    end
end

%%
function install_gnutools(config)
    % Step 2: GNU ARM tools
    visibleLogSection('Starting Gnu ARM Tools Install');
       
    if isempty(config.GnuTools_EXEFile) || isempty(config.GnuTools_InstallationDirectory)
        visibleLog('  Skipping installation\n');
        return;
    end     

    % check if gnu tools are already installed
    try
        gnuPath = winqueryreg('HKEY_LOCAL_MACHINE', 'SOFTWARE\GNUARM\4.0.2', 'InstallPath');
        elfgcc = fullfile(config.GnuTools_InstallationDirectory, 'bin', 'arm-elf-gcc.exe');
        alreadyInstalled = compareDirectoryNames(config.GnuTools_InstallationDirectory, gnuPath) ...
                           && verifyFile(elfgcc);        
    catch  %#ok<CTCH>
        % key is not present, so GNU tools aren't installed
        alreadyInstalled = false;
    end
    
    if alreadyInstalled
        visibleLog('GNU ARM Tools appear to be already installed\n');
        return;
    end
        
    % The list of components was identified by generating a INF file (using
    % the  /SAVEINF parameter). Below, we stash the list of components in a
    % separate string to prevent the backslashes from being interpreted as
    % FPRINTF control characters.        
    % all components (little & big endian)
    % componentStr = 'l,l\o,l\n,l\i,l\t,l\t\o,l\t\i,b,b\o,b\n,b\i,b\t,b\t\o,b\t\i';
    
    % only little endian components, without floating-point support
    componentStr = 'l\o,l\n,l\i,l\t,l\t\o,l\t\i';
       
    gnuSetupExe = fullfile(config.ArchiveDir, config.GnuTools_EXEFile);    
    
    switch config.InstallType
        case 'RequireClickthrough'
            cmdstr =  ['"' gnuSetupExe '"' ...
                ' /norestart '     ...
                ' /lang=default ' ...
                ' /dir="' config.GnuTools_InstallationDirectory '"' ...
                ' /noicons ' ...
                ' /components="' componentStr '"' ...
                ' /tasks="!cygwindlls" ' ...
                ]; 
            
        case 'Automated'
            cmdstr =  ['"' gnuSetupExe '"' ...
                ' /silent ' ...
                ' /norestart '     ...
                ' /lang=default ' ...
                ' /dir="' config.GnuTools_InstallationDirectory '"' ...
                ' /noicons ' ...
                ' /components="' componentStr '"' ...
                ' /tasks="!cygwindlls" ' ...
                ];
            
        otherwise
            error('Invalid InstallType option. Aborting GNU ARM install');            
    end
           
    visibleLog('  setup file = %s\n  install dir = %s\n', gnuSetupExe, config.GnuTools_InstallationDirectory);
    
    [status1,result]=system(cmdstr);
    silentLog('\n%s\n%s\n', cmdstr, result);
    if status1 && ~isempty(result)
        visibleLog('Possible problem with GNU ARM setup: "%s"', result);
    end
    
    elfgcc = fullfile(config.GnuTools_InstallationDirectory, 'bin', 'arm-elf-gcc.exe');
    
    cygwinBinDir = fullfile(config.Cygwin_InstallationDirectory,'bin');    
    gccCmdstr = ['"' elfgcc '"' ' -dumpversion'];
    cmdstr = ['set path=' cygwinBinDir ';%PATH%; && ' gccCmdstr];
    
    [status2,result]=system(cmdstr);
    silentLog('\n%s\n%s\n', cmdstr, result);
    if status2 && ~isempty(result)
        visibleLog('Possible problem invoking arm-elf-gcc.exe: "%s"', result);
    end
end

%%
function install_lego_usb_driver(config)    
    visibleLogSection('Starting LEGO USB Driver installation');    
    
    if isempty(config.LEGOUSBDriver_ZIPFile) || isempty(config.LEGOUSBDriver_InstallationDirectory)
        visibleLog('  Skipping installation\n');
        return;
    end   
    
    zipfile = fullfile(config.ArchiveDir, config.LEGOUSBDriver_ZIPFile);
    do_unzip(zipfile, config.LEGOUSBDriver_InstallationDirectory);

    setupExe = fullfile(config.LEGOUSBDriver_InstallationDirectory, 'MINDSTORMS NXT Driver v1.02', 'setup.exe');
    
    % The following command should do an automated install, but it keeps
    % complaining that the <RootDirectory> key in silent_install.txt is
    % incorrect (even though that file comes with the zip file). 
    %   silentInstallFile = fullfile(config.LEGOUSBDriver_InstallationDirectory, ...
    %                               'MINDSTORMS NXT Driver v1.02', 'Bin', 'silent_install.txt');    
    %   cmdstr = sprintf('"%s" "%s" /q /AcceptLicenses yes /r:n', setupExe, silentInstallFile);
    %  (rhe /r:n suppresses the automatic restart)
    
    % instead just do a default install (this will install under
    % C:\Program Files\LEGO Software\LEGO MINDSTORMS NXT)
    
    switch config.InstallType
        case 'RequireClickthrough'
            % Let the user do the install (safe to do even if LEGO driver is already installed)
            % The /r:n suppresses the automatic restart
            cmdstr = sprintf('"%s" /r:n', setupExe);
            
        case 'Automated'
            cmdstr = sprintf('"%s" /q /AcceptLicenses yes /r:n', setupExe);
            
        otherwise
            error('Invalid InstallType option. Aborting LEGO NXT Driver install');            
    end
    
    visibleLog(' Starting automated install ...\n');
    [status,result]=system(cmdstr);
    silentLog('\n%s\n%s\n', cmdstr, result);    
    if status 
        visibleLog(' LEGO USB Driver does not seem to be installed correctly (result = "%s")!\n', result);
    else
        visibleLog(' Installed LEGO USB Driver\n');
    end        
        
end


%%
function install_nxt_tools(config)
    visibleLogSection('Starting NXT Tools installation');
    if isempty(config.NXTTOOL_ZIPFile) || isempty(config.NXTTOOL_InstallationDirectory)
        visibleLog('  Skipping installation\n');
        return;
    end            
    zipfile = fullfile(config.ArchiveDir, config.NXTTOOL_ZIPFile);
    
    do_unzip(zipfile, config.NXTTOOL_InstallationDirectory);
end

%%
function install_nxtosek(config)
    visibleLogSection('Starting NXT OSEK installation');
    if isempty(config.NXTOSEK_ZIPFile) || isempty(config.NXTOSEK_InstallationDirectory)
        visibleLog('  Skipping installation\n');
        return;
    end
    zipfile = fullfile(config.ArchiveDir, config.NXTOSEK_ZIPFile);
    
    do_unzip(zipfile, config.NXTOSEK_InstallationDirectory);
end

%%
function install_nxt_firmware(config)
    visibleLogSection('Starting NXT Enhanced firmware installation');
    if isempty(config.NXTEnhancedFirmware_ZIPFile) || isempty(config.NXTEnhancedFirmware_InstallationDirectory)
        visibleLog('  Skipping installation\n');
        return;
    end        
    zipfile = fullfile(config.ArchiveDir, config.NXTEnhancedFirmware_ZIPFile);    
    do_unzip(zipfile, config.NXTEnhancedFirmware_InstallationDirectory);
end

%%
function install_ecrobot(config)
    visibleLogSection('Starting ECRobot installation');
    if isempty(config.ECRobot_ZIPFile) || isempty(config.ECRobot_InstallationDirectory)
        visibleLog('  Skipping installation\n');
        return;
    end        
    zipfile = fullfile(config.ArchiveDir, config.ECRobot_ZIPFile);    
    do_unzip(zipfile, config.ECRobot_InstallationDirectory);
end

%%
function configure_ecrobot(config)
    visibleLogSection('Configuring ECRobot');
        
    % check version of ecrobot by looking up the date of ecrobotnxtsetup.m
    ecobotv316plus_datenum = 733808; % datenum for ecrobot ver 3.16+ (Feb 4, 2009)
    ecrobotv317_datenum = 733856; % datenum for ecrobot ver 3.17 (March 24, 2009)
    
    ecrobotMainDir = fullfile(config.ECRobot_InstallationDirectory,'ecrobotNXT');
    environmentDir = fullfile(ecrobotMainDir, 'environment');    
    setupFile =  fullfile(ecrobotMainDir,'ecrobotnxtsetup.m');    
    
    d = dir(setupFile);
    if numel(d) == 0
        errormsg = sprintf('Cannot find setup file for ECRobot (%s)', setupFile);
        visibeLog(errormsg);
        error(errormsg); %#ok<SPERR>
    end
    
    if floor(d.datenum) < ecrobotv317_datenum
        visibleLog('Cannot set up ECRobot automatically (version is older than 3.17)\n');
        visibleLog('Please download latest version\n');
        return;
    else 
        visibleLog('ECRobot version 3.17 or later detected\n');
    end

    d = dir(config.NXTOSEK_InstallationDirectory);
    if numel(d) == 0
        errormsg = 'Cannot find nxtOSEK installation';
        visibeLog(errormsg);
        error(errormsg);        
    end

    % ver_nxtOSEK.m is used by the setup file. It is possible to have a stale .p
    % copy of this file; if so, need to delete it
    vernxtOSEK_pFile = fullfile(config.NXTOSEK_InstallationDirectory, 'nxtOSEK', 'ecrobot', 'ver_nxtOSEK.p');
    if exist(vernxtOSEK_pFile, 'file')
        % we should already have write access to this folder (since the 
        % ecrobot files were unzipped on top of it)
        delete(vernxtOSEK_pFile);
    end        

    % ECRobot setup file checks for make ver 3.81. Patch it to also allow ver 3.82
    if ~replaceStringInFile(setupFile, 'if \(findstr\(ret,[ ]*''3.81''\)\)', 'if \(regexp\(ret,''3.8\[12\]''\)\)')
        visibleLog('Unable to patch ecrobotnxtsetup.m to handle make ver 3.82\n');
    else
        visibleLog('Successfully patched ecrobotnxtsetup.m to handle make ver 3.82\n');
    end
    
    % now invoke the ECRobot setup utility
    
    cygwin_bin_dir = strrep(fullfile(config.Cygwin_InstallationDirectory, 'bin'), '\', '/');
    % strip any trailing slashes, just in case
    gnuarm_dir = config.GnuTools_InstallationDirectory;
    if gnuarm_dir(end)=='\', gnuarm_dir(end)=[]; end
    nexttool_dir = config.NXTTOOL_InstallationDirectory;
    if nexttool_dir (end)=='\', nexttool_dir (end)=[]; end

    % 
    oldDir = pwd;
    try
        cd(ecrobotMainDir);
        ecrobotnxtsetup(cygwin_bin_dir, gnuarm_dir, nexttool_dir);
        cd(oldDir);
    catch 
        cd(oldDir);
        visibleLog('Problem with automatic configuration of ECRobot\n\n');
        rethrow(lasterror);
    end

    infoFile =  fullfile(environmentDir,'ecrobotnxtsetupinfo.m');
    if ~exist(infoFile,'file')==2
        visibleLog('Problem with automatic configuration of ECRobot\n\n');
        visibleLog('(ecrobotnxtsetup did not create environment\\ecrobotnxtsetupinfo.m)\n\n');
        return;
    end
                   
    visibleLog('\n  ECRobot is set up and ready to use\n');
end

%%
% do_unzip will throw on error
function numFiles = do_unzip(zipfile, installdir)
    numFiles = 0;
    visibleLog('  zip file = %s\n  install dir = %s\n', zipfile, installdir);        
    
    oldWarnState = warning('off', 'MATLAB:unzip:unableToOverwrite');
    warning('off', 'MATLAB:unzip:fileattrib');
    try
        % unzip will throw an error if zipfile is not a valid zip file
        unzippedFiles = unzip(zipfile, installdir);
        isError = false;
    catch %#ok<CTCH>
        isError = true;
    end
    warning(oldWarnState);    
    
    if isError
        error('Problem unzipping %s to %s', zipfile, installdir);
    end
    numFiles = numel(unzippedFiles);
    if numFiles > 0
        visibleLog('  Unzipped %d files\n', numFiles);
    else
        visibleLog('  Nothing to do -- zipfile is already unzipped\n')
    end
end



% replaceStringInFile(filename, oldStr, newStr)
%   Replaces oldStr with newStr in filename. oldStr and newStr will be used
%   in a regular expression, so all control characters should be properly escaped.
function success = replaceStringInFile(filename, oldStr, newStr)

[fpath, fname, ext] = fileparts(filename);
newFile = fullfile(fpath, [fname '_tmp' ext]);

newfid = fopen(newFile, 'wt');
oldfid = fopen(filename, 'rt');
try
    while ~feof(oldfid)
        line = fgets(oldfid);
        newline = regexprep(line, oldStr, newStr);
        fprintf(newfid,'%s',newline);
    end
catch ME
    fclose(newfid);
    fclose(oldfid);
    rethrow(ME);
end
fclose(newfid);
fclose(oldfid);

delete(filename);
[status,msg] = movefile(newFile,filename,'f');
if status ~= 1
    warning('replaceStringInFile: Problem renaming %s to %s ("%s")', newFile, filename, msg);
end
success = (status == 1);
end

Contact us