#include "button.hpp" #include #include "armor_time.hpp" #include "driver/gpio.h" Button::Button(int gpio, bool active_low) : _pin(gpio), _active_low(active_low) { gpio_config_t cfg = {}; cfg.pin_bit_mask = 1ULL << gpio; cfg.mode = GPIO_MODE_INPUT; cfg.pull_up_en = active_low ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE; cfg.pull_down_en = active_low ? GPIO_PULLDOWN_DISABLE : GPIO_PULLDOWN_ENABLE; cfg.intr_type = GPIO_INTR_DISABLE; gpio_config(&cfg); } void Button::attachClick(callback_t cb) { _clickFunc = cb; } void Button::attachLongPressStart(callback_t cb) { _longPressStartFunc = cb; } void Button::setClickMs(uint32_t ms) { _click_ms = ms; } void Button::setPressMs(uint32_t ms) { _press_ms = ms; } // Port of OneButton::debounce(): a level only becomes the debounced level once // it has been stable for _debounce_ms. bool Button::debounce(bool value) { now = millis(); if (_lastDebounceLevel == value) { if (now - _lastDebounceTime >= static_cast(std::abs(_debounce_ms))) { debouncedLevel = value; } } else { _lastDebounceTime = now; _lastDebounceLevel = value; } return debouncedLevel; } void Button::tick() { if (_pin < 0) { return; } const int level = gpio_get_level(static_cast(_pin)); const bool active_level = _active_low ? (level == 0) : (level != 0); fsm(debounce(active_level)); } void Button::reset() { _state = OCS_INIT; _nClicks = 0; _startTime = millis(); } // Port of OneButton::_fsm() (single click + long press start only). void Button::fsm(bool active_level) { const unsigned long waitTime = now - _startTime; switch (_state) { case OCS_INIT: // Waiting for the level to become active. if (active_level) { _state = OCS_DOWN; _startTime = now; _nClicks = 0; } break; case OCS_DOWN: // Waiting for the level to become inactive again... or a long press. if (!active_level) { _state = OCS_UP; _startTime = now; } else if (waitTime > _press_ms) { if (_longPressStartFunc) { _longPressStartFunc(); } _state = OCS_PRESS; } break; case OCS_UP: // Level is inactive: count as a short button down. _nClicks++; _state = OCS_COUNT; break; case OCS_COUNT: if (active_level) { // Button is down again. _state = OCS_DOWN; _startTime = now; } else if ((waitTime >= _click_ms) || (_nClicks == _maxClicks)) { if (_nClicks == 1 && _clickFunc) { _clickFunc(); } reset(); } break; case OCS_PRESS: // Waiting for the pin to be released after a long press. if (!active_level) { _state = OCS_PRESSEND; } break; case OCS_PRESSEND: reset(); break; default: _state = OCS_INIT; break; } }