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