diff --git a/code/espurna/button.h b/code/espurna/button.h index 22b8c815..445c5c4f 100644 --- a/code/espurna/button.h +++ b/code/espurna/button.h @@ -8,10 +8,12 @@ Copyright (C) 2016-2019 by Xose Pérez #pragma once -#include "debounce.h" +#include "libs/DebounceEvent.h" #include +constexpr size_t BUTTONS_MAX = 32; + struct button_event_delays_t { button_event_delays_t(); button_event_delays_t(unsigned long debounce, unsigned long dblclick, unsigned long lngclick, unsigned long lnglngclick); diff --git a/code/espurna/button.ino b/code/espurna/button.ino index 3eafc517..7df0476f 100644 --- a/code/espurna/button.ino +++ b/code/espurna/button.ino @@ -8,6 +8,7 @@ Copyright (C) 2016-2019 by Xose Pérez #if BUTTON_SUPPORT +#include #include #include @@ -18,7 +19,7 @@ Copyright (C) 2016-2019 by Xose Pérez #include "button.h" #include "button_config.h" -#include "debounce.h" +#include "libs/DebounceEvent.h" // ----------------------------------------------------------------------------- @@ -90,10 +91,18 @@ unsigned char buttonCount() { #if MQTT_SUPPORT +std::bitset _buttons_mqtt_retain( + (1 == BUTTON_MQTT_RETAIN) ? 0xFFFFFFFFUL : 0UL +); +std::bitset _buttons_mqtt_send_all( + (1 == BUTTON_MQTT_SEND_ALL_EVENTS) ? 0xFFFFFFFFUL : 0UL +); + void buttonMQTT(unsigned char id, uint8_t event) { char payload[4] = {0}; itoa(event, payload, 10); - mqttSend(MQTT_TOPIC_BUTTON, id, payload, false, false); // 1st bool = force, 2nd = retain + // mqttSend(topic, id, payload, force, retail) + mqttSend(MQTT_TOPIC_BUTTON, id, payload, false, _buttons_mqtt_retain[id]); } #endif @@ -106,6 +115,57 @@ void _buttonWebSocketOnVisible(JsonObject& root) { } } +// XXX: unused! pending webui changes + +void _buttonWebSocketOnConnected(JsonObject& root) { +#if 0 + if (buttonCount() < 1) return; + + JsonObject& module = root.createNestedObject("btn"); + + // TODO: hardware can sometimes use a different event source + // e.g. Sonoff Dual does not need `Pin`, `Mode` or any of `Del` + // TODO: schema names are uppercase to easily match settings? + // TODO: schema name->type map to generate WebUI elements? + + JsonArray& schema = module.createNestedArray("_schema"); + + schema.add("Pin"); + schema.add("Mode"); + + schema.add("Relay"); + + schema.add("DebDel"); + schema.add("DblDel"); + schema.add("LngDel"); + schema.add("LngLngDel"); + + #if MQTT_SUPPORT + schema.add("MqttSnd"); + schema.add("MqttRetain"); + #endif + + JsonArray& buttons = module.createNestedArray("list"); + + for (unsigned char i=0; i - - 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