Skip to content

Commit ab7e857

Browse files
committed
upload: added serial port handling
1 parent 7ffe9b0 commit ab7e857

File tree

2 files changed

+116
-5
lines changed

2 files changed

+116
-5
lines changed

commands/compile/compile.go

+1
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ func run(cmd *cobra.Command, args []string) {
232232
os.Exit(commands.ErrGeneric)
233233
}
234234

235+
// FIXME: Make a function to obtain these info...
235236
outputPath := ctx.BuildProperties.ExpandPropsInString("{build.path}/{recipe.output.tmp_file}")
236237
ext := filepath.Ext(outputPath)
237238
fqbn = strings.Replace(fqbn, ":", ".", -1)

commands/upload/upload.go

+115-5
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,16 @@ import (
3434
"os"
3535
"path/filepath"
3636
"strings"
37+
"time"
3738

3839
properties "github.com/arduino/go-properties-map"
3940
"github.com/bcmi-labs/arduino-cli/commands"
4041
"github.com/bcmi-labs/arduino-cli/common/formatter"
4142
"github.com/bcmi-labs/arduino-cli/cores"
4243
"github.com/bcmi-labs/arduino-cli/executils"
44+
"github.com/sirupsen/logrus"
4345
"github.com/spf13/cobra"
46+
serial "go.bug.st/serial.v1"
4447
)
4548

4649
// Init prepares the command.
@@ -203,9 +206,6 @@ func run(command *cobra.Command, args []string) {
203206
}
204207
}
205208

206-
// Set serial port property
207-
uploadProperties["serial.port.file"] = flags.port
208-
209209
// Set properties for verbose upload
210210
if flags.verbose {
211211
if v, ok := uploadProperties["upload.params.verbose"]; ok {
@@ -229,7 +229,8 @@ func run(command *cobra.Command, args []string) {
229229
fqbn = strings.Replace(fqbn, ":", ".", -1)
230230
uploadProperties["build.path"] = sketch.FullPath
231231
uploadProperties["build.project_name"] = sketch.Name + "." + fqbn
232-
if _, err := os.Stat(filepath.Join(sketch.FullPath, sketch.Name+"."+fqbn)); err != nil {
232+
ext := filepath.Ext(uploadProperties.ExpandPropsInString("{recipe.output.tmp_file}"))
233+
if _, err := os.Stat(filepath.Join(sketch.FullPath, sketch.Name+"."+fqbn+ext)); err != nil {
233234
if os.IsNotExist(err) {
234235
formatter.PrintErrorMessage("Compiled sketch not found. Please compile first.")
235236
} else {
@@ -238,7 +239,57 @@ func run(command *cobra.Command, args []string) {
238239
os.Exit(commands.ErrGeneric)
239240
}
240241

241-
// Build recipe for upload and run tool
242+
// Perform reset via 1200bps touch if requested
243+
if uploadProperties.GetBoolean("upload.use_1200bps_touch") {
244+
ports, err := serial.GetPortsList()
245+
if err != nil {
246+
formatter.PrintError(err, "Can't get serial port list")
247+
os.Exit(commands.ErrGeneric)
248+
}
249+
for _, p := range ports {
250+
if p == port {
251+
if err := touchSerialPortAt1200bps(p); err != nil {
252+
formatter.PrintError(err, "Can't perform reset via 1200bps-touch on serial port")
253+
os.Exit(commands.ErrGeneric)
254+
}
255+
break
256+
}
257+
}
258+
259+
// Scanning for available ports seems to open the port or
260+
// otherwise assert DTR, which would cancel the WDT reset if
261+
// it happened within 250 ms. So we wait until the reset should
262+
// have already occurred before we start scanning.
263+
time.Sleep(500 * time.Millisecond)
264+
}
265+
266+
// Wait for upload port if requested
267+
actualPort := port // default
268+
if uploadProperties.GetBoolean("upload.wait_for_upload_port") {
269+
if p, err := waitForNewSerialPort(); err != nil {
270+
formatter.PrintError(err, "Could not detect serial ports")
271+
os.Exit(commands.ErrGeneric)
272+
} else if p == "" {
273+
formatter.Print("No new serial port detected.")
274+
} else {
275+
actualPort = p
276+
}
277+
278+
// on OS X, if the port is opened too quickly after it is detected,
279+
// a "Resource busy" error occurs, add a delay to workaround.
280+
// This apply to other platforms as well.
281+
time.Sleep(500 * time.Millisecond)
282+
}
283+
284+
// Set serial port property
285+
uploadProperties["serial.port"] = actualPort
286+
if strings.HasPrefix(actualPort, "/dev/") {
287+
uploadProperties["serial.port.file"] = actualPort[5:]
288+
} else {
289+
uploadProperties["serial.port.file"] = actualPort
290+
}
291+
292+
// Build recipe for upload
242293
recipe := uploadProperties["upload.pattern"]
243294
cmdLine := uploadProperties.ExpandPropsInString(recipe)
244295
cmdArgs, err := properties.SplitQuotedString(cmdLine, `"'`, false)
@@ -247,6 +298,7 @@ func run(command *cobra.Command, args []string) {
247298
os.Exit(commands.ErrCoreConfig)
248299
}
249300

301+
// Run Tool
250302
cmd, err := executils.Command(cmdArgs)
251303
if err != nil {
252304
formatter.PrintError(err, "Could not execute upload tool.")
@@ -265,3 +317,61 @@ func run(command *cobra.Command, args []string) {
265317
os.Exit(commands.ErrGeneric)
266318
}
267319
}
320+
321+
func touchSerialPortAt1200bps(port string) error {
322+
logrus.Infof("Touching port %s at 1200bps", port)
323+
324+
// Open port
325+
p, err := serial.Open(port, &serial.Mode{BaudRate: 1200})
326+
if err != nil {
327+
return fmt.Errorf("open port: %s", err)
328+
}
329+
defer p.Close()
330+
331+
if err = p.SetDTR(false); err != nil {
332+
return fmt.Errorf("can't set DTR")
333+
}
334+
return nil
335+
}
336+
337+
// waitForNewSerialPort is meant to be called just after a reset. It watches the ports connected
338+
// to the machine until a port appears. The new appeared port is returned
339+
func waitForNewSerialPort() (string, error) {
340+
logrus.Infof("Waiting for upload port...")
341+
342+
getPortMap := func() (map[string]bool, error) {
343+
ports, err := serial.GetPortsList()
344+
if err != nil {
345+
return nil, err
346+
}
347+
res := map[string]bool{}
348+
for _, port := range ports {
349+
res[port] = true
350+
}
351+
return res, nil
352+
}
353+
354+
last, err := getPortMap()
355+
if err != nil {
356+
return "", fmt.Errorf("scanning serial port: %s", err)
357+
}
358+
359+
deadline := time.Now().Add(10 * time.Second)
360+
for time.Now().Before(deadline) {
361+
now, err := getPortMap()
362+
if err != nil {
363+
return "", fmt.Errorf("scanning serial port: %s", err)
364+
}
365+
366+
for p := range now {
367+
if !last[p] {
368+
return p, nil // Found it!
369+
}
370+
}
371+
372+
last = now
373+
time.Sleep(250 * time.Millisecond)
374+
}
375+
376+
return "", nil
377+
}

0 commit comments

Comments
 (0)