From 06b4cf0c7163156285fffd4b8099ca5d95792f5a Mon Sep 17 00:00:00 2001 From: Dave Allie Date: Sat, 6 Dec 2025 11:32:45 +1100 Subject: [PATCH] Update InputManager to record any button hold time --- .../InputManager/include/InputManager.h | 105 +++++++++++++----- libs/hardware/InputManager/library.json | 2 +- .../InputManager/src/InputManager.cpp | 85 +++++++++----- 3 files changed, 134 insertions(+), 58 deletions(-) diff --git a/libs/hardware/InputManager/include/InputManager.h b/libs/hardware/InputManager/include/InputManager.h index d03e351..ea58f54 100644 --- a/libs/hardware/InputManager/include/InputManager.h +++ b/libs/hardware/InputManager/include/InputManager.h @@ -1,5 +1,4 @@ -#ifndef INPUT_MANAGER_H -#define INPUT_MANAGER_H +#pragma once #include @@ -8,52 +7,100 @@ class InputManager { InputManager(); void begin(); uint8_t getState(); + + /** + * Updates the button states. Should be called regularly in the main loop. + */ void update(); - bool isPressed(uint8_t buttonIndex); - bool wasPressed(uint8_t buttonIndex); - bool wasReleased(uint8_t buttonIndex); + + /** + * Returns true if the button was being held at the time of the last #update() call. + * + * @param buttonIndex the button indexes + * @return the button current press state + */ + bool isPressed(uint8_t buttonIndex) const; + + /** + * Returns true if the button went from unpressed to pressed between the last two #update() calls. + * + * This differs from #isPressed() in that pressing and holding a button will cause this function + * to return true after the first #update() call, but false on subsequent calls, whereas #isPressed() + * will continue to return true. + * + * @param buttonIndex + * @return the button pressed state + */ + bool wasPressed(uint8_t buttonIndex) const; + + /** + * Returns true if any button started being pressed between the last two #update() calls + * + * @return true if any button started being pressed between the last two #update() calls + */ + bool wasAnyPressed() const; + + /** + * Returns true if the button went from pressed to unpressed between the last two #update() calls + * + * @param buttonIndex the button indexes + * @return the button release state + */ + bool wasReleased(uint8_t buttonIndex) const; + + /** + * Returns true if any button was released between the last two #update() calls + * + * @return true if any button was released between the last two #update() calls + */ + bool wasAnyReleased() const; + + /** + * Returns the time between any button starting to be depressed and all buttons between released + * + * @return duration in milliseconds + */ + unsigned long getHeldTime() const; // Button indices - static const uint8_t BTN_BACK = 0; - static const uint8_t BTN_CONFIRM = 1; - static const uint8_t BTN_LEFT = 2; - static const uint8_t BTN_RIGHT = 3; - static const uint8_t BTN_UP = 4; - static const uint8_t BTN_DOWN = 5; - static const uint8_t BTN_POWER = 6; + static constexpr uint8_t BTN_BACK = 0; + static constexpr uint8_t BTN_CONFIRM = 1; + static constexpr uint8_t BTN_LEFT = 2; + static constexpr uint8_t BTN_RIGHT = 3; + static constexpr uint8_t BTN_UP = 4; + static constexpr uint8_t BTN_DOWN = 5; + static constexpr uint8_t BTN_POWER = 6; + + // Pins + static constexpr int BUTTON_ADC_PIN_1 = 1; + static constexpr int BUTTON_ADC_PIN_2 = 2; + static constexpr int POWER_BUTTON_PIN = 3; // Power button methods - bool isPowerButtonPressed(); + bool isPowerButtonPressed() const; // Button names static const char* getButtonName(uint8_t buttonIndex); private: - int getButtonFromADC(int adcValue, const int thresholds[], int numButtons); + int getButtonFromADC(int adcValue, const int ranges[], int numButtons); uint8_t currentState; uint8_t lastState; uint8_t pressedEvents; uint8_t releasedEvents; unsigned long lastDebounceTime; - unsigned long powerButtonPressStart; - bool powerButtonWasPressed; + unsigned long buttonPressStart; + unsigned long buttonPressFinish; - static const int BUTTON_ADC_PIN_1 = 1; - static const int NUM_BUTTONS_1 = 4; - static const int ADC_THRESHOLDS_1[]; + static constexpr int NUM_BUTTONS_1 = 4; + static const int ADC_RANGES_1[]; - static const int POWER_BUTTON_PIN = 3; + static constexpr int NUM_BUTTONS_2 = 2; + static const int ADC_RANGES_2[]; - static const int BUTTON_ADC_PIN_2 = 2; - static const int NUM_BUTTONS_2 = 2; - static const int ADC_THRESHOLDS_2[]; - - static const int ADC_TOLERANCE = 200; - static const int ADC_NO_BUTTON = 3800; - static const unsigned long DEBOUNCE_DELAY = 5; + static constexpr int ADC_NO_BUTTON = 3800; + static constexpr unsigned long DEBOUNCE_DELAY = 5; static const char* BUTTON_NAMES[]; }; - -#endif diff --git a/libs/hardware/InputManager/library.json b/libs/hardware/InputManager/library.json index b61ecde..43cd426 100644 --- a/libs/hardware/InputManager/library.json +++ b/libs/hardware/InputManager/library.json @@ -1,6 +1,6 @@ { "name": "InputManager", - "version": "1.0.0", + "version": "1.1.0", "description": "Button inputs", "authors": [ { diff --git a/libs/hardware/InputManager/src/InputManager.cpp b/libs/hardware/InputManager/src/InputManager.cpp index 17fdb42..80af2e7 100644 --- a/libs/hardware/InputManager/src/InputManager.cpp +++ b/libs/hardware/InputManager/src/InputManager.cpp @@ -1,7 +1,21 @@ #include "InputManager.h" -const int InputManager::ADC_THRESHOLDS_1[] = {3470, 2655, 1470, 3}; -const int InputManager::ADC_THRESHOLDS_2[] = {2205, 3}; +// Recorded ADC values from real devices +// BACK CONF LEFT RGHT UP DOWN +// 3597 2760 1530 6 2300 6 +// 3470 2666 1480 6 2222 5 +// 3470 2655 1470 3 2205 3 + +// Averages +// BACK CONF LEFT RGHT UP DOWN +// 3512 2694 1493 5 2242 5 + +// Setup ranges, if ADC value is between value `i` and `i + 1`, button `i` is being pressed +// These ranges are based on real world values above, and are much more tolerant of different +// devices than a fixed threshold check +// These values are calculated by taking the midpoint of the pairs of averaged values above +const int InputManager::ADC_RANGES_1[] = {ADC_NO_BUTTON, 3100, 2090, 750, INT32_MIN}; +const int InputManager::ADC_RANGES_2[] = {ADC_NO_BUTTON, 1120, INT32_MIN}; const char* InputManager::BUTTON_NAMES[] = {"Back", "Confirm", "Left", "Right", "Up", "Down", "Power"}; InputManager::InputManager() @@ -10,8 +24,8 @@ InputManager::InputManager() pressedEvents(0), releasedEvents(0), lastDebounceTime(0), - powerButtonPressStart(0), - powerButtonWasPressed(false) {} + buttonPressStart(0), + buttonPressFinish(0) {} void InputManager::begin() { pinMode(BUTTON_ADC_PIN_1, INPUT); @@ -20,13 +34,9 @@ void InputManager::begin() { analogSetAttenuation(ADC_11db); } -int InputManager::getButtonFromADC(int adcValue, const int thresholds[], int numButtons) { - if (adcValue > ADC_NO_BUTTON) { - return -1; - } - +int InputManager::getButtonFromADC(const int adcValue, const int ranges[], const int numButtons) { for (int i = 0; i < numButtons; i++) { - if (abs(adcValue - thresholds[i]) < ADC_TOLERANCE) { + if (ranges[i + 1] < adcValue && adcValue <= ranges[i]) { return i; } } @@ -38,15 +48,15 @@ uint8_t InputManager::getState() { uint8_t state = 0; // Read GPIO1 buttons - int adcValue1 = analogRead(BUTTON_ADC_PIN_1); - int button1 = getButtonFromADC(adcValue1, ADC_THRESHOLDS_1, NUM_BUTTONS_1); + const int adcValue1 = analogRead(BUTTON_ADC_PIN_1); + const int button1 = getButtonFromADC(adcValue1, ADC_RANGES_1, NUM_BUTTONS_1); if (button1 >= 0) { state |= (1 << button1); } // Read GPIO2 buttons - int adcValue2 = analogRead(BUTTON_ADC_PIN_2); - int button2 = getButtonFromADC(adcValue2, ADC_THRESHOLDS_2, NUM_BUTTONS_2); + const int adcValue2 = analogRead(BUTTON_ADC_PIN_2); + const int button2 = getButtonFromADC(adcValue2, ADC_RANGES_2, NUM_BUTTONS_2); if (button2 >= 0) { state |= (1 << (button2 + 4)); } @@ -60,8 +70,8 @@ uint8_t InputManager::getState() { } void InputManager::update() { - unsigned long currentTime = millis(); - uint8_t state = getState(); + const unsigned long currentTime = millis(); + const uint8_t state = getState(); // Always clear events first pressedEvents = 0; @@ -78,39 +88,58 @@ void InputManager::update() { // Calculate pressed and released events pressedEvents = state & ~currentState; releasedEvents = currentState & ~state; - currentState = state; - // Track power button press timing - if (pressedEvents & (1 << BTN_POWER)) { - powerButtonPressStart = currentTime; - powerButtonWasPressed = true; + // If pressing buttons and wasn't before, start recording time + if (pressedEvents > 0 && currentState == 0) { + buttonPressStart = currentTime; } - if (releasedEvents & (1 << BTN_POWER)) { - powerButtonWasPressed = false; + + // If releasing a button and no other buttons being pressed, record finish time + if (releasedEvents > 0 && state == 0) { + buttonPressFinish = currentTime; } + + currentState = state; } } } -bool InputManager::isPressed(uint8_t buttonIndex) { +bool InputManager::isPressed(const uint8_t buttonIndex) const { return currentState & (1 << buttonIndex); } -bool InputManager::wasPressed(uint8_t buttonIndex) { +bool InputManager::wasPressed(const uint8_t buttonIndex) const { return pressedEvents & (1 << buttonIndex); } -bool InputManager::wasReleased(uint8_t buttonIndex) { +bool InputManager::wasAnyPressed() const { + return pressedEvents > 0; +} + +bool InputManager::wasReleased(const uint8_t buttonIndex) const { return releasedEvents & (1 << buttonIndex); } -const char* InputManager::getButtonName(uint8_t buttonIndex) { +bool InputManager::wasAnyReleased() const { + return releasedEvents > 0; +} + +unsigned long InputManager::getHeldTime() const { + // Still hold a button + if (currentState > 0) { + return millis() - buttonPressStart; + } + + return buttonPressFinish - buttonPressStart; +} + +const char* InputManager::getButtonName(const uint8_t buttonIndex) { if (buttonIndex <= BTN_POWER) { return BUTTON_NAMES[buttonIndex]; } return "Unknown"; } -bool InputManager::isPowerButtonPressed() { +bool InputManager::isPowerButtonPressed() const { return isPressed(BTN_POWER); }