#pragma once #include /** * @brief Button gesture detector, ported from OneButton 2.6.1. * * Only the parts the firmware actually uses are kept (single click and long * press start); the state machine, the debounce rule and the default timings * are a line-by-line port of `OneButton::_fsm()` from * https://github.com/mathertel/OneButton (BSD-3-Clause) so gesture behaviour * matches the original Arduino build exactly. * * The unused OneButton features (double click, multi click, press, long press * stop/during, idle) were never attached in the original firmware and are * therefore not reproduced here. */ class Button { public: using callback_t = void (*)(); /** * @param gpio GPIO the button is attached to. * @param active_low true when pressing connects the pin to GND * (the original used `OneButton button(9, true)`). */ Button(int gpio, bool active_low); void attachClick(callback_t cb); void attachLongPressStart(callback_t cb); /** Milliseconds before a click is considered stable (OneButton setClickMs). */ void setClickMs(uint32_t ms); /** Milliseconds before the press counts as a long press (setPressMs). */ void setPressMs(uint32_t ms); /** Poll the pin and advance the state machine. Call periodically (10 ms). */ void tick(); private: enum state_t { OCS_INIT = 0, OCS_DOWN = 1, OCS_UP = 2, OCS_COUNT = 3, OCS_PRESS = 6, OCS_PRESSEND = 7, }; bool debounce(bool value); void fsm(bool active_level); void reset(); int _pin; bool _active_low; state_t _state = OCS_INIT; int _nClicks = 0; // Debounce state (OneButton: _debounce_ms defaults to 50 ms). int _debounce_ms = 50; bool debouncedLevel = false; bool _lastDebounceLevel = false; unsigned long _lastDebounceTime = 0; unsigned long now = 0; unsigned long _startTime = 0; // `setClickMs(50)` / `setPressMs(500)` are applied by the application; // these are the OneButton defaults. unsigned int _click_ms = 400; unsigned int _press_ms = 800; int _maxClicks = 1; callback_t _clickFunc = nullptr; callback_t _longPressStartFunc = nullptr; };