diff --git a/cmd/load/main.go b/cmd/load/main.go new file mode 100644 index 0000000..4204e88 --- /dev/null +++ b/cmd/load/main.go @@ -0,0 +1,60 @@ +package main + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "time" + + "github.com/theater/picomap/lib/picoserial" + "github.com/theater/picomap/lib/picotool" +) + +func main() { + wd, err := os.Getwd() + if err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } + buildDir := filepath.Join(wd, "build") + + if err := run(buildDir); err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } +} + +func run(buildDir string) error { + fmt.Println("Building...") + cmd := exec.Command("make", "-C", buildDir) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return fmt.Errorf("build failed: %w", err) + } + + dev, err := picoserial.FindDevice() + if err != nil { + return err + } + if dev != "" { + fmt.Printf("Sending 'b' to %s to enter BOOTSEL mode...\n", dev) + if err := picoserial.SendByte(dev, 'b'); err != nil { + return err + } + time.Sleep(2 * time.Second) + } + + uf2 := filepath.Join(buildDir, "picomap.uf2") + fmt.Println("Loading firmware...") + if err := picotool.Load(uf2); err != nil { + return err + } + + fmt.Println("Rebooting...") + _ = picotool.Reboot() + + fmt.Println("Done.") + return nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..a29da21 --- /dev/null +++ b/go.mod @@ -0,0 +1,10 @@ +module github.com/theater/picomap + +go 1.25.0 + +require go.bug.st/serial v1.6.4 + +require ( + github.com/creack/goselect v0.1.2 // indirect + golang.org/x/sys v0.19.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..df37244 --- /dev/null +++ b/go.sum @@ -0,0 +1,14 @@ +github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0= +github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +go.bug.st/serial v1.6.4 h1:7FmqNPgVp3pu2Jz5PoPtbZ9jJO5gnEnZIvnI1lzve8A= +go.bug.st/serial v1.6.4/go.mod h1:nofMJxTeNVny/m6+KaafC6vJGj3miwQZ6vW4BZUGJPI= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/lib/picoserial/picoserial.go b/lib/picoserial/picoserial.go new file mode 100644 index 0000000..4bf7618 --- /dev/null +++ b/lib/picoserial/picoserial.go @@ -0,0 +1,35 @@ +package picoserial + +import ( + "fmt" + + "go.bug.st/serial" + "go.bug.st/serial/enumerator" +) + +func FindDevice() (string, error) { + ports, err := enumerator.GetDetailedPortsList() + if err != nil { + return "", fmt.Errorf("enumerating ports: %w", err) + } + for _, p := range ports { + if p.IsUSB { + return p.Name, nil + } + } + return "", nil +} + +func SendByte(portName string, b byte) error { + port, err := serial.Open(portName, &serial.Mode{BaudRate: 115200}) + if err != nil { + return fmt.Errorf("opening %s: %w", portName, err) + } + defer port.Close() + + _, err = port.Write([]byte{b}) + if err != nil { + return fmt.Errorf("writing to %s: %w", portName, err) + } + return nil +} diff --git a/lib/picotool/picotool.go b/lib/picotool/picotool.go new file mode 100644 index 0000000..0777e4a --- /dev/null +++ b/lib/picotool/picotool.go @@ -0,0 +1,24 @@ +package picotool + +import ( + "fmt" + "os/exec" +) + +func Load(uf2Path string) error { + cmd := exec.Command("picotool", "load", "-f", uf2Path) + out, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("picotool load: %w\n%s", err, out) + } + return nil +} + +func Reboot() error { + cmd := exec.Command("picotool", "reboot") + out, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("picotool reboot: %w\n%s", err, out) + } + return nil +} diff --git a/load.sh b/load.sh deleted file mode 100755 index 481625e..0000000 --- a/load.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -set -e - -BUILDDIR="$(dirname "$0")/build" - -echo "Building..." -make -C "$BUILDDIR" - -DEV=$(ls /dev/cu.usbmodem* 2>/dev/null | head -1) -if [ -n "$DEV" ]; then - echo "Sending 'b' to $DEV to enter BOOTSEL mode..." - printf 'b' > "$DEV" - sleep 2 -fi - -echo "Loading firmware..." -picotool load -f "$BUILDDIR/picomap.uf2" - -echo "Rebooting..." -picotool reboot || true - -echo "Done."