2828
2929package processing .app .debug ;
3030
31+ import java .util .ArrayList ;
32+ import java .util .List ;
33+
3134import processing .app .Base ;
3235import processing .app .Preferences ;
36+ import processing .app .Serial ;
3337import processing .app .SerialException ;
3438import processing .app .helpers .PreferencesMap ;
3539import processing .app .helpers .StringReplacer ;
3640
41+ import static processing .app .I18n ._ ;
42+
3743public class BasicUploader extends Uploader {
3844
3945 public boolean uploadUsingPreferences (String buildPath , String className ,
@@ -50,14 +56,101 @@ public boolean uploadUsingPreferences(String buildPath, String className,
5056 if (usingProgrammer || prefs .get ("upload.protocol" ) == null ) {
5157 return uploadUsingProgrammer (buildPath , className );
5258 }
59+
60+ // need to do a little dance for Leonardo and derivatives:
61+ // open then close the port at the magic baudrate (usually 1200 bps) first
62+ // to signal to the sketch that it should reset into bootloader. after doing
63+ // this wait a moment for the bootloader to enumerate. On Windows, also must
64+ // deal with the fact that the COM port number changes from bootloader to
65+ // sketch.
66+ String use1200bpsTouch = prefs .get ("upload.use_1200bps_touch" );
67+ boolean doTouch = use1200bpsTouch != null && use1200bpsTouch .equals ("true" );
68+ if (doTouch ) {
69+ String uploadPort = prefs .get ("serial.port" );
70+ String caterinaUploadPort = null ;
71+ try {
72+ // Toggle 1200 bps on selected serial port to force board reset.
73+ List <String > before = Serial .list ();
74+ if (before .contains (uploadPort )) {
75+ if (verbose || Preferences .getBoolean ("upload.verbose" ))
76+ System .out
77+ .println (_ ("Forcing reset using 1200bps open/close on port " ) +
78+ uploadPort );
79+ Serial .touchPort (uploadPort , 1200 );
80+
81+ // Scanning for available ports seems to open the port or
82+ // otherwise assert DTR, which would cancel the WDT reset if
83+ // it happened within 250 ms. So we wait until the reset should
84+ // have already occured before we start scanning.
85+ if (!Base .isMacOS ())
86+ Thread .sleep (300 );
87+ }
88+ // Wait for a port to appear on the list
89+ int elapsed = 0 ;
90+ while (elapsed < 10000 ) {
91+ List <String > now = Serial .list ();
92+ List <String > diff = new ArrayList <String >(now );
93+ diff .removeAll (before );
94+ if (verbose || Preferences .getBoolean ("upload.verbose" )) {
95+ System .out .print ("PORTS {" );
96+ for (String p : before )
97+ System .out .print (p + ", " );
98+ System .out .print ("} / {" );
99+ for (String p : now )
100+ System .out .print (p + ", " );
101+ System .out .print ("} => {" );
102+ for (String p : diff )
103+ System .out .print (p + ", " );
104+ System .out .println ("}" );
105+ }
106+ if (diff .size () > 0 ) {
107+ caterinaUploadPort = diff .get (0 );
108+ if (verbose || Preferences .getBoolean ("upload.verbose" ))
109+ System .out .println ("Found Leonardo upload port: " +
110+ caterinaUploadPort );
111+ break ;
112+ }
113+
114+ // Keep track of port that disappears
115+ before = now ;
116+ Thread .sleep (250 );
117+ elapsed += 250 ;
118+
119+ // On Windows, it can take a long time for the port to disappear and
120+ // come back, so use a longer time out before assuming that the
121+ // selected
122+ // port is the bootloader (not the sketch).
123+ if (((!Base .isWindows () && elapsed >= 500 ) || elapsed >= 5000 ) &&
124+ now .contains (uploadPort )) {
125+ if (verbose || Preferences .getBoolean ("upload.verbose" ))
126+ System .out
127+ .println ("Uploading using selected port: " + uploadPort );
128+ caterinaUploadPort = uploadPort ;
129+ break ;
130+ }
131+ }
132+ if (caterinaUploadPort == null )
133+ // Something happened while detecting port
134+ throw new RunnerException (
135+ _ ("Couldn’t find a Leonardo on the selected port. Check that you have the correct port selected. If it is correct, try pressing the board's reset button after initiating the upload." ));
53136
137+ uploadPort = caterinaUploadPort ;
138+ } catch (SerialException e ) {
139+ throw new RunnerException (e .getMessage ());
140+ } catch (InterruptedException e ) {
141+ throw new RunnerException (e .getMessage ());
142+ }
143+ prefs .put ("serial.port" , uploadPort );
144+ }
145+
54146 prefs .put ("build.path" , buildPath );
55147 prefs .put ("build.project_name" , className );
56148 if (verbose )
57149 prefs .put ("upload.verbose" , prefs .get ("upload.params.verbose" ));
58150 else
59151 prefs .put ("upload.verbose" , prefs .get ("upload.params.quiet" ));
60152
153+ boolean uploadResult ;
61154 try {
62155// if (prefs.get("upload.disable_flushing") == null
63156// || prefs.get("upload.disable_flushing").toLowerCase().equals("false")) {
@@ -66,10 +159,29 @@ public boolean uploadUsingPreferences(String buildPath, String className,
66159
67160 String pattern = prefs .get ("upload.pattern" );
68161 String [] cmd = StringReplacer .formatAndSplit (pattern , prefs , true );
69- return executeUploadCommand (cmd );
162+ uploadResult = executeUploadCommand (cmd );
70163 } catch (Exception e ) {
71164 throw new RunnerException (e );
72165 }
166+
167+ // For Leonardo wait until the bootloader serial port disconnects and the
168+ // sketch serial port reconnects (or timeout after a few seconds if the
169+ // sketch port never comes back). Doing this saves users from accidentally
170+ // opening Serial Monitor on the soon-to-be-orphaned bootloader port.
171+ try {
172+ if (uploadResult && doTouch ) {
173+ Thread .sleep (500 );
174+ long timeout = System .currentTimeMillis () + 2000 ;
175+ while (timeout > System .currentTimeMillis ()) {
176+ List <String > portList = Serial .list ();
177+ if (portList .contains (Preferences .get ("serial.port" )))
178+ break ;
179+ Thread .sleep (100 );
180+ }
181+ }
182+ } catch (InterruptedException ex ) {
183+ }
184+ return uploadResult ;
73185 }
74186
75187 public boolean uploadUsingProgrammer (String buildPath , String className )
0 commit comments