mirror of
https://github.com/esp8266/Arduino.git
synced 2025-06-17 22:23:10 +03:00
Introducing uploader "warnings", a first attempt to give users more feedback with complex uploads
This commit is contained in:
@ -61,8 +61,9 @@ public abstract class Uploader implements MessageConsumer {
|
|||||||
"avrdude: error: buffered memory access not supported.");
|
"avrdude: error: buffered memory access not supported.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected final boolean verbose;
|
||||||
|
|
||||||
private String error;
|
private String error;
|
||||||
protected boolean verbose;
|
|
||||||
protected boolean notFoundError;
|
protected boolean notFoundError;
|
||||||
|
|
||||||
protected Uploader() {
|
protected Uploader() {
|
||||||
@ -71,7 +72,7 @@ public abstract class Uploader implements MessageConsumer {
|
|||||||
this.notFoundError = false;
|
this.notFoundError = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract boolean uploadUsingPreferences(File sourcePath, String buildPath, String className, boolean usingProgrammer) throws RunnerException;
|
public abstract boolean uploadUsingPreferences(File sourcePath, String buildPath, String className, boolean usingProgrammer, List<String> warningsAccumulator) throws RunnerException;
|
||||||
|
|
||||||
public abstract boolean burnBootloader() throws RunnerException;
|
public abstract boolean burnBootloader() throws RunnerException;
|
||||||
|
|
||||||
@ -84,7 +85,7 @@ public abstract class Uploader implements MessageConsumer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected boolean executeUploadCommand(Collection<String> command) throws RunnerException {
|
protected boolean executeUploadCommand(Collection<String> command) throws RunnerException {
|
||||||
return executeUploadCommand(command.toArray(new String[0]));
|
return executeUploadCommand(command.toArray(new String[command.size()]));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean executeUploadCommand(String command[]) throws RunnerException {
|
protected boolean executeUploadCommand(String command[]) throws RunnerException {
|
||||||
|
@ -5,7 +5,7 @@ import cc.arduino.packages.uploaders.SerialUploader;
|
|||||||
import processing.app.*;
|
import processing.app.*;
|
||||||
import processing.app.debug.TargetBoard;
|
import processing.app.debug.TargetBoard;
|
||||||
|
|
||||||
public class UploaderFactory {
|
public class UploaderAndMonitorFactory {
|
||||||
|
|
||||||
public Uploader newUploader(TargetBoard board, String port) {
|
public Uploader newUploader(TargetBoard board, String port) {
|
||||||
if ("true".equals(board.getPreferences().get("upload.via_ssh")) && Constants.IPV4_ADDRESS.matcher(port).find()) {
|
if ("true".equals(board.getPreferences().get("upload.via_ssh")) && Constants.IPV4_ADDRESS.matcher(port).find()) {
|
@ -46,10 +46,9 @@ public class SSHUploader extends Uploader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean uploadUsingPreferences(File sourcePath, String buildPath, String className, boolean usingProgrammer) throws RunnerException {
|
public boolean uploadUsingPreferences(File sourcePath, String buildPath, String className, boolean usingProgrammer, List<String> warningsAccumulator) throws RunnerException {
|
||||||
if (usingProgrammer) {
|
if (usingProgrammer) {
|
||||||
System.err.println(_("Http upload using programmer not supported"));
|
throw new RunnerException(_("Network upload using programmer not supported"));
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Session session = null;
|
Session session = null;
|
||||||
@ -65,7 +64,7 @@ public class SSHUploader extends Uploader {
|
|||||||
scp = new SCP(session);
|
scp = new SCP(session);
|
||||||
SSH ssh = new SSH(session);
|
SSH ssh = new SSH(session);
|
||||||
|
|
||||||
scpFiles(scp, ssh, sourcePath, buildPath, className, session);
|
scpFiles(scp, ssh, sourcePath, buildPath, className, warningsAccumulator);
|
||||||
|
|
||||||
return runAVRDude(ssh);
|
return runAVRDude(ssh);
|
||||||
} catch (JSchException e) {
|
} catch (JSchException e) {
|
||||||
@ -103,14 +102,14 @@ public class SSHUploader extends Uploader {
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scpFiles(SCP scp, SSH ssh, File sourcePath, String buildPath, String className, Session session) throws JSchException, IOException {
|
private void scpFiles(SCP scp, SSH ssh, File sourcePath, String buildPath, String className, List<String> warningsAccumulator) throws JSchException, IOException {
|
||||||
try {
|
try {
|
||||||
scp.open();
|
scp.open();
|
||||||
scp.startFolder("tmp");
|
scp.startFolder("tmp");
|
||||||
scp.sendFile(new File(buildPath, className + ".hex"), "sketch.hex");
|
scp.sendFile(new File(buildPath, className + ".hex"), "sketch.hex");
|
||||||
scp.endFolder();
|
scp.endFolder();
|
||||||
|
|
||||||
if (canUploadWWWFiles(sourcePath, ssh)) {
|
if (canUploadWWWFiles(sourcePath, ssh, warningsAccumulator)) {
|
||||||
scp.startFolder("www");
|
scp.startFolder("www");
|
||||||
scp.startFolder("sd");
|
scp.startFolder("sd");
|
||||||
scp.startFolder(sourcePath.getName());
|
scp.startFolder(sourcePath.getName());
|
||||||
@ -124,17 +123,17 @@ public class SSHUploader extends Uploader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean canUploadWWWFiles(File sourcePath, SSH ssh) throws IOException, JSchException {
|
private boolean canUploadWWWFiles(File sourcePath, SSH ssh, List<String> warningsAccumulator) throws IOException, JSchException {
|
||||||
File www = new File(sourcePath, "www");
|
File www = new File(sourcePath, "www");
|
||||||
if (!www.exists() || !www.isDirectory()) {
|
if (!www.exists() || !www.isDirectory()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!www.canExecute()) {
|
if (!www.canExecute()) {
|
||||||
System.out.println("Problem accessing files in folder " + www);
|
warningsAccumulator.add(_("Problem accessing files in folder ") + www);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!ssh.execSyncCommand("special-storage-available")) {
|
if (!ssh.execSyncCommand("special-storage-available")) {
|
||||||
System.out.println("Problem accessing board folder /www/sd");
|
warningsAccumulator.add(_("Problem accessing board folder /www/sd"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -28,27 +28,22 @@
|
|||||||
|
|
||||||
package cc.arduino.packages.uploaders;
|
package cc.arduino.packages.uploaders;
|
||||||
|
|
||||||
import static processing.app.I18n._;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import cc.arduino.packages.Uploader;
|
import cc.arduino.packages.Uploader;
|
||||||
|
import processing.app.*;
|
||||||
import processing.app.Base;
|
|
||||||
import processing.app.I18n;
|
|
||||||
import processing.app.Preferences;
|
|
||||||
import processing.app.Serial;
|
|
||||||
import processing.app.SerialException;
|
|
||||||
import processing.app.debug.RunnerException;
|
import processing.app.debug.RunnerException;
|
||||||
import processing.app.debug.TargetPlatform;
|
import processing.app.debug.TargetPlatform;
|
||||||
import processing.app.helpers.PreferencesMap;
|
import processing.app.helpers.PreferencesMap;
|
||||||
import processing.app.helpers.StringReplacer;
|
import processing.app.helpers.StringReplacer;
|
||||||
|
|
||||||
public class SerialUploader extends Uploader {
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public boolean uploadUsingPreferences(File sourcePath, String buildPath, String className, boolean usingProgrammer) throws RunnerException {
|
import static processing.app.I18n._;
|
||||||
|
|
||||||
|
public class SerialUploader extends Uploader {
|
||||||
|
|
||||||
|
public boolean uploadUsingPreferences(File sourcePath, String buildPath, String className, boolean usingProgrammer, List<String> warningsAccumulator) throws RunnerException {
|
||||||
// FIXME: Preferences should be reorganized
|
// FIXME: Preferences should be reorganized
|
||||||
TargetPlatform targetPlatform = Base.getTargetPlatform();
|
TargetPlatform targetPlatform = Base.getTargetPlatform();
|
||||||
PreferencesMap prefs = Preferences.getMap();
|
PreferencesMap prefs = Preferences.getMap();
|
||||||
@ -80,9 +75,7 @@ public class SerialUploader extends Uploader {
|
|||||||
List<String> before = Serial.list();
|
List<String> before = Serial.list();
|
||||||
if (before.contains(uploadPort)) {
|
if (before.contains(uploadPort)) {
|
||||||
if (verbose)
|
if (verbose)
|
||||||
System.out
|
System.out.println(_("Forcing reset using 1200bps open/close on port ") + uploadPort);
|
||||||
.println(_("Forcing reset using 1200bps open/close on port ") +
|
|
||||||
uploadPort);
|
|
||||||
Serial.touchPort(uploadPort, 1200);
|
Serial.touchPort(uploadPort, 1200);
|
||||||
}
|
}
|
||||||
if (waitForUploadPort) {
|
if (waitForUploadPort) {
|
||||||
@ -163,12 +156,12 @@ public class SerialUploader extends Uploader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (InterruptedException ex) {
|
} catch (InterruptedException ex) {
|
||||||
|
// noop
|
||||||
}
|
}
|
||||||
return uploadResult;
|
return uploadResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String waitForUploadPort(String uploadPort, List<String> before)
|
private String waitForUploadPort(String uploadPort, List<String> before) throws InterruptedException, RunnerException {
|
||||||
throws InterruptedException, RunnerException {
|
|
||||||
// Wait for a port to appear on the list
|
// Wait for a port to appear on the list
|
||||||
int elapsed = 0;
|
int elapsed = 0;
|
||||||
while (elapsed < 10000) {
|
while (elapsed < 10000) {
|
||||||
@ -203,22 +196,18 @@ public class SerialUploader extends Uploader {
|
|||||||
// come back, so use a longer time out before assuming that the
|
// come back, so use a longer time out before assuming that the
|
||||||
// selected
|
// selected
|
||||||
// port is the bootloader (not the sketch).
|
// port is the bootloader (not the sketch).
|
||||||
if (((!Base.isWindows() && elapsed >= 500) || elapsed >= 5000) &&
|
if (((!Base.isWindows() && elapsed >= 500) || elapsed >= 5000) && now.contains(uploadPort)) {
|
||||||
now.contains(uploadPort)) {
|
|
||||||
if (verbose)
|
if (verbose)
|
||||||
System.out.println("Uploading using selected port: " +
|
System.out.println("Uploading using selected port: " + uploadPort);
|
||||||
uploadPort);
|
|
||||||
return uploadPort;
|
return uploadPort;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Something happened while detecting port
|
// Something happened while detecting port
|
||||||
throw new RunnerException(
|
throw new RunnerException(_("Couldn't find a Board 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."));
|
||||||
_("Couldn't find a Board 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."));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean uploadUsingProgrammer(String buildPath, String className)
|
public boolean uploadUsingProgrammer(String buildPath, String className) throws RunnerException {
|
||||||
throws RunnerException {
|
|
||||||
|
|
||||||
TargetPlatform targetPlatform = Base.getTargetPlatform();
|
TargetPlatform targetPlatform = Base.getTargetPlatform();
|
||||||
String programmer = Preferences.get("programmer");
|
String programmer = Preferences.get("programmer");
|
||||||
@ -264,8 +253,7 @@ public class SerialUploader extends Uploader {
|
|||||||
String programmer = Preferences.get("programmer");
|
String programmer = Preferences.get("programmer");
|
||||||
if (programmer.contains(":")) {
|
if (programmer.contains(":")) {
|
||||||
String[] split = programmer.split(":", 2);
|
String[] split = programmer.split(":", 2);
|
||||||
TargetPlatform platform = Base
|
TargetPlatform platform = Base.getCurrentTargetPlatformFromPackage(split[0]);
|
||||||
.getCurrentTargetPlatformFromPackage(split[0]);
|
|
||||||
programmer = split[1];
|
programmer = split[1];
|
||||||
programmerPrefs = platform.getProgrammer(programmer);
|
programmerPrefs = platform.getProgrammer(programmer);
|
||||||
} else {
|
} else {
|
||||||
@ -286,14 +274,11 @@ public class SerialUploader extends Uploader {
|
|||||||
tool = split[1];
|
tool = split[1];
|
||||||
toolPrefs.putAll(platform.getTool(tool));
|
toolPrefs.putAll(platform.getTool(tool));
|
||||||
if (toolPrefs.size() == 0)
|
if (toolPrefs.size() == 0)
|
||||||
throw new RunnerException(
|
throw new RunnerException(I18n.format(_("Could not find tool {0} from package {1}"), tool, split[0]));
|
||||||
I18n.format(_("Could not find tool {0} from package {1}"), tool,
|
|
||||||
split[0]));
|
|
||||||
}
|
}
|
||||||
toolPrefs.putAll(targetPlatform.getTool(tool));
|
toolPrefs.putAll(targetPlatform.getTool(tool));
|
||||||
if (toolPrefs.size() == 0)
|
if (toolPrefs.size() == 0)
|
||||||
throw new RunnerException(I18n.format(_("Could not find tool {0}"),
|
throw new RunnerException(I18n.format(_("Could not find tool {0}"), tool));
|
||||||
tool));
|
|
||||||
|
|
||||||
// Merge tool with global configuration
|
// Merge tool with global configuration
|
||||||
prefs.putAll(toolPrefs);
|
prefs.putAll(toolPrefs);
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
package processing.app;
|
package processing.app;
|
||||||
|
|
||||||
|
import cc.arduino.packages.UploaderAndMonitorFactory;
|
||||||
import com.jcraft.jsch.JSchException;
|
import com.jcraft.jsch.JSchException;
|
||||||
import processing.app.debug.*;
|
import processing.app.debug.*;
|
||||||
import processing.app.forms.PasswordAuthorizationDialog;
|
import processing.app.forms.PasswordAuthorizationDialog;
|
||||||
@ -47,7 +48,6 @@ import javax.swing.undo.*;
|
|||||||
|
|
||||||
import cc.arduino.packages.BoardPort;
|
import cc.arduino.packages.BoardPort;
|
||||||
import cc.arduino.packages.Uploader;
|
import cc.arduino.packages.Uploader;
|
||||||
import cc.arduino.packages.UploaderFactory;
|
|
||||||
import cc.arduino.packages.uploaders.SerialUploader;
|
import cc.arduino.packages.uploaders.SerialUploader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -209,7 +209,7 @@ public class Editor extends JFrame implements RunnerListener {
|
|||||||
//sketchbook = new Sketchbook(this);
|
//sketchbook = new Sketchbook(this);
|
||||||
|
|
||||||
if (serialMonitor == null) {
|
if (serialMonitor == null) {
|
||||||
serialMonitor = new UploaderFactory().newMonitor(Preferences.get("serial.port"), base);
|
serialMonitor = new UploaderAndMonitorFactory().newMonitor(Preferences.get("serial.port"), base);
|
||||||
serialMonitor.setIconImage(getIconImage());
|
serialMonitor.setIconImage(getIconImage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -975,7 +975,7 @@ public class Editor extends JFrame implements RunnerListener {
|
|||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
serialMonitor.setVisible(false);
|
serialMonitor.setVisible(false);
|
||||||
serialMonitor = new UploaderFactory().newMonitor(Preferences.get("serial.port"), base);
|
serialMonitor = new UploaderAndMonitorFactory().newMonitor(Preferences.get("serial.port"), base);
|
||||||
|
|
||||||
onBoardOrPortChange();
|
onBoardOrPortChange();
|
||||||
|
|
||||||
|
@ -23,10 +23,10 @@
|
|||||||
|
|
||||||
package processing.app;
|
package processing.app;
|
||||||
|
|
||||||
|
import cc.arduino.packages.UploaderAndMonitorFactory;
|
||||||
import org.apache.commons.codec.digest.DigestUtils;
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
|
|
||||||
import cc.arduino.packages.Uploader;
|
import cc.arduino.packages.Uploader;
|
||||||
import cc.arduino.packages.UploaderFactory;
|
|
||||||
import processing.app.debug.*;
|
import processing.app.debug.*;
|
||||||
import processing.app.debug.Compiler;
|
import processing.app.debug.Compiler;
|
||||||
import processing.app.forms.PasswordAuthorizationDialog;
|
import processing.app.forms.PasswordAuthorizationDialog;
|
||||||
@ -1519,8 +1519,8 @@ public class Sketch {
|
|||||||
* Map an error from a set of processed .java files back to its location
|
* Map an error from a set of processed .java files back to its location
|
||||||
* in the actual sketch.
|
* in the actual sketch.
|
||||||
* @param message The error message.
|
* @param message The error message.
|
||||||
* @param filename The .java file where the exception was found.
|
* @param dotJavaFilename The .java file where the exception was found.
|
||||||
* @param line Line number of the .java file for the exception (0-indexed!)
|
* @param dotJavaLine Line number of the .java file for the exception (0-indexed!)
|
||||||
* @return A RunnerException to be sent to the editor, or null if it wasn't
|
* @return A RunnerException to be sent to the editor, or null if it wasn't
|
||||||
* possible to place the exception to the sketch code.
|
* possible to place the exception to the sketch code.
|
||||||
*/
|
*/
|
||||||
@ -1666,7 +1666,7 @@ public class Sketch {
|
|||||||
TargetPlatform target = Base.getTargetPlatform();
|
TargetPlatform target = Base.getTargetPlatform();
|
||||||
String board = Preferences.get("board");
|
String board = Preferences.get("board");
|
||||||
|
|
||||||
Uploader uploader = new UploaderFactory().newUploader(target.getBoards().get(board), Preferences.get("serial.port"));
|
Uploader uploader = new UploaderAndMonitorFactory().newUploader(target.getBoards().get(board), Preferences.get("serial.port"));
|
||||||
|
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
do {
|
do {
|
||||||
@ -1683,14 +1683,21 @@ public class Sketch {
|
|||||||
Preferences.set(uploader.getAuthorizationKey(), dialog.getPassword());
|
Preferences.set(uploader.getAuthorizationKey(), dialog.getPassword());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<String> warningsAccumulator = new LinkedList<String>();
|
||||||
try {
|
try {
|
||||||
success = uploader.uploadUsingPreferences(getFolder(), buildPath, suggestedClassName, usingProgrammer);
|
success = uploader.uploadUsingPreferences(getFolder(), buildPath, suggestedClassName, usingProgrammer, warningsAccumulator);
|
||||||
} finally {
|
} finally {
|
||||||
if (uploader.requiresAuthorization() && !success) {
|
if (uploader.requiresAuthorization() && !success) {
|
||||||
Preferences.remove(uploader.getAuthorizationKey());
|
Preferences.remove(uploader.getAuthorizationKey());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (String warning : warningsAccumulator) {
|
||||||
|
System.out.print(_("Warning"));
|
||||||
|
System.out.print(": ");
|
||||||
|
System.out.println(warning);
|
||||||
|
}
|
||||||
|
|
||||||
} while (uploader.requiresAuthorization() && !success);
|
} while (uploader.requiresAuthorization() && !success);
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package processing.app.debug;
|
package processing.app.debug;
|
||||||
|
|
||||||
import cc.arduino.packages.Uploader;
|
import cc.arduino.packages.Uploader;
|
||||||
import cc.arduino.packages.UploaderFactory;
|
import cc.arduino.packages.UploaderAndMonitorFactory;
|
||||||
import cc.arduino.packages.uploaders.SSHUploader;
|
import cc.arduino.packages.uploaders.SSHUploader;
|
||||||
import cc.arduino.packages.uploaders.SerialUploader;
|
import cc.arduino.packages.uploaders.SerialUploader;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@ -24,7 +24,7 @@ public class UploaderFactoryTest extends AbstractWithPreferencesTest {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldCreateAnInstanceOfSSHUploader() throws Exception {
|
public void shouldCreateAnInstanceOfSSHUploader() throws Exception {
|
||||||
TargetBoard board = targetPackage.getPlatforms().get("avr").getBoards().get("yun");
|
TargetBoard board = targetPackage.getPlatforms().get("avr").getBoards().get("yun");
|
||||||
Uploader uploader = new UploaderFactory().newUploader(board, "192.168.0.1 (yun)");
|
Uploader uploader = new UploaderAndMonitorFactory().newUploader(board, "192.168.0.1 (yun)");
|
||||||
|
|
||||||
assertTrue(uploader instanceof SSHUploader);
|
assertTrue(uploader instanceof SSHUploader);
|
||||||
}
|
}
|
||||||
@ -32,7 +32,7 @@ public class UploaderFactoryTest extends AbstractWithPreferencesTest {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldCreateAnInstanceOfBasicUploaderWhenSSHIsUnsupported() throws Exception {
|
public void shouldCreateAnInstanceOfBasicUploaderWhenSSHIsUnsupported() throws Exception {
|
||||||
TargetBoard board = targetPackage.getPlatforms().get("avr").getBoards().get("uno");
|
TargetBoard board = targetPackage.getPlatforms().get("avr").getBoards().get("uno");
|
||||||
Uploader uploader = new UploaderFactory().newUploader(board, "192.168.0.1 (myyun)");
|
Uploader uploader = new UploaderAndMonitorFactory().newUploader(board, "192.168.0.1 (myyun)");
|
||||||
|
|
||||||
assertTrue(uploader instanceof SerialUploader);
|
assertTrue(uploader instanceof SerialUploader);
|
||||||
}
|
}
|
||||||
@ -40,7 +40,7 @@ public class UploaderFactoryTest extends AbstractWithPreferencesTest {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldCreateAnInstanceOfBasicUploaderWhenPortIsSerial() throws Exception {
|
public void shouldCreateAnInstanceOfBasicUploaderWhenPortIsSerial() throws Exception {
|
||||||
TargetBoard board = targetPackage.getPlatforms().get("avr").getBoards().get("uno");
|
TargetBoard board = targetPackage.getPlatforms().get("avr").getBoards().get("uno");
|
||||||
Uploader uploader = new UploaderFactory().newUploader(board, "/dev/ttyACM0 (Arduino Leonardo)");
|
Uploader uploader = new UploaderAndMonitorFactory().newUploader(board, "/dev/ttyACM0 (Arduino Leonardo)");
|
||||||
|
|
||||||
assertTrue(uploader instanceof SerialUploader);
|
assertTrue(uploader instanceof SerialUploader);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user