From e541576f80d708cc499af44f8bec6bead837b016 Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Wed, 22 Sep 2021 18:03:16 +0200 Subject: [PATCH 01/15] refactor DownloadLoaderSketch to be more generic --- cli/certificates/flash.go | 2 +- cli/firmware/flash.go | 2 +- indexes/download/download.go | 18 +++++++++--------- indexes/download/download_test.go | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cli/certificates/flash.go b/cli/certificates/flash.go index e091e8b0..369591cf 100644 --- a/cli/certificates/flash.go +++ b/cli/certificates/flash.go @@ -113,7 +113,7 @@ func run(cmd *cobra.Command, args []string) { os.Exit(errorcodes.ErrGeneric) } - loaderSketchPath, err := download.DownloadLoaderSketch(board.LoaderSketch) + loaderSketchPath, err := download.DownloadSketch(board.LoaderSketch) if err != nil { feedback.Errorf("Error downloading loader sketch from %s: %s", board.LoaderSketch.URL, err) os.Exit(errorcodes.ErrGeneric) diff --git a/cli/firmware/flash.go b/cli/firmware/flash.go index c1b96df5..529114d2 100644 --- a/cli/firmware/flash.go +++ b/cli/firmware/flash.go @@ -143,7 +143,7 @@ func run(cmd *cobra.Command, args []string) { os.Exit(errorcodes.ErrGeneric) } - loaderSketchPath, err := download.DownloadLoaderSketch(board.LoaderSketch) + loaderSketchPath, err := download.DownloadSketch(board.LoaderSketch) if err != nil { feedback.Errorf("Error downloading loader sketch from %s: %s", board.LoaderSketch.URL, err) os.Exit(errorcodes.ErrGeneric) diff --git a/indexes/download/download.go b/indexes/download/download.go index fd24603e..32f9ad02 100644 --- a/indexes/download/download.go +++ b/indexes/download/download.go @@ -105,16 +105,16 @@ func DownloadFirmware(firmware *firmwareindex.IndexFirmware) (*paths.Path, error return firmwarePath, nil } -func DownloadLoaderSketch(loader *firmwareindex.IndexLoaderSketch) (*paths.Path, error) { - loaderPath := globals.FwUploaderPath.Join( - "loader", +func DownloadSketch(loader *firmwareindex.IndexSketch) (*paths.Path, error) { + sketchPath := globals.FwUploaderPath.Join( + "sketch", path.Base(loader.URL)) - loaderPath.Parent().MkdirAll() - if err := loaderPath.WriteFile(nil); err != nil { + sketchPath.Parent().MkdirAll() + if err := sketchPath.WriteFile(nil); err != nil { logrus.Error(err) return nil, err } - d, err := downloader.Download(loaderPath.String(), loader.URL) + d, err := downloader.Download(sketchPath.String(), loader.URL) if err != nil { logrus.Error(err) return nil, err @@ -123,16 +123,16 @@ func DownloadLoaderSketch(loader *firmwareindex.IndexLoaderSketch) (*paths.Path, logrus.Error(err) return nil, err } - if err := VerifyFileChecksum(loader.Checksum, loaderPath); err != nil { + if err := VerifyFileChecksum(loader.Checksum, sketchPath); err != nil { logrus.Error(err) return nil, err } size, _ := loader.Size.Int64() - if err := VerifyFileSize(size, loaderPath); err != nil { + if err := VerifyFileSize(size, sketchPath); err != nil { logrus.Error(err) return nil, err } - return loaderPath, nil + return sketchPath, nil } // Download will take a downloader.Downloader as parameter. It will Download the file specified in the downloader diff --git a/indexes/download/download_test.go b/indexes/download/download_test.go index b157bf58..efc87813 100644 --- a/indexes/download/download_test.go +++ b/indexes/download/download_test.go @@ -149,7 +149,7 @@ func TestDownloadLoaderSketch(t *testing.T) { index, e := firmwareindex.LoadIndexNoSign(indexFile) require.NoError(t, e) require.NotEmpty(t, index) - loaderPath, err := DownloadLoaderSketch(index.Boards[0].LoaderSketch) + loaderPath, err := DownloadSketch(index.Boards[0].LoaderSketch) require.NoError(t, err) require.NotEmpty(t, loaderPath) require.FileExists(t, loaderPath.String()) From 5a98dbce64706f00b10a42711bde7cd10b928f97 Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Wed, 22 Sep 2021 18:05:07 +0200 Subject: [PATCH 02/15] make OpenSerial public --- flasher/flasher.go | 2 +- flasher/nina.go | 2 +- flasher/winc.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/flasher/flasher.go b/flasher/flasher.go index 92b4e680..ac0deae6 100644 --- a/flasher/flasher.go +++ b/flasher/flasher.go @@ -66,7 +66,7 @@ type Flasher interface { // https://github.com/arduino-libraries/WiFiNINA/blob/master/examples/Tools/FirmwareUpdater/FirmwareUpdater.ino const baudRate = 1000000 -func openSerial(portAddress string) (serial.Port, error) { +func OpenSerial(portAddress string) (serial.Port, error) { port, err := serial.Open(portAddress, &serial.Mode{BaudRate: baudRate}) if err != nil { diff --git a/flasher/nina.go b/flasher/nina.go index 5e12cd18..e059f707 100644 --- a/flasher/nina.go +++ b/flasher/nina.go @@ -37,7 +37,7 @@ import ( // NewNinaFlasher creates an new instance of NinaFlasher func NewNinaFlasher(portAddress string) (*NinaFlasher, error) { - port, err := openSerial(portAddress) + port, err := OpenSerial(portAddress) if err != nil { logrus.Error(err) return nil, err diff --git a/flasher/winc.go b/flasher/winc.go index 105722b1..bbd035f8 100644 --- a/flasher/winc.go +++ b/flasher/winc.go @@ -36,7 +36,7 @@ import ( ) func NewWincFlasher(portAddress string) (*WincFlasher, error) { - port, err := openSerial(portAddress) + port, err := OpenSerial(portAddress) if err != nil { logrus.Error(err) return nil, err From a733f343e34cdc1d52dd1b189c65d7f49d176c0a Mon Sep 17 00:00:00 2001 From: umbynos Date: Thu, 23 Sep 2021 10:27:40 +0200 Subject: [PATCH 03/15] now the program is able to parse the new `module_firmware_index.json` --- indexes/firmwareindex/firmwareindex.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/indexes/firmwareindex/firmwareindex.go b/indexes/firmwareindex/firmwareindex.go index 1cbb52cb..d17917c9 100644 --- a/indexes/firmwareindex/firmwareindex.go +++ b/indexes/firmwareindex/firmwareindex.go @@ -40,7 +40,8 @@ type Index struct { type IndexBoard struct { Fqbn string `json:"fqbn,required"` Firmwares []*IndexFirmware `json:"firmware,required"` - LoaderSketch *IndexLoaderSketch `json:"loader_sketch,required"` + LoaderSketch *IndexSketch `json:"loader_sketch,required"` + VersionSketch *IndexSketch `json:"version_sketch"` Module string `json:"module,required"` Name string `json:"name,required"` Uploader string `json:"uploader,required"` @@ -65,8 +66,8 @@ type IndexFirmware struct { Module string `json:"module,required"` } -// IndexLoaderSketch represents the sketch used to upload the new firmware on a board. -type IndexLoaderSketch struct { +// IndexSketch represents the sketch used to upload the new firmware on a board. +type IndexSketch struct { URL string `json:"url,required"` Checksum string `json:"checksum,required"` Size json.Number `json:"size,required"` From b501a51cdb1a55c773884d16b232eb292dec3f87 Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Fri, 24 Sep 2021 17:20:49 +0200 Subject: [PATCH 04/15] =?UTF-8?q?refactor=20business=20logic=20to=20prepar?= =?UTF-8?q?e=20for=20=C2=B4get-version=C2=B4=20command?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cli/arguments/arguments.go | 19 ++++++ cli/certificates/flash.go | 97 ++++----------------------- cli/common/common.go | 130 +++++++++++++++++++++++++++++++++++++ cli/firmware/flash.go | 108 +++++------------------------- 4 files changed, 178 insertions(+), 176 deletions(-) create mode 100644 cli/arguments/arguments.go create mode 100644 cli/common/common.go diff --git a/cli/arguments/arguments.go b/cli/arguments/arguments.go new file mode 100644 index 00000000..7d44d7b4 --- /dev/null +++ b/cli/arguments/arguments.go @@ -0,0 +1,19 @@ +package arguments + +import ( + "github.com/spf13/cobra" +) + +// Flags contains various common flags. +// This is useful so all flags used by commands that need +// this information are consistent with each other. +type Flags struct { + Address string + Fqbn string +} + +// AddToCommand adds the flags used to set address and fqbn to the specified Command +func (f *Flags) AddToCommand(cmd *cobra.Command) { + cmd.Flags().StringVarP(&f.Fqbn, "fqbn", "b", "", "Fully Qualified Board Name, e.g.: arduino:samd:mkr1000, arduino:mbed_nano:nanorp2040connect") + cmd.Flags().StringVarP(&f.Address, "address", "a", "", "Upload port, e.g.: COM10, /dev/ttyACM0") +} diff --git a/cli/certificates/flash.go b/cli/certificates/flash.go index 369591cf..4e0c554f 100644 --- a/cli/certificates/flash.go +++ b/cli/certificates/flash.go @@ -23,26 +23,21 @@ import ( "bytes" "fmt" "os" - "path/filepath" "strings" "time" - "github.com/arduino/arduino-cli/arduino/serialutils" "github.com/arduino/arduino-cli/cli/errorcodes" "github.com/arduino/arduino-cli/cli/feedback" + "github.com/arduino/arduino-fwuploader/cli/arguments" + "github.com/arduino/arduino-fwuploader/cli/common" "github.com/arduino/arduino-fwuploader/flasher" - "github.com/arduino/arduino-fwuploader/indexes" "github.com/arduino/arduino-fwuploader/indexes/download" - programmer "github.com/arduino/arduino-fwuploader/programmers" "github.com/arduino/go-paths-helper" - "github.com/arduino/go-properties-orderedmap" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) var ( - fqbn string - address string + commonFlags arguments.Flags certificateURLs []string certificatePaths []string ) @@ -58,61 +53,26 @@ func NewFlashCommand() *cobra.Command { " " + os.Args[0] + " certificates flash -b arduino:samd:mkr1000 -a COM10 -u arduino.cc:443 -u google.cc:443\n" + " " + os.Args[0] + " certificates flash -b arduino:samd:mkr1000 -a COM10 -f /home/me/VeriSign.cer -f /home/me/Digicert.cer\n", Args: cobra.NoArgs, - Run: run, + Run: runFlash, } - - command.Flags().StringVarP(&fqbn, "fqbn", "b", "", "Fully Qualified Board Name, e.g.: arduino:samd:mkr1000, arduino:mbed_nano:nanorp2040connect") - command.Flags().StringVarP(&address, "address", "a", "", "Upload port, e.g.: COM10, /dev/ttyACM0") + commonFlags.AddToCommand(command) command.Flags().StringSliceVarP(&certificateURLs, "url", "u", []string{}, "List of urls to download root certificates, e.g.: arduino.cc:443") command.Flags().StringSliceVarP(&certificatePaths, "file", "f", []string{}, "List of paths to certificate file, e.g.: /home/me/Digicert.cer") return command } -func run(cmd *cobra.Command, args []string) { - packageIndex, err := indexes.GetPackageIndex() - if err != nil { - feedback.Errorf("Can't load package index: %s", err) - os.Exit(errorcodes.ErrGeneric) - } +func runFlash(cmd *cobra.Command, args []string) { - firmwareIndex, err := indexes.GetFirmwareIndex() - if err != nil { - feedback.Errorf("Can't load firmware index: %s", err) - os.Exit(errorcodes.ErrGeneric) - } - - if fqbn == "" { - feedback.Errorf("Error during certificates flashing: missing board fqbn") - os.Exit(errorcodes.ErrBadArgument) - } - - if address == "" { - feedback.Errorf("Error during certificates flashing: missing board address") - os.Exit(errorcodes.ErrBadArgument) - } + packageIndex, firmwareIndex := common.InitIndexes() + common.CheckFlags(commonFlags.Fqbn, commonFlags.Address) + board := common.GetBoard(firmwareIndex, commonFlags.Fqbn) + uploadToolDir := common.GetUploadToolDir(packageIndex, board) if len(certificateURLs) == 0 && len(certificatePaths) == 0 { feedback.Errorf("Error during certificates flashing: no certificates provided") os.Exit(errorcodes.ErrBadArgument) } - board := firmwareIndex.GetBoard(fqbn) - if board == nil { - feedback.Errorf("Can't find board with %s fqbn", fqbn) - os.Exit(errorcodes.ErrBadArgument) - } - - toolRelease := indexes.GetToolRelease(packageIndex, board.Uploader) - if toolRelease == nil { - feedback.Errorf("Error getting upload tool %s for board %s", board.Uploader, board.Fqbn) - os.Exit(errorcodes.ErrGeneric) - } - uploadToolDir, err := download.DownloadTool(toolRelease) - if err != nil { - feedback.Errorf("Error downloading tool %s: %s", board.Uploader, err) - os.Exit(errorcodes.ErrGeneric) - } - loaderSketchPath, err := download.DownloadSketch(board.LoaderSketch) if err != nil { feedback.Errorf("Error downloading loader sketch from %s: %s", board.LoaderSketch.URL, err) @@ -121,42 +81,9 @@ func run(cmd *cobra.Command, args []string) { loaderSketch := strings.ReplaceAll(loaderSketchPath.String(), loaderSketchPath.Ext(), "") - // Check if board needs a 1200bps touch for upload - bootloaderPort := address - if board.UploadTouch { - logrus.Info("Putting board into bootloader mode") - newUploadPort, err := serialutils.Reset(address, board.UploadWait, nil) - if err != nil { - feedback.Errorf("Error during certificates flashing: missing board address") - os.Exit(errorcodes.ErrGeneric) - } - if newUploadPort != "" { - logrus.Infof("Found port to upload Loader: %s", newUploadPort) - bootloaderPort = newUploadPort - } - } - - uploaderCommand := board.GetUploaderCommand() - uploaderCommand = strings.ReplaceAll(uploaderCommand, "{tool_dir}", filepath.FromSlash(uploadToolDir.String())) - uploaderCommand = strings.ReplaceAll(uploaderCommand, "{serial.port.file}", bootloaderPort) - uploaderCommand = strings.ReplaceAll(uploaderCommand, "{loader.sketch}", loaderSketch) - - commandLine, err := properties.SplitQuotedString(uploaderCommand, "\"", false) + programmerOut, programmerErr, err := common.FlashSketch(board, loaderSketch, uploadToolDir, commonFlags.Address) if err != nil { - feedback.Errorf(`Error splitting command line "%s": %s`, uploaderCommand, err) - os.Exit(errorcodes.ErrGeneric) - } - - // Flash loader Sketch - programmerOut := new(bytes.Buffer) - programmerErr := new(bytes.Buffer) - if feedback.GetFormat() == feedback.JSON { - err = programmer.Flash(commandLine, programmerOut, programmerErr) - } else { - err = programmer.Flash(commandLine, os.Stdout, os.Stderr) - } - if err != nil { - feedback.Errorf("Error during certificates flashing: %s", err) + feedback.Error(err) os.Exit(errorcodes.ErrGeneric) } diff --git a/cli/common/common.go b/cli/common/common.go new file mode 100644 index 00000000..d8e4271d --- /dev/null +++ b/cli/common/common.go @@ -0,0 +1,130 @@ +package common + +import ( + "bytes" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/arduino/arduino-cli/arduino/cores/packageindex" + "github.com/arduino/arduino-cli/arduino/serialutils" + "github.com/arduino/arduino-cli/cli/errorcodes" + "github.com/arduino/arduino-cli/cli/feedback" + "github.com/arduino/arduino-fwuploader/indexes" + "github.com/arduino/arduino-fwuploader/indexes/download" + "github.com/arduino/arduino-fwuploader/indexes/firmwareindex" + programmer "github.com/arduino/arduino-fwuploader/programmers" + "github.com/arduino/go-paths-helper" + "github.com/arduino/go-properties-orderedmap" + "github.com/sirupsen/logrus" +) + +// InitIndexes does exactly what the name implies +func InitIndexes() (*packageindex.Index, *firmwareindex.Index) { + packageIndex, err := indexes.GetPackageIndex() + if err != nil { + feedback.Errorf("Can't load package index: %s", err) + os.Exit(errorcodes.ErrGeneric) + } + + firmwareIndex, err := indexes.GetFirmwareIndex() + if err != nil { + feedback.Errorf("Can't load firmware index: %s", err) + os.Exit(errorcodes.ErrGeneric) + } + return packageIndex, firmwareIndex +} + +// CheckFlags runs a basic check, errors if the flags are not defined +func CheckFlags(fqbn, address string) { + if fqbn == "" { + feedback.Errorf("Error during firmware flashing: missing board fqbn") + os.Exit(errorcodes.ErrBadArgument) + } + + if address == "" { + feedback.Errorf("Error during firmware flashing: missing board address") + os.Exit(errorcodes.ErrBadArgument) + } +} + +// GetBoard is an helper function useful to get the IndexBoard, +// the struct that contains all the infos to make all the operations possible +func GetBoard(firmwareIndex *firmwareindex.Index, fqbn string) *firmwareindex.IndexBoard { + board := firmwareIndex.GetBoard(fqbn) + if board == nil { + feedback.Errorf("Can't find board with %s fqbn", fqbn) + os.Exit(errorcodes.ErrBadArgument) + } + return board +} + +// GetUploadToolDir is an helper function that downloads the correct tool to flash a board, +// it returns the path of the downloaded tool +func GetUploadToolDir(packageIndex *packageindex.Index, board *firmwareindex.IndexBoard) *paths.Path { + toolRelease := indexes.GetToolRelease(packageIndex, board.Uploader) + if toolRelease == nil { + feedback.Errorf("Error getting upload tool %s for board %s", board.Uploader, board.Fqbn) + os.Exit(errorcodes.ErrGeneric) + } + uploadToolDir, err := download.DownloadTool(toolRelease) + if err != nil { + feedback.Errorf("Error downloading tool %s: %s", board.Uploader, err) + os.Exit(errorcodes.ErrGeneric) + } + return uploadToolDir +} + +// flashSketch is the business logic that handles the flashing procedure, +// it returns using a buffer the out and the err of the programmer +func FlashSketch(board *firmwareindex.IndexBoard, sketch string, uploadToolDir *paths.Path, address string) (programmerOut, programmerErr *bytes.Buffer, err error) { + bootloaderPort, err := GetNewAddress(board, address) + if err != nil { + return nil, nil, err + } + + uploaderCommand := board.GetUploaderCommand() + uploaderCommand = strings.ReplaceAll(uploaderCommand, "{tool_dir}", filepath.FromSlash(uploadToolDir.String())) + uploaderCommand = strings.ReplaceAll(uploaderCommand, "{serial.port.file}", bootloaderPort) + uploaderCommand = strings.ReplaceAll(uploaderCommand, "{loader.sketch}", sketch) // we leave that name here because it's only a template, + + commandLine, err := properties.SplitQuotedString(uploaderCommand, "\"", false) + if err != nil { + feedback.Errorf(`Error splitting command line "%s": %s`, uploaderCommand, err) + os.Exit(errorcodes.ErrGeneric) + } + + // Flash the actual sketch + programmerOut = new(bytes.Buffer) + programmerErr = new(bytes.Buffer) + if feedback.GetFormat() == feedback.JSON { + err = programmer.Flash(commandLine, programmerOut, programmerErr) + } else { + err = programmer.Flash(commandLine, os.Stdout, os.Stderr) + } + if err != nil { + return nil, nil, fmt.Errorf("error during sketch flashing: %s", err) + } + return programmerOut, programmerErr, err +} + +// getNewAddress is a function used to reset a board and put it in bootloader mode +// it could happen that the board is assigned to a different serial port, after the reset, +// this fuction handles also this possibility +func GetNewAddress(board *firmwareindex.IndexBoard, oldAddress string) (string, error) { + // Check if board needs a 1200bps touch for upload + bootloaderPort := oldAddress + if board.UploadTouch { + logrus.Info("Putting board into bootloader mode") + newUploadPort, err := serialutils.Reset(oldAddress, board.UploadWait, nil) + if err != nil { + return "", fmt.Errorf("error during sketch flashing: missing board address. %s", err) + } + if newUploadPort != "" { + logrus.Infof("Found port to upload Loader: %s", newUploadPort) + bootloaderPort = newUploadPort + } + } + return bootloaderPort, nil +} diff --git a/cli/firmware/flash.go b/cli/firmware/flash.go index 529114d2..45799321 100644 --- a/cli/firmware/flash.go +++ b/cli/firmware/flash.go @@ -23,29 +23,25 @@ import ( "bytes" "fmt" "os" - "path/filepath" "strings" "time" - "github.com/arduino/arduino-cli/arduino/serialutils" "github.com/arduino/arduino-cli/cli/errorcodes" "github.com/arduino/arduino-cli/cli/feedback" + "github.com/arduino/arduino-fwuploader/cli/arguments" + "github.com/arduino/arduino-fwuploader/cli/common" "github.com/arduino/arduino-fwuploader/flasher" - "github.com/arduino/arduino-fwuploader/indexes" "github.com/arduino/arduino-fwuploader/indexes/download" "github.com/arduino/arduino-fwuploader/indexes/firmwareindex" - programmer "github.com/arduino/arduino-fwuploader/programmers" "github.com/arduino/go-paths-helper" - "github.com/arduino/go-properties-orderedmap" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) var ( - fqbn string - address string - module string - retries uint8 + commonFlags arguments.Flags // contains fqbn and address + module string + retries uint8 ) // NewCommand created a new `version` command @@ -59,44 +55,20 @@ func NewFlashCommand() *cobra.Command { " " + os.Args[0] + " firmware flash -b arduino:samd:mkr1000 -a COM10 -m WINC15000\n" + " " + os.Args[0] + " firmware flash -b arduino:samd:mkr1000 -a COM10\n", Args: cobra.NoArgs, - Run: run, + Run: runFlash, } - - command.Flags().StringVarP(&fqbn, "fqbn", "b", "", "Fully Qualified Board Name, e.g.: arduino:samd:mkr1000, arduino:mbed_nano:nanorp2040connect") - command.Flags().StringVarP(&address, "address", "a", "", "Upload port, e.g.: COM10, /dev/ttyACM0") + commonFlags.AddToCommand(command) command.Flags().StringVarP(&module, "module", "m", "", "Firmware module ID, e.g.: WINC1500, NINA") command.Flags().Uint8Var(&retries, "retries", 9, "Number of retries in case of upload failure (default 9)") return command } -func run(cmd *cobra.Command, args []string) { - packageIndex, err := indexes.GetPackageIndex() - if err != nil { - feedback.Errorf("Can't load package index: %s", err) - os.Exit(errorcodes.ErrGeneric) - } - - firmwareIndex, err := indexes.GetFirmwareIndex() - if err != nil { - feedback.Errorf("Can't load firmware index: %s", err) - os.Exit(errorcodes.ErrGeneric) - } - - if fqbn == "" { - feedback.Errorf("Error during firmware flashing: missing board fqbn") - os.Exit(errorcodes.ErrBadArgument) - } +func runFlash(cmd *cobra.Command, args []string) { - if address == "" { - feedback.Errorf("Error during firmware flashing: missing board address") - os.Exit(errorcodes.ErrBadArgument) - } - - board := firmwareIndex.GetBoard(fqbn) - if board == nil { - feedback.Errorf("Can't find board with %s fqbn", fqbn) - os.Exit(errorcodes.ErrBadArgument) - } + packageIndex, firmwareIndex := common.InitIndexes() + common.CheckFlags(commonFlags.Fqbn, commonFlags.Address) + board := common.GetBoard(firmwareIndex, commonFlags.Fqbn) + uploadToolDir := common.GetUploadToolDir(packageIndex, board) // Get module name if not specified moduleName := "" @@ -122,7 +94,7 @@ func run(cmd *cobra.Command, args []string) { firmware = board.GetFirmware(moduleVersion) } if firmware == nil { - feedback.Errorf("Error getting firmware for board: %s", fqbn) + feedback.Errorf("Error getting firmware for board: %s", commonFlags.Fqbn) os.Exit(errorcodes.ErrGeneric) } @@ -132,17 +104,6 @@ func run(cmd *cobra.Command, args []string) { os.Exit(errorcodes.ErrGeneric) } - toolRelease := indexes.GetToolRelease(packageIndex, board.Uploader) - if toolRelease == nil { - feedback.Errorf("Error getting upload tool %s for board %s", board.Uploader, board.Fqbn) - os.Exit(errorcodes.ErrGeneric) - } - uploadToolDir, err := download.DownloadTool(toolRelease) - if err != nil { - feedback.Errorf("Error downloading tool %s: %s", board.Uploader, err) - os.Exit(errorcodes.ErrGeneric) - } - loaderSketchPath, err := download.DownloadSketch(board.LoaderSketch) if err != nil { feedback.Errorf("Error downloading loader sketch from %s: %s", board.LoaderSketch.URL, err) @@ -168,44 +129,10 @@ func run(cmd *cobra.Command, args []string) { } func updateFirmware(board *firmwareindex.IndexBoard, loaderSketch, moduleName string, uploadToolDir, firmwareFile *paths.Path) error { - var err error - // Check if board needs a 1200bps touch for upload - bootloaderPort := address - if board.UploadTouch { - logrus.Info("Putting board into bootloader mode") - newUploadPort, err := serialutils.Reset(address, board.UploadWait, nil) - if err != nil { - return fmt.Errorf("error during firmware flashing: missing board address. %s", err) - } - if newUploadPort != "" { - logrus.Infof("Found port to upload Loader: %s", newUploadPort) - bootloaderPort = newUploadPort - } - } - - uploaderCommand := board.GetUploaderCommand() - uploaderCommand = strings.ReplaceAll(uploaderCommand, "{tool_dir}", filepath.FromSlash(uploadToolDir.String())) - uploaderCommand = strings.ReplaceAll(uploaderCommand, "{serial.port.file}", bootloaderPort) - uploaderCommand = strings.ReplaceAll(uploaderCommand, "{loader.sketch}", loaderSketch) - - commandLine, err := properties.SplitQuotedString(uploaderCommand, "\"", false) - if err != nil { - feedback.Errorf(`Error splitting command line "%s": %s`, uploaderCommand, err) - os.Exit(errorcodes.ErrGeneric) - } - - // Flash loader Sketch - programmerOut := new(bytes.Buffer) - programmerErr := new(bytes.Buffer) - if feedback.GetFormat() == feedback.JSON { - err = programmer.Flash(commandLine, programmerOut, programmerErr) - } else { - err = programmer.Flash(commandLine, os.Stdout, os.Stderr) - } + programmerOut, programmerErr, err := common.FlashSketch(board, loaderSketch, uploadToolDir, commonFlags.Address) if err != nil { - return fmt.Errorf("error during loader sketch flashing: %s", err) + return err } - // Wait a bit after flashing the loader sketch for the board to become // available again. time.Sleep(3 * time.Second) @@ -240,6 +167,7 @@ func updateFirmware(board *firmwareindex.IndexBoard, loaderSketch, moduleName st } if err != nil { flasherErr.Write([]byte(fmt.Sprintf("Error during firmware flashing: %s", err))) + return err } // Print the results @@ -253,12 +181,10 @@ func updateFirmware(board *firmwareindex.IndexBoard, loaderSketch, moduleName st Stderr: flasherErr.String(), }), }) - if err != nil { - return fmt.Errorf("error during firmware flashing: %s", err) - } return nil } +// callback used to print the progress func printProgress(progress int) { fmt.Printf("Flashing progress: %d%%\r", progress) } From 5cc7c5f440eda3a356ceabe25aaa4e298d900d9d Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Fri, 24 Sep 2021 17:25:17 +0200 Subject: [PATCH 05/15] add baudrate to OpenSerial (it makes more sense), usefull for new cmd --- cli/certificates/flash.go | 8 ++++++-- cli/firmware/flash.go | 8 ++++++-- flasher/flasher.go | 6 +----- flasher/nina.go | 4 ++-- flasher/winc.go | 4 ++-- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/cli/certificates/flash.go b/cli/certificates/flash.go index 4e0c554f..0d938ed4 100644 --- a/cli/certificates/flash.go +++ b/cli/certificates/flash.go @@ -94,12 +94,16 @@ func runFlash(cmd *cobra.Command, args []string) { // Get flasher depending on which module to use var f flasher.Flasher moduleName := board.Module + + // This matches the baudrate used in the FirmwareUpdater.ino sketch + // https://github.com/arduino-libraries/WiFiNINA/blob/master/examples/Tools/FirmwareUpdater/FirmwareUpdater.ino + const baudRate = 1000000 switch moduleName { case "NINA": // we use address and not bootloaderPort because the board should not be in bootloader mode - f, err = flasher.NewNinaFlasher(address) + f, err = flasher.NewNinaFlasher(commonFlags.Address, baudRate) case "WINC1500": - f, err = flasher.NewWincFlasher(address) + f, err = flasher.NewWincFlasher(commonFlags.Address, baudRate) default: err = fmt.Errorf("unknown module: %s", moduleName) } diff --git a/cli/firmware/flash.go b/cli/firmware/flash.go index 45799321..17cad3d4 100644 --- a/cli/firmware/flash.go +++ b/cli/firmware/flash.go @@ -139,12 +139,16 @@ func updateFirmware(board *firmwareindex.IndexBoard, loaderSketch, moduleName st // Get flasher depending on which module to use var f flasher.Flasher + + // This matches the baudrate used in the FirmwareUpdater.ino sketch + // https://github.com/arduino-libraries/WiFiNINA/blob/master/examples/Tools/FirmwareUpdater/FirmwareUpdater.ino + const baudRate = 1000000 switch moduleName { case "NINA": // we use address and not bootloaderPort because the board should not be in bootloader mode - f, err = flasher.NewNinaFlasher(address) + f, err = flasher.NewNinaFlasher(commonFlags.Address, baudRate) case "WINC1500": - f, err = flasher.NewWincFlasher(address) + f, err = flasher.NewWincFlasher(commonFlags.Address, baudRate) default: err = fmt.Errorf("unknown module: %s", moduleName) feedback.Errorf("Error during firmware flashing: %s", err) diff --git a/flasher/flasher.go b/flasher/flasher.go index ac0deae6..46266435 100644 --- a/flasher/flasher.go +++ b/flasher/flasher.go @@ -62,11 +62,7 @@ type Flasher interface { sendCommand(data CommandData) error } -// This matches the baudrate used in the FirmwareUpdater.ino sketch -// https://github.com/arduino-libraries/WiFiNINA/blob/master/examples/Tools/FirmwareUpdater/FirmwareUpdater.ino -const baudRate = 1000000 - -func OpenSerial(portAddress string) (serial.Port, error) { +func OpenSerial(portAddress string, baudRate int) (serial.Port, error) { port, err := serial.Open(portAddress, &serial.Mode{BaudRate: baudRate}) if err != nil { diff --git a/flasher/nina.go b/flasher/nina.go index e059f707..30fd6b79 100644 --- a/flasher/nina.go +++ b/flasher/nina.go @@ -36,8 +36,8 @@ import ( ) // NewNinaFlasher creates an new instance of NinaFlasher -func NewNinaFlasher(portAddress string) (*NinaFlasher, error) { - port, err := OpenSerial(portAddress) +func NewNinaFlasher(portAddress string, baudRate int) (*NinaFlasher, error) { + port, err := OpenSerial(portAddress, baudRate) if err != nil { logrus.Error(err) return nil, err diff --git a/flasher/winc.go b/flasher/winc.go index bbd035f8..db0288b7 100644 --- a/flasher/winc.go +++ b/flasher/winc.go @@ -35,8 +35,8 @@ import ( "go.bug.st/serial" ) -func NewWincFlasher(portAddress string) (*WincFlasher, error) { - port, err := OpenSerial(portAddress) +func NewWincFlasher(portAddress string, baudRate int) (*WincFlasher, error) { + port, err := OpenSerial(portAddress, baudRate) if err != nil { logrus.Error(err) return nil, err From 91f8c09da3b79f8f0ac0250efde934913df08a1c Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Mon, 27 Sep 2021 14:22:55 +0200 Subject: [PATCH 06/15] add readTimeout parameter to OpenSerial It has to be different when reading from serial to check the version and when uploading the firmware. --- cli/certificates/flash.go | 4 ++-- cli/firmware/flash.go | 4 ++-- flasher/flasher.go | 4 ++-- flasher/nina.go | 4 ++-- flasher/winc.go | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cli/certificates/flash.go b/cli/certificates/flash.go index 0d938ed4..02d170c7 100644 --- a/cli/certificates/flash.go +++ b/cli/certificates/flash.go @@ -101,9 +101,9 @@ func runFlash(cmd *cobra.Command, args []string) { switch moduleName { case "NINA": // we use address and not bootloaderPort because the board should not be in bootloader mode - f, err = flasher.NewNinaFlasher(commonFlags.Address, baudRate) + f, err = flasher.NewNinaFlasher(commonFlags.Address, baudRate, 30) case "WINC1500": - f, err = flasher.NewWincFlasher(commonFlags.Address, baudRate) + f, err = flasher.NewWincFlasher(commonFlags.Address, baudRate, 30) default: err = fmt.Errorf("unknown module: %s", moduleName) } diff --git a/cli/firmware/flash.go b/cli/firmware/flash.go index 17cad3d4..5b6a0158 100644 --- a/cli/firmware/flash.go +++ b/cli/firmware/flash.go @@ -146,9 +146,9 @@ func updateFirmware(board *firmwareindex.IndexBoard, loaderSketch, moduleName st switch moduleName { case "NINA": // we use address and not bootloaderPort because the board should not be in bootloader mode - f, err = flasher.NewNinaFlasher(commonFlags.Address, baudRate) + f, err = flasher.NewNinaFlasher(commonFlags.Address, baudRate, 30) case "WINC1500": - f, err = flasher.NewWincFlasher(commonFlags.Address, baudRate) + f, err = flasher.NewWincFlasher(commonFlags.Address, baudRate, 30) default: err = fmt.Errorf("unknown module: %s", moduleName) feedback.Errorf("Error during firmware flashing: %s", err) diff --git a/flasher/flasher.go b/flasher/flasher.go index 46266435..ea1e453e 100644 --- a/flasher/flasher.go +++ b/flasher/flasher.go @@ -62,7 +62,7 @@ type Flasher interface { sendCommand(data CommandData) error } -func OpenSerial(portAddress string, baudRate int) (serial.Port, error) { +func OpenSerial(portAddress string, baudRate int, readTimeout int) (serial.Port, error) { port, err := serial.Open(portAddress, &serial.Mode{BaudRate: baudRate}) if err != nil { @@ -70,7 +70,7 @@ func OpenSerial(portAddress string, baudRate int) (serial.Port, error) { } logrus.Infof("Opened port %s at %d", portAddress, baudRate) - if err := port.SetReadTimeout(30 * time.Second); err != nil { + if err := port.SetReadTimeout(time.Duration(readTimeout) * time.Second); err != nil { err = fmt.Errorf("could not set timeout on serial port: %s", err) logrus.Error(err) return nil, err diff --git a/flasher/nina.go b/flasher/nina.go index 30fd6b79..c218ab6c 100644 --- a/flasher/nina.go +++ b/flasher/nina.go @@ -36,8 +36,8 @@ import ( ) // NewNinaFlasher creates an new instance of NinaFlasher -func NewNinaFlasher(portAddress string, baudRate int) (*NinaFlasher, error) { - port, err := OpenSerial(portAddress, baudRate) +func NewNinaFlasher(portAddress string, baudRate, readTimeout int) (*NinaFlasher, error) { + port, err := OpenSerial(portAddress, baudRate, readTimeout) if err != nil { logrus.Error(err) return nil, err diff --git a/flasher/winc.go b/flasher/winc.go index db0288b7..c57d0861 100644 --- a/flasher/winc.go +++ b/flasher/winc.go @@ -35,8 +35,8 @@ import ( "go.bug.st/serial" ) -func NewWincFlasher(portAddress string, baudRate int) (*WincFlasher, error) { - port, err := OpenSerial(portAddress, baudRate) +func NewWincFlasher(portAddress string, baudRate, readTimeout int) (*WincFlasher, error) { + port, err := OpenSerial(portAddress, baudRate, readTimeout) if err != nil { logrus.Error(err) return nil, err From e5ae5db14503d39304c027a6332108d67a7afb60 Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Mon, 27 Sep 2021 15:21:21 +0200 Subject: [PATCH 07/15] add some debug printing --- cli/certificates/flash.go | 3 +++ cli/common/common.go | 6 +++++- cli/firmware/flash.go | 4 ++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/cli/certificates/flash.go b/cli/certificates/flash.go index 02d170c7..1bf51d5e 100644 --- a/cli/certificates/flash.go +++ b/cli/certificates/flash.go @@ -33,6 +33,7 @@ import ( "github.com/arduino/arduino-fwuploader/flasher" "github.com/arduino/arduino-fwuploader/indexes/download" "github.com/arduino/go-paths-helper" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -78,6 +79,7 @@ func runFlash(cmd *cobra.Command, args []string) { feedback.Errorf("Error downloading loader sketch from %s: %s", board.LoaderSketch.URL, err) os.Exit(errorcodes.ErrGeneric) } + logrus.Debugf("loader sketch downloaded in %s", loaderSketchPath.String()) loaderSketch := strings.ReplaceAll(loaderSketchPath.String(), loaderSketchPath.Ext(), "") @@ -89,6 +91,7 @@ func runFlash(cmd *cobra.Command, args []string) { // Wait a bit after flashing the loader sketch for the board to become // available again. + logrus.Debug("sleeping for 3 sec") time.Sleep(3 * time.Second) // Get flasher depending on which module to use diff --git a/cli/common/common.go b/cli/common/common.go index d8e4271d..47c98bea 100644 --- a/cli/common/common.go +++ b/cli/common/common.go @@ -47,6 +47,7 @@ func CheckFlags(fqbn, address string) { feedback.Errorf("Error during firmware flashing: missing board address") os.Exit(errorcodes.ErrBadArgument) } + logrus.Debugf("fqbn: %s, address: %s", fqbn, address) } // GetBoard is an helper function useful to get the IndexBoard, @@ -57,6 +58,7 @@ func GetBoard(firmwareIndex *firmwareindex.Index, fqbn string) *firmwareindex.In feedback.Errorf("Can't find board with %s fqbn", fqbn) os.Exit(errorcodes.ErrBadArgument) } + logrus.Debugf("got board: %s", board.Fqbn) return board } @@ -73,6 +75,7 @@ func GetUploadToolDir(packageIndex *packageindex.Index, board *firmwareindex.Ind feedback.Errorf("Error downloading tool %s: %s", board.Uploader, err) os.Exit(errorcodes.ErrGeneric) } + logrus.Debugf("upload tool downloaded in %s", uploadToolDir.String()) return uploadToolDir } @@ -89,6 +92,7 @@ func FlashSketch(board *firmwareindex.IndexBoard, sketch string, uploadToolDir * uploaderCommand = strings.ReplaceAll(uploaderCommand, "{serial.port.file}", bootloaderPort) uploaderCommand = strings.ReplaceAll(uploaderCommand, "{loader.sketch}", sketch) // we leave that name here because it's only a template, + logrus.Debugf("uploading with command: %s", uploaderCommand) commandLine, err := properties.SplitQuotedString(uploaderCommand, "\"", false) if err != nil { feedback.Errorf(`Error splitting command line "%s": %s`, uploaderCommand, err) @@ -122,7 +126,7 @@ func GetNewAddress(board *firmwareindex.IndexBoard, oldAddress string) (string, return "", fmt.Errorf("error during sketch flashing: missing board address. %s", err) } if newUploadPort != "" { - logrus.Infof("Found port to upload Loader: %s", newUploadPort) + logrus.Infof("Found port to upload: %s", newUploadPort) bootloaderPort = newUploadPort } } diff --git a/cli/firmware/flash.go b/cli/firmware/flash.go index 5b6a0158..bb629b39 100644 --- a/cli/firmware/flash.go +++ b/cli/firmware/flash.go @@ -93,6 +93,7 @@ func runFlash(cmd *cobra.Command, args []string) { } else { firmware = board.GetFirmware(moduleVersion) } + logrus.Debugf("module name: %s, firmware version: %s", firmware.Module, firmware.Version.String()) if firmware == nil { feedback.Errorf("Error getting firmware for board: %s", commonFlags.Fqbn) os.Exit(errorcodes.ErrGeneric) @@ -103,12 +104,14 @@ func runFlash(cmd *cobra.Command, args []string) { feedback.Errorf("Error downloading firmware from %s: %s", firmware.URL, err) os.Exit(errorcodes.ErrGeneric) } + logrus.Debugf("firmware file downloaded in %s", firmwareFile.String()) loaderSketchPath, err := download.DownloadSketch(board.LoaderSketch) if err != nil { feedback.Errorf("Error downloading loader sketch from %s: %s", board.LoaderSketch.URL, err) os.Exit(errorcodes.ErrGeneric) } + logrus.Debugf("loader sketch downloaded in %s", loaderSketchPath.String()) loaderSketch := strings.ReplaceAll(loaderSketchPath.String(), loaderSketchPath.Ext(), "") @@ -135,6 +138,7 @@ func updateFirmware(board *firmwareindex.IndexBoard, loaderSketch, moduleName st } // Wait a bit after flashing the loader sketch for the board to become // available again. + logrus.Debug("sleeping for 3 sec") time.Sleep(3 * time.Second) // Get flasher depending on which module to use From 67a376e864c6b320e2dd310e95d7a6f1680403fe Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Mon, 27 Sep 2021 15:22:47 +0200 Subject: [PATCH 08/15] =?UTF-8?q?finally=20add=20the=20implementation=20of?= =?UTF-8?q?=20the=20new=20=C2=B4=C7=B5et-version=C2=B4=20command?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cli/firmware/firmware.go | 1 + cli/firmware/getversion.go | 132 +++++++++++++++++++++++++++++++++++++ flasher/flasher.go | 3 +- 3 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 cli/firmware/getversion.go diff --git a/cli/firmware/firmware.go b/cli/firmware/firmware.go index d21f4929..51903edc 100644 --- a/cli/firmware/firmware.go +++ b/cli/firmware/firmware.go @@ -35,5 +35,6 @@ func NewCommand() *cobra.Command { firmwareCmd.AddCommand(NewFlashCommand()) firmwareCmd.AddCommand(newListCommand()) + firmwareCmd.AddCommand(NewGetVersionCommand()) return firmwareCmd } diff --git a/cli/firmware/getversion.go b/cli/firmware/getversion.go new file mode 100644 index 00000000..3cd2ab61 --- /dev/null +++ b/cli/firmware/getversion.go @@ -0,0 +1,132 @@ +/* + arduino-fwuploader + Copyright (c) 2021 Arduino LLC. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +package firmware + +import ( + "fmt" + "log" + "os" + "strings" + "time" + + "github.com/arduino/arduino-cli/cli/errorcodes" + "github.com/arduino/arduino-cli/cli/feedback" + "github.com/arduino/arduino-fwuploader/cli/common" + "github.com/arduino/arduino-fwuploader/flasher" + "github.com/arduino/arduino-fwuploader/indexes/download" + "github.com/arduino/arduino-fwuploader/indexes/firmwareindex" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + semver "go.bug.st/relaxed-semver" +) + +// NewCommand created a new `version` command +func NewGetVersionCommand() *cobra.Command { + + command := &cobra.Command{ + Use: "get-version", + Short: "Gets the version of the firmware the board is using.", + Long: "Flashes a sketch to a board to obtain the firmware version used by the board", + Example: "" + + " " + os.Args[0] + " firmware get-version --fqbn arduino:samd:mkr1000 --address COM10\n" + + " " + os.Args[0] + " firmware get-version -b arduino:samd:mkr1000 -a COM10\n", + Args: cobra.NoArgs, + Run: runGetVersion, + } + commonFlags.AddToCommand(command) + return command +} + +func runGetVersion(cmd *cobra.Command, args []string) { + + packageIndex, firmwareIndex := common.InitIndexes() + common.CheckFlags(commonFlags.Fqbn, commonFlags.Address) + board := common.GetBoard(firmwareIndex, commonFlags.Fqbn) + uploadToolDir := common.GetUploadToolDir(packageIndex, board) + + versionSketchPath, err := download.DownloadSketch(board.VersionSketch) + if err != nil { + feedback.Errorf("Error downloading loader sketch from %s: %s", board.LoaderSketch.URL, err) + os.Exit(errorcodes.ErrGeneric) + } + logrus.Debugf("version sketch downloaded in %s", versionSketchPath.String()) + + versionSketch := strings.ReplaceAll(versionSketchPath.String(), versionSketchPath.Ext(), "") + + programmerOut, programmerErr, err := common.FlashSketch(board, versionSketch, uploadToolDir, commonFlags.Address) + if err != nil { + feedback.Error(err) + os.Exit(errorcodes.ErrGeneric) + } + + // Wait a bit after flashing the sketch for the board to become available again. + logrus.Debug("sleeping for 3 sec") + time.Sleep(3 * time.Second) + + currentVersion, err := getVersion(board) + if err != nil { + feedback.Error(err) + os.Exit(1) + } + feedback.Printf("Firmware version installed: %s", currentVersion) + + // Print the results + feedback.PrintResult(&flasher.FlashResult{ + Programmer: (&flasher.ExecOutput{ + Stdout: programmerOut.String(), + Stderr: programmerErr.String(), + }), + Version: currentVersion, + }) +} + +func getVersion(board *firmwareindex.IndexBoard) (fwVersion string, err error) { + + // 9600 is the baudrate used in the CheckVersion sketch + port, err := flasher.OpenSerial(commonFlags.Address, 9600, 2) + if err != nil { + feedback.Error(err) + os.Exit(errorcodes.ErrGeneric) + } + + buff := make([]byte, 200) + serialResult := make([]byte, 0) + for { + n, err := port.Read(buff) + if err != nil { + log.Fatal(err) + break + } + serialResult = append(serialResult, buff[:n]...) + if n == 0 { // exit when done reading from serial + break + } + logrus.Info(string(buff[:n])) + } + lines := strings.Split(string(serialResult), "\n") + for _, line := range lines { + if strings.HasPrefix(line, "Firmware version installed: ") { + version := strings.Replace(line, "Firmware version installed: ", "", 1) + semver := semver.ParseRelaxed(version) + return semver.String(), nil + } + } + return "", fmt.Errorf("could not find the version string to parse") +} diff --git a/flasher/flasher.go b/flasher/flasher.go index ea1e453e..cf87cea6 100644 --- a/flasher/flasher.go +++ b/flasher/flasher.go @@ -80,7 +80,8 @@ func OpenSerial(portAddress string, baudRate int, readTimeout int) (serial.Port, type FlashResult struct { Programmer *ExecOutput `json:"programmer"` - Flasher *ExecOutput `json:"flasher"` + Flasher *ExecOutput `json:"flasher,omitempty"` + Version string `json:"version,omitempty"` } type ExecOutput struct { From f7d48e2bab145bbbc37ae2a0701fea5966ef2797 Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Mon, 27 Sep 2021 16:21:19 +0200 Subject: [PATCH 09/15] add/fix some comments --- cli/certificates/flash.go | 2 +- cli/common/common.go | 4 ++-- cli/firmware/flash.go | 2 +- cli/firmware/getversion.go | 2 +- flasher/flasher.go | 3 +++ indexes/download/download.go | 9 +++++++-- indexes/firmwareindex/firmwareindex.go | 6 ++++-- version/version.go | 3 ++- 8 files changed, 21 insertions(+), 10 deletions(-) diff --git a/cli/certificates/flash.go b/cli/certificates/flash.go index 1bf51d5e..05789575 100644 --- a/cli/certificates/flash.go +++ b/cli/certificates/flash.go @@ -43,7 +43,7 @@ var ( certificatePaths []string ) -// NewCommand created a new `version` command +// NewFlashCommand creates a new `flash` command func NewFlashCommand() *cobra.Command { command := &cobra.Command{ Use: "flash", diff --git a/cli/common/common.go b/cli/common/common.go index 47c98bea..e34a52b5 100644 --- a/cli/common/common.go +++ b/cli/common/common.go @@ -79,7 +79,7 @@ func GetUploadToolDir(packageIndex *packageindex.Index, board *firmwareindex.Ind return uploadToolDir } -// flashSketch is the business logic that handles the flashing procedure, +// FlashSketch is the business logic that handles the flashing procedure, // it returns using a buffer the out and the err of the programmer func FlashSketch(board *firmwareindex.IndexBoard, sketch string, uploadToolDir *paths.Path, address string) (programmerOut, programmerErr *bytes.Buffer, err error) { bootloaderPort, err := GetNewAddress(board, address) @@ -113,7 +113,7 @@ func FlashSketch(board *firmwareindex.IndexBoard, sketch string, uploadToolDir * return programmerOut, programmerErr, err } -// getNewAddress is a function used to reset a board and put it in bootloader mode +// GetNewAddress is a function used to reset a board and put it in bootloader mode // it could happen that the board is assigned to a different serial port, after the reset, // this fuction handles also this possibility func GetNewAddress(board *firmwareindex.IndexBoard, oldAddress string) (string, error) { diff --git a/cli/firmware/flash.go b/cli/firmware/flash.go index bb629b39..d34bf419 100644 --- a/cli/firmware/flash.go +++ b/cli/firmware/flash.go @@ -44,7 +44,7 @@ var ( retries uint8 ) -// NewCommand created a new `version` command +// NewFlashCommand creates a new `flash` command func NewFlashCommand() *cobra.Command { command := &cobra.Command{ Use: "flash", diff --git a/cli/firmware/getversion.go b/cli/firmware/getversion.go index 3cd2ab61..990caeee 100644 --- a/cli/firmware/getversion.go +++ b/cli/firmware/getversion.go @@ -37,7 +37,7 @@ import ( semver "go.bug.st/relaxed-semver" ) -// NewCommand created a new `version` command +// NewGetVersionCommand creates a new `get-version` command func NewGetVersionCommand() *cobra.Command { command := &cobra.Command{ diff --git a/flasher/flasher.go b/flasher/flasher.go index cf87cea6..25966e9a 100644 --- a/flasher/flasher.go +++ b/flasher/flasher.go @@ -62,6 +62,7 @@ type Flasher interface { sendCommand(data CommandData) error } +// OpenSerial opens a new serial connection with the specified portAddress func OpenSerial(portAddress string, baudRate int, readTimeout int) (serial.Port, error) { port, err := serial.Open(portAddress, &serial.Mode{BaudRate: baudRate}) @@ -78,12 +79,14 @@ func OpenSerial(portAddress string, baudRate int, readTimeout int) (serial.Port, return port, nil } +// FlashResult contains the result of the flashing procedure type FlashResult struct { Programmer *ExecOutput `json:"programmer"` Flasher *ExecOutput `json:"flasher,omitempty"` Version string `json:"version,omitempty"` } +// ExecOutput contais the stdout and stderr output, they are used to store the output of the flashing and upload type ExecOutput struct { Stdout string `json:"stdout"` Stderr string `json:"stderr"` diff --git a/indexes/download/download.go b/indexes/download/download.go index 32f9ad02..cd004b89 100644 --- a/indexes/download/download.go +++ b/indexes/download/download.go @@ -43,6 +43,7 @@ import ( "go.bug.st/downloader/v2" ) +// DownloadTool downloads and returns the path on the local filesystem of a tool func DownloadTool(toolRelease *cores.ToolRelease) (*paths.Path, error) { resource := toolRelease.GetCompatibleFlavour() installDir := globals.FwUploaderPath.Join( @@ -73,6 +74,7 @@ func DownloadTool(toolRelease *cores.ToolRelease) (*paths.Path, error) { return installDir, nil } +// DownloadFirmware downloads and returns the path on the local filesystem of a firmware func DownloadFirmware(firmware *firmwareindex.IndexFirmware) (*paths.Path, error) { firmwarePath := globals.FwUploaderPath.Join( "firmwares", @@ -105,6 +107,7 @@ func DownloadFirmware(firmware *firmwareindex.IndexFirmware) (*paths.Path, error return firmwarePath, nil } +// DownloadSketch downloads and returns the path on the local filesystem of a sketch func DownloadSketch(loader *firmwareindex.IndexSketch) (*paths.Path, error) { sketchPath := globals.FwUploaderPath.Join( "sketch", @@ -151,7 +154,7 @@ func Download(d *downloader.Downloader) error { return nil } -// taken and adapted from https://github.com/arduino/arduino-cli/blob/59b6277a4d6731a1c1579d43aef6df2a46a771d5/arduino/resources/checksums.go +// VerifyFileChecksum is taken and adapted from https://github.com/arduino/arduino-cli/blob/59b6277a4d6731a1c1579d43aef6df2a46a771d5/arduino/resources/checksums.go func VerifyFileChecksum(checksum string, filePath *paths.Path) error { if checksum == "" { return fmt.Errorf("missing checksum for: %s", filePath) @@ -193,7 +196,7 @@ func VerifyFileChecksum(checksum string, filePath *paths.Path) error { return nil } -// taken and adapted from https://github.com/arduino/arduino-cli/blob/59b6277a4d6731a1c1579d43aef6df2a46a771d5/arduino/resources/checksums.go +// VerifyFileSize is taken and adapted from https://github.com/arduino/arduino-cli/blob/59b6277a4d6731a1c1579d43aef6df2a46a771d5/arduino/resources/checksums.go func VerifyFileSize(size int64, filePath *paths.Path) error { info, err := filePath.Stat() if err != nil { @@ -290,6 +293,7 @@ func verifyIndex(indexPath *paths.Path, URL *url.URL) error { return nil } +// verifyPackageIndex verify if the signature is valid for the provided package index func verifyPackageIndex(indexPath, signaturePath *paths.Path) (bool, error) { valid, _, err := security.VerifyArduinoDetachedSignature(indexPath, signaturePath) if err != nil { @@ -302,6 +306,7 @@ func verifyPackageIndex(indexPath, signaturePath *paths.Path) (bool, error) { return valid, nil } +// verifyModuleFirmwareIndex verify if the signature is valid for the provided module firmware index func verifyModuleFirmwareIndex(indexPath, signaturePath *paths.Path) (bool, error) { keysBox, err := rice.FindBox("gpg_keys") if err != nil { diff --git a/indexes/firmwareindex/firmwareindex.go b/indexes/firmwareindex/firmwareindex.go index d17917c9..d21e81a2 100644 --- a/indexes/firmwareindex/firmwareindex.go +++ b/indexes/firmwareindex/firmwareindex.go @@ -36,7 +36,7 @@ type Index struct { IsTrusted bool } -// indexPackage represents a single entry from module_firmware_index.json file. +// IndexBoard represents a single entry from module_firmware_index.json file. type IndexBoard struct { Fqbn string `json:"fqbn,required"` Firmwares []*IndexFirmware `json:"firmware,required"` @@ -51,6 +51,7 @@ type IndexBoard struct { LatestFirmware *IndexFirmware `json:"-"` } +// IndexUploaderCommand represents the command-line to use for different OS type IndexUploaderCommand struct { Linux string `json:"linux,required"` Windows string `json:"windows"` @@ -141,7 +142,7 @@ func (i *Index) GetBoard(fqbn string) *IndexBoard { return nil } -// GetLatestFirmware returns the specified IndexFirmware version for this board. +// GetFirmware returns the specified IndexFirmware version for this board. // Returns nil if version is not found. func (b *IndexBoard) GetFirmware(version string) *IndexFirmware { v := semver.ParseRelaxed(version) @@ -153,6 +154,7 @@ func (b *IndexBoard) GetFirmware(version string) *IndexFirmware { return nil } +// GetUploaderCommand returns the command to use for the upload func (b *IndexBoard) GetUploaderCommand() string { if runtime.GOOS == "windows" && b.UploaderCommand.Windows != "" { return b.UploaderCommand.Linux diff --git a/version/version.go b/version/version.go index 867f07ea..bd8cf575 100644 --- a/version/version.go +++ b/version/version.go @@ -26,7 +26,8 @@ var ( versionString = "" commit = "" date = "" - VersionInfo *info + // VersionInfo contains info regarding the version + VersionInfo *info ) type info struct { From 1961ca0c11bc0f2da018716b6c76ab2ca923a96c Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Mon, 27 Sep 2021 17:53:36 +0200 Subject: [PATCH 10/15] add docs --- docs/usage.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/usage.md b/docs/usage.md index 59cd7bc3..26f00862 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -40,6 +40,25 @@ but you can also filter the results by specifying the `-b` or `--fqbn` flag The tool offers the ability to print output in JSON, with the `--format json` flag +### Get Version + +You can also obtain the version of the firmware the board is currently running with: + +``` +./arduino-fwuploader firmware get-version -b arduino:samd:mkrwifi1010 -a /dev/ttyACM0 +``` + +The `get-version` subcommand flashes a special sketch in order to be able to read that information using the serial +connection: + +``` +... + +Firmware version installed: 1.4.8 +``` + +You can also use the `--format json` to parse the output with more ease. + ### Certificates The tool offers also the ability to flash SSL certificates to a module: From 726eb8549a92b56972f9088ec027fc12b555c8f1 Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Mon, 27 Sep 2021 17:54:34 +0200 Subject: [PATCH 11/15] handle the communication problem between a board and a module --- cli/firmware/getversion.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cli/firmware/getversion.go b/cli/firmware/getversion.go index 990caeee..53af7e6e 100644 --- a/cli/firmware/getversion.go +++ b/cli/firmware/getversion.go @@ -127,6 +127,9 @@ func getVersion(board *firmwareindex.IndexBoard) (fwVersion string, err error) { semver := semver.ParseRelaxed(version) return semver.String(), nil } + if strings.HasPrefix(line, "Communication with WiFi module failed!") { + return "", fmt.Errorf("communication with WiFi module failed") + } } return "", fmt.Errorf("could not find the version string to parse") } From dfbcf1550c9512b06821249df11cd8cad47a7d8f Mon Sep 17 00:00:00 2001 From: Umberto Baldi <34278123+umbynos@users.noreply.github.com> Date: Tue, 28 Sep 2021 17:02:27 +0200 Subject: [PATCH 12/15] Apply suggestions from code review Co-authored-by: per1234 --- cli/common/common.go | 2 +- docs/usage.md | 2 +- flasher/flasher.go | 2 +- indexes/firmwareindex/firmwareindex.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cli/common/common.go b/cli/common/common.go index e34a52b5..795d9699 100644 --- a/cli/common/common.go +++ b/cli/common/common.go @@ -80,7 +80,7 @@ func GetUploadToolDir(packageIndex *packageindex.Index, board *firmwareindex.Ind } // FlashSketch is the business logic that handles the flashing procedure, -// it returns using a buffer the out and the err of the programmer +// it returns using a buffer the stdout and the stderr of the programmer func FlashSketch(board *firmwareindex.IndexBoard, sketch string, uploadToolDir *paths.Path, address string) (programmerOut, programmerErr *bytes.Buffer, err error) { bootloaderPort, err := GetNewAddress(board, address) if err != nil { diff --git a/docs/usage.md b/docs/usage.md index 26f00862..497f8b8a 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -57,7 +57,7 @@ connection: Firmware version installed: 1.4.8 ``` -You can also use the `--format json` to parse the output with more ease. +You can also use the `--format json` flag to parse the output with more ease. ### Certificates diff --git a/flasher/flasher.go b/flasher/flasher.go index 25966e9a..f65bf8f0 100644 --- a/flasher/flasher.go +++ b/flasher/flasher.go @@ -86,7 +86,7 @@ type FlashResult struct { Version string `json:"version,omitempty"` } -// ExecOutput contais the stdout and stderr output, they are used to store the output of the flashing and upload +// ExecOutput contains the stdout and stderr output, they are used to store the output of the flashing and upload type ExecOutput struct { Stdout string `json:"stdout"` Stderr string `json:"stderr"` diff --git a/indexes/firmwareindex/firmwareindex.go b/indexes/firmwareindex/firmwareindex.go index d21e81a2..3a2b3b34 100644 --- a/indexes/firmwareindex/firmwareindex.go +++ b/indexes/firmwareindex/firmwareindex.go @@ -67,7 +67,7 @@ type IndexFirmware struct { Module string `json:"module,required"` } -// IndexSketch represents the sketch used to upload the new firmware on a board. +// IndexSketch represents a sketch used to manage firmware on a board. type IndexSketch struct { URL string `json:"url,required"` Checksum string `json:"checksum,required"` From 79e3d27dfc8024cb99907e6965c43fe1705bc35f Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Wed, 29 Sep 2021 12:09:38 +0200 Subject: [PATCH 13/15] print version string only when using `--format text` (default behavior) --- cli/firmware/getversion.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cli/firmware/getversion.go b/cli/firmware/getversion.go index 53af7e6e..3647afd9 100644 --- a/cli/firmware/getversion.go +++ b/cli/firmware/getversion.go @@ -85,8 +85,9 @@ func runGetVersion(cmd *cobra.Command, args []string) { feedback.Error(err) os.Exit(1) } - feedback.Printf("Firmware version installed: %s", currentVersion) - + if feedback.GetFormat() == feedback.Text { + feedback.Printf("Firmware version installed: %s", currentVersion) + } // Print the results feedback.PrintResult(&flasher.FlashResult{ Programmer: (&flasher.ExecOutput{ From 40f1ad4968c326d6eaa367da31b274a83abc6898 Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Wed, 29 Sep 2021 13:03:48 +0200 Subject: [PATCH 14/15] remove the `\r` from the version output --- cli/firmware/getversion.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/firmware/getversion.go b/cli/firmware/getversion.go index 3647afd9..507e5ba0 100644 --- a/cli/firmware/getversion.go +++ b/cli/firmware/getversion.go @@ -126,7 +126,7 @@ func getVersion(board *firmwareindex.IndexBoard) (fwVersion string, err error) { if strings.HasPrefix(line, "Firmware version installed: ") { version := strings.Replace(line, "Firmware version installed: ", "", 1) semver := semver.ParseRelaxed(version) - return semver.String(), nil + return strings.Replace(semver.String(), "\r", "", 1), nil } if strings.HasPrefix(line, "Communication with WiFi module failed!") { return "", fmt.Errorf("communication with WiFi module failed") From 00140b21574fb81e71c30b67c73cd589ad5da822 Mon Sep 17 00:00:00 2001 From: Umberto Baldi <34278123+umbynos@users.noreply.github.com> Date: Thu, 30 Sep 2021 10:33:58 +0200 Subject: [PATCH 15/15] remove line ending before parsing Co-authored-by: per1234 --- cli/firmware/getversion.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/firmware/getversion.go b/cli/firmware/getversion.go index 507e5ba0..1826ecef 100644 --- a/cli/firmware/getversion.go +++ b/cli/firmware/getversion.go @@ -124,9 +124,9 @@ func getVersion(board *firmwareindex.IndexBoard) (fwVersion string, err error) { lines := strings.Split(string(serialResult), "\n") for _, line := range lines { if strings.HasPrefix(line, "Firmware version installed: ") { - version := strings.Replace(line, "Firmware version installed: ", "", 1) + version := strings.TrimSpace(strings.Replace(line, "Firmware version installed: ", "", 1)) semver := semver.ParseRelaxed(version) - return strings.Replace(semver.String(), "\r", "", 1), nil + return semver.String(), nil } if strings.HasPrefix(line, "Communication with WiFi module failed!") { return "", fmt.Errorf("communication with WiFi module failed")