feat: add Arduino 1200bps reset support before flashing
- Add touchSerialPortAt1200bps function from TinyGo for Arduino bootloader reset - Update Flash function to trigger 1200bps reset when flash-1200-bps-reset is true - Add 2-second wait after reset for device to enter bootloader mode - Support retry mechanism with Windows-specific error handling 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
"github.com/goplus/llgo/internal/crosscompile"
|
||||
"github.com/goplus/llgo/internal/env"
|
||||
"go.bug.st/serial"
|
||||
"go.bug.st/serial/enumerator"
|
||||
)
|
||||
|
||||
@@ -119,6 +120,49 @@ func GetPort(portFlag string, usbInterfaces []string) (string, error) {
|
||||
return "", errors.New("port you specified '" + strings.Join(portCandidates, ",") + "' does not exist, available ports are " + strings.Join(ports, ", "))
|
||||
}
|
||||
|
||||
// From tinygo/main.go touchSerialPortAt1200bps
|
||||
// touchSerialPortAt1200bps triggers Arduino-compatible devices to enter bootloader mode.
|
||||
// This function implements the Arduino auto-reset mechanism used before flashing firmware.
|
||||
//
|
||||
// Working principle:
|
||||
// 1. Opens serial port at 1200 baud rate (special reset baudrate for Arduino)
|
||||
// 2. Sets DTR (Data Terminal Ready) signal to false
|
||||
// 3. This triggers the device to reset and enter bootloader mode for firmware upload
|
||||
//
|
||||
// Usage scenarios:
|
||||
// - Required for Arduino Uno, Leonardo, Micro and other compatible devices
|
||||
// - Executed when target config has "flash-1200-bps-reset": "true"
|
||||
// - Ensures device is in correct state to receive new firmware
|
||||
//
|
||||
// Retry mechanism:
|
||||
// - Retries up to 3 times due to potential temporary serial port access issues
|
||||
// - Windows special handling: InvalidSerialPort error during bootloader transition is normal
|
||||
func touchSerialPortAt1200bps(port string) (err error) {
|
||||
retryCount := 3
|
||||
for i := 0; i < retryCount; i++ {
|
||||
// Open port at 1200bps to trigger Arduino reset
|
||||
p, e := serial.Open(port, &serial.Mode{BaudRate: 1200})
|
||||
if e != nil {
|
||||
if runtime.GOOS == `windows` {
|
||||
se, ok := e.(*serial.PortError)
|
||||
if ok && se.Code() == serial.InvalidSerialPort {
|
||||
// InvalidSerialPort error occurs when transitioning to boot
|
||||
return nil
|
||||
}
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
err = e
|
||||
continue
|
||||
}
|
||||
defer p.Close()
|
||||
|
||||
// Set DTR to false to trigger reset
|
||||
p.SetDTR(false)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("opening port: %s", err)
|
||||
}
|
||||
|
||||
// Flash flashes firmware to a device based on the crosscompile configuration
|
||||
func Flash(crossCompile crosscompile.Export, app string, port string, verbose bool) error {
|
||||
method := crossCompile.Flash.Method
|
||||
@@ -140,6 +184,18 @@ func Flash(crossCompile crosscompile.Export, app string, port string, verbose bo
|
||||
fmt.Fprintf(os.Stderr, "Using port: %s\n", port)
|
||||
}
|
||||
|
||||
// Execute 1200bps reset before flashing if needed (except for openocd)
|
||||
if method != "openocd" && crossCompile.Flash.Flash1200BpsReset {
|
||||
if verbose {
|
||||
fmt.Fprintf(os.Stderr, "Triggering 1200bps reset on port: %s\n", port)
|
||||
}
|
||||
if err := touchSerialPortAt1200bps(port); err != nil {
|
||||
return fmt.Errorf("failed to trigger 1200bps reset: %w", err)
|
||||
}
|
||||
// Wait a bit for device to enter bootloader mode
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
|
||||
switch method {
|
||||
case "command":
|
||||
return flashCommand(crossCompile.Flash, app, port, verbose)
|
||||
@@ -176,17 +232,6 @@ func flashCommand(flash crosscompile.Flash, app string, port string, verbose boo
|
||||
return fmt.Errorf("empty flash command after expansion")
|
||||
}
|
||||
|
||||
// Handle 1200bps reset if required
|
||||
if flash.Flash1200BpsReset && port != "" {
|
||||
if err := reset1200bps(port, verbose); err != nil {
|
||||
if verbose {
|
||||
fmt.Fprintf(os.Stderr, "Warning: 1200bps reset failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
// Wait for bootloader
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
|
||||
// Execute flash command
|
||||
cmd := exec.Command(parts[0], parts[1:]...)
|
||||
cmd.Stdout = os.Stdout
|
||||
@@ -376,19 +421,6 @@ func expandEnv(template string, envs map[string]string) string {
|
||||
return result
|
||||
}
|
||||
|
||||
// reset1200bps performs 1200bps reset for Arduino-compatible boards
|
||||
func reset1200bps(port string, verbose bool) error {
|
||||
if verbose {
|
||||
fmt.Fprintf(os.Stderr, "Performing 1200bps reset on %s\n", port)
|
||||
}
|
||||
|
||||
// This is a simplified implementation
|
||||
// In practice, this would need platform-specific serial port handling
|
||||
// For now, just try to touch the port to trigger reset
|
||||
_, err := os.Stat(port)
|
||||
return err
|
||||
}
|
||||
|
||||
// copyFile copies a file from src to dst
|
||||
func copyFile(src, dst string) error {
|
||||
sourceFile, err := os.Open(src)
|
||||
|
||||
Reference in New Issue
Block a user