From 5d038aec9e04378ca8c079bd93ef0d86fdaa7e9d Mon Sep 17 00:00:00 2001 From: DeFiDude <59237470+DeFiDude@users.noreply.github.com> Date: Fri, 6 Mar 2026 15:43:13 -0700 Subject: [PATCH] Fix trackball dead after USB web flash After ESP32-S3 USB download mode, GPIO peripheral state may persist across soft reset. The trackball interrupts silently fail because pins are in an unknown state from the bootloader. Fix: call gpio_reset_pin() and detachInterrupt() before configuring the trackball GPIOs, ensuring a clean state regardless of how the device was reset (power cycle, RTS toggle, or software reset). --- src/hal/Trackball.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/hal/Trackball.cpp b/src/hal/Trackball.cpp index 266a82b..4721b32 100644 --- a/src/hal/Trackball.cpp +++ b/src/hal/Trackball.cpp @@ -1,4 +1,5 @@ #include "Trackball.h" +#include volatile int8_t Trackball::_deltaX = 0; volatile int8_t Trackball::_deltaY = 0; @@ -8,6 +9,20 @@ Trackball* Trackball::_instance = nullptr; bool Trackball::begin() { _instance = this; + // Detach any stale interrupts (GPIO state may persist after USB download mode reset) + detachInterrupt(digitalPinToInterrupt(TBALL_UP)); + detachInterrupt(digitalPinToInterrupt(TBALL_DOWN)); + detachInterrupt(digitalPinToInterrupt(TBALL_LEFT)); + detachInterrupt(digitalPinToInterrupt(TBALL_RIGHT)); + detachInterrupt(digitalPinToInterrupt(TBALL_CLICK)); + + // Reset pins to a known state before configuring + gpio_reset_pin((gpio_num_t)TBALL_UP); + gpio_reset_pin((gpio_num_t)TBALL_DOWN); + gpio_reset_pin((gpio_num_t)TBALL_LEFT); + gpio_reset_pin((gpio_num_t)TBALL_RIGHT); + gpio_reset_pin((gpio_num_t)TBALL_CLICK); + // Configure trackball GPIOs as inputs with pullup pinMode(TBALL_UP, INPUT_PULLUP); pinMode(TBALL_DOWN, INPUT_PULLUP); @@ -15,6 +30,9 @@ bool Trackball::begin() { pinMode(TBALL_RIGHT, INPUT_PULLUP); pinMode(TBALL_CLICK, INPUT_PULLUP); + // Small delay to let pullups stabilize + delay(5); + // Attach interrupts for movement detection attachInterrupt(digitalPinToInterrupt(TBALL_UP), isrUp, FALLING); attachInterrupt(digitalPinToInterrupt(TBALL_DOWN), isrRight, FALLING); // Physical down pin = rightward @@ -22,7 +40,7 @@ bool Trackball::begin() { attachInterrupt(digitalPinToInterrupt(TBALL_RIGHT), isrDown, FALLING); // Physical right pin = downward attachInterrupt(digitalPinToInterrupt(TBALL_CLICK), isrClick, FALLING); - Serial.println("[TRACKBALL] Initialized"); + Serial.println("[TRACKBALL] Initialized (GPIO reset + interrupts attached)"); return true; }