diff --git a/src/input/HapticFeedback.cpp b/src/input/HapticFeedback.cpp index ef277779c..fcd215be0 100644 --- a/src/input/HapticFeedback.cpp +++ b/src/input/HapticFeedback.cpp @@ -35,9 +35,9 @@ void HapticFeedback::pulse(uint16_t durationMs) { motorWrite(true); pulseOffAt = millis() + durationMs; - if (pulseOffAt == 0) // disambiguate from "no pulse active" sentinel on millis() wrap + if (pulseOffAt == 0) // 0 is the "no pulse" sentinel pulseOffAt = 1; - setIntervalFromNow(durationMs); + scheduleNext(); } void HapticFeedback::armDelayedPulse(uint16_t delayMs, uint16_t durationMs) @@ -46,7 +46,7 @@ void HapticFeedback::armDelayedPulse(uint16_t delayMs, uint16_t durationMs) if (delayedPulseAt == 0) delayedPulseAt = 1; delayedPulseDuration = durationMs; - setIntervalFromNow(delayMs); + scheduleNext(); } void HapticFeedback::cancelDelayedPulse() @@ -54,31 +54,42 @@ void HapticFeedback::cancelDelayedPulse() delayedPulseAt = 0; } -int32_t HapticFeedback::runOnce() +void HapticFeedback::scheduleNext() { uint32_t now = millis(); - - // End an in-flight pulse if its time has come. - if (pulseOffAt != 0 && (int32_t)(now - pulseOffAt) >= 0) { - motorWrite(false); - pulseOffAt = 0; - } - - // Fire an armed delayed pulse if its time has come. - if (delayedPulseAt != 0 && (int32_t)(now - delayedPulseAt) >= 0) { - uint16_t dur = delayedPulseDuration; - delayedPulseAt = 0; - pulse(dur); - } - - // Sleep until the next scheduled event, or idle long if nothing pending. uint32_t next = 0; if (pulseOffAt != 0) next = pulseOffAt; if (delayedPulseAt != 0 && (next == 0 || (int32_t)(delayedPulseAt - next) < 0)) next = delayedPulseAt; if (next == 0) - return 60 * 1000; // nothing pending — idle for a minute + return; + int32_t delay = (int32_t)(next - now); + setIntervalFromNow(delay > 0 ? (unsigned long)delay : 0); +} + +int32_t HapticFeedback::runOnce() +{ + uint32_t now = millis(); + + if (pulseOffAt != 0 && (int32_t)(now - pulseOffAt) >= 0) { + motorWrite(false); + pulseOffAt = 0; + } + + if (delayedPulseAt != 0 && (int32_t)(now - delayedPulseAt) >= 0) { + uint16_t dur = delayedPulseDuration; + delayedPulseAt = 0; + pulse(dur); + } + + uint32_t next = 0; + if (pulseOffAt != 0) + next = pulseOffAt; + if (delayedPulseAt != 0 && (next == 0 || (int32_t)(delayedPulseAt - next) < 0)) + next = delayedPulseAt; + if (next == 0) + return 60 * 1000; int32_t delay = (int32_t)(next - now); return delay > 0 ? delay : 0; } diff --git a/src/input/HapticFeedback.h b/src/input/HapticFeedback.h index cb0f94873..da542edeb 100644 --- a/src/input/HapticFeedback.h +++ b/src/input/HapticFeedback.h @@ -7,41 +7,29 @@ #include "concurrency/OSThread.h" #include -// Drives short, non-blocking pulses on a GPIO-controlled vibration motor. -// A variant opts in by defining HAPTIC_FEEDBACK_PIN; HAPTIC_FEEDBACK_ACTIVE_LOW -// inverts the drive polarity (default: active-high — pin HIGH = motor on). -// -// Used by the touch button to produce button-like haptic feedback. Coexists -// with ExternalNotificationModule if both target the same pin — pulses are -// fire-and-forget, no synchronization, last writer wins. +// Non-blocking pulses on a GPIO vibration motor. HAPTIC_FEEDBACK_ACTIVE_LOW inverts polarity. class HapticFeedback : public concurrency::OSThread { public: HapticFeedback(); - - // Turn motor on now, schedule off after durationMs. void pulse(uint16_t durationMs = 30); - - // Schedule a one-shot pulse to fire delayMs from now. void armDelayedPulse(uint16_t delayMs, uint16_t durationMs = 30); - - // Cancel a previously-armed delayed pulse (no effect if none pending). void cancelDelayedPulse(); protected: int32_t runOnce() override; private: - uint32_t pulseOffAt = 0; // millis() when current pulse should end (0 = no pulse active) - uint32_t delayedPulseAt = 0; // millis() when armed pulse should fire (0 = nothing armed) + uint32_t pulseOffAt = 0; + uint32_t delayedPulseAt = 0; uint16_t delayedPulseDuration = 0; void motorWrite(bool on); + // Reschedule to the soonest pending event so later arms don't clobber earlier wakes. + void scheduleNext(); }; extern HapticFeedback *hapticFeedback; - -// Lazy-instantiate the global on first call. Safe to call repeatedly. void initHapticFeedback(); #endif // HAPTIC_FEEDBACK_PIN diff --git a/src/input/InputBroker.cpp b/src/input/InputBroker.cpp index 1b4b64cad..c84eb2c0c 100644 --- a/src/input/InputBroker.cpp +++ b/src/input/InputBroker.cpp @@ -240,14 +240,12 @@ void InputBroker::Init() }; #endif #if defined(HAPTIC_FEEDBACK_PIN) - // Haptic feedback: short pulse on touch contact, and a second short - // pulse when the long-press fires (BACK). The delayed pulse delay - // matches touchConfig.longPressTime's default (500 ms). + // Blip on touch, second blip when long-press fires (500 ms = touchConfig.longPressTime default). touchConfig.suppressLeadUpSound = true; initHapticFeedback(); touchConfig.onPress = []() { - hapticFeedback->pulse(30); - hapticFeedback->armDelayedPulse(500, 30); + hapticFeedback->pulse(80); + hapticFeedback->armDelayedPulse(500, 80); }; touchConfig.onRelease = []() { hapticFeedback->cancelDelayedPulse(); }; #endif