From ef194c9c2430ed14ddf5905214e2968c0f5f9980 Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Thu, 14 Jan 2021 15:24:08 +0300 Subject: [PATCH] button: custom action + ifan fixes --- code/espurna/button.cpp | 208 ++++++++++++++++++++------------- code/espurna/button.h | 53 ++++++--- code/espurna/button_config.h | 14 +-- code/espurna/config/hardware.h | 8 ++ code/espurna/config/types.h | 31 ++--- code/espurna/ifan.cpp | 29 ++--- 6 files changed, 210 insertions(+), 133 deletions(-) diff --git a/code/espurna/button.cpp b/code/espurna/button.cpp index 9812df3c..cfff21ed 100644 --- a/code/espurna/button.cpp +++ b/code/espurna/button.cpp @@ -136,7 +136,34 @@ ButtonProvider convert(const String& value) { return ButtonProvider::None; } -} // namespace settings::internal +template<> +ButtonAction convert(const String& value) { + auto num = strtoul(value.c_str(), nullptr, 10); + if (num < ButtonsActionMax) { + auto action = static_cast(num); + switch (action) { + case ButtonAction::None: + case ButtonAction::Toggle: + case ButtonAction::On: + case ButtonAction::Off: + case ButtonAction::AccessPoint: + case ButtonAction::Reset: + case ButtonAction::Pulse: + case ButtonAction::FactoryReset: + case ButtonAction::Wps: + case ButtonAction::SmartConfig: + case ButtonAction::BrightnessIncrease: + case ButtonAction::BrightnessDecrease: + case ButtonAction::DisplayOn: + case ButtonAction::Custom: + return action; + } + } + + return ButtonAction::None; +} + +} // namespace internal } // namespace settings // ----------------------------------------------------------------------------- @@ -156,7 +183,7 @@ constexpr debounce_event::types::Config _buttonDecodeConfigBitmask(int bitmask) }; } -constexpr button_action_t _buttonDecodeEventAction(const button_actions_t& actions, button_event_t event) { +constexpr ButtonAction _buttonDecodeEventAction(const ButtonActions& actions, button_event_t event) { return ( (event == button_event_t::Pressed) ? actions.pressed : (event == button_event_t::Released) ? actions.released : @@ -164,7 +191,7 @@ constexpr button_action_t _buttonDecodeEventAction(const button_actions_t& actio (event == button_event_t::DoubleClick) ? actions.dblclick : (event == button_event_t::LongClick) ? actions.lngclick : (event == button_event_t::LongLongClick) ? actions.lnglngclick : - (event == button_event_t::TripleClick) ? actions.trplclick : 0U + (event == button_event_t::TripleClick) ? actions.trplclick : ButtonAction::None ); } @@ -181,7 +208,7 @@ constexpr button_event_t _buttonMapReleased(uint8_t count, unsigned long length, ); } -button_actions_t _buttonConstructActions(unsigned char index) { +ButtonActions _buttonConstructActions(unsigned char index) { return { _buttonPress(index), _buttonRelease(index), @@ -222,12 +249,12 @@ button_event_delays_t::button_event_delays_t(unsigned long debounce, unsigned lo lnglngclick(lnglngclick) {} -button_t::button_t(button_actions_t&& actions_, button_event_delays_t&& delays_) : +button_t::button_t(ButtonActions&& actions_, button_event_delays_t&& delays_) : actions(std::move(actions_)), event_delays(std::move(delays_)) {} -button_t::button_t(BasePinPtr&& pin, const debounce_event::types::Config& config, button_actions_t&& actions_, button_event_delays_t&& delays_) : +button_t::button_t(BasePinPtr&& pin, const debounce_event::types::Config& config, ButtonActions&& actions_, button_event_delays_t&& delays_) : event_emitter(std::make_unique(std::move(pin), config, delays_.debounce, delays_.repeat)), actions(std::move(actions_)), event_delays(std::move(delays_)) @@ -382,14 +409,22 @@ bool _buttonWebSocketOnKeyCheck(const char * key, JsonVariant&) { #endif // WEB_SUPPORT +ButtonCustomAction _button_custom_action { nullptr }; + +void buttonSetCustomAction(ButtonCustomAction action) { + _button_custom_action = action; +} + bool buttonState(unsigned char id) { - if (id >= _buttons.size()) return false; - return _buttons[id].state(); + return (id < _buttons.size()) + ? _buttons[id].state() + : false; } -button_action_t buttonAction(unsigned char id, const button_event_t event) { - if (id >= _buttons.size()) return 0; - return _buttonDecodeEventAction(_buttons[id].actions, event); +ButtonAction buttonAction(unsigned char id, const button_event_t event) { + return (id < _buttons.size()) + ? _buttonDecodeEventAction(_buttons[id].actions, event) + : ButtonAction::None; } // Note that we don't directly return F(...), but use a temporary to assign it conditionally @@ -442,21 +477,28 @@ unsigned char _buttonRelaySetting(unsigned char id) { return relays[id]; } -void _buttonRelayAction(unsigned char id, button_action_t action) { +void _buttonRelayAction(unsigned char id, ButtonAction action) { auto relayId = _buttonRelaySetting(id); switch (action) { - case BUTTON_ACTION_TOGGLE: + case ButtonAction::Toggle: relayToggle(relayId); break; - case BUTTON_ACTION_ON: + case ButtonAction::On: relayStatus(relayId, true); break; - case BUTTON_ACTION_OFF: + case ButtonAction::Off: relayStatus(relayId, false); break; + + case ButtonAction::Pulse: + // TODO + break; + + default: + break; } } @@ -470,75 +512,88 @@ void buttonEvent(unsigned char id, button_event_t event) { if (event == button_event_t::None) return; auto& button = _buttons[id]; + auto action = _buttonDecodeEventAction(button.actions, event); - #if BROKER_SUPPORT - ButtonBroker::Publish(id, event); - #endif +#if BROKER_SUPPORT + ButtonBroker::Publish(id, event); +#endif - #if MQTT_SUPPORT - if (action || _buttons_mqtt_send_all[id]) { - mqttSend(MQTT_TOPIC_BUTTON, id, _buttonEventString(event).c_str(), false, _buttons_mqtt_retain[id]); - } - #endif +#if MQTT_SUPPORT + if ((action != ButtonAction::None) || _buttons_mqtt_send_all[id]) { + mqttSend(MQTT_TOPIC_BUTTON, id, _buttonEventString(event).c_str(), false, _buttons_mqtt_retain[id]); + } +#endif switch (action) { - #if RELAY_SUPPORT - case BUTTON_ACTION_TOGGLE: - case BUTTON_ACTION_ON: - case BUTTON_ACTION_OFF: - _buttonRelayAction(id, action); - break; - #endif +#if RELAY_SUPPORT + case ButtonAction::Toggle: + case ButtonAction::On: + case ButtonAction::Off: + case ButtonAction::Pulse: + _buttonRelayAction(id, action); + break; +#endif - case BUTTON_ACTION_AP: - if (wifiState() & WIFI_STATE_AP) { - wifiStartSTA(); - } else { - wifiStartAP(); - } - break; + case ButtonAction::AccessPoint: + if (wifiState() & WIFI_STATE_AP) { + wifiStartSTA(); + } else { + wifiStartAP(); + } + break; - case BUTTON_ACTION_RESET: - deferredReset(100, CUSTOM_RESET_HARDWARE); - break; + case ButtonAction::Reset: + deferredReset(100, CUSTOM_RESET_HARDWARE); + break; - case BUTTON_ACTION_FACTORY: - DEBUG_MSG_P(PSTR("\n\nFACTORY RESET\n\n")); - resetSettings(); - deferredReset(100, CUSTOM_RESET_FACTORY); - break; + case ButtonAction::FactoryReset: + DEBUG_MSG_P(PSTR("\n\nFACTORY RESET\n\n")); + resetSettings(); + deferredReset(100, CUSTOM_RESET_FACTORY); + break; - #if defined(JUSTWIFI_ENABLE_WPS) - case BUTTON_ACTION_WPS: - wifiStartWPS(); - break; - #endif // defined(JUSTWIFI_ENABLE_WPS) + case ButtonAction::Wps: +#if defined(JUSTWIFI_ENABLE_WPS) + wifiStartWPS(); +#endif + break; - #if defined(JUSTWIFI_ENABLE_SMARTCONFIG) - case BUTTON_ACTION_SMART_CONFIG: - wifiStartSmartConfig(); - break; - #endif // defined(JUSTWIFI_ENABLE_SMARTCONFIG) + case ButtonAction::SmartConfig: +#if defined(JUSTWIFI_ENABLE_SMARTCONFIG) + wifiStartSmartConfig(); +#endif + break; - #if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE - case BUTTON_ACTION_DIM_UP: - lightBrightnessStep(1); - lightUpdate(); - break; + case ButtonAction::BrightnessIncrease: +#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE + lightBrightnessStep(1); + lightUpdate(); +#endif + break; - case BUTTON_ACTION_DIM_DOWN: - lightBrightnessStep(-1); - lightUpdate(); - break; - #endif // LIGHT_PROVIDER != LIGHT_PROVIDER_NONE + case ButtonAction::BrightnessDecrease: +#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE + lightBrightnessStep(-1); + lightUpdate(); +#endif + break; - #if THERMOSTAT_DISPLAY_SUPPORT - case BUTTON_ACTION_DISPLAY_ON: - displayOn(); - break; - #endif + case ButtonAction::DisplayOn: +#if THERMOSTAT_DISPLAY_SUPPORT + displayOn(); +#endif + break; + + case ButtonAction::Custom: + if (_button_custom_action) { + _button_custom_action(id); + } + break; + + case ButtonAction::None: + break; } @@ -746,8 +801,8 @@ BasePinPtr _buttonGpioPin(unsigned char index, ButtonProvider provider) { return result; } -inline button_actions_t _buttonActions(unsigned char index) { - button_actions_t actions { +ButtonActions _buttonActions(unsigned char index) { + return { getSetting({"btnPress", index}, _buttonPress(index)), getSetting({"btnRlse", index}, _buttonRelease(index)), getSetting({"btnClick", index}, _buttonClick(index)), @@ -756,20 +811,17 @@ inline button_actions_t _buttonActions(unsigned char index) { getSetting({"btnLLclk", index}, _buttonLongLongClick(index)), getSetting({"btnTclk", index}, _buttonTripleClick(index)) }; - - return actions; } -// Note that we use settings without indexes as default values +// Note that we use settings without indexes as default values to preserve backwards compatibility + button_event_delays_t _buttonDelays(unsigned char index) { - button_event_delays_t delays { + return { _buttonGetSetting("btnDebDel", index, _buttonDebounceDelay(index)), _buttonGetSetting("btnRepDel", index, _buttonRepeatDelay(index)), _buttonGetSetting("btnLclkDel", index, _buttonLongClickDelay(index)), _buttonGetSetting("btnLLclkDel", index, _buttonLongLongClickDelay(index)), }; - - return delays; } bool _buttonSetupProvider(unsigned char index, ButtonProvider provider) { diff --git a/code/espurna/button.h b/code/espurna/button.h index 6be6c441..aa2d3338 100644 --- a/code/espurna/button.h +++ b/code/espurna/button.h @@ -17,10 +17,12 @@ Copyright (C) 2016-2019 by Xose PĂ©rez #include -constexpr size_t ButtonsPresetMax = 8; -constexpr size_t ButtonsMax = 32; +constexpr size_t ButtonsActionMax { 255ul }; -using button_action_t = uint8_t; +constexpr size_t ButtonsPresetMax { 8ul }; +constexpr size_t ButtonsMax { 32ul }; + +using ButtonCustomAction = void(*)(unsigned char id); enum class ButtonProvider : int { None, @@ -39,14 +41,33 @@ enum class button_event_t { TripleClick }; -struct button_actions_t { - button_action_t pressed; - button_action_t released; - button_action_t click; - button_action_t dblclick; - button_action_t lngclick; - button_action_t lnglngclick; - button_action_t trplclick; +// button actions, limited to 8-bit number (0b11111111 / 0xff / 255) + +enum class ButtonAction : uint8_t { + None, + Toggle, + On, + Off, + AccessPoint, + Reset, + Pulse, + FactoryReset, + Wps, + SmartConfig, + BrightnessIncrease, + BrightnessDecrease, + DisplayOn, + Custom +}; + +struct ButtonActions { + ButtonAction pressed; + ButtonAction released; + ButtonAction click; + ButtonAction dblclick; + ButtonAction lngclick; + ButtonAction lnglngclick; + ButtonAction trplclick; }; struct button_event_delays_t { @@ -60,23 +81,25 @@ struct button_event_delays_t { }; struct button_t { - button_t(button_actions_t&& actions, button_event_delays_t&& delays); + button_t(ButtonActions&& actions, button_event_delays_t&& delays); button_t(BasePinPtr&& pin, const debounce_event::types::Config& config, - button_actions_t&& actions, button_event_delays_t&& delays); + ButtonActions&& actions, button_event_delays_t&& delays); bool state(); button_event_t loop(); std::unique_ptr event_emitter; - button_actions_t actions; + ButtonActions actions; button_event_delays_t event_delays; }; BrokerDeclare(ButtonBroker, void(unsigned char id, button_event_t event)); +void buttonSetCustomAction(ButtonCustomAction); + bool buttonState(unsigned char id); -button_action_t buttonAction(unsigned char id, const button_event_t event); +ButtonAction buttonAction(unsigned char id, const button_event_t event); void buttonEvent(unsigned char id, button_event_t event); diff --git a/code/espurna/button_config.h b/code/espurna/button_config.h index 6d78653c..096c7366 100644 --- a/code/espurna/button_config.h +++ b/code/espurna/button_config.h @@ -59,7 +59,7 @@ constexpr int _buttonConfigBitmask(unsigned char index) { ); } -constexpr unsigned char _buttonRelease(unsigned char index) { +constexpr ButtonAction _buttonRelease(unsigned char index) { return ( (index == 0) ? BUTTON1_RELEASE : (index == 1) ? BUTTON2_RELEASE : @@ -72,7 +72,7 @@ constexpr unsigned char _buttonRelease(unsigned char index) { ); } -constexpr unsigned char _buttonPress(unsigned char index) { +constexpr ButtonAction _buttonPress(unsigned char index) { return ( (index == 0) ? BUTTON1_PRESS : (index == 1) ? BUTTON2_PRESS : @@ -85,7 +85,7 @@ constexpr unsigned char _buttonPress(unsigned char index) { ); } -constexpr unsigned char _buttonClick(unsigned char index) { +constexpr ButtonAction _buttonClick(unsigned char index) { return ( (index == 0) ? BUTTON1_CLICK : (index == 1) ? BUTTON2_CLICK : @@ -98,7 +98,7 @@ constexpr unsigned char _buttonClick(unsigned char index) { ); } -constexpr unsigned char _buttonDoubleClick(unsigned char index) { +constexpr ButtonAction _buttonDoubleClick(unsigned char index) { return ( (index == 0) ? BUTTON1_DBLCLICK : (index == 1) ? BUTTON2_DBLCLICK : @@ -111,7 +111,7 @@ constexpr unsigned char _buttonDoubleClick(unsigned char index) { ); } -constexpr unsigned char _buttonTripleClick(unsigned char index) { +constexpr ButtonAction _buttonTripleClick(unsigned char index) { return ( (index == 0) ? BUTTON1_TRIPLECLICK : (index == 1) ? BUTTON2_TRIPLECLICK : @@ -124,7 +124,7 @@ constexpr unsigned char _buttonTripleClick(unsigned char index) { ); } -constexpr unsigned char _buttonLongClick(unsigned char index) { +constexpr ButtonAction _buttonLongClick(unsigned char index) { return ( (index == 0) ? BUTTON1_LNGCLICK : (index == 1) ? BUTTON2_LNGCLICK : @@ -137,7 +137,7 @@ constexpr unsigned char _buttonLongClick(unsigned char index) { ); } -constexpr unsigned char _buttonLongLongClick(unsigned char index) { +constexpr ButtonAction _buttonLongLongClick(unsigned char index) { return ( (index == 0) ? BUTTON1_LNGLNGCLICK : (index == 1) ? BUTTON2_LNGLNGCLICK : diff --git a/code/espurna/config/hardware.h b/code/espurna/config/hardware.h index 56e2eb9d..afc500dd 100644 --- a/code/espurna/config/hardware.h +++ b/code/espurna/config/hardware.h @@ -1014,10 +1014,18 @@ #define IFAN_SUPPORT 1 // These buttons are triggered by the remote + // iFan module adds a custom button handler #define BUTTON1_PIN 0 + #define BUTTON1_CLICK BUTTON_ACTION_TOGGLE + #define BUTTON2_PIN 9 + #define BUTTON2_CLICK BUTTON_ACTION_CUSTOM + #define BUTTON3_PIN 10 + #define BUTTON3_CLICK BUTTON_ACTION_CUSTOM + #define BUTTON4_PIN 14 + #define BUTTON4_CLICK BUTTON_ACTION_CUSTOM // Only one relay by default, controlling the ON / OFF #define RELAY1_PIN 12 diff --git a/code/espurna/config/types.h b/code/espurna/config/types.h index 072fd124..275e1d28 100644 --- a/code/espurna/config/types.h +++ b/code/espurna/config/types.h @@ -29,21 +29,22 @@ // BUTTONS //------------------------------------------------------------------------------ -// button actions, limited to 8-bit number (0b11111111 / 0xff / 255) -#define BUTTON_ACTION_NONE 0u -#define BUTTON_ACTION_TOGGLE 1u -#define BUTTON_ACTION_ON 2u -#define BUTTON_ACTION_OFF 3u -#define BUTTON_ACTION_AP 4u -#define BUTTON_ACTION_RESET 5u -#define BUTTON_ACTION_PULSE 6u -#define BUTTON_ACTION_FACTORY 7u -#define BUTTON_ACTION_WPS 8u -#define BUTTON_ACTION_SMART_CONFIG 9u -#define BUTTON_ACTION_DIM_UP 10u -#define BUTTON_ACTION_DIM_DOWN 11u -#define BUTTON_ACTION_DISPLAY_ON 12u -#define BUTTON_ACTION_MAX 255u +#define BUTTON_ACTION_NONE ButtonAction::None +#define BUTTON_ACTION_TOGGLE ButtonAction::Toggle +#define BUTTON_ACTION_ON ButtonAction::On +#define BUTTON_ACTION_OFF ButtonAction::Off +#define BUTTON_ACTION_AP ButtonAction::AccessPoint +#define BUTTON_ACTION_RESET ButtonAction::Reset +#define BUTTON_ACTION_PULSE ButtonAction::Pulse +#define BUTTON_ACTION_FACTORY ButtonAction::FactoryReset +#define BUTTON_ACTION_WPS ButtonAction::Wps +#define BUTTON_ACTION_SMART_CONFIG ButtonAction::SmartConfig +#define BUTTON_ACTION_DIM_UP ButtonAction::BrightnessIncrease +#define BUTTON_ACTION_DIM_DOWN ButtonAction::BrightnessDecrease +#define BUTTON_ACTION_DISPLAY_ON ButtonAction::DisplayOn +#define BUTTON_ACTION_CUSTOM ButtonAction::Custom + +#define BUTTON_ACTION_MAX ButtonsActionMax // Deprecated: legacy mapping, changed to action from above #define BUTTON_MODE_NONE BUTTON_ACTION_NONE diff --git a/code/espurna/ifan.cpp b/code/espurna/ifan.cpp index 1fca883b..dc989671 100644 --- a/code/espurna/ifan.cpp +++ b/code/espurna/ifan.cpp @@ -101,10 +101,9 @@ constexpr unsigned long DefaultSaveDelay { 1000ul }; constexpr unsigned char DefaultRelayId { 0u }; -constexpr unsigned char DefaultStateButton { 0u }; -constexpr unsigned char DefaultLowButton { 1u }; -constexpr unsigned char DefaultMediumButton { 2u }; -constexpr unsigned char DefaultHighButton { 3u }; +constexpr unsigned char DefaultLowButtonId { 1u }; +constexpr unsigned char DefaultMediumButtonId { 2u }; +constexpr unsigned char DefaultHighButtonId { 3u }; // We expect to write a specific 'mask' via GPIO LOW & HIGH to set the speed // Sync up with the relay and write it on ON / OFF status events @@ -128,10 +127,10 @@ StatePins statePins() { struct Config { unsigned long save { DefaultSaveDelay }; - unsigned char relayId { RELAY_NONE }; - unsigned char buttonLowId { RELAY_NONE }; - unsigned char buttonMediumId { RELAY_NONE }; - unsigned char buttonHighId { RELAY_NONE }; + unsigned char relayId { DefaultRelayId }; + unsigned char buttonLowId { DefaultLowButtonId }; + unsigned char buttonMediumId { DefaultMediumButtonId }; + unsigned char buttonHighId { DefaultHighButtonId }; Speed speed { Speed::Off }; StatePins state_pins; }; @@ -140,9 +139,9 @@ Config readSettings() { return { getSetting("ifanSave", DefaultSaveDelay), getSetting("ifanRelayId", DefaultRelayId), - getSetting("ifanBtnLowId", DefaultMediumButton), - getSetting("ifanBtnLowId", DefaultMediumButton), - getSetting("ifanBtnHighId", DefaultHighButton), + getSetting("ifanBtnLowId", DefaultLowButtonId), + getSetting("ifanBtnLowId", DefaultMediumButtonId), + getSetting("ifanBtnHighId", DefaultHighButtonId), getSetting("ifanSpeed", Speed::Medium) }; } @@ -299,13 +298,7 @@ void setup() { espurnaRegisterReload(configure); #if BUTTON_SUPPORT - ButtonBroker::Register([](unsigned char id, button_event_t event) { - // TODO: add special 'custom' action for buttons, and trigger via basic callback? - // that way we don't depend on the event type and directly trigger with whatever cfg says - if (event != button_event_t::Click) { - return; - } - + buttonSetCustomAction([](unsigned char id) { if (config.buttonLowId == id) { setSpeed(Speed::Low); } else if (config.buttonMediumId == id) {