From e541576f80d708cc499af44f8bec6bead837b016 Mon Sep 17 00:00:00 2001
From: Umberto Baldi <u.baldi@arduino.cc>
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 <u.baldi@arduino.cc>
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 <umberto.baldi@edu.unito.it>
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 <u.baldi@arduino.cc>
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 <u.baldi@arduino.cc>
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 <u.baldi@arduino.cc>
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 <u.baldi@arduino.cc>
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 <u.baldi@arduino.cc>
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 <u.baldi@arduino.cc>
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 <u.baldi@arduino.cc>
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 <u.baldi@arduino.cc>
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 <accounts@perglass.com>
---
 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 <u.baldi@arduino.cc>
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 <u.baldi@arduino.cc>
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 <accounts@perglass.com>
---
 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")
