From 44763f2a35200f734a63a64e9d872b0b7696014e Mon Sep 17 00:00:00 2001 From: Jae Gangemi Date: Thu, 9 Apr 2026 07:56:39 -0600 Subject: [PATCH] feat: add ResetAuto mode for automatic reset strategy detection - rotate through classic DTR/RTS, USB-JTAG, and no-reset strategies - add ResetMode.String() method - extend hardReset with USB timing support --- pkg/espflasher/flasher.go | 9 +++++++++ pkg/espflasher/flasher_test.go | 22 ++++++++++++++++++++++ pkg/espflasher/reset.go | 22 ++++++++++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/pkg/espflasher/flasher.go b/pkg/espflasher/flasher.go index f05df6e..d4fc650 100644 --- a/pkg/espflasher/flasher.go +++ b/pkg/espflasher/flasher.go @@ -211,6 +211,15 @@ func (f *Flasher) connect() error { } case ResetUSBJTAG: usbJTAGSerialReset(f.port) + case ResetAuto: + switch attempt % 3 { + case 0: + classicReset(f.port, defaultResetDelay) + case 1: + usbJTAGSerialReset(f.port) + case 2: + // No reset — device may already be in bootloader + } } // ResetNoReset: skip reset entirely diff --git a/pkg/espflasher/flasher_test.go b/pkg/espflasher/flasher_test.go index 15ab759..61b4495 100644 --- a/pkg/espflasher/flasher_test.go +++ b/pkg/espflasher/flasher_test.go @@ -8,6 +8,28 @@ import ( "testing" ) +func TestResetModeString(t *testing.T) { + tests := []struct { + mode ResetMode + want string + }{ + {ResetDefault, "default"}, + {ResetNoReset, "no-reset"}, + {ResetUSBJTAG, "usb-jtag"}, + {ResetAuto, "auto"}, + {ResetMode(99), "unknown(99)"}, + } + + for _, tt := range tests { + t.Run(tt.want, func(t *testing.T) { + got := tt.mode.String() + if got != tt.want { + t.Errorf("String() = %q, want %q", got, tt.want) + } + }) + } +} + func TestDefaultOptions(t *testing.T) { opts := DefaultOptions() diff --git a/pkg/espflasher/reset.go b/pkg/espflasher/reset.go index 3b78df8..c3084fe 100644 --- a/pkg/espflasher/reset.go +++ b/pkg/espflasher/reset.go @@ -1,6 +1,7 @@ package espflasher import ( + "fmt" "time" "go.bug.st/serial" @@ -19,6 +20,11 @@ const ( // ResetUSBJTAG uses the USB-JTAG/Serial reset sequence (ESP32-S3, ESP32-C3, etc.). ResetUSBJTAG + + // ResetAuto tries multiple reset strategies in sequence. + // First attempts DTR/RTS classic reset, then USB-JTAG, then no-signal. + // Useful when the interface type is unknown. + ResetAuto ) const ( @@ -112,3 +118,19 @@ func hardReset(port serial.Port, usesUSB bool) { port.SetRTS(false) //nolint:errcheck } } + +// String returns the string representation of the ResetMode. +func (r ResetMode) String() string { + switch r { + case ResetDefault: + return "default" + case ResetNoReset: + return "no-reset" + case ResetUSBJTAG: + return "usb-jtag" + case ResetAuto: + return "auto" + default: + return fmt.Sprintf("unknown(%d)", int(r)) + } +}