/* Original code: Debounce buttons and trigger events Copyright (C) 2015-2018 by Xose PĂ©rez The DebounceEvent library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The DebounceEvent library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the DebounceEvent library. If not, see . ---------------------------------------------------------------------------------- Modified to include generic INPUT / OUTPUT pin support through a custom interface. Definitions are incompatible with DebounceEvent, you should not include it's headers. */ #pragma once #include namespace DebounceEvent { #include #include namespace Types { enum event_t { EventNone, EventChanged, EventPressed, EventReleased }; enum mode_t { ModePushbutton = 1 << 0, ModeSwitch = 1 << 1, ModeDefaultHigh = 1 << 2, ModeSetPullup = 1 << 3 }; } constexpr const unsigned long DebounceDelay = 50UL; constexpr const unsigned long RepeatDelay = 500UL; // base interface for generic pin handler. class PinBase { public: PinBase(unsigned char pin) : pin(pin) {} virtual void pinMode(int8_t mode) = 0; virtual void digitalWrite(int8_t val) = 0; virtual int digitalRead() = 0; const unsigned char pin; }; // real hardware pin class DigitalPin : public PinBase { public: DigitalPin(unsigned char pin) : PinBase(pin) {} void pinMode(int8_t mode) { // Note: proxy for pinMode so it doesn't ignore INPUT_PULLUP with GPIO16 if (((mode == INPUT) || (mode == INPUT_PULLUP)) && this->pin == 16) { ::pinMode(this->pin, ((mode == INPUT_PULLUP) ? INPUT : INPUT_PULLDOWN_16)); return; } ::pinMode(this->pin, mode); } void digitalWrite(int8_t val) { ::digitalWrite(this->pin, val); } int digitalRead() { return ::digitalRead(this->pin); } }; class DebounceEvent { public: // TODO: not used in espurna buttons node using callback_f = std::function; DebounceEvent(std::shared_ptr pin, int mode = Types::ModePushbutton | Types::ModeDefaultHigh, unsigned long delay = DebounceDelay, unsigned long repeat = RepeatDelay); DebounceEvent(std::shared_ptr pin, callback_f callback, int mode = Types::ModePushbutton | Types::ModeDefaultHigh, unsigned long delay = DebounceDelay, unsigned long repeat = RepeatDelay); Types::event_t loop(); bool pressed(); unsigned long getEventLength(); unsigned long getEventCount(); std::shared_ptr pin; callback_f callback; const int mode; private: const bool _is_switch; const bool _default_status; const unsigned long _delay; const unsigned long _repeat; bool _status; bool _ready; bool _reset_count; unsigned long _event_start; unsigned long _event_length; unsigned char _event_count; }; DebounceEvent::DebounceEvent(std::shared_ptr pin, DebounceEvent::callback_f callback, int mode, unsigned long debounce_delay, unsigned long repeat) : pin(pin), callback(callback), mode(mode), _is_switch(mode & Types::ModeSwitch), _default_status(mode & Types::ModeDefaultHigh), _delay(debounce_delay), _repeat(repeat), _status(false), _ready(false), _reset_count(true), _event_start(0), _event_length(0), _event_count(0) { pin->pinMode((mode & Types::ModeSetPullup) ? INPUT_PULLUP : INPUT); _status = (mode & Types::ModeSwitch) ? pin->digitalRead() : _default_status; } DebounceEvent::DebounceEvent(std::shared_ptr pin, int mode, unsigned long delay, unsigned long repeat) : DebounceEvent(pin, nullptr, mode, delay, repeat) {} bool DebounceEvent::pressed() { return (_status != _default_status); } unsigned long DebounceEvent::getEventLength() { return _event_length; } unsigned long DebounceEvent::getEventCount() { return _event_count; } Types::event_t DebounceEvent::loop() { auto event = Types::EventNone; if (pin->digitalRead() != _status) { // TODO: check each loop instead of blocking? auto start = millis(); while (millis() - start < _delay) delay(1); if (pin->digitalRead() != _status) { _status = !_status; if (_is_switch) { event = Types::EventChanged; } else { if (_status == _default_status) { _event_length = millis() - _event_start; _ready = true; } else { event = Types::EventPressed; _event_start = millis(); _event_length = 0; if (_reset_count) { _event_count = 1; _reset_count = false; } else { ++_event_count; } _ready = false; } } } } if (_ready && (millis() - _event_start > _repeat)) { _ready = false; _reset_count = true; event = Types::EventReleased; } if (callback && (event != Types::EventNone)) { callback(this, event, _event_count, _event_length); } return event; } // compat definitions from the original lib #define BUTTON_PUSHBUTTON DebounceEvent::Types::ModePushbutton #define BUTTON_SWITCH DebounceEvent::Types::ModeSwitch #define BUTTON_DEFAULT_HIGH DebounceEvent::Types::ModeDefaultHigh #define BUTTON_SET_PULLUP DebounceEvent::Types::ModeSetPullup #define EVENT_NONE DebounceEvent::Types::EventNone #define EVENT_CHANGED DebounceEvent::Types::EventChanged #define EVENT_PRESSED DebounceEvent::Types::EventPressed #define EVENT_RELEASED DebounceEvent::Types::EventReleased } // namespace DebounceEvent