From 7ad4de5208ddfc19371ad3e5830f83459b2b335e Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 25 May 2012 17:33:29 +0200 Subject: [PATCH] Added support for USB serial port 1200bps touch (Leonardo) --- .../processing/app/debug/BasicUploader.java | 114 +++++++++++++++++- 1 file changed, 113 insertions(+), 1 deletion(-) diff --git a/app/src/processing/app/debug/BasicUploader.java b/app/src/processing/app/debug/BasicUploader.java index cb369d657..202e17932 100644 --- a/app/src/processing/app/debug/BasicUploader.java +++ b/app/src/processing/app/debug/BasicUploader.java @@ -28,12 +28,18 @@ package processing.app.debug; +import java.util.ArrayList; +import java.util.List; + import processing.app.Base; import processing.app.Preferences; +import processing.app.Serial; import processing.app.SerialException; import processing.app.helpers.PreferencesMap; import processing.app.helpers.StringReplacer; +import static processing.app.I18n._; + public class BasicUploader extends Uploader { public boolean uploadUsingPreferences(String buildPath, String className, @@ -50,7 +56,93 @@ public class BasicUploader extends Uploader { if (usingProgrammer || prefs.get("upload.protocol") == null) { return uploadUsingProgrammer(buildPath, className); } + + // need to do a little dance for Leonardo and derivatives: + // open then close the port at the magic baudrate (usually 1200 bps) first + // to signal to the sketch that it should reset into bootloader. after doing + // this wait a moment for the bootloader to enumerate. On Windows, also must + // deal with the fact that the COM port number changes from bootloader to + // sketch. + String use1200bpsTouch = prefs.get("upload.use_1200bps_touch"); + boolean doTouch = use1200bpsTouch != null && use1200bpsTouch.equals("true"); + if (doTouch) { + String uploadPort = prefs.get("serial.port"); + String caterinaUploadPort = null; + try { + // Toggle 1200 bps on selected serial port to force board reset. + List before = Serial.list(); + if (before.contains(uploadPort)) { + if (verbose || Preferences.getBoolean("upload.verbose")) + System.out + .println(_("Forcing reset using 1200bps open/close on port ") + + uploadPort); + Serial.touchPort(uploadPort, 1200); + // Scanning for available ports seems to open the port or + // otherwise assert DTR, which would cancel the WDT reset if + // it happened within 250 ms. So we wait until the reset should + // have already occured before we start scanning. + if (!Base.isMacOS()) + Thread.sleep(300); + } + // Wait for a port to appear on the list + int elapsed = 0; + while (elapsed < 10000) { + List now = Serial.list(); + List diff = new ArrayList(now); + diff.removeAll(before); + if (verbose || Preferences.getBoolean("upload.verbose")) { + System.out.print("PORTS {"); + for (String p : before) + System.out.print(p + ", "); + System.out.print("} / {"); + for (String p : now) + System.out.print(p + ", "); + System.out.print("} => {"); + for (String p : diff) + System.out.print(p + ", "); + System.out.println("}"); + } + if (diff.size() > 0) { + caterinaUploadPort = diff.get(0); + if (verbose || Preferences.getBoolean("upload.verbose")) + System.out.println("Found Leonardo upload port: " + + caterinaUploadPort); + break; + } + + // Keep track of port that disappears + before = now; + Thread.sleep(250); + elapsed += 250; + + // On Windows, it can take a long time for the port to disappear and + // come back, so use a longer time out before assuming that the + // selected + // port is the bootloader (not the sketch). + if (((!Base.isWindows() && elapsed >= 500) || elapsed >= 5000) && + now.contains(uploadPort)) { + if (verbose || Preferences.getBoolean("upload.verbose")) + System.out + .println("Uploading using selected port: " + uploadPort); + caterinaUploadPort = uploadPort; + break; + } + } + if (caterinaUploadPort == null) + // Something happened while detecting port + throw new RunnerException( + _("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.")); + + uploadPort = caterinaUploadPort; + } catch (SerialException e) { + throw new RunnerException(e.getMessage()); + } catch (InterruptedException e) { + throw new RunnerException(e.getMessage()); + } + prefs.put("serial.port", uploadPort); + } + prefs.put("build.path", buildPath); prefs.put("build.project_name", className); if (verbose) @@ -58,6 +150,7 @@ public class BasicUploader extends Uploader { else prefs.put("upload.verbose", prefs.get("upload.params.quiet")); + boolean uploadResult; try { // if (prefs.get("upload.disable_flushing") == null // || prefs.get("upload.disable_flushing").toLowerCase().equals("false")) { @@ -66,10 +159,29 @@ public class BasicUploader extends Uploader { String pattern = prefs.get("upload.pattern"); String[] cmd = StringReplacer.formatAndSplit(pattern, prefs, true); - return executeUploadCommand(cmd); + uploadResult = executeUploadCommand(cmd); } catch (Exception e) { throw new RunnerException(e); } + + // For Leonardo wait until the bootloader serial port disconnects and the + // sketch serial port reconnects (or timeout after a few seconds if the + // sketch port never comes back). Doing this saves users from accidentally + // opening Serial Monitor on the soon-to-be-orphaned bootloader port. + try { + if (uploadResult && doTouch) { + Thread.sleep(500); + long timeout = System.currentTimeMillis() + 2000; + while (timeout > System.currentTimeMillis()) { + List portList = Serial.list(); + if (portList.contains(Preferences.get("serial.port"))) + break; + Thread.sleep(100); + } + } + } catch (InterruptedException ex) { + } + return uploadResult; } public boolean uploadUsingProgrammer(String buildPath, String className)