diff --git a/main.go b/main.go index 498c65b5..c28d974b 100644 --- a/main.go +++ b/main.go @@ -7,6 +7,7 @@ import ( "log" "os" "strings" + "time" "github.com/arduino/FirmwareUploader/modules/nina" "github.com/arduino/FirmwareUploader/modules/sara" @@ -28,11 +29,11 @@ func init() { flag.StringVar(&ctx.ProgrammerPath, "programmer", "", "path of programmer in use (avrdude/bossac)") flag.StringVar(&ctx.Model, "model", "", "module model (winc, nina or sara)") flag.StringVar(&ctx.Compatible, "get_available_for", "", "Ask for available firmwares matching a given board") + flag.IntVar(&ctx.Retries, "retries", 9, "Number of retries in case of upload failure") } func main() { flag.Parse() - if ctx.Compatible != "" { el, _ := json.Marshal(utils.GetCompatibleWith(ctx.Compatible, "")) fmt.Println(string(el)) @@ -43,11 +44,31 @@ func main() { log.Fatal("Please specify a serial port") } - if ctx.Model == "nina" || strings.Contains(ctx.FirmwareFile, "NINA") || strings.Contains(ctx.FWUploaderBinary, "NINA") { - nina.Run(ctx) - } else if ctx.Model == "winc" || strings.Contains(ctx.FirmwareFile, "WINC") || strings.Contains(ctx.FWUploaderBinary, "WINC") { - winc.Run(ctx) - } else { - sara.Run(ctx) + retry := 0 + for { + var ctxCopy context.Context + ctxCopy = *ctx + var err error + if ctx.Model == "nina" || strings.Contains(ctx.FirmwareFile, "NINA") || strings.Contains(ctx.FWUploaderBinary, "NINA") { + err = nina.Run(&ctxCopy) + } else if ctx.Model == "winc" || strings.Contains(ctx.FirmwareFile, "WINC") || strings.Contains(ctx.FWUploaderBinary, "WINC") { + err = winc.Run(&ctxCopy) + } else { + err = sara.Run(&ctxCopy) + } + if err == nil { + log.Println("Operation completed: success! :-)") + break + } + log.Println("Error: " + err.Error()) + + if retry >= ctx.Retries { + log.Fatal("Operation failed. :-(") + } + + retry++ + log.Println("Waiting 1 second before retrying...") + time.Sleep(time.Second) + log.Printf("Retrying upload (%d of %d)", retry, ctx.Retries) } } diff --git a/modules/nina/main.go b/modules/nina/main.go index b2121b0f..99343482 100644 --- a/modules/nina/main.go +++ b/modules/nina/main.go @@ -28,17 +28,18 @@ import ( "strconv" "strings" - "github.com/arduino/FirmwareUploader/programmers/rp2040load" "github.com/arduino/FirmwareUploader/programmers/avrdude" "github.com/arduino/FirmwareUploader/programmers/bossac" + "github.com/arduino/FirmwareUploader/programmers/rp2040load" "github.com/arduino/FirmwareUploader/utils/context" + "github.com/pkg/errors" ) var flasher *Flasher var payloadSize uint16 var programmer context.Programmer -func Run(ctx *context.Context) { +func Run(ctx *context.Context) error { if ctx.ProgrammerPath != "" { if strings.Contains(filepath.Base(ctx.ProgrammerPath), "bossac") { @@ -48,60 +49,61 @@ func Run(ctx *context.Context) { } else if strings.Contains(filepath.Base(ctx.ProgrammerPath), "rp2040load") { programmer = rp2040load.NewRP2040Load(ctx) } else { - log.Fatal("Programmer path not specified correctly, programmer path set to: " + ctx.ProgrammerPath) + return errors.New("Programmer path not specified correctly, programmer path set to: " + ctx.ProgrammerPath) } } if ctx.FWUploaderBinary != "" { log.Println("Flashing firmware uploader nina") if programmer == nil { - log.Fatal("ERROR: You must specify a programmer!") + return errors.New("ERROR: You must specify a programmer!") } if err := programmer.Flash(ctx.FWUploaderBinary, nil); err != nil { - log.Fatal(err) + return err } } log.Println("Connecting to programmer") if f, err := OpenFlasher(ctx.PortName); err != nil { - log.Fatal(err) + return err } else { flasher = f } + defer flasher.Close() // Synchronize with programmer log.Println("Sync with programmer") if err := flasher.Hello(); err != nil { - log.Fatal(err) + return err } // Check maximum supported payload size log.Println("Reading max payload size") if _payloadSize, err := flasher.GetMaximumPayloadSize(); err != nil { - log.Fatal(err) + return err } else { payloadSize = _payloadSize } if payloadSize < 1024 { - log.Fatalf("Programmer reports %d as maximum payload size (1024 is needed)", payloadSize) + return errors.Errorf("Programmer reports %d as maximum payload size (1024 is needed)", payloadSize) } if ctx.FirmwareFile != "" { if err := flashFirmware(ctx); err != nil { - log.Fatal(err) + return err } } if ctx.RootCertDir != "" || len(ctx.Addresses) != 0 { if err := flashCerts(ctx); err != nil { - log.Fatal(err) + return err } } if ctx.ReadAll { log.Println("Reading all flash") if err := readAllFlash(); err != nil { - log.Fatal(err) + return err } } @@ -110,18 +112,19 @@ func Run(ctx *context.Context) { if ctx.BinaryToRestore != "" { log.Println("Restoring binary") if programmer == nil { - log.Fatal("ERROR: You must specify a programmer!") + errors.New("ERROR: You must specify a programmer!") } if err := programmer.Flash(ctx.BinaryToRestore, nil); err != nil { - log.Fatal(err) + return err } } + return nil } func readAllFlash() error { for i := 0; i < 256; i++ { if data, err := flasher.Read(uint32(i*1024), 1024); err != nil { - log.Fatal(err) + return err } else { os.Stdout.Write(data) } @@ -142,7 +145,7 @@ func flashCerts(ctx *context.Context) error { } if len(certificatesData) > 0x20000 { - log.Fatal("Too many certificates! Aborting") + errors.New("Too many certificates! Aborting") } // pad certificatesData to flash page diff --git a/modules/sara/flasher.go b/modules/sara/flasher.go index 570783ff..02375816 100644 --- a/modules/sara/flasher.go +++ b/modules/sara/flasher.go @@ -41,10 +41,9 @@ type Flasher struct { } func (flasher *Flasher) Hello() error { - - f.Expect("ATE0", "OK", 100) - f.Expect("ATE0", "OK", 100) - f.Expect("ATE0", "OK", 100) + flasher.Expect("ATE0", "OK", 100) + flasher.Expect("ATE0", "OK", 100) + flasher.Expect("ATE0", "OK", 100) _, err := flasher.Expect("AT", "OK", 100) return err } diff --git a/modules/sara/main.go b/modules/sara/main.go index 54713c52..7b93f0e8 100644 --- a/modules/sara/main.go +++ b/modules/sara/main.go @@ -21,48 +21,50 @@ package sara import ( "fmt" - "github.com/arduino/FirmwareUploader/programmers/bossac" - "github.com/arduino/FirmwareUploader/utils/context" "io/ioutil" "log" "strconv" "time" + + "github.com/arduino/FirmwareUploader/programmers/bossac" + "github.com/arduino/FirmwareUploader/utils/context" ) -var f *Flasher +var flasher *Flasher var payloadSize uint16 var programmer context.Programmer -func Run(ctx *context.Context) { +func Run(ctx *context.Context) error { programmer := bossac.NewBossac(ctx) if ctx.FWUploaderBinary != "" { log.Println("Flashing firmware uploader sara") if err := programmer.Flash(ctx.FWUploaderBinary, nil); err != nil { - log.Fatal(err) + return err } } log.Println("Connecting to programmer") - if _f, err := OpenFlasher(ctx.PortName); err != nil { - log.Fatal(err) + if f, err := OpenFlasher(ctx.PortName); err != nil { + return err } else { - f = _f + flasher = f } + defer flasher.Close() time.Sleep(2 * time.Second) // Synchronize with programmer log.Println("Sync with programmer") - if err := f.Hello(); err != nil { - log.Fatal(err) + if err := flasher.Hello(); err != nil { + return err } // Check maximum supported payload size log.Println("Reading actual firmware version") - if fwVersion, err := f.GetFwVersion(); err != nil { - log.Fatal(err) + if fwVersion, err := flasher.GetFwVersion(); err != nil { + return err } else { log.Println("Initial firmware version: " + fwVersion) } @@ -71,25 +73,26 @@ func Run(ctx *context.Context) { if ctx.FirmwareFile != "" { if err := flashFirmware(ctx); err != nil { - log.Fatal(err) + return err } } - if fwVersion, err := f.GetFwVersion(); err != nil { - log.Fatal(err) + if fwVersion, err := flasher.GetFwVersion(); err != nil { + return err } else { log.Println("After applying update firmware version: " + fwVersion) } - f.Close() + flasher.Close() if ctx.BinaryToRestore != "" { log.Println("Restoring previous sketch") if err := programmer.Flash(ctx.BinaryToRestore, nil); err != nil { - log.Fatal(err) + return err } } + return nil } func flashFirmware(ctx *context.Context) error { @@ -102,12 +105,12 @@ func flashFirmware(ctx *context.Context) error { return err } - _, err = f.Expect("AT+ULSTFILE", "+ULSTFILE:", 1000) + _, err = flasher.Expect("AT+ULSTFILE", "+ULSTFILE:", 1000) if err != nil { return err } - _, err = f.Expect("AT+UDWNFILE=\"UPDATE.BIN\","+strconv.Itoa(len(fwData))+",\"FOAT\"", ">", 20000) + _, err = flasher.Expect("AT+UDWNFILE=\"UPDATE.BIN\","+strconv.Itoa(len(fwData))+",\"FOAT\"", ">", 20000) if err != nil { return err } @@ -119,12 +122,12 @@ func flashFirmware(ctx *context.Context) error { time.Sleep(1 * time.Second) - _, err = f.Expect("", "OK", 1000) + _, err = flasher.Expect("", "OK", 1000) if err != nil { return err } - _, err = f.Expect("AT+UFWINSTALL", "OK", 60000) + _, err = flasher.Expect("AT+UFWINSTALL", "OK", 60000) if err != nil { return err } @@ -134,7 +137,7 @@ func flashFirmware(ctx *context.Context) error { // wait up to 20 minutes trying to ping the module. After 20 minutes signal the error start := time.Now() for time.Since(start) < time.Minute*20 { - err = f.Hello() + err = flasher.Hello() if err == nil { return nil } @@ -154,7 +157,7 @@ func flashChunk(offset int, buffer []byte) error { if end > bufferLength { end = bufferLength } - if err := f.Write(uint32(offset+i), buffer[start:end]); err != nil { + if err := flasher.Write(uint32(offset+i), buffer[start:end]); err != nil { return err } //time.Sleep(1 * time.Millisecond) diff --git a/modules/winc/main.go b/modules/winc/main.go index 61b4360b..5e693424 100644 --- a/modules/winc/main.go +++ b/modules/winc/main.go @@ -21,7 +21,6 @@ package winc import ( "bytes" - "errors" "fmt" "io/ioutil" "log" @@ -30,81 +29,84 @@ import ( "github.com/arduino/FirmwareUploader/programmers/bossac" "github.com/arduino/FirmwareUploader/utils/context" + "github.com/pkg/errors" ) -var f *Flasher +var flasher *Flasher var payloadSize uint16 -func Run(ctx *context.Context) { +func Run(ctx *context.Context) error { programmer := bossac.NewBossac(ctx) if ctx.FWUploaderBinary != "" { log.Println("Flashing firmware uploader winc") if err := programmer.Flash(ctx.FWUploaderBinary, nil); err != nil { - log.Fatal(err) + return err } } log.Println("Connecting to programmer") - if _f, err := OpenFlasher(ctx.PortName); err != nil { - log.Fatal(err) + if f, err := OpenFlasher(ctx.PortName); err != nil { + return err } else { - f = _f + flasher = f } + defer flasher.Close() // Synchronize with programmer log.Println("Sync with programmer") - if err := f.Hello(); err != nil { - log.Fatal(err) + if err := flasher.Hello(); err != nil { + return err } // Check maximum supported payload size log.Println("Reading max payload size") - _payloadSize, err := f.GetMaximumPayloadSize() + _payloadSize, err := flasher.GetMaximumPayloadSize() if err != nil { - log.Fatal(err) + return err } else { payloadSize = _payloadSize } if payloadSize < 1024 { - log.Fatalf("Programmer reports %d as maximum payload size (1024 is needed)", payloadSize) + return errors.Errorf("Programmer reports %d as maximum payload size (1024 is needed)", payloadSize) } if ctx.FirmwareFile != "" { if err := flashFirmware(ctx); err != nil { - log.Fatal(err) + return err } } if ctx.RootCertDir != "" || len(ctx.Addresses) != 0 { if err := flashCerts(ctx); err != nil { - log.Fatal(err) + return err } } if ctx.ReadAll { log.Println("Reading all flash") if err := readAllFlash(); err != nil { - log.Fatal(err) + return err } } - f.Close() + flasher.Close() if ctx.BinaryToRestore != "" { log.Println("Restoring previous sketch") if err := programmer.Flash(ctx.BinaryToRestore, nil); err != nil { - log.Fatal(err) + return err } } + return nil } func readAllFlash() error { for i := 0; i < 256; i++ { - if data, err := f.Read(uint32(i*1024), 1024); err != nil { - log.Fatal(err) + if data, err := flasher.Read(uint32(i*1024), 1024); err != nil { + return err } else { os.Stdout.Write(data) } @@ -144,7 +146,7 @@ func flashChunk(offset int, buffer []byte) error { chunkSize := int(payloadSize) bufferLength := len(buffer) - if err := f.Erase(uint32(offset), uint32(bufferLength)); err != nil { + if err := flasher.Erase(uint32(offset), uint32(bufferLength)); err != nil { return err } @@ -155,7 +157,7 @@ func flashChunk(offset int, buffer []byte) error { if end > bufferLength { end = bufferLength } - if err := f.Write(uint32(offset+i), buffer[start:end]); err != nil { + if err := flasher.Write(uint32(offset+i), buffer[start:end]); err != nil { return err } } @@ -167,7 +169,7 @@ func flashChunk(offset int, buffer []byte) error { readLength = bufferLength % chunkSize } - data, err := f.Read(uint32(offset+i), uint32(readLength)) + data, err := flasher.Read(uint32(offset+i), uint32(readLength)) if err != nil { return err } diff --git a/utils/context/context.go b/utils/context/context.go index 5490f4f5..2c52562f 100644 --- a/utils/context/context.go +++ b/utils/context/context.go @@ -28,6 +28,7 @@ type Context struct { ProgrammerPath string Model string Compatible string + Retries int } type Programmer interface {