Skip to content

Commit 9220c33

Browse files
umbynosper1234
andauthored
add get-version command (#116)
* refactor DownloadLoaderSketch to be more generic * make OpenSerial public * now the program is able to parse the new `module_firmware_index.json` * refactor business logic to prepare for ´get-version´ command * add baudrate to OpenSerial (it makes more sense), usefull for new cmd * add readTimeout parameter to OpenSerial It has to be different when reading from serial to check the version and when uploading the firmware. * add some debug printing * finally add the implementation of the new ´ǵet-version´ command * add/fix some comments * add docs * handle the communication problem between a board and a module * Apply suggestions from code review Co-authored-by: per1234 <[email protected]> * print version string only when using `--format text` (default behavior) * remove the `\r` from the version output * remove line ending before parsing Co-authored-by: per1234 <[email protected]> Co-authored-by: per1234 <[email protected]>
1 parent 66a28f5 commit 9220c33

File tree

14 files changed

+398
-212
lines changed

14 files changed

+398
-212
lines changed

Diff for: cli/arguments/arguments.go

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package arguments
2+
3+
import (
4+
"github.com/spf13/cobra"
5+
)
6+
7+
// Flags contains various common flags.
8+
// This is useful so all flags used by commands that need
9+
// this information are consistent with each other.
10+
type Flags struct {
11+
Address string
12+
Fqbn string
13+
}
14+
15+
// AddToCommand adds the flags used to set address and fqbn to the specified Command
16+
func (f *Flags) AddToCommand(cmd *cobra.Command) {
17+
cmd.Flags().StringVarP(&f.Fqbn, "fqbn", "b", "", "Fully Qualified Board Name, e.g.: arduino:samd:mkr1000, arduino:mbed_nano:nanorp2040connect")
18+
cmd.Flags().StringVarP(&f.Address, "address", "a", "", "Upload port, e.g.: COM10, /dev/ttyACM0")
19+
}

Diff for: cli/certificates/flash.go

+22-88
Original file line numberDiff line numberDiff line change
@@ -23,31 +23,27 @@ import (
2323
"bytes"
2424
"fmt"
2525
"os"
26-
"path/filepath"
2726
"strings"
2827
"time"
2928

30-
"github.com/arduino/arduino-cli/arduino/serialutils"
3129
"github.com/arduino/arduino-cli/cli/errorcodes"
3230
"github.com/arduino/arduino-cli/cli/feedback"
31+
"github.com/arduino/arduino-fwuploader/cli/arguments"
32+
"github.com/arduino/arduino-fwuploader/cli/common"
3333
"github.com/arduino/arduino-fwuploader/flasher"
34-
"github.com/arduino/arduino-fwuploader/indexes"
3534
"github.com/arduino/arduino-fwuploader/indexes/download"
36-
programmer "github.com/arduino/arduino-fwuploader/programmers"
3735
"github.com/arduino/go-paths-helper"
38-
"github.com/arduino/go-properties-orderedmap"
3936
"github.com/sirupsen/logrus"
4037
"github.com/spf13/cobra"
4138
)
4239

4340
var (
44-
fqbn string
45-
address string
41+
commonFlags arguments.Flags
4642
certificateURLs []string
4743
certificatePaths []string
4844
)
4945

50-
// NewCommand created a new `version` command
46+
// NewFlashCommand creates a new `flash` command
5147
func NewFlashCommand() *cobra.Command {
5248
command := &cobra.Command{
5349
Use: "flash",
@@ -58,121 +54,59 @@ func NewFlashCommand() *cobra.Command {
5854
" " + os.Args[0] + " certificates flash -b arduino:samd:mkr1000 -a COM10 -u arduino.cc:443 -u google.cc:443\n" +
5955
" " + os.Args[0] + " certificates flash -b arduino:samd:mkr1000 -a COM10 -f /home/me/VeriSign.cer -f /home/me/Digicert.cer\n",
6056
Args: cobra.NoArgs,
61-
Run: run,
57+
Run: runFlash,
6258
}
63-
64-
command.Flags().StringVarP(&fqbn, "fqbn", "b", "", "Fully Qualified Board Name, e.g.: arduino:samd:mkr1000, arduino:mbed_nano:nanorp2040connect")
65-
command.Flags().StringVarP(&address, "address", "a", "", "Upload port, e.g.: COM10, /dev/ttyACM0")
59+
commonFlags.AddToCommand(command)
6660
command.Flags().StringSliceVarP(&certificateURLs, "url", "u", []string{}, "List of urls to download root certificates, e.g.: arduino.cc:443")
6761
command.Flags().StringSliceVarP(&certificatePaths, "file", "f", []string{}, "List of paths to certificate file, e.g.: /home/me/Digicert.cer")
6862
return command
6963
}
7064

71-
func run(cmd *cobra.Command, args []string) {
72-
packageIndex, err := indexes.GetPackageIndex()
73-
if err != nil {
74-
feedback.Errorf("Can't load package index: %s", err)
75-
os.Exit(errorcodes.ErrGeneric)
76-
}
65+
func runFlash(cmd *cobra.Command, args []string) {
7766

78-
firmwareIndex, err := indexes.GetFirmwareIndex()
79-
if err != nil {
80-
feedback.Errorf("Can't load firmware index: %s", err)
81-
os.Exit(errorcodes.ErrGeneric)
82-
}
83-
84-
if fqbn == "" {
85-
feedback.Errorf("Error during certificates flashing: missing board fqbn")
86-
os.Exit(errorcodes.ErrBadArgument)
87-
}
88-
89-
if address == "" {
90-
feedback.Errorf("Error during certificates flashing: missing board address")
91-
os.Exit(errorcodes.ErrBadArgument)
92-
}
67+
packageIndex, firmwareIndex := common.InitIndexes()
68+
common.CheckFlags(commonFlags.Fqbn, commonFlags.Address)
69+
board := common.GetBoard(firmwareIndex, commonFlags.Fqbn)
70+
uploadToolDir := common.GetUploadToolDir(packageIndex, board)
9371

9472
if len(certificateURLs) == 0 && len(certificatePaths) == 0 {
9573
feedback.Errorf("Error during certificates flashing: no certificates provided")
9674
os.Exit(errorcodes.ErrBadArgument)
9775
}
9876

99-
board := firmwareIndex.GetBoard(fqbn)
100-
if board == nil {
101-
feedback.Errorf("Can't find board with %s fqbn", fqbn)
102-
os.Exit(errorcodes.ErrBadArgument)
103-
}
104-
105-
toolRelease := indexes.GetToolRelease(packageIndex, board.Uploader)
106-
if toolRelease == nil {
107-
feedback.Errorf("Error getting upload tool %s for board %s", board.Uploader, board.Fqbn)
108-
os.Exit(errorcodes.ErrGeneric)
109-
}
110-
uploadToolDir, err := download.DownloadTool(toolRelease)
111-
if err != nil {
112-
feedback.Errorf("Error downloading tool %s: %s", board.Uploader, err)
113-
os.Exit(errorcodes.ErrGeneric)
114-
}
115-
116-
loaderSketchPath, err := download.DownloadLoaderSketch(board.LoaderSketch)
77+
loaderSketchPath, err := download.DownloadSketch(board.LoaderSketch)
11778
if err != nil {
11879
feedback.Errorf("Error downloading loader sketch from %s: %s", board.LoaderSketch.URL, err)
11980
os.Exit(errorcodes.ErrGeneric)
12081
}
82+
logrus.Debugf("loader sketch downloaded in %s", loaderSketchPath.String())
12183

12284
loaderSketch := strings.ReplaceAll(loaderSketchPath.String(), loaderSketchPath.Ext(), "")
12385

124-
// Check if board needs a 1200bps touch for upload
125-
bootloaderPort := address
126-
if board.UploadTouch {
127-
logrus.Info("Putting board into bootloader mode")
128-
newUploadPort, err := serialutils.Reset(address, board.UploadWait, nil)
129-
if err != nil {
130-
feedback.Errorf("Error during certificates flashing: missing board address")
131-
os.Exit(errorcodes.ErrGeneric)
132-
}
133-
if newUploadPort != "" {
134-
logrus.Infof("Found port to upload Loader: %s", newUploadPort)
135-
bootloaderPort = newUploadPort
136-
}
137-
}
138-
139-
uploaderCommand := board.GetUploaderCommand()
140-
uploaderCommand = strings.ReplaceAll(uploaderCommand, "{tool_dir}", filepath.FromSlash(uploadToolDir.String()))
141-
uploaderCommand = strings.ReplaceAll(uploaderCommand, "{serial.port.file}", bootloaderPort)
142-
uploaderCommand = strings.ReplaceAll(uploaderCommand, "{loader.sketch}", loaderSketch)
143-
144-
commandLine, err := properties.SplitQuotedString(uploaderCommand, "\"", false)
86+
programmerOut, programmerErr, err := common.FlashSketch(board, loaderSketch, uploadToolDir, commonFlags.Address)
14587
if err != nil {
146-
feedback.Errorf(`Error splitting command line "%s": %s`, uploaderCommand, err)
147-
os.Exit(errorcodes.ErrGeneric)
148-
}
149-
150-
// Flash loader Sketch
151-
programmerOut := new(bytes.Buffer)
152-
programmerErr := new(bytes.Buffer)
153-
if feedback.GetFormat() == feedback.JSON {
154-
err = programmer.Flash(commandLine, programmerOut, programmerErr)
155-
} else {
156-
err = programmer.Flash(commandLine, os.Stdout, os.Stderr)
157-
}
158-
if err != nil {
159-
feedback.Errorf("Error during certificates flashing: %s", err)
88+
feedback.Error(err)
16089
os.Exit(errorcodes.ErrGeneric)
16190
}
16291

16392
// Wait a bit after flashing the loader sketch for the board to become
16493
// available again.
94+
logrus.Debug("sleeping for 3 sec")
16595
time.Sleep(3 * time.Second)
16696

16797
// Get flasher depending on which module to use
16898
var f flasher.Flasher
16999
moduleName := board.Module
100+
101+
// This matches the baudrate used in the FirmwareUpdater.ino sketch
102+
// https://github.com/arduino-libraries/WiFiNINA/blob/master/examples/Tools/FirmwareUpdater/FirmwareUpdater.ino
103+
const baudRate = 1000000
170104
switch moduleName {
171105
case "NINA":
172106
// we use address and not bootloaderPort because the board should not be in bootloader mode
173-
f, err = flasher.NewNinaFlasher(address)
107+
f, err = flasher.NewNinaFlasher(commonFlags.Address, baudRate, 30)
174108
case "WINC1500":
175-
f, err = flasher.NewWincFlasher(address)
109+
f, err = flasher.NewWincFlasher(commonFlags.Address, baudRate, 30)
176110
default:
177111
err = fmt.Errorf("unknown module: %s", moduleName)
178112
}

Diff for: cli/common/common.go

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package common
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
"strings"
9+
10+
"github.com/arduino/arduino-cli/arduino/cores/packageindex"
11+
"github.com/arduino/arduino-cli/arduino/serialutils"
12+
"github.com/arduino/arduino-cli/cli/errorcodes"
13+
"github.com/arduino/arduino-cli/cli/feedback"
14+
"github.com/arduino/arduino-fwuploader/indexes"
15+
"github.com/arduino/arduino-fwuploader/indexes/download"
16+
"github.com/arduino/arduino-fwuploader/indexes/firmwareindex"
17+
programmer "github.com/arduino/arduino-fwuploader/programmers"
18+
"github.com/arduino/go-paths-helper"
19+
"github.com/arduino/go-properties-orderedmap"
20+
"github.com/sirupsen/logrus"
21+
)
22+
23+
// InitIndexes does exactly what the name implies
24+
func InitIndexes() (*packageindex.Index, *firmwareindex.Index) {
25+
packageIndex, err := indexes.GetPackageIndex()
26+
if err != nil {
27+
feedback.Errorf("Can't load package index: %s", err)
28+
os.Exit(errorcodes.ErrGeneric)
29+
}
30+
31+
firmwareIndex, err := indexes.GetFirmwareIndex()
32+
if err != nil {
33+
feedback.Errorf("Can't load firmware index: %s", err)
34+
os.Exit(errorcodes.ErrGeneric)
35+
}
36+
return packageIndex, firmwareIndex
37+
}
38+
39+
// CheckFlags runs a basic check, errors if the flags are not defined
40+
func CheckFlags(fqbn, address string) {
41+
if fqbn == "" {
42+
feedback.Errorf("Error during firmware flashing: missing board fqbn")
43+
os.Exit(errorcodes.ErrBadArgument)
44+
}
45+
46+
if address == "" {
47+
feedback.Errorf("Error during firmware flashing: missing board address")
48+
os.Exit(errorcodes.ErrBadArgument)
49+
}
50+
logrus.Debugf("fqbn: %s, address: %s", fqbn, address)
51+
}
52+
53+
// GetBoard is an helper function useful to get the IndexBoard,
54+
// the struct that contains all the infos to make all the operations possible
55+
func GetBoard(firmwareIndex *firmwareindex.Index, fqbn string) *firmwareindex.IndexBoard {
56+
board := firmwareIndex.GetBoard(fqbn)
57+
if board == nil {
58+
feedback.Errorf("Can't find board with %s fqbn", fqbn)
59+
os.Exit(errorcodes.ErrBadArgument)
60+
}
61+
logrus.Debugf("got board: %s", board.Fqbn)
62+
return board
63+
}
64+
65+
// GetUploadToolDir is an helper function that downloads the correct tool to flash a board,
66+
// it returns the path of the downloaded tool
67+
func GetUploadToolDir(packageIndex *packageindex.Index, board *firmwareindex.IndexBoard) *paths.Path {
68+
toolRelease := indexes.GetToolRelease(packageIndex, board.Uploader)
69+
if toolRelease == nil {
70+
feedback.Errorf("Error getting upload tool %s for board %s", board.Uploader, board.Fqbn)
71+
os.Exit(errorcodes.ErrGeneric)
72+
}
73+
uploadToolDir, err := download.DownloadTool(toolRelease)
74+
if err != nil {
75+
feedback.Errorf("Error downloading tool %s: %s", board.Uploader, err)
76+
os.Exit(errorcodes.ErrGeneric)
77+
}
78+
logrus.Debugf("upload tool downloaded in %s", uploadToolDir.String())
79+
return uploadToolDir
80+
}
81+
82+
// FlashSketch is the business logic that handles the flashing procedure,
83+
// it returns using a buffer the stdout and the stderr of the programmer
84+
func FlashSketch(board *firmwareindex.IndexBoard, sketch string, uploadToolDir *paths.Path, address string) (programmerOut, programmerErr *bytes.Buffer, err error) {
85+
bootloaderPort, err := GetNewAddress(board, address)
86+
if err != nil {
87+
return nil, nil, err
88+
}
89+
90+
uploaderCommand := board.GetUploaderCommand()
91+
uploaderCommand = strings.ReplaceAll(uploaderCommand, "{tool_dir}", filepath.FromSlash(uploadToolDir.String()))
92+
uploaderCommand = strings.ReplaceAll(uploaderCommand, "{serial.port.file}", bootloaderPort)
93+
uploaderCommand = strings.ReplaceAll(uploaderCommand, "{loader.sketch}", sketch) // we leave that name here because it's only a template,
94+
95+
logrus.Debugf("uploading with command: %s", uploaderCommand)
96+
commandLine, err := properties.SplitQuotedString(uploaderCommand, "\"", false)
97+
if err != nil {
98+
feedback.Errorf(`Error splitting command line "%s": %s`, uploaderCommand, err)
99+
os.Exit(errorcodes.ErrGeneric)
100+
}
101+
102+
// Flash the actual sketch
103+
programmerOut = new(bytes.Buffer)
104+
programmerErr = new(bytes.Buffer)
105+
if feedback.GetFormat() == feedback.JSON {
106+
err = programmer.Flash(commandLine, programmerOut, programmerErr)
107+
} else {
108+
err = programmer.Flash(commandLine, os.Stdout, os.Stderr)
109+
}
110+
if err != nil {
111+
return nil, nil, fmt.Errorf("error during sketch flashing: %s", err)
112+
}
113+
return programmerOut, programmerErr, err
114+
}
115+
116+
// GetNewAddress is a function used to reset a board and put it in bootloader mode
117+
// it could happen that the board is assigned to a different serial port, after the reset,
118+
// this fuction handles also this possibility
119+
func GetNewAddress(board *firmwareindex.IndexBoard, oldAddress string) (string, error) {
120+
// Check if board needs a 1200bps touch for upload
121+
bootloaderPort := oldAddress
122+
if board.UploadTouch {
123+
logrus.Info("Putting board into bootloader mode")
124+
newUploadPort, err := serialutils.Reset(oldAddress, board.UploadWait, nil)
125+
if err != nil {
126+
return "", fmt.Errorf("error during sketch flashing: missing board address. %s", err)
127+
}
128+
if newUploadPort != "" {
129+
logrus.Infof("Found port to upload: %s", newUploadPort)
130+
bootloaderPort = newUploadPort
131+
}
132+
}
133+
return bootloaderPort, nil
134+
}

Diff for: cli/firmware/firmware.go

+1
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,6 @@ func NewCommand() *cobra.Command {
3535

3636
firmwareCmd.AddCommand(NewFlashCommand())
3737
firmwareCmd.AddCommand(newListCommand())
38+
firmwareCmd.AddCommand(NewGetVersionCommand())
3839
return firmwareCmd
3940
}

0 commit comments

Comments
 (0)