diff --git a/go.mod b/go.mod index 7d4ea5f18..677f50638 100644 --- a/go.mod +++ b/go.mod @@ -13,11 +13,12 @@ require ( github.com/mattn/go-tty v0.0.4 github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 github.com/tetratelabs/wazero v1.6.0 - go.bug.st/serial v1.6.0 + go.bug.st/serial v1.6.2 golang.org/x/net v0.35.0 golang.org/x/sys v0.30.0 golang.org/x/tools v0.30.0 gopkg.in/yaml.v2 v2.4.0 + tinygo.org/x/espflash v0.1.0 tinygo.org/x/go-llvm v0.0.0-20250422114502-b8f170971e74 ) diff --git a/go.sum b/go.sum index 8c2330c3c..301225e24 100644 --- a/go.sum +++ b/go.sum @@ -33,8 +33,8 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tetratelabs/wazero v1.6.0 h1:z0H1iikCdP8t+q341xqepY4EWvHEw8Es7tlqiVzlP3g= github.com/tetratelabs/wazero v1.6.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A= -go.bug.st/serial v1.6.0 h1:mAbRGN4cKE2J5gMwsMHC2KQisdLRQssO9WSM+rbZJ8A= -go.bug.st/serial v1.6.0/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE= +go.bug.st/serial v1.6.2 h1:kn9LRX3sdm+WxWKufMlIRndwGfPWsH1/9lCWXQCasq8= +go.bug.st/serial v1.6.2/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE= golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= @@ -58,5 +58,7 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +tinygo.org/x/espflash v0.1.0 h1:3OsU8BwQ8glikAY9J0OLWOYpP3baef/OpW5BqjPu+MU= +tinygo.org/x/espflash v0.1.0/go.mod h1:n4sWxsjN4wjHN1lKfEYjXke6/eSeLTq/lRRLJS5FNfg= tinygo.org/x/go-llvm v0.0.0-20250422114502-b8f170971e74 h1:ovavgTdIBWCH8YWlcfq9gkpoyT1+IxMKSn+Df27QwE8= tinygo.org/x/go-llvm v0.0.0-20250422114502-b8f170971e74/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0= diff --git a/main.go b/main.go index e2736fd17..80c2d35c7 100644 --- a/main.go +++ b/main.go @@ -32,6 +32,7 @@ import ( "github.com/tinygo-org/tinygo/goenv" "github.com/tinygo-org/tinygo/loader" "golang.org/x/tools/go/buildutil" + "tinygo.org/x/espflash" "tinygo.org/x/go-llvm" "go.bug.st/serial" @@ -385,6 +386,8 @@ func Flash(pkgName, port, outpath string, options *compileopts.Options) error { fileExt = ".hex" case "bmp": fileExt = ".elf" + case "esp32flash": + fileExt = ".bin" case "native": return errors.New("unknown flash method \"native\" - did you miss a -target flag?") default: @@ -519,6 +522,15 @@ func Flash(pkgName, port, outpath string, options *compileopts.Options) error { if err != nil { return &commandError{"failed to flash", result.Binary, err} } + case "esp32flash": + port, err := getDefaultPort(port, config.Target.SerialPort) + if err != nil { + return &commandError{"failed to find port", port, err} + } + + if err := flashBinUsingEsp32(port, result.Binary, config.Options); err != nil { + return &commandError{"failed to flash", result.Binary, err} + } default: return fmt.Errorf("unknown flash method: %s", flashMethod) } @@ -1019,6 +1031,43 @@ func flashHexUsingMSD(volumes []string, tmppath string, options *compileopts.Opt return errors.New("unable to locate any volume: [" + strings.Join(volumes, ",") + "]") } +func flashBinUsingEsp32(port, tmppath string, options *compileopts.Options) error { + flasher, err := espflash.NewFlasher(port, nil) + if err != nil { + return err + } + defer flasher.Close() + + chipName := flasher.ChipName() + fmt.Printf("Connected to %s\n", chipName) + + offset := uint32(0x0) + if chipName == "ESP32" { + offset = 0x1000 + } + + // Read the firmware binary + data, err := os.ReadFile(tmppath) + if err != nil { + return err + } + + // Flash with progress reporting + err = flasher.FlashImage(data, offset, func(current, total int) { + fmt.Printf("\rFlashing: %d/%d bytes (%.0f%%)", current, total, + float64(current)/float64(total)*100) + }) + if err != nil { + return err + } + fmt.Println() + + // Reset the device to run the new firmware + flasher.Reset() + + return nil +} + type mountPoint struct { name string path string