@@ -34,13 +34,16 @@ import (
34
34
"os"
35
35
"path/filepath"
36
36
"strings"
37
+ "time"
37
38
38
39
properties "github.com/arduino/go-properties-map"
39
40
"github.com/bcmi-labs/arduino-cli/commands"
40
41
"github.com/bcmi-labs/arduino-cli/common/formatter"
41
42
"github.com/bcmi-labs/arduino-cli/cores"
42
43
"github.com/bcmi-labs/arduino-cli/executils"
44
+ "github.com/sirupsen/logrus"
43
45
"github.com/spf13/cobra"
46
+ serial "go.bug.st/serial.v1"
44
47
)
45
48
46
49
// Init prepares the command.
@@ -203,9 +206,6 @@ func run(command *cobra.Command, args []string) {
203
206
}
204
207
}
205
208
206
- // Set serial port property
207
- uploadProperties ["serial.port.file" ] = flags .port
208
-
209
209
// Set properties for verbose upload
210
210
if flags .verbose {
211
211
if v , ok := uploadProperties ["upload.params.verbose" ]; ok {
@@ -229,7 +229,8 @@ func run(command *cobra.Command, args []string) {
229
229
fqbn = strings .Replace (fqbn , ":" , "." , - 1 )
230
230
uploadProperties ["build.path" ] = sketch .FullPath
231
231
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 {
233
234
if os .IsNotExist (err ) {
234
235
formatter .PrintErrorMessage ("Compiled sketch not found. Please compile first." )
235
236
} else {
@@ -238,7 +239,57 @@ func run(command *cobra.Command, args []string) {
238
239
os .Exit (commands .ErrGeneric )
239
240
}
240
241
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
242
293
recipe := uploadProperties ["upload.pattern" ]
243
294
cmdLine := uploadProperties .ExpandPropsInString (recipe )
244
295
cmdArgs , err := properties .SplitQuotedString (cmdLine , `"'` , false )
@@ -247,6 +298,7 @@ func run(command *cobra.Command, args []string) {
247
298
os .Exit (commands .ErrCoreConfig )
248
299
}
249
300
301
+ // Run Tool
250
302
cmd , err := executils .Command (cmdArgs )
251
303
if err != nil {
252
304
formatter .PrintError (err , "Could not execute upload tool." )
@@ -265,3 +317,61 @@ func run(command *cobra.Command, args []string) {
265
317
os .Exit (commands .ErrGeneric )
266
318
}
267
319
}
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