diff --git a/code/espurna/button.cpp b/code/espurna/button.cpp index 35a2aebd..55601da1 100644 --- a/code/espurna/button.cpp +++ b/code/espurna/button.cpp @@ -3,6 +3,7 @@ BUTTON MODULE Copyright (C) 2016-2019 by Xose Pérez +Copyright (C) 2019-2021 by Maxim Prokhorov */ @@ -30,9 +31,7 @@ Copyright (C) 2016-2019 by Xose Pérez #include "button_config.h" -BrokerBind(ButtonBroker); - -// TODO: if we are using such conversion helpers across the codebase, should convert() be in internal ns? +// ----------------------------------------------------------------------------- namespace settings { namespace internal { @@ -169,93 +168,77 @@ ButtonAction convert(const String& value) { // ----------------------------------------------------------------------------- -constexpr debounce_event::types::Config _buttonDecodeConfigBitmask(int bitmask) { - return { - ((bitmask & ButtonMask::Pushbutton) - ? debounce_event::types::Mode::Pushbutton - : debounce_event::types::Mode::Switch), - ((bitmask & ButtonMask::DefaultLow) ? debounce_event::types::PinValue::Low - : (bitmask & ButtonMask::DefaultHigh) ? debounce_event::types::PinValue::High - : (bitmask & ButtonMask::DefaultBoot) ? debounce_event::types::PinValue::Initial - : debounce_event::types::PinValue::Low), - ((bitmask & ButtonMask::SetPullup) ? debounce_event::types::PinMode::InputPullup - : (bitmask & ButtonMask::SetPulldown) ? debounce_event::types::PinMode::InputPulldown - : debounce_event::types::PinMode::Input) - }; -} - -constexpr ButtonAction _buttonDecodeEventAction(const ButtonActions& actions, button_event_t event) { +constexpr ButtonAction _buttonDecodeEventAction(const ButtonActions& actions, ButtonEvent event) { return ( - (event == button_event_t::Pressed) ? actions.pressed : - (event == button_event_t::Released) ? actions.released : - (event == button_event_t::Click) ? actions.click : - (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 : ButtonAction::None + (event == ButtonEvent::Pressed) ? actions.pressed : + (event == ButtonEvent::Released) ? actions.released : + (event == ButtonEvent::Click) ? actions.click : + (event == ButtonEvent::DoubleClick) ? actions.dblclick : + (event == ButtonEvent::LongClick) ? actions.lngclick : + (event == ButtonEvent::LongLongClick) ? actions.lnglngclick : + (event == ButtonEvent::TripleClick) ? actions.trplclick : ButtonAction::None ); } -constexpr button_event_t _buttonMapReleased(uint8_t count, unsigned long length, unsigned long lngclick_delay, unsigned long lnglngclick_delay) { +constexpr ButtonEvent _buttonMapReleased(uint8_t count, unsigned long length, unsigned long lngclick_delay, unsigned long lnglngclick_delay) { return ( - (0 == count) ? button_event_t::Released : + (0 == count) ? ButtonEvent::Released : (1 == count) ? ( - (length > lnglngclick_delay) ? button_event_t::LongLongClick : - (length > lngclick_delay) ? button_event_t::LongClick : button_event_t::Click + (length > lnglngclick_delay) ? ButtonEvent::LongLongClick : + (length > lngclick_delay) ? ButtonEvent::LongClick : ButtonEvent::Click ) : - (2 == count) ? button_event_t::DoubleClick : - (3 == count) ? button_event_t::TripleClick : - button_event_t::None + (2 == count) ? ButtonEvent::DoubleClick : + (3 == count) ? ButtonEvent::TripleClick : + ButtonEvent::None ); } -ButtonActions _buttonConstructActions(unsigned char index) { +ButtonActions _buttonConstructActions(size_t index) { return { - _buttonPress(index), - _buttonRelease(index), - _buttonClick(index), - _buttonDoubleClick(index), - _buttonLongClick(index), - _buttonLongLongClick(index), - _buttonTripleClick(index) + button::build::press(index), + button::build::release(index), + button::build::click(index), + button::build::doubleClick(index), + button::build::longClick(index), + button::build::longLongClick(index), + button::build::tripleClick(index) }; } -debounce_event::types::Config _buttonRuntimeConfig(unsigned char index) { - const auto config = _buttonDecodeConfigBitmask(_buttonConfigBitmask(index)); +debounce_event::types::Config _buttonRuntimeConfig(size_t index) { return { - getSetting({"btnMode", index}, config.mode), - getSetting({"btnDefVal", index}, config.default_value), - getSetting({"btnPinMode", index}, config.pin_mode) + getSetting({"btnMode", index}, button::build::mode(index)), + getSetting({"btnDefVal", index}, button::build::defaultValue(index)), + getSetting({"btnPinMode", index}, button::build::pinMode(index)) }; } -int _buttonEventNumber(button_event_t event) { +int _buttonEventNumber(ButtonEvent event) { return static_cast(event); } // ----------------------------------------------------------------------------- -button_event_delays_t::button_event_delays_t() : - debounce(_buttonDebounceDelay()), - repeat(_buttonRepeatDelay()), - lngclick(_buttonLongClickDelay()), - lnglngclick(_buttonLongLongClickDelay()) +ButtonEventDelays::ButtonEventDelays() : + debounce(button::build::debounceDelay()), + repeat(button::build::repeatDelay()), + lngclick(button::build::longClickDelay()), + lnglngclick(button::build::longLongClickDelay()) {} -button_event_delays_t::button_event_delays_t(unsigned long debounce, unsigned long repeat, unsigned long lngclick, unsigned long lnglngclick) : +ButtonEventDelays::ButtonEventDelays(unsigned long debounce, unsigned long repeat, unsigned long lngclick, unsigned long lnglngclick) : debounce(debounce), repeat(repeat), lngclick(lngclick), lnglngclick(lnglngclick) {} -button_t::button_t(ButtonActions&& actions_, button_event_delays_t&& delays_) : +button_t::button_t(ButtonActions&& actions_, ButtonEventDelays&& delays_) : actions(std::move(actions_)), event_delays(std::move(delays_)) {} -button_t::button_t(BasePinPtr&& pin, const debounce_event::types::Config& config, ButtonActions&& actions_, button_event_delays_t&& delays_) : +button_t::button_t(BasePinPtr&& pin, const debounce_event::types::Config& config, ButtonActions&& actions_, ButtonEventDelays&& delays_) : event_emitter(std::make_unique(std::move(pin), config, delays_.debounce, delays_.repeat)), actions(std::move(actions_)), event_delays(std::move(delays_)) @@ -265,11 +248,11 @@ bool button_t::state() { return event_emitter->isPressed(); } -button_event_t button_t::loop() { +ButtonEvent button_t::loop() { if (event_emitter) { switch (event_emitter->loop()) { case debounce_event::types::EventPressed: - return button_event_t::Pressed; + return ButtonEvent::Pressed; case debounce_event::types::EventReleased: { return _buttonMapReleased( event_emitter->getEventCount(), @@ -283,146 +266,118 @@ button_event_t button_t::loop() { } } - return button_event_t::None; + return ButtonEvent::None; } std::vector _buttons; // ----------------------------------------------------------------------------- -unsigned char buttonCount() { +size_t buttonCount() { return _buttons.size(); } #if MQTT_SUPPORT std::bitset _buttons_mqtt_send_all( - (1 == BUTTON_MQTT_SEND_ALL_EVENTS) ? 0xFFFFFFFFUL : 0UL + button::build::mqttSendAllEvents() + ? std::numeric_limits::max() + : std::numeric_limits::min() ); std::bitset _buttons_mqtt_retain( - (1 == BUTTON_MQTT_RETAIN) ? 0xFFFFFFFFUL : 0UL + button::build::mqttRetain() + ? std::numeric_limits::max() + : std::numeric_limits::min() ); #endif -#if WEB_SUPPORT - -void _buttonWebSocketOnVisible(JsonObject& root) { - if (buttonCount() > 0) { - root["btnVisible"] = 1; - } -} - -void _buttonWebSocketOnConnected(JsonObject& root) { - root["btnRepDel"] = getSetting("btnRepDel", _buttonRepeatDelay()); - - // XXX: unused! pending webui changes - -#if 0 - if (buttonCount() < 1) return; - - JsonObject& module = root.createNestedObject("btn"); +// ----------------------------------------------------------------------------- - // TODO: hardware can sometimes use a different providers - // 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? +#if RELAY_SUPPORT - JsonArray& schema = module.createNestedArray("_schema"); +std::vector _button_relays; - schema.add("Prov"); +size_t _buttonRelay(size_t id) { + return _button_relays[id]; +} - schema.add("GPIO"); - schema.add("Mode"); - schema.add("DefVal"); - schema.add("PinMode"); +void _buttonRelayAction(size_t id, ButtonAction action) { + auto relayId = _buttonRelay(id); - schema.add("Press"); - schema.add("Click"); - schema.add("Dclk"); - schema.add("Lclk"); - schema.add("LLclk"); - schema.add("Tclk"); + switch (action) { + case ButtonAction::Toggle: + relayToggle(relayId); + break; - schema.add("DebDel"); - schema.add("RepDel"); - schema.add("LclkDel"); - schema.add("LLclkDel"); + case ButtonAction::On: + relayStatus(relayId, true); + break; -#if RELAY_SUPPORT - schema.add("Relay"); -#endif + case ButtonAction::Off: + relayStatus(relayId, false); + break; -#if MQTT_SUPPORT - schema.add("MqttSendAll"); - schema.add("MqttRetain"); -#endif + case ButtonAction::Pulse: + // TODO + break; - JsonArray& buttons = module.createNestedArray("list"); + default: + break; + } +} - for (unsigned char i=0; iBUTTON - button.add(getSetting({"btnProv", index}, _buttonProvider(index))); - if (_buttons[i].pin()) { - button.add(getSetting({"btnGPIO", index}, _buttonPin(index))); - const auto config = _buttonRuntimeConfig(index); - button.add(static_cast(config.mode)); - button.add(static_cast(config.default_value)); - button.add(static_cast(config.pin_mode)); - } else { - button.add(GPIO_NONE); - button.add(static_cast(BUTTON_PUSHBUTTON)); - button.add(0); - button.add(0); - button.add(0); - } +// ----------------------------------------------------------------------------- - button.add(_buttons[i].actions.pressed); - button.add(_buttons[i].actions.click); - button.add(_buttons[i].actions.dblclick); - button.add(_buttons[i].actions.lngclick); - button.add(_buttons[i].actions.lnglngclick); - button.add(_buttons[i].actions.trplclick); +#if WEB_SUPPORT - button.add(_buttons[i].event_delays.debounce); - button.add(_buttons[i].event_delays.repeat); - button.add(_buttons[i].event_delays.lngclick); - button.add(_buttons[i].event_delays.lnglngclick); +namespace { -#if RELAY_SUPPORT - button.add(_buttons[i].relayID); -#endif +void _buttonWebSocketOnVisible(JsonObject& root) { + if (buttonCount()) { + root["btnVisible"] = 1; + } +} - // TODO: send bitmask as number? -#if MQTT_SUPPORT - button.add(_buttons_mqtt_send_all[i] ? 1 : 0); - button.add(_buttons_mqtt_retain[i] ? 1 : 0); -#endif +void _buttonWebSocketOnConnected(JsonObject& root) { + if (buttonCount()) { + root["btnRepDel"] = getSetting("btnRepDel", button::build::repeatDelay()); } -#endif } bool _buttonWebSocketOnKeyCheck(const char * key, JsonVariant&) { return (strncmp(key, "btn", 3) == 0); } +} // namespace + #endif // WEB_SUPPORT -ButtonCustomAction _button_custom_action { nullptr }; +//------------------------------------------------------------------------------ + +ButtonEventHandler _button_custom_action { nullptr }; -void buttonSetCustomAction(ButtonCustomAction action) { - _button_custom_action = action; +void buttonSetCustomAction(ButtonEventHandler handler) { + _button_custom_action = handler; } -bool buttonState(unsigned char id) { +std::forward_list _button_notify_event; + +void buttonSetEventNotify(ButtonEventHandler handler) { + _button_notify_event.push_front(handler); +} + +//------------------------------------------------------------------------------ + +bool buttonState(size_t id) { return (id < _buttons.size()) ? _buttons[id].state() : false; } -ButtonAction buttonAction(unsigned char id, const button_event_t event) { +ButtonAction buttonAction(size_t id, ButtonEvent event) { return (id < _buttons.size()) ? _buttonDecodeEventAction(_buttons[id].actions, event) : ButtonAction::None; @@ -432,93 +387,53 @@ ButtonAction buttonAction(unsigned char id, const button_event_t event) { // (ref. https://github.com/esp8266/Arduino/pull/6950 "PROGMEM footprint cleanup for responseCodeToString") // In this particular case, saves 76 bytes (120 vs 44) -String _buttonEventString(button_event_t event) { +String _buttonEventString(ButtonEvent event) { const __FlashStringHelper* ptr = nullptr; switch (event) { - case button_event_t::Pressed: - ptr = F("pressed"); - break; - case button_event_t::Released: - ptr = F("released"); - break; - case button_event_t::Click: - ptr = F("click"); - break; - case button_event_t::DoubleClick: - ptr = F("double-click"); - break; - case button_event_t::LongClick: - ptr = F("long-click"); - break; - case button_event_t::LongLongClick: - ptr = F("looong-click"); - break; - case button_event_t::TripleClick: - ptr = F("triple-click"); - break; - case button_event_t::None: - ptr = F("none"); - break; - } - return String(ptr); -} - -#if RELAY_SUPPORT - -unsigned char _buttonRelaySetting(unsigned char id) { - static std::vector relays; - - if (!relays.size()) { - relays.reserve(_buttons.size()); - for (unsigned char button = 0; button < _buttons.size(); ++button) { - relays.push_back(getSetting({"btnRelay", button}, _buttonRelay(button))); - } - } - - return relays[id]; -} - -void _buttonRelayAction(unsigned char id, ButtonAction action) { - auto relayId = _buttonRelaySetting(id); - - switch (action) { - case ButtonAction::Toggle: - relayToggle(relayId); + case ButtonEvent::Pressed: + ptr = F("pressed"); break; - - case ButtonAction::On: - relayStatus(relayId, true); + case ButtonEvent::Released: + ptr = F("released"); break; - - case ButtonAction::Off: - relayStatus(relayId, false); + case ButtonEvent::Click: + ptr = F("click"); break; - - case ButtonAction::Pulse: - // TODO + case ButtonEvent::DoubleClick: + ptr = F("double-click"); break; - - default: + case ButtonEvent::LongClick: + ptr = F("long-click"); + break; + case ButtonEvent::LongLongClick: + ptr = F("looong-click"); + break; + case ButtonEvent::TripleClick: + ptr = F("triple-click"); + break; + case ButtonEvent::None: + ptr = F("none"); break; } + return String(ptr); } -#endif // RELAY_SUPPORT - -void buttonEvent(unsigned char id, button_event_t event) { +void buttonEvent(size_t id, ButtonEvent event) { DEBUG_MSG_P(PSTR("[BUTTON] Button #%u event %d (%s)\n"), id, _buttonEventNumber(event), _buttonEventString(event).c_str() ); - if (event == button_event_t::None) return; + + if (event == ButtonEvent::None) { + return; + } auto& button = _buttons[id]; auto action = _buttonDecodeEventAction(button.actions, event); - -#if BROKER_SUPPORT - ButtonBroker::Publish(id, event); -#endif + for (auto& notify : _button_notify_event) { + notify(id, event); + } #if MQTT_SUPPORT if ((action != ButtonAction::None) || _buttons_mqtt_send_all[id]) { @@ -617,24 +532,54 @@ void buttonEvent(unsigned char id, button_event_t event) { } void _buttonConfigure() { - #if MQTT_SUPPORT - for (unsigned char index = 0; index < _buttons.size(); ++index) { - _buttons_mqtt_send_all[index] = getSetting({"btnMqttSendAll", index}, _buttonMqttSendAllEvents(index)); - _buttons_mqtt_retain[index] = getSetting({"btnMqttRetain", index}, _buttonMqttRetain(index)); - } - #endif + auto buttons = _buttons.size(); + +#if RELAY_SUPPORT + _button_relays.clear(); +#endif + + for (decltype(buttons) id = 0; id < buttons; ++id) { +#if RELAY_SUPPORT + _button_relays.push_back(getSetting({"btnRelay", id}, button::build::relay(id))); +#endif +#if MQTT_SUPPORT + _buttons_mqtt_send_all[id] = getSetting({"btnMqttSendAll", id}, button::build::mqttSendAllEvents(id)); + _buttons_mqtt_retain[id] = getSetting({"btnMqttRetain", id}, button::build::mqttRetain(id)); +#endif + } } // TODO: compatibility proxy, fetch global key before indexed -template -unsigned long _buttonGetSetting(const char* key, unsigned char index, T default_value) { - return getSetting({key, index}, getSetting(key, default_value)); +unsigned long _buttonGetDelay(const char* key, size_t index, unsigned long default_value) { + unsigned long result { default_value }; + + bool found { false }; + auto indexed = SettingsKey(key, index); + auto global = String(key); + + settings::kv_store.foreach([&](settings::kvs_type::KeyValueResult&& kv) { + if (found) { + return; + } + + if ((kv.key.length != indexed.length()) && (kv.key.length != global.length())) { + return; + } + + auto other = kv.key.read(); + found = indexed == other; + if (found || (global == other)) { + result = settings::internal::convert(kv.value.read()); + } + }); + + return result; } void buttonLoop() { for (size_t id = 0; id < _buttons.size(); ++id) { auto event = _buttons[id].loop(); - if (event != button_event_t::None) { + if (event != ButtonEvent::None) { buttonEvent(id, event); } } @@ -773,15 +718,15 @@ std::vector AnalogPin::pins; #endif // BUTTON_PROVIDER_ANALOG_SUPPORT -BasePinPtr _buttonGpioPin(unsigned char index, ButtonProvider provider) { +BasePinPtr _buttonGpioPin(size_t index, ButtonProvider provider) { BasePinPtr result; - auto pin = getSetting({"btnGPIO", index}, _buttonPin(index)); + auto pin = getSetting({"btnGpio", index}, button::build::pin(index)); switch (provider) { case ButtonProvider::Gpio: { #if BUTTON_PROVIDER_GPIO_SUPPORT - auto* base = gpioBase(getSetting({"btnGPIOType", index}, _buttonPinType(index))); + auto* base = gpioBase(getSetting({"btnGpioType", index}, button::build::pinType(index))); if (!base) { break; } @@ -801,7 +746,7 @@ BasePinPtr _buttonGpioPin(unsigned char index, ButtonProvider provider) { break; } - auto level = getSetting({"btnLevel", index}, _buttonAnalogLevel(index)); + auto level = getSetting({"btnLevel", index}, button::build::analogLevel(index)); if (!AnalogPin::checkExpectedLevel(level)) { break; } @@ -818,30 +763,30 @@ BasePinPtr _buttonGpioPin(unsigned char index, ButtonProvider provider) { return result; } -ButtonActions _buttonActions(unsigned char index) { +ButtonActions _buttonActions(size_t index) { return { - getSetting({"btnPress", index}, _buttonPress(index)), - getSetting({"btnRlse", index}, _buttonRelease(index)), - getSetting({"btnClick", index}, _buttonClick(index)), - getSetting({"btnDclk", index}, _buttonDoubleClick(index)), - getSetting({"btnLclk", index}, _buttonLongClick(index)), - getSetting({"btnLLclk", index}, _buttonLongLongClick(index)), - getSetting({"btnTclk", index}, _buttonTripleClick(index)) + getSetting({"btnPress", index}, button::build::press(index)), + getSetting({"btnRlse", index}, button::build::release(index)), + getSetting({"btnClick", index}, button::build::click(index)), + getSetting({"btnDclk", index}, button::build::doubleClick(index)), + getSetting({"btnLclk", index}, button::build::longClick(index)), + getSetting({"btnLLclk", index}, button::build::longLongClick(index)), + getSetting({"btnTclk", index}, button::build::tripleClick(index)) }; } // Note that we use settings without indexes as default values to preserve backwards compatibility -button_event_delays_t _buttonDelays(unsigned char index) { +ButtonEventDelays _buttonDelays(size_t index) { return { - _buttonGetSetting("btnDebDel", index, _buttonDebounceDelay(index)), - _buttonGetSetting("btnRepDel", index, _buttonRepeatDelay(index)), - _buttonGetSetting("btnLclkDel", index, _buttonLongClickDelay(index)), - _buttonGetSetting("btnLLclkDel", index, _buttonLongLongClickDelay(index)), + _buttonGetDelay("btnDebDel", index, button::build::debounceDelay(index)), + _buttonGetDelay("btnRepDel", index, button::build::repeatDelay(index)), + _buttonGetDelay("btnLclkDel", index, button::build::longClickDelay(index)), + _buttonGetDelay("btnLLclkDel", index, button::build::longLongClickDelay(index)), }; } -bool _buttonSetupProvider(unsigned char index, ButtonProvider provider) { +bool _buttonSetupProvider(size_t index, ButtonProvider provider) { bool result { false }; switch (provider) { @@ -883,8 +828,8 @@ void _buttonSettingsMigrate(int version) { void buttonSetup() { _buttonSettingsMigrate(migrateVersion()); - for (unsigned char index = 0; index < ButtonsMax; ++index) { - auto provider = getSetting({"btnProv", index}, _buttonProvider(index)); + for (size_t index = 0; index < ButtonsMax; ++index) { + auto provider = getSetting({"btnProv", index}, button::build::provider(index)); if (!_buttonSetupProvider(index, provider)) { break; } @@ -892,9 +837,6 @@ void buttonSetup() { auto count = _buttons.size(); DEBUG_MSG_P(PSTR("[BUTTON] Number of buttons: %u\n"), count); - if (!count) { - return; - } #if TERMINAL_SUPPORT terminalRegisterCommand(F("BUTTON"), [](const terminal::CommandContext& ctx) { @@ -913,19 +855,19 @@ void buttonSetup() { }); #endif - _buttonConfigure(); - - // Websocket Callbacks - #if WEB_SUPPORT + if (count) { +#if WEB_SUPPORT wsRegister() .onVisible(_buttonWebSocketOnVisible) .onConnected(_buttonWebSocketOnConnected) .onKeyCheck(_buttonWebSocketOnKeyCheck); - #endif +#endif - // Register system callbacks - espurnaRegisterLoop(buttonLoop); - espurnaRegisterReload(_buttonConfigure); + _buttonConfigure(); + espurnaRegisterReload(_buttonConfigure); + + espurnaRegisterLoop(buttonLoop); + } } #endif // BUTTON_SUPPORT diff --git a/code/espurna/button.h b/code/espurna/button.h index 3354b684..033dc0f5 100644 --- a/code/espurna/button.h +++ b/code/espurna/button.h @@ -10,8 +10,6 @@ Copyright (C) 2016-2019 by Xose Pérez #include "espurna.h" -#include "broker.h" - #include "libs/BasePin.h" #include "libs/DebounceEvent.h" @@ -28,7 +26,7 @@ enum class ButtonProvider : int { Analog }; -enum class button_event_t { +enum class ButtonEvent { None, Pressed, Released, @@ -71,9 +69,9 @@ struct ButtonActions { ButtonAction trplclick; }; -struct button_event_delays_t { - button_event_delays_t(); - button_event_delays_t(unsigned long debounce, unsigned long repeat, unsigned long lngclick, unsigned long lnglngclick); +struct ButtonEventDelays { + ButtonEventDelays(); + ButtonEventDelays(unsigned long debounce, unsigned long repeat, unsigned long lngclick, unsigned long lnglngclick); unsigned long debounce; unsigned long repeat; @@ -82,28 +80,27 @@ struct button_event_delays_t { }; struct button_t { - button_t(ButtonActions&& actions, button_event_delays_t&& delays); + button_t(ButtonActions&& actions, ButtonEventDelays&& delays); button_t(BasePinPtr&& pin, const debounce_event::types::Config& config, - ButtonActions&& actions, button_event_delays_t&& delays); + ButtonActions&& actions, ButtonEventDelays&& delays); bool state(); - button_event_t loop(); + ButtonEvent loop(); std::unique_ptr event_emitter; ButtonActions actions; - button_event_delays_t event_delays; + ButtonEventDelays event_delays; }; -BrokerDeclare(ButtonBroker, void(unsigned char id, button_event_t event)); - -using ButtonCustomAction = void(*)(unsigned char id, button_event_t event); -void buttonSetCustomAction(ButtonCustomAction); +using ButtonEventHandler = void(*)(size_t id, ButtonEvent event); +void buttonSetCustomAction(ButtonEventHandler); +void buttonSetNotifyAction(ButtonEventHandler); -bool buttonState(unsigned char id); -ButtonAction buttonAction(unsigned char id, const button_event_t event); +bool buttonState(size_t id); +ButtonAction buttonAction(size_t id, const ButtonEvent event); -void buttonEvent(unsigned char id, button_event_t event); +void buttonEvent(size_t id, ButtonEvent event); -unsigned char buttonCount(); +size_t buttonCount(); void buttonSetup(); diff --git a/code/espurna/button_config.h b/code/espurna/button_config.h index 096c7366..dd2feb63 100644 --- a/code/espurna/button_config.h +++ b/code/espurna/button_config.h @@ -20,7 +20,10 @@ constexpr int SetPulldown { 1 << 6 }; } // namespace ButtonMask -constexpr unsigned char _buttonPin(unsigned char index) { +namespace button { +namespace build { + +constexpr size_t pin(size_t index) { return ( (index == 0) ? BUTTON1_PIN : (index == 1) ? BUTTON2_PIN : @@ -33,7 +36,7 @@ constexpr unsigned char _buttonPin(unsigned char index) { ); } -constexpr GpioType _buttonPinType(unsigned char index) { +constexpr GpioType pinType(size_t index) { return ( (index == 0) ? BUTTON1_PIN_TYPE : (index == 1) ? BUTTON2_PIN_TYPE : @@ -46,7 +49,7 @@ constexpr GpioType _buttonPinType(unsigned char index) { ); } -constexpr int _buttonConfigBitmask(unsigned char index) { +constexpr int configBitmask(size_t index) { return ( (index == 0) ? (BUTTON1_CONFIG) : (index == 1) ? (BUTTON2_CONFIG) : @@ -59,7 +62,38 @@ constexpr int _buttonConfigBitmask(unsigned char index) { ); } -constexpr ButtonAction _buttonRelease(unsigned char index) { +namespace internal { + +constexpr debounce_event::types::Config decode(int bitmask) { + return { + ((bitmask & ButtonMask::Pushbutton) + ? debounce_event::types::Mode::Pushbutton + : debounce_event::types::Mode::Switch), + ((bitmask & ButtonMask::DefaultLow) ? debounce_event::types::PinValue::Low + : (bitmask & ButtonMask::DefaultHigh) ? debounce_event::types::PinValue::High + : (bitmask & ButtonMask::DefaultBoot) ? debounce_event::types::PinValue::Initial + : debounce_event::types::PinValue::Low), + ((bitmask & ButtonMask::SetPullup) ? debounce_event::types::PinMode::InputPullup + : (bitmask & ButtonMask::SetPulldown) ? debounce_event::types::PinMode::InputPulldown + : debounce_event::types::PinMode::Input) + }; +} + +} // namespace internal + +constexpr debounce_event::types::Mode mode(size_t index) { + return internal::decode(configBitmask(index)).mode; +} + +constexpr debounce_event::types::PinValue defaultValue(size_t index) { + return internal::decode(configBitmask(index)).default_value; +} + +constexpr debounce_event::types::PinMode pinMode(size_t index) { + return internal::decode(configBitmask(index)).pin_mode; +} + +constexpr ButtonAction release(size_t index) { return ( (index == 0) ? BUTTON1_RELEASE : (index == 1) ? BUTTON2_RELEASE : @@ -72,7 +106,7 @@ constexpr ButtonAction _buttonRelease(unsigned char index) { ); } -constexpr ButtonAction _buttonPress(unsigned char index) { +constexpr ButtonAction press(size_t index) { return ( (index == 0) ? BUTTON1_PRESS : (index == 1) ? BUTTON2_PRESS : @@ -85,7 +119,7 @@ constexpr ButtonAction _buttonPress(unsigned char index) { ); } -constexpr ButtonAction _buttonClick(unsigned char index) { +constexpr ButtonAction click(size_t index) { return ( (index == 0) ? BUTTON1_CLICK : (index == 1) ? BUTTON2_CLICK : @@ -98,7 +132,7 @@ constexpr ButtonAction _buttonClick(unsigned char index) { ); } -constexpr ButtonAction _buttonDoubleClick(unsigned char index) { +constexpr ButtonAction doubleClick(size_t index) { return ( (index == 0) ? BUTTON1_DBLCLICK : (index == 1) ? BUTTON2_DBLCLICK : @@ -111,7 +145,7 @@ constexpr ButtonAction _buttonDoubleClick(unsigned char index) { ); } -constexpr ButtonAction _buttonTripleClick(unsigned char index) { +constexpr ButtonAction tripleClick(size_t index) { return ( (index == 0) ? BUTTON1_TRIPLECLICK : (index == 1) ? BUTTON2_TRIPLECLICK : @@ -124,7 +158,7 @@ constexpr ButtonAction _buttonTripleClick(unsigned char index) { ); } -constexpr ButtonAction _buttonLongClick(unsigned char index) { +constexpr ButtonAction longClick(size_t index) { return ( (index == 0) ? BUTTON1_LNGCLICK : (index == 1) ? BUTTON2_LNGCLICK : @@ -137,7 +171,7 @@ constexpr ButtonAction _buttonLongClick(unsigned char index) { ); } -constexpr ButtonAction _buttonLongLongClick(unsigned char index) { +constexpr ButtonAction longLongClick(size_t index) { return ( (index == 0) ? BUTTON1_LNGLNGCLICK : (index == 1) ? BUTTON2_LNGLNGCLICK : @@ -150,7 +184,7 @@ constexpr ButtonAction _buttonLongLongClick(unsigned char index) { ); } -constexpr unsigned char _buttonRelay(unsigned char index) { +constexpr size_t relay(size_t index) { return ( (index == 0) ? (BUTTON1_RELAY - 1) : (index == 1) ? (BUTTON2_RELAY - 1) : @@ -163,11 +197,11 @@ constexpr unsigned char _buttonRelay(unsigned char index) { ); } -constexpr unsigned long _buttonDebounceDelay() { +constexpr unsigned long debounceDelay() { return BUTTON_DEBOUNCE_DELAY; } -constexpr unsigned long _buttonDebounceDelay(unsigned char index) { +constexpr unsigned long debounceDelay(size_t index) { return ( (index == 0) ? BUTTON1_DEBOUNCE_DELAY : (index == 1) ? BUTTON2_DEBOUNCE_DELAY : @@ -176,15 +210,15 @@ constexpr unsigned long _buttonDebounceDelay(unsigned char index) { (index == 4) ? BUTTON5_DEBOUNCE_DELAY : (index == 5) ? BUTTON6_DEBOUNCE_DELAY : (index == 6) ? BUTTON7_DEBOUNCE_DELAY : - (index == 7) ? BUTTON8_DEBOUNCE_DELAY : _buttonDebounceDelay() + (index == 7) ? BUTTON8_DEBOUNCE_DELAY : debounceDelay() ); } -constexpr unsigned long _buttonRepeatDelay() { +constexpr unsigned long repeatDelay() { return BUTTON_REPEAT_DELAY; } -constexpr unsigned long _buttonRepeatDelay(unsigned char index) { +constexpr unsigned long repeatDelay(size_t index) { return ( (index == 0) ? BUTTON1_REPEAT_DELAY : (index == 1) ? BUTTON2_REPEAT_DELAY : @@ -193,15 +227,15 @@ constexpr unsigned long _buttonRepeatDelay(unsigned char index) { (index == 4) ? BUTTON5_REPEAT_DELAY : (index == 5) ? BUTTON6_REPEAT_DELAY : (index == 6) ? BUTTON7_REPEAT_DELAY : - (index == 7) ? BUTTON8_REPEAT_DELAY : _buttonRepeatDelay() + (index == 7) ? BUTTON8_REPEAT_DELAY : repeatDelay() ); } -constexpr unsigned long _buttonLongClickDelay() { +constexpr unsigned long longClickDelay() { return BUTTON_LNGCLICK_DELAY; } -constexpr unsigned long _buttonLongClickDelay(unsigned char index) { +constexpr unsigned long longClickDelay(size_t index) { return ( (index == 0) ? BUTTON1_LNGCLICK_DELAY : (index == 1) ? BUTTON2_LNGCLICK_DELAY : @@ -210,15 +244,15 @@ constexpr unsigned long _buttonLongClickDelay(unsigned char index) { (index == 4) ? BUTTON5_LNGCLICK_DELAY : (index == 5) ? BUTTON6_LNGCLICK_DELAY : (index == 6) ? BUTTON7_LNGCLICK_DELAY : - (index == 7) ? BUTTON8_LNGCLICK_DELAY : _buttonLongClickDelay() + (index == 7) ? BUTTON8_LNGCLICK_DELAY : longClickDelay() ); } -constexpr unsigned long _buttonLongLongClickDelay() { +constexpr unsigned long longLongClickDelay() { return BUTTON_LNGLNGCLICK_DELAY; } -constexpr unsigned long _buttonLongLongClickDelay(unsigned char index) { +constexpr unsigned long longLongClickDelay(size_t index) { return ( (index == 0) ? BUTTON1_LNGLNGCLICK_DELAY : (index == 1) ? BUTTON2_LNGLNGCLICK_DELAY : @@ -227,11 +261,15 @@ constexpr unsigned long _buttonLongLongClickDelay(unsigned char index) { (index == 4) ? BUTTON5_LNGLNGCLICK_DELAY : (index == 5) ? BUTTON6_LNGLNGCLICK_DELAY : (index == 6) ? BUTTON7_LNGLNGCLICK_DELAY : - (index == 7) ? BUTTON8_LNGLNGCLICK_DELAY : _buttonLongLongClickDelay() + (index == 7) ? BUTTON8_LNGLNGCLICK_DELAY : longLongClickDelay() ); } -constexpr bool _buttonMqttSendAllEvents(unsigned char index) { +constexpr bool mqttSendAllEvents() { + return (1 == BUTTON_MQTT_SEND_ALL_EVENTS); +} + +constexpr bool mqttSendAllEvents(size_t index) { return ( (index == 0) ? (1 == BUTTON1_MQTT_SEND_ALL_EVENTS) : (index == 1) ? (1 == BUTTON2_MQTT_SEND_ALL_EVENTS) : @@ -240,11 +278,15 @@ constexpr bool _buttonMqttSendAllEvents(unsigned char index) { (index == 4) ? (1 == BUTTON5_MQTT_SEND_ALL_EVENTS) : (index == 5) ? (1 == BUTTON6_MQTT_SEND_ALL_EVENTS) : (index == 6) ? (1 == BUTTON7_MQTT_SEND_ALL_EVENTS) : - (index == 7) ? (1 == BUTTON8_MQTT_SEND_ALL_EVENTS) : (1 == BUTTON_MQTT_SEND_ALL_EVENTS) + (index == 7) ? (1 == BUTTON8_MQTT_SEND_ALL_EVENTS) : mqttSendAllEvents() ); } -constexpr bool _buttonMqttRetain(unsigned char index) { +constexpr bool mqttRetain() { + return (1 == BUTTON_MQTT_RETAIN); +} + +constexpr bool mqttRetain(size_t index) { return ( (index == 0) ? (1 == BUTTON1_MQTT_RETAIN) : (index == 1) ? (1 == BUTTON2_MQTT_RETAIN) : @@ -253,11 +295,11 @@ constexpr bool _buttonMqttRetain(unsigned char index) { (index == 4) ? (1 == BUTTON5_MQTT_RETAIN) : (index == 5) ? (1 == BUTTON6_MQTT_RETAIN) : (index == 6) ? (1 == BUTTON7_MQTT_RETAIN) : - (index == 7) ? (1 == BUTTON8_MQTT_RETAIN) : (1 == BUTTON_MQTT_RETAIN) + (index == 7) ? (1 == BUTTON8_MQTT_RETAIN) : mqttRetain() ); } -constexpr ButtonProvider _buttonProvider(unsigned char index) { +constexpr ButtonProvider provider(size_t index) { return ( (index == 0) ? (BUTTON1_PROVIDER) : (index == 1) ? (BUTTON2_PROVIDER) : @@ -270,7 +312,7 @@ constexpr ButtonProvider _buttonProvider(unsigned char index) { ); } -constexpr int _buttonAnalogLevel(unsigned char index) { +constexpr int analogLevel(size_t index) { return ( (index == 0) ? (BUTTON1_ANALOG_LEVEL) : (index == 1) ? (BUTTON2_ANALOG_LEVEL) : @@ -282,3 +324,6 @@ constexpr int _buttonAnalogLevel(unsigned char index) { (index == 7) ? (BUTTON8_ANALOG_LEVEL) : 0 ); } + +} // namespace build +} // namespace button diff --git a/code/espurna/config/types.h b/code/espurna/config/types.h index d2a5e4bd..7ea49fb8 100644 --- a/code/espurna/config/types.h +++ b/code/espurna/config/types.h @@ -199,16 +199,16 @@ // LED //------------------------------------------------------------------------------ -#define LED_MODE_MANUAL 0 // LED will be managed manually (OFF by default) -#define LED_MODE_WIFI 1 // LED will blink according to the WIFI status -#define LED_MODE_FOLLOW 2 // LED will follow state of linked LED#_RELAY relay ID -#define LED_MODE_FOLLOW_INVERSE 3 // LED will follow the opposite state of linked LED#_RELAY relay ID -#define LED_MODE_FINDME 4 // LED will be ON if all relays are OFF -#define LED_MODE_FINDME_WIFI 5 // A mixture between WIFI and FINDME -#define LED_MODE_ON 6 // LED always ON -#define LED_MODE_OFF 7 // LED always OFF -#define LED_MODE_RELAY 8 // If any relay is ON, LED will be ON, otherwise OFF -#define LED_MODE_RELAY_WIFI 9 // A mixture between WIFI and RELAY, the reverse of MIXED +#define LED_MODE_MANUAL LedMode::Manual // LED will be managed manually (OFF by default) +#define LED_MODE_WIFI LedMode::WiFi // LED will blink according to the WIFI status +#define LED_MODE_FOLLOW LedMode::Follow // LED will follow state of linked LED#_RELAY relay ID +#define LED_MODE_FOLLOW_INVERSE LedMode::FollowInverse // LED will follow the opposite state of linked LED#_RELAY relay ID +#define LED_MODE_FINDME LedMode::FindMe // LED will be ON if all relays are OFF +#define LED_MODE_FINDME_WIFI LedMode::FindMeWiFi // A mixture between WIFI and FINDME +#define LED_MODE_ON LedMode::On // LED always ON +#define LED_MODE_OFF LedMode::Off // LED always OFF +#define LED_MODE_RELAY LedMode::Relay // If any relay is ON, LED will be ON, otherwise OFF +#define LED_MODE_RELAY_WIFI LedMode::RelayWiFi // A mixture between WIFI and RELAY, the reverse of MIXED // ----------------------------------------------------------------------------- // UI diff --git a/code/espurna/led.cpp b/code/espurna/led.cpp index 1f116ac7..f06ba733 100644 --- a/code/espurna/led.cpp +++ b/code/espurna/led.cpp @@ -3,6 +3,7 @@ LED MODULE Copyright (C) 2016-2019 by Xose Pérez +Copyright (C) 2019-2021 by Maxim Prokhorov */ @@ -20,25 +21,18 @@ Copyright (C) 2016-2019 by Xose Pérez #include "led_pattern.h" #include "led_config.h" -// LED helper class - -led_t::led_t(unsigned char pin_, bool inverse_, unsigned char mode_, unsigned char relayID_) : - pin(pin_), - inverse(inverse_), - mode(mode_), - relayID(relayID_) -{ - pinMode(pin, OUTPUT); +void led_t::init() { + pinMode(_pin, OUTPUT); status(false); } bool led_t::status() { - bool result = digitalRead(pin); - return inverse ? !result : result; + bool result = digitalRead(_pin); + return _inverse ? !result : result; } bool led_t::status(bool new_status) { - digitalWrite(pin, inverse ? !new_status : new_status); + digitalWrite(_pin, _inverse ? !new_status : new_status); return new_status; } @@ -46,50 +40,34 @@ bool led_t::toggle() { return status(!status()); } -led_delay_t::led_delay_t(unsigned long on_ms, unsigned long off_ms, unsigned char repeats) : - type(repeats ? led_delay_mode_t::Finite : led_delay_mode_t::Infinite), - on(microsecondsToClockCycles(on_ms * 1000)), - off(microsecondsToClockCycles(off_ms * 1000)), - repeats(repeats ? repeats : 0) -{} - -led_delay_t::led_delay_t(unsigned long on_ms, unsigned long off_ms) : - led_delay_t(on_ms, off_ms, 0) -{} - -led_pattern_t::led_pattern_t(const std::vector& delays) : +LedPattern::LedPattern(const LedPattern::Delays& delays) : delays(delays), queue(), clock_last(ESP.getCycleCount()), - clock_delay(delays.size() ? delays.back().on : 0) + clock_delay(delays.size() ? delays.back().on() : 0) {} -bool led_pattern_t::started() { +bool LedPattern::started() { return queue.size() > 0; } -bool led_pattern_t::ready() { +bool LedPattern::ready() { return delays.size() > 0; } -void led_pattern_t::start() { +void LedPattern::start() { clock_last = ESP.getCycleCount(); clock_delay = 0; - queue = { - delays.rbegin(), delays.rend() - }; + queue = { delays.rbegin(), delays.rend() }; } -void led_pattern_t::stop() { +void LedPattern::stop() { queue.clear(); } -// For relay-based modes -bool _led_update = false; - // For network-based modes, cycle ON & OFF (time in milliseconds) // XXX: internals convert these to clock cycles, delay cannot be longer than 25000 / 50000 ms -const led_delay_t _ledDelays[] { +static const LedDelay _ledDelays[] { {100, 100}, // Autoconfig {100, 4900}, // Connected {4900, 100}, // Connected (inverse) @@ -98,30 +76,85 @@ const led_delay_t _ledDelays[] { {500, 500} // Idle }; +enum class LedDelayName { + NetworkAutoconfig, + NetworkConnected, + NetworkConnectedInverse, + NetworkConfig, + NetworkConfigInverse, + NetworkIdle, + None +}; + +bool _led_update { false }; + std::vector _leds; // ----------------------------------------------------------------------------- -unsigned char ledCount() { +namespace settings { +namespace internal { + +template <> +LedMode convert(const String& value) { + if (value.length() == 1) { + switch (*value.c_str()) { + case '0': + return LedMode::Manual; + case '1': + return LedMode::WiFi; +#if RELAY_SUPPORT + case '2': + return LedMode::Follow; + case '3': + return LedMode::FollowInverse; + case '4': + return LedMode::FindMe; + case '5': + return LedMode::FindMeWiFi; +#endif + case '6': + return LedMode::On; + case '7': + return LedMode::Off; +#if RELAY_SUPPORT + case '8': + return LedMode::Relay; + case '9': + return LedMode::RelayWiFi; +#endif + } + } + + return LedMode::Manual; +} + +} // namespace internal +} // namespace settings + +// ----------------------------------------------------------------------------- + +size_t ledCount() { return _leds.size(); } bool _ledStatus(led_t& led) { - return led.pattern.started() || led.status(); + return led.started() || led.status(); } bool _ledStatus(led_t& led, bool status) { bool result = false; // when led has pattern, status depends on whether it's running - if (led.pattern.ready()) { + auto& pattern = led.pattern(); + if (pattern.ready()) { if (status) { - if (!led.pattern.started()) { - led.pattern.start(); + if (!pattern.started()) { + pattern.start(); } result = true; } else { - led.pattern.stop(); + pattern.stop(); led.status(false); result = false; } @@ -137,63 +170,71 @@ bool _ledToggle(led_t& led) { return _ledStatus(led, !_ledStatus(led)); } -bool ledStatus(unsigned char id, bool status) { - if (id >= ledCount()) return false; - return _ledStatus(_leds[id], status); +bool ledStatus(size_t id, bool status) { + if (id < ledCount()) { + return _ledStatus(_leds[id], status); + } + + return status; } -bool ledStatus(unsigned char id) { - if (id >= ledCount()) return false; - return _ledStatus(_leds[id]); +bool ledStatus(size_t id) { + if (id < ledCount()) { + return _ledStatus(_leds[id]); + } + + return false; } -const led_delay_t& _ledModeToDelay(LedMode mode) { +const LedDelay& _ledDelayFromName(LedDelayName pattern) { static_assert( - (sizeof(_ledDelays) / sizeof(_ledDelays[0])) <= static_cast(LedMode::None), - "LedMode mapping out-of-bounds" + (sizeof(_ledDelays) / sizeof(_ledDelays[0])) <= static_cast(LedDelayName::None), + "Out-of-bounds" ); - return _ledDelays[static_cast(mode)]; + return _ledDelays[static_cast(pattern)]; } void _ledPattern(led_t& led) { const auto clock_current = ESP.getCycleCount(); - if (clock_current - led.pattern.clock_last >= led.pattern.clock_delay) { + + auto& pattern = led.pattern(); + if (clock_current - pattern.clock_last >= pattern.clock_delay) { const bool status = led.toggle(); - auto& current = led.pattern.queue.back(); - switch (current.type) { - case led_delay_mode_t::Finite: - if (status && !--current.repeats) { - led.pattern.queue.pop_back(); - if (!led.pattern.queue.size()) { - led.status(false); - return; - } + + auto& current = pattern.queue.back(); + switch (current.mode()) { + case LedDelayMode::Finite: + if (status && current.repeat()) { + pattern.queue.pop_back(); + if (!pattern.queue.size()) { + led.status(false); + return; } - break; - case led_delay_mode_t::Infinite: - case led_delay_mode_t::None: - default: - break; + } + break; + case LedDelayMode::Infinite: + case LedDelayMode::None: + break; } - led.pattern.clock_delay = status ? current.on : current.off; - led.pattern.clock_last = ESP.getCycleCount(); + pattern.clock_delay = status ? current.on() : current.off(); + pattern.clock_last = ESP.getCycleCount(); } } -void _ledBlink(led_t& led, const led_delay_t& delays) { +void _ledBlink(led_t& led, const LedDelay& delays) { static auto clock_last = ESP.getCycleCount(); - static auto delay_for = delays.on; + static auto delay_for = delays.on(); const auto clock_current = ESP.getCycleCount(); if (clock_current - clock_last >= delay_for) { - delay_for = led.toggle() ? delays.on : delays.off; + delay_for = led.toggle() ? delays.on() : delays.off(); clock_last = clock_current; } } -inline void _ledBlink(led_t& led, const LedMode mode) { - _ledBlink(led, _ledModeToDelay(mode)); +inline void _ledBlink(led_t& led, LedDelayName name) { + _ledBlink(led, _ledDelayFromName(name)); } #if WEB_SUPPORT @@ -216,73 +257,87 @@ void _ledWebSocketOnConnected(JsonObject& root) { schema.add("GPIO"); schema.add("Inv"); schema.add("Mode"); +#if RELAY_SUPPORT schema.add("Relay"); +#endif JsonArray& leds = module.createNestedArray("list"); - for (unsigned char index = 0; index < ledCount(); ++index) { + for (size_t index = 0; index < ledCount(); ++index) { JsonArray& led = leds.createNestedArray(); - led.add(getSetting({"ledGPIO", index}, _ledPin(index))); - led.add(static_cast(getSetting({"ledInv", index}, _ledInverse(index)))); - led.add(getSetting({"ledMode", index}, _ledMode(index))); - led.add(getSetting({"ledRelay", index}, _ledRelay(index))); + led.add(getSetting({"ledGpio", index}, led::build::pin(index))); + led.add(static_cast(getSetting({"ledInv", index}, led::build::inverse(index)))); + led.add(static_cast(getSetting({"ledMode", index}, led::build::mode(index)))); +#if RELAY_SUPPORT + led.add(getSetting({"ledRelay", index}, led::build::relay(index))); +#endif } } #endif #if MQTT_SUPPORT -void _ledMQTTCallback(unsigned int type, const char * topic, const char * payload) { - +void _ledMQTTCallback(unsigned int type, const char* topic, const char* payload) { if (type == MQTT_CONNECT_EVENT) { char buffer[strlen(MQTT_TOPIC_LED) + 3]; snprintf_P(buffer, sizeof(buffer), PSTR("%s/+"), MQTT_TOPIC_LED); mqttSubscribe(buffer); + return; } + // Only want `led/+/` + // We get the led ID from the `+` if (type == MQTT_MESSAGE_EVENT) { - - // Only want `led/+/` const String magnitude = mqttMagnitude((char *) topic); - if (!magnitude.startsWith(MQTT_TOPIC_LED)) return; + if (!magnitude.startsWith(MQTT_TOPIC_LED)) { + return; + } - // Get led ID from after the slash when t is `led/` - unsigned int ledID = magnitude.substring(strlen(MQTT_TOPIC_LED) + 1).toInt(); - if (ledID >= ledCount()) { - DEBUG_MSG_P(PSTR("[LED] Wrong ledID (%d)\n"), ledID); + size_t ledID; + if (!tryParseId(magnitude.substring(strlen(MQTT_TOPIC_LED) + 1).c_str(), ledCount, ledID)) { return; } - // Check if LED is managed - if (_leds[ledID].mode != LED_MODE_MANUAL) return; + auto& led = _leds[ledID]; + if (led.mode() != LED_MODE_MANUAL) { + return; + } - // Get value based on rpc payload logic (see rpc.ino) const auto value = rpcParsePayload(payload); switch (value) { - case PayloadStatus::On: - case PayloadStatus::Off: - _ledStatus(_leds[ledID], (value == PayloadStatus::On)); - break; - case PayloadStatus::Toggle: - _ledToggle(_leds[ledID]); - break; - case PayloadStatus::Unknown: - default: - _ledLoadPattern(_leds[ledID], payload); - _ledStatus(_leds[ledID], true); - break; + case PayloadStatus::On: + case PayloadStatus::Off: + _ledStatus(led, (value == PayloadStatus::On)); + break; + case PayloadStatus::Toggle: + _ledToggle(led); + break; + case PayloadStatus::Unknown: + default: + _ledLoadPattern(led, payload); + _ledStatus(led, true); + break; } - } - } + +#endif + +#if RELAY_SUPPORT +std::vector _led_relays; #endif void _ledConfigure() { - for (unsigned char id = 0; id < _leds.size(); ++id) { - _leds[id].mode = getSetting({"ledMode", id}, _ledMode(id)); - _leds[id].relayID = getSetting({"ledRelay", id}, _ledRelay(id)); - _leds[id].pattern.stop(); +#if RELAY_SUPPORT + _led_relays.resize(relayCount(), RelaysMax); +#endif + + for (size_t id = 0; id < _leds.size(); ++id) { +#if RELAY_SUPPORT + _led_relays[id] = getSetting({"ledRelay", id}, led::build::relay(id)); +#endif + _leds[id].mode(getSetting({"ledMode", id}, led::build::mode(id))); + _leds[id].stop(); _ledLoadPattern(_leds[id], getSetting({"ledPattern", id}).c_str()); } _led_update = true; @@ -295,21 +350,25 @@ void ledUpdate(bool do_update) { } void ledLoop() { - const auto wifi_state = wifiState(); - for (auto& led : _leds) { + for (size_t id = 0; id < _leds.size(); ++id) { + auto& led = _leds[id]; + + switch (led.mode()) { + + case LED_MODE_MANUAL: + break; - switch (led.mode) { case LED_MODE_WIFI: if ((wifi_state & WIFI_STATE_WPS) || (wifi_state & WIFI_STATE_SMARTCONFIG)) { - _ledBlink(led, LedMode::NetworkAutoconfig); + _ledBlink(led, LedDelayName::NetworkAutoconfig); } else if (wifi_state & WIFI_STATE_STA) { - _ledBlink(led, LedMode::NetworkConnected); + _ledBlink(led, LedDelayName::NetworkConnected); } else if (wifi_state & WIFI_STATE_AP) { - _ledBlink(led, LedMode::NetworkConfig); + _ledBlink(led, LedDelayName::NetworkConfig); } else { - _ledBlink(led, LedMode::NetworkIdle); + _ledBlink(led, LedDelayName::NetworkIdle); } break; @@ -317,60 +376,60 @@ void ledLoop() { case LED_MODE_FINDME_WIFI: if ((wifi_state & WIFI_STATE_WPS) || (wifi_state & WIFI_STATE_SMARTCONFIG)) { - _ledBlink(led, LedMode::NetworkAutoconfig); + _ledBlink(led, LedDelayName::NetworkAutoconfig); } else if (wifi_state & WIFI_STATE_STA) { - if (relayStatus(led.relayID)) { - _ledBlink(led, LedMode::NetworkConnected); + if (relayStatus(_led_relays[id])) { + _ledBlink(led, LedDelayName::NetworkConnected); } else { - _ledBlink(led, LedMode::NetworkConnectedInverse); + _ledBlink(led, LedDelayName::NetworkConnectedInverse); } } else if (wifi_state & WIFI_STATE_AP) { - if (relayStatus(led.relayID)) { - _ledBlink(led, LedMode::NetworkConfig); + if (relayStatus(_led_relays[id])) { + _ledBlink(led, LedDelayName::NetworkConfig); } else { - _ledBlink(led, LedMode::NetworkConfigInverse); + _ledBlink(led, LedDelayName::NetworkConfigInverse); } } else { - _ledBlink(led, LedMode::NetworkIdle); + _ledBlink(led, LedDelayName::NetworkIdle); } break; case LED_MODE_RELAY_WIFI: if ((wifi_state & WIFI_STATE_WPS) || (wifi_state & WIFI_STATE_SMARTCONFIG)) { - _ledBlink(led, LedMode::NetworkAutoconfig); + _ledBlink(led, LedDelayName::NetworkAutoconfig); } else if (wifi_state & WIFI_STATE_STA) { - if (relayStatus(led.relayID)) { - _ledBlink(led, LedMode::NetworkConnected); + if (relayStatus(_led_relays[id])) { + _ledBlink(led, LedDelayName::NetworkConnected); } else { - _ledBlink(led, LedMode::NetworkConnectedInverse); + _ledBlink(led, LedDelayName::NetworkConnectedInverse); } } else if (wifi_state & WIFI_STATE_AP) { - if (relayStatus(led.relayID)) { - _ledBlink(led, LedMode::NetworkConfig); + if (relayStatus(_led_relays[id])) { + _ledBlink(led, LedDelayName::NetworkConfig); } else { - _ledBlink(led, LedMode::NetworkConfigInverse); + _ledBlink(led, LedDelayName::NetworkConfigInverse); } } else { - _ledBlink(led, LedMode::NetworkIdle); + _ledBlink(led, LedDelayName::NetworkIdle); } break; case LED_MODE_FOLLOW: if (!_led_update) break; - _ledStatus(led, relayStatus(led.relayID)); + _ledStatus(led, relayStatus(_led_relays[id])); break; case LED_MODE_FOLLOW_INVERSE: if (!_led_update) break; - led.status(!relayStatus(led.relayID)); - _ledStatus(led, !relayStatus(led.relayID)); + led.status(!relayStatus(_led_relays[id])); + _ledStatus(led, !relayStatus(_led_relays[id])); break; case LED_MODE_FINDME: { if (!_led_update) break; bool status = true; - for (unsigned char relayID = 0; relayID < relayCount(); ++relayID) { - if (relayStatus(relayID)) { + for (size_t relayId = 0; relayId < relayCount(); ++relayId) { + if (relayStatus(relayId)) { status = false; break; } @@ -382,8 +441,8 @@ void ledLoop() { case LED_MODE_RELAY: { if (!_led_update) break; bool status = false; - for (unsigned char relayID = 0; relayID < relayCount(); ++relayID) { - if (relayStatus(relayID)) { + for (size_t relayId = 0; relayId < relayCount(); ++relayId) { + if (relayStatus(relayId)) { status = true; break; } @@ -406,7 +465,7 @@ void ledLoop() { } - if (led.pattern.started()) { + if (led.started()) { _ledPattern(led); continue; } @@ -430,75 +489,45 @@ void _ledSettingsMigrate(int version) { void ledSetup() { _ledSettingsMigrate(migrateVersion()); + _leds.reserve(led::build::preconfiguredLeds()); - size_t leds = 0; - - #if LED1_PIN != GPIO_NONE - ++leds; - #endif - #if LED2_PIN != GPIO_NONE - ++leds; - #endif - #if LED3_PIN != GPIO_NONE - ++leds; - #endif - #if LED4_PIN != GPIO_NONE - ++leds; - #endif - #if LED5_PIN != GPIO_NONE - ++leds; - #endif - #if LED6_PIN != GPIO_NONE - ++leds; - #endif - #if LED7_PIN != GPIO_NONE - ++leds; - #endif - #if LED8_PIN != GPIO_NONE - ++leds; - #endif - - _leds.reserve(leds); - - for (unsigned char index=0; index < LedsMax; ++index) { - const auto pin = getSetting({"ledGPIO", index}, _ledPin(index)); + for (size_t index = 0; index < LedsMax; ++index) { + const auto pin = getSetting({"ledGpio", index}, led::build::pin(index)); if (!gpioLock(pin)) { break; } - _leds.emplace_back( - pin, - getSetting({"ledInv", index}, _ledInverse(index)), - getSetting({"ledMode", index}, _ledMode(index)), - getSetting({"ledRelay", index}, _ledRelay(index)) - ); + _leds.emplace_back(pin, + getSetting({"ledInv", index}, led::build::inverse(index)), + getSetting({"ledMode", index}, led::build::mode(index))); } - _led_update = true; + auto leds = _leds.size(); + + DEBUG_MSG_P(PSTR("[LED] Number of leds: %u\n"), leds); + if (leds) { + _ledConfigure(); - #if MQTT_SUPPORT +#if MQTT_SUPPORT mqttRegister(_ledMQTTCallback); - #endif +#endif - #if WEB_SUPPORT +#if WEB_SUPPORT wsRegister() .onVisible(_ledWebSocketOnVisible) .onConnected(_ledWebSocketOnConnected) .onKeyCheck(_ledWebSocketOnKeyCheck); - #endif +#endif - #if RELAY_SUPPORT +#if RELAY_SUPPORT relaySetStatusNotify([](size_t, bool) { ledUpdate(true); }); - #endif - - DEBUG_MSG_P(PSTR("[LED] Number of leds: %d\n"), _leds.size()); - - // Main callbacks - espurnaRegisterLoop(ledLoop); - espurnaRegisterReload(_ledConfigure); +#endif + espurnaRegisterLoop(ledLoop); + espurnaRegisterReload(_ledConfigure); + } } diff --git a/code/espurna/led.h b/code/espurna/led.h index 5acf38ae..6da0ebf6 100644 --- a/code/espurna/led.h +++ b/code/espurna/led.h @@ -12,38 +12,77 @@ Copyright (C) 2016-2019 by Xose Pérez #include -constexpr size_t LedsMax = 8; +constexpr size_t LedsMax { 8ul }; enum class LedMode { - NetworkAutoconfig, - NetworkConnected, - NetworkConnectedInverse, - NetworkConfig, - NetworkConfigInverse, - NetworkIdle, - None + Manual, + WiFi, + Follow, + FollowInverse, + FindMe, + FindMeWiFi, + On, + Off, + Relay, + RelayWiFi }; -enum class led_delay_mode_t { +enum class LedDelayMode { Finite, Infinite, None }; -struct led_delay_t { - led_delay_t() = delete; - led_delay_t(unsigned long on_ms, unsigned long off_ms); - led_delay_t(unsigned long on_ms, unsigned long off_ms, unsigned char repeats); - - led_delay_mode_t type; - unsigned long on; - unsigned long off; - unsigned char repeats; +struct LedDelay { + LedDelay() = delete; + + constexpr LedDelay(unsigned long on_ms, unsigned long off_ms, unsigned char repeats) : + _mode(repeats ? LedDelayMode::Finite : LedDelayMode::Infinite), + _on(microsecondsToClockCycles(on_ms * 1000)), + _off(microsecondsToClockCycles(off_ms * 1000)), + _repeats(repeats) + {} + + constexpr LedDelay(unsigned long on_ms, unsigned long off_ms) : + LedDelay(on_ms, off_ms, 0) + {} + + LedDelayMode mode() const { + return _mode; + } + + unsigned long on() const { + return _on; + } + + unsigned long off() const { + return _off; + } + + unsigned char repeats() const { + return _repeats; + } + + bool repeat() { + if (_repeats) { + --_repeats; + } + + return _repeats; + } + +private: + LedDelayMode _mode; + unsigned long _on; + unsigned long _off; + unsigned char _repeats; }; -struct led_pattern_t { - led_pattern_t() = default; - led_pattern_t(const std::vector& delays); +struct LedPattern { + using Delays = std::vector; + + LedPattern() = default; + LedPattern(const Delays& delays); void start(); void stop(); @@ -51,32 +90,71 @@ struct led_pattern_t { bool started(); bool ready(); - std::vector delays; - std::vector queue; + Delays delays; + Delays queue; unsigned long clock_last; unsigned long clock_delay; }; struct led_t { led_t() = delete; - led_t(unsigned char pin, bool inverse, unsigned char mode, unsigned char relayID); + led_t(unsigned char pin, bool inverse, LedMode mode) : + _pin(pin), + _inverse(inverse), + _mode(mode) + { + init(); + } + + unsigned char pin() const { + return _pin; + } + + LedMode mode() const { + return _mode; + } + + void mode(LedMode mode) { + _mode = mode; + } + + bool inverse() const { + return _inverse; + } + + LedPattern& pattern() { + return _pattern; + } + + void init(); + + void start() { + _pattern.stop(); + } + + bool started() { + return _pattern.started(); + } + + void stop() { + _pattern.stop(); + } bool status(); bool status(bool new_status); bool toggle(); - unsigned char pin; - bool inverse; - unsigned char mode; - unsigned char relayID; - - led_pattern_t pattern; +private: + unsigned char _pin; + bool _inverse; + LedMode _mode; + LedPattern _pattern; }; +size_t ledCount(); + void ledUpdate(bool do_update); -unsigned char ledCount(); -bool ledStatus(unsigned char id, bool status); -bool ledStatus(unsigned char id); +bool ledStatus(size_t id, bool status); +bool ledStatus(size_t id); void ledSetup(); - diff --git a/code/espurna/led_config.h b/code/espurna/led_config.h index b8df5caf..cd521f07 100644 --- a/code/espurna/led_config.h +++ b/code/espurna/led_config.h @@ -8,7 +8,39 @@ LED MODULE #include "espurna.h" -constexpr const unsigned char _ledPin(unsigned char index) { +namespace led { +namespace build { + +constexpr size_t preconfiguredLeds() { + return 0ul + #if LED1_PIN != GPIO_NONE + + 1ul + #endif + #if LED2_PIN != GPIO_NONE + + 1ul + #endif + #if LED3_PIN != GPIO_NONE + + 1ul + #endif + #if LED4_PIN != GPIO_NONE + + 1ul + #endif + #if LED5_PIN != GPIO_NONE + + 1ul + #endif + #if LED6_PIN != GPIO_NONE + + 1ul + #endif + #if LED7_PIN != GPIO_NONE + + 1ul + #endif + #if LED8_PIN != GPIO_NONE + + 1ul + #endif + ; +} + +constexpr unsigned char pin(size_t index) { return ( (index == 0) ? LED1_PIN : (index == 1) ? LED2_PIN : @@ -21,7 +53,7 @@ constexpr const unsigned char _ledPin(unsigned char index) { ); } -constexpr const unsigned char _ledMode(unsigned char index) { +constexpr LedMode mode(size_t index) { return ( (index == 0) ? LED1_MODE : (index == 1) ? LED2_MODE : @@ -30,11 +62,11 @@ constexpr const unsigned char _ledMode(unsigned char index) { (index == 4) ? LED5_MODE : (index == 5) ? LED6_MODE : (index == 6) ? LED7_MODE : - (index == 7) ? LED8_MODE : LED_MODE_WIFI + (index == 7) ? LED8_MODE : LedMode::Manual ); } -constexpr const unsigned char _ledRelay(unsigned char index) { +constexpr unsigned char relay(size_t index) { return ( (index == 0) ? (LED1_RELAY - 1) : (index == 1) ? (LED2_RELAY - 1) : @@ -47,7 +79,7 @@ constexpr const unsigned char _ledRelay(unsigned char index) { ); } -constexpr const bool _ledInverse(unsigned char index) { +constexpr bool inverse(size_t index) { return ( (index == 0) ? (1 == LED1_PIN_INVERSE) : (index == 1) ? (1 == LED2_PIN_INVERSE) : @@ -59,3 +91,6 @@ constexpr const bool _ledInverse(unsigned char index) { (index == 7) ? (1 == LED8_PIN_INVERSE) : false ); } + +} // namespace build +} // namespace led diff --git a/code/espurna/led_pattern.h b/code/espurna/led_pattern.h index 8ec5b66e..dfb4377b 100644 --- a/code/espurna/led_pattern.h +++ b/code/espurna/led_pattern.h @@ -16,7 +16,7 @@ Copyright (C) 2020 by Maxim Prokhorov // Scans input string with format // ',, ,, ...' -// Directly changing `led.pattern.delays` contents +// Directly changing `LedPattern::delays` contents void _ledLoadPattern(led_t& led, const char* input) { char buffer[16]; @@ -28,7 +28,8 @@ void _ledLoadPattern(led_t& led, const char* input) { const char* p = input; const char* marker; - led.pattern.delays.clear(); + auto& pattern = led.pattern(); + pattern.delays.clear(); loop: const char *yyt1;const char *yyt2;const char *yyt3; @@ -153,9 +154,7 @@ yy17: buffer[int(p - d3)] = '\0'; repeats = strtoul(buffer, nullptr, 10); - led.pattern.delays.emplace_back( - on, off, repeats - ); + pattern.delays.emplace_back(on, off, repeats); goto loop; } diff --git a/code/espurna/led_pattern.h.in b/code/espurna/led_pattern.h.in index 7d5bf8f2..5b562b3b 100644 --- a/code/espurna/led_pattern.h.in +++ b/code/espurna/led_pattern.h.in @@ -26,7 +26,8 @@ void _ledLoadPattern(led_t& led, const char* input) { const char* p = input; const char* marker; - led.pattern.delays.clear(); + auto& pattern = led.pattern; + pattern.delays.clear(); loop: /*!stags:re2c format = 'const char *@@;'; */ @@ -61,9 +62,7 @@ loop: buffer[int(p - d3)] = '\0'; repeats = strtoul(buffer, nullptr, 10); - led.pattern.delays.emplace_back( - on, off, repeats - ); + pattern.delays.emplace_back(on, off, repeats); goto loop; } diff --git a/code/espurna/light.cpp b/code/espurna/light.cpp index fab1dfd0..c3ff380c 100644 --- a/code/espurna/light.cpp +++ b/code/espurna/light.cpp @@ -75,7 +75,7 @@ constexpr long Hsv::ValueMax; class LightChannelProvider : public RelayProviderBase { public: LightChannelProvider() = delete; - explicit LightChannelProvider(unsigned char id) : + explicit LightChannelProvider(size_t id) : _id(id) {} @@ -90,7 +90,7 @@ public: } private: - unsigned char _id { RELAY_NONE }; + size_t _id { RelaysMax }; }; class LightGlobalProvider : public RelayProviderBase { @@ -382,8 +382,8 @@ my92xx_model_t convert(const String& value) { #endif -bool _setValue(unsigned char, unsigned int) __attribute__((warn_unused_result)); -bool _setValue(unsigned char id, unsigned int value) { +bool _setValue(size_t, unsigned int) __attribute__((warn_unused_result)); +bool _setValue(size_t id, unsigned int value) { if (_light_channels[id].value != value) { _light_channels[id].value = value; return true; @@ -392,7 +392,7 @@ bool _setValue(unsigned char id, unsigned int value) { return false; } -void _setInputValue(unsigned char id, long value) { +void _setInputValue(size_t id, long value) { _light_channels[id].inputValue = std::clamp(value, Light::ValueMin, Light::ValueMax); } @@ -408,7 +408,7 @@ bool _lightApplyBrightnessChannels(size_t channels) { channels = std::min(channels, lightChannels()); OnceFlag changed; - for (unsigned char channel = 0; channel < lightChannels(); ++channel) { + for (size_t channel = 0; channel < lightChannels(); ++channel) { if (channel >= channels) { scale = 1.0f; } @@ -468,32 +468,33 @@ bool _lightApplyBrightnessColor() { // Scale up to equal input values. So [250,150,50] -> [200,100,0,50] -> [250, 125, 0, 63] unsigned char max_in = std::max({_light_channels[0].inputValue, _light_channels[1].inputValue, _light_channels[2].inputValue}); unsigned char max_out = std::max({_light_channels[0].value, _light_channels[1].value, _light_channels[2].value, _light_channels[3].value}); - unsigned char channelSize = _light_use_cct ? 5 : 4; + + size_t channelSize = _light_use_cct ? 5 : 4; if (_light_use_cct) { max_out = std::max(max_out, _light_channels[4].value); } double factor = (max_out > 0) ? (double) (max_in / max_out) : 0; - for (unsigned char i=0; i < channelSize; i++) { + for (size_t i = 0; i < channelSize; ++i) { changed = _setValue(i, lround((double) _light_channels[i].value * factor * brightness)); } // Scale white channel to match brightness - for (unsigned char i=3; i < channelSize; i++) { + for (size_t i = 3; i < channelSize; ++i) { changed = _setValue(i, constrain(static_cast(_light_channels[i].value * LIGHT_WHITE_FACTOR), Light::BrightnessMin, Light::BrightnessMax)); } // For the rest of channels, don't apply brightness, it is already in the inputValue // i should be 4 when RGBW and 5 when RGBWW - for (unsigned char i=channelSize; i < _light_channels.size(); i++) { + for (size_t i = channelSize; i < _light_channels.size(); ++i) { changed = _setValue(i, _light_channels[i].inputValue); } return changed.get(); } -char _lightTag(size_t channels, unsigned char index) { +char _lightTag(size_t channels, size_t index) { constexpr size_t Columns { 5ul }; constexpr size_t Rows { 5ul }; @@ -513,12 +514,12 @@ char _lightTag(size_t channels, unsigned char index) { return 0; } -char _lightTag(unsigned char index) { +char _lightTag(size_t index) { return _lightTag(_light_channels.size(), index); } // UI hint about channel distribution -const char* _lightDesc(size_t channels, unsigned char index) { +const char* _lightDesc(size_t channels, size_t index) { const __FlashStringHelper* ptr { F("UNKNOWN") }; switch (_lightTag(channels, index)) { case 'W': @@ -541,7 +542,7 @@ const char* _lightDesc(size_t channels, unsigned char index) { return reinterpret_cast(ptr); } -const char* _lightDesc(unsigned char index) { +const char* _lightDesc(size_t index) { return _lightDesc(_light_channels.size(), index); } @@ -827,11 +828,11 @@ void _lightAdjustBrightness(const String& payload) { _lightAdjustBrightness(payload.c_str()); } -void _lightAdjustChannel(unsigned char id, const char* payload) { +void _lightAdjustChannel(size_t id, const char* payload) { lightChannel(id, _lightAdjustValue(lightChannel(id), payload)); } -void _lightAdjustChannel(unsigned char id, const String& payload) { +void _lightAdjustChannel(size_t id, const String& payload) { _lightAdjustChannel(id, payload.c_str()); } @@ -967,7 +968,7 @@ public: state(_state); } - for (unsigned char index = 0; index < _transitions.size(); ++index) { + for (size_t index = 0; index < _transitions.size(); ++index) { auto& transition = _transitions[index]; if (!transition.count) { continue; @@ -1118,7 +1119,7 @@ inline bool _lightPwmMap(long value, long& result) { } // both require original values to be scaled into a PWM frequency -void _lightProviderHandleValue(unsigned char channel, float value) { +void _lightProviderHandleValue(size_t channel, float value) { long pwm; if (!_lightPwmMap(std::lround(value), pwm)) { return; @@ -1145,7 +1146,7 @@ void _lightProviderHandleState(bool state) { _light_provider->state(state); } -void _lightProviderHandleValue(unsigned char channel, float value) { +void _lightProviderHandleValue(size_t channel, float value) { _light_provider->channel(channel, value); } @@ -1281,7 +1282,7 @@ void lightSave(bool save) { void _lightSaveRtcmem() { auto channels = LightRtcmem::defaultChannels(); - for (unsigned char channel = 0; channel < lightChannels(); ++channel) { + for (size_t channel = 0; channel < lightChannels(); ++channel) { channels[channel] = _light_channels[channel].inputValue; } @@ -1294,7 +1295,7 @@ void _lightRestoreRtcmem() { LightRtcmem light(value); auto& channels = light.channels(); - for (unsigned char channel = 0; channel < lightChannels(); ++channel) { + for (size_t channel = 0; channel < lightChannels(); ++channel) { _light_channels[channel].inputValue = channels[channel]; } @@ -1307,7 +1308,7 @@ void _lightSaveSettings() { return; } - for (unsigned char channel = 0; channel < lightChannels(); ++channel) { + for (size_t channel = 0; channel < lightChannels(); ++channel) { setSetting({"ch", channel}, _light_channels[channel].inputValue); } @@ -1318,7 +1319,7 @@ void _lightSaveSettings() { } void _lightRestoreSettings() { - for (unsigned char channel = 0; channel < lightChannels(); ++channel) { + for (size_t channel = 0; channel < lightChannels(); ++channel) { auto value = getSetting({"ch", channel}, (channel == 0) ? Light::ValueMax : Light::ValueMin); _light_channels[channel].inputValue = value; } @@ -1349,16 +1350,8 @@ bool _lightParsePayload(const String& payload) { return _lightParsePayload(payload.c_str()); } -bool _lightTryParseChannel(const char* p, unsigned char& id) { - char* endp { nullptr }; - const unsigned long result { strtoul(p, &endp, 10) }; - if ((endp == p) || (*endp != '\0') || (result >= lightChannels())) { - DEBUG_MSG_P(PSTR("[LIGHT] Invalid channelID (%s)\n"), p); - return false; - } - - id = result; - return true; +bool _lightTryParseChannel(const char* p, size_t& id) { + return tryParseId(p, lightChannels, id); } // ----------------------------------------------------------------------------- @@ -1485,7 +1478,7 @@ void _lightMqttCallback(unsigned int type, const char * topic, const char * payl // Channel if (t.startsWith(MQTT_TOPIC_CHANNEL)) { - unsigned char id; + size_t id; if (!_lightTryParseChannel(t.c_str() + strlen(MQTT_TOPIC_CHANNEL) + 1, id)) { return; } @@ -1561,7 +1554,7 @@ void lightMQTTGroup() { template bool _lightApiTryHandle(ApiRequest& request, T&& callback) { auto id_param = request.wildcard(0); - unsigned char id; + size_t id; if (!_lightTryParseChannel(id_param.c_str(), id)) { return false; } @@ -1659,13 +1652,13 @@ void _lightApiSetup() { apiRegister(F(MQTT_TOPIC_CHANNEL "/+"), [](ApiRequest& request) { - return _lightApiTryHandle(request, [&](unsigned char id) { + return _lightApiTryHandle(request, [&](size_t id) { request.send(String(static_cast(_light_channels[id].target))); return true; }); }, [](ApiRequest& request) { - return _lightApiTryHandle(request, [&](unsigned char id) { + return _lightApiTryHandle(request, [&](size_t id) { _lightAdjustChannel(id, request.param(F("value"))); lightUpdate(); return true; @@ -1716,7 +1709,7 @@ void _lightWebSocketStatus(JsonObject& root) { root["useCCT"] = _light_use_cct; } JsonArray& channels = root.createNestedArray("channels"); - for (unsigned char id=0; id < _light_channels.size(); id++) { + for (size_t id = 0; id < _light_channels.size(); ++id) { channels.add(lightChannel(id)); } root["brightness"] = lightBrightness(); @@ -1813,29 +1806,26 @@ void _lightInitCommands() { return; } - auto id = -1; - if (ctx.argc > 1) { - id = ctx.argv[1].toInt(); - } - - auto description = [&](unsigned char channel) { - ctx.output.printf("#%hhu (%s): %hhu\n", channel, _lightDesc(channels, channel), _light_channels[channel].inputValue); + auto description = [&](size_t channel) { + ctx.output.printf("#%u (%s): %hhu\n", channel, _lightDesc(channels, channel), _light_channels[channel].inputValue); }; - if (id < 0 || id >= static_cast(channels)) { - for (unsigned char index = 0; index < channels; ++index) { - description(index); + if (ctx.argc > 2) { + size_t id; + if (!_lightTryParseChannel(ctx.argv[1].c_str(), id)) { + terminalError(ctx, F("Invalid channel ID")); + return; } - terminalOK(ctx); - return; - } - if (ctx.argc > 2) { _lightAdjustChannel(id, ctx.argv[2].c_str()); lightUpdate(); + description(id); + } else { + for (size_t index = 0; index < channels; ++index) { + description(index); + } } - description(id); terminalOK(ctx); }); @@ -2116,17 +2106,19 @@ void lightUpdate() { lightUpdate(lightTransition()); } -void lightState(unsigned char id, bool state) { - if (id >= _light_channels.size()) return; - if (_light_channels[id].state != state) { +void lightState(size_t id, bool state) { + if ((id < _light_channels.size()) && _light_channels[id].state != state) { _light_channels[id].state = state; _light_state_changed = true; } } -bool lightState(unsigned char id) { - if (id >= _light_channels.size()) return false; - return _light_channels[id].state; +bool lightState(size_t id) { + if (id < _light_channels.size()) { + return _light_channels[id].state; + } + + return false; } void lightState(bool state) { @@ -2232,18 +2224,22 @@ Light::MiredsRange lightMiredsRange() { return { _light_cold_mireds, _light_warm_mireds }; } -long lightChannel(unsigned char id) { - if (id >= _light_channels.size()) return 0; - return _light_channels[id].inputValue; +long lightChannel(size_t id) { + if (id < _light_channels.size()) { + return _light_channels[id].inputValue; + } + + return 0l; } -void lightChannel(unsigned char id, long value) { - if (id >= _light_channels.size()) return; - _setInputValue(id, value); +void lightChannel(size_t id, long value) { + if (id < _light_channels.size()) { + _setInputValue(id, value); + } } -void lightChannelStep(unsigned char id, long steps, long multiplier) { - lightChannel(id, static_cast(lightChannel(id)) + (steps * multiplier)); +void lightChannelStep(size_t id, long steps, long multiplier) { + lightChannel(id, lightChannel(id) + (steps * multiplier)); } long lightBrightness() { @@ -2316,7 +2312,7 @@ const unsigned long _light_iofunc[16] PROGMEM = { namespace { -inline bool _lightUseGamma(size_t channels, unsigned char index) { +inline bool _lightUseGamma(size_t channels, size_t index) { switch (_lightTag(channels, index)) { case 'R': case 'G': @@ -2327,7 +2323,7 @@ inline bool _lightUseGamma(size_t channels, unsigned char index) { return false; } -inline bool _lightUseGamma(unsigned char index) { +inline bool _lightUseGamma(size_t index) { return _lightUseGamma(_light_channels.size(), index); } @@ -2376,7 +2372,7 @@ void _lightConfigure() { _light_transition_step = getSetting("ltStep", LIGHT_TRANSITION_STEP); _light_use_gamma = getSetting("useGamma", 1 == LIGHT_USE_GAMMA); - for (unsigned char index = 0; index < lightChannels(); ++index) { + for (size_t index = 0; index < lightChannels(); ++index) { #if LIGHT_PROVIDER == LIGHT_PROVIDER_MY92XX _light_my92xx_channel_map[index] = getSetting({"ltMy92xxCh", index}, Light::build::my92xxChannel(index)); #endif @@ -2535,7 +2531,7 @@ void lightSetup() { getSetting("ltMy92xxDiGPIO", Light::build::my92xxDiPin()), getSetting("ltMy92xxDckiGPIO", Light::build::my92xxDckiPin()), Light::build::my92xxCommand()); - for (unsigned char index = 0; index < channels; ++index) { + for (size_t index = 0; index < channels; ++index) { _light_channels.emplace_back(GPIO_NONE); } } @@ -2547,7 +2543,7 @@ void lightSetup() { // 3-tuples of MUX_REGISTER, MUX_VALUE and GPIO number uint32_t io_info[Light::ChannelsMax][3]; - for (unsigned char index = 0; index < Light::ChannelsMax; ++index) { + for (size_t index = 0; index < Light::ChannelsMax; ++index) { // Load up until first GPIO_NONE. Allow settings to override, but not remove values const auto pin = getSetting({"ltDimmerGPIO", index}, Light::build::channelPin(index)); diff --git a/code/espurna/light.h b/code/espurna/light.h index 953520e2..7d49bb6b 100644 --- a/code/espurna/light.h +++ b/code/espurna/light.h @@ -164,7 +164,7 @@ class LightProvider { public: virtual void update() = 0; virtual void state(bool) = 0; - virtual void channel(unsigned char ch, float value) = 0; + virtual void channel(size_t ch, float value) = 0; }; struct LightTransition { @@ -221,8 +221,8 @@ long lightColdWhite(); void lightWarmWhite(long value); long lightWarmWhite(); -void lightState(unsigned char i, bool state); -bool lightState(unsigned char i); +void lightState(size_t id, bool state); +bool lightState(size_t id); void lightState(bool state); bool lightState(); @@ -230,11 +230,11 @@ bool lightState(); void lightBrightness(long brightness); long lightBrightness(); -long lightChannel(unsigned char id); -void lightChannel(unsigned char id, long value); +long lightChannel(size_t id); +void lightChannel(size_t id, long value); void lightBrightnessStep(long steps, long multiplier = LIGHT_STEP); -void lightChannelStep(unsigned char id, long steps, long multiplier = LIGHT_STEP); +void lightChannelStep(size_t id, long steps, long multiplier = LIGHT_STEP); void lightUpdate(bool save, LightTransition transition, Light::Report report); void lightUpdate(bool save, LightTransition transition, int report); diff --git a/code/espurna/light_config.h b/code/espurna/light_config.h index 0d435385..d327ac8c 100644 --- a/code/espurna/light_config.h +++ b/code/espurna/light_config.h @@ -15,7 +15,7 @@ constexpr unsigned char enablePin() { return LIGHT_ENABLE_PIN; } -constexpr unsigned char channelPin(unsigned char index) { +constexpr unsigned char channelPin(size_t index) { return ( (index == 0) ? LIGHT_CH1_PIN : (index == 1) ? LIGHT_CH2_PIN : @@ -25,7 +25,7 @@ constexpr unsigned char channelPin(unsigned char index) { ); } -constexpr bool inverse(unsigned char index) { +constexpr bool inverse(size_t index) { return ( (index == 0) ? (1 == LIGHT_CH1_INVERSE) : (index == 1) ? (1 == LIGHT_CH2_INVERSE) : @@ -41,7 +41,7 @@ constexpr my92xx_cmd_t my92xxCommand() { return MY92XX_COMMAND; } -constexpr unsigned char my92xxChannels() { +constexpr size_t my92xxChannels() { return MY92XX_CHANNELS; } @@ -67,15 +67,15 @@ constexpr unsigned char _my92xx_mapping[MY92XX_CHANNELS] { MY92XX_MAPPING }; -constexpr unsigned char my92xxChannel(unsigned char) +constexpr unsigned char my92xxChannel(size_t) __attribute__((deprecated("MY92XX_CH# flags should be used instead of MY92XX_MAPPING"))); -constexpr unsigned char my92xxChannel(unsigned char channel) { +constexpr unsigned char my92xxChannel(size_t channel) { return _my92xx_mapping[channel]; } #else -constexpr unsigned char my92xxChannel(unsigned char channel) { +constexpr unsigned char my92xxChannel(size_t channel) { return (channel == 0) ? MY92XX_CH1 : (channel == 1) ? MY92XX_CH2 : (channel == 2) ? MY92XX_CH3 : diff --git a/code/espurna/relay.cpp b/code/espurna/relay.cpp index ecb20cf0..3416aea2 100644 --- a/code/espurna/relay.cpp +++ b/code/espurna/relay.cpp @@ -62,7 +62,7 @@ struct RelayMaskHelper { _mask.reset(); } - void set(unsigned char id, bool status) { + void set(size_t id, bool status) { _mask.set(id, status); } @@ -255,8 +255,8 @@ std::vector _relays; bool _relayRecursive { false }; size_t _relayDummy { 0ul }; -unsigned long _relay_flood_window { _relayFloodWindowMs() }; -unsigned long _relay_flood_changes { _relayFloodChanges() }; +unsigned long _relay_flood_window { relay::build::floodWindowMs() }; +unsigned long _relay_flood_changes { relay::build::floodChanges() }; unsigned long _relay_delay_interlock; int _relay_sync_mode { RELAY_SYNC_ANY }; @@ -337,7 +337,7 @@ RelayProviderBase* _relayDummyProvider() { // Real GPIO provider, using BasePin interface to implement writers struct GpioProvider : public RelayProviderBase { - GpioProvider(unsigned char id, RelayType type, std::unique_ptr&& pin, std::unique_ptr&& reset_pin) : + GpioProvider(size_t id, RelayType type, std::unique_ptr&& pin, std::unique_ptr&& reset_pin) : _id(id), _type(type), _pin(std::move(pin)), @@ -397,7 +397,7 @@ struct GpioProvider : public RelayProviderBase { } private: - unsigned char _id { RelaysMax }; + size_t _id { RelaysMax }; RelayType _type { RelayType::Normal }; std::unique_ptr _pin; std::unique_ptr _reset_pin; @@ -410,7 +410,7 @@ private: class DualProvider : public RelayProviderBase { public: DualProvider() = delete; - explicit DualProvider(unsigned char id) : _id(id) { + explicit DualProvider(size_t id) : _id(id) { _instances.push_back(this); } @@ -444,7 +444,7 @@ public: } } - unsigned char relayId() const { + size_t relayId() const { return _id; } @@ -465,7 +465,7 @@ public: static void flush() { bool sync { true }; RelayMaskHelper mask; - for (unsigned char index = 0; index < _instances.size(); ++index) { + for (size_t index = 0; index < _instances.size(); ++index) { bool status { relayStatus(_instances[index]->relayId()) }; sync = sync && status; mask.set(index, status); @@ -505,13 +505,13 @@ public: } // Then, manage relays individually - for (unsigned char index = 0; index < _instances.size(); ++index) { + for (size_t index = 0; index < _instances.size(); ++index) { relayStatus(_instances[index]->relayId(), mask[index]); } } private: - unsigned char _id { 0 }; + size_t _id; static std::vector _instances; }; @@ -527,7 +527,7 @@ std::vector DualProvider::_instances; class StmProvider : public RelayProviderBase { public: StmProvider() = delete; - explicit StmProvider(unsigned char id) : + explicit StmProvider(size_t id) : _id(id) {} @@ -564,7 +564,7 @@ public: } private: - unsigned char _id; + size_t _id; }; #endif // RELAY_PROVIDER_STM_SUPPORT @@ -573,18 +573,11 @@ private: // UTILITY // ----------------------------------------------------------------------------- -bool _relayTryParseId(const char* p, unsigned char& relayID) { - char* endp { nullptr }; - const unsigned long result { strtoul(p, &endp, 10) }; - if ((endp == p) || (*endp != '\0') || (result >= relayCount())) { - return false; - } - - relayID = result; - return true; +bool _relayTryParseId(const char* p, size_t& id) { + return tryParseId(p, relayCount, id); } -bool _relayTryParseIdFromPath(const String& endpoint, unsigned char& relayID) { +bool _relayTryParseIdFromPath(const String& endpoint, size_t& id) { int next_slash { endpoint.lastIndexOf('/') }; if (next_slash < 0) { return false; @@ -596,10 +589,10 @@ bool _relayTryParseIdFromPath(const String& endpoint, unsigned char& relayID) { return false; } - return _relayTryParseId(p, relayID); + return _relayTryParseId(p, id); } -void _relayHandleStatus(unsigned char id, PayloadStatus status) { +void _relayHandleStatus(size_t id, PayloadStatus status) { switch (status) { case PayloadStatus::Off: relayStatus(id, false); @@ -615,7 +608,7 @@ void _relayHandleStatus(unsigned char id, PayloadStatus status) { } } -bool _relayHandlePayload(unsigned char id, const char* payload) { +bool _relayHandlePayload(size_t id, const char* payload) { auto status = relayParsePayload(payload); if (status != PayloadStatus::Unknown) { _relayHandleStatus(id, status); @@ -626,11 +619,11 @@ bool _relayHandlePayload(unsigned char id, const char* payload) { return false; } -bool _relayHandlePayload(unsigned char id, const String& payload) { +bool _relayHandlePayload(size_t id, const String& payload) { return _relayHandlePayload(id, payload.c_str()); } -bool _relayHandlePulsePayload(unsigned char id, const char* payload) { +bool _relayHandlePulsePayload(size_t id, const char* payload) { unsigned long pulse = 1000 * atof(payload); if (!pulse) { return false; @@ -647,7 +640,7 @@ bool _relayHandlePulsePayload(unsigned char id, const char* payload) { return true; } -bool _relayHandlePulsePayload(unsigned char id, const String& payload) { +bool _relayHandlePulsePayload(size_t id, const String& payload) { return _relayHandlePulsePayload(id, payload.c_str()); } @@ -665,7 +658,7 @@ PayloadStatus _relayInvertStatus(PayloadStatus status) { return PayloadStatus::Unknown; } -PayloadStatus _relayPayloadStatus(unsigned char id) { +PayloadStatus _relayPayloadStatus(size_t id) { if (id < _relays.size()) { return _relays[id].current_status ? PayloadStatus::On @@ -689,7 +682,7 @@ void _relayUnlockAll() { _relay_sync_locked = false; } -bool _relayStatusLock(unsigned char id, bool status) { +bool _relayStatusLock(size_t id, bool status) { if (_relays[id].lock != RelayLock::None) { bool lock = _relays[id].lock == RelayLock::On; if ((lock != status) || (lock != _relays[id].target_status)) { @@ -705,7 +698,7 @@ bool _relayStatusLock(unsigned char id, bool status) { // https://github.com/xoseperez/espurna/issues/1510#issuecomment-461894516 // completely reset timing on the other relay to sync with this one // to ensure that they change state sequentially -void _relaySyncRelaysDelay(unsigned char first, unsigned char second) { +void _relaySyncRelaysDelay(size_t first, size_t second) { _relays[second].fw_start = _relays[first].change_start; _relays[second].fw_count = 1; _relays[second].change_delay = std::max({ @@ -780,7 +773,7 @@ inline void _relayMaskSettings(const RelayMaskHelper& mask) { // Pulse timers (timer after ON or OFF event) // TODO: integrate with scheduled ON or OFF -void relayPulse(unsigned char id) { +void relayPulse(size_t id) { auto& relay = _relays[id]; if (!relay.pulseTicker) { @@ -814,15 +807,15 @@ void relayPulse(unsigned char id) { DEBUG_MSG_P(PSTR("[RELAY] Scheduling relay #%u back in %lums (pulse)\n"), id, ms); relay.pulseTicker->once_ms(ms, relayToggle, id); // Reconfigure after dynamic pulse - relay.pulse = getSetting({"relayPulse", id}, _relayPulseMode(id)); - relay.pulse_ms = static_cast(1000.0f * getSetting({"relayTime", id}, _relayPulseTime(id))); + relay.pulse = getSetting({"relayPulse", id},relay::build::pulseMode(id)); + relay.pulse_ms = static_cast(1000.0f * getSetting({"relayTime", id}, relay::build::pulseTime(id))); } } // General relay status control -bool relayStatus(unsigned char id, bool status, bool report, bool group_report) { +bool relayStatus(size_t id, bool status, bool report, bool group_report) { if ((id >= RelaysMax) || (id >= _relays.size())) { return false; @@ -903,7 +896,7 @@ bool relayStatus(unsigned char id, bool status, bool report, bool group_report) } -bool relayStatus(unsigned char id, bool status) { +bool relayStatus(size_t id, bool status) { #if MQTT_SUPPORT return relayStatus(id, status, mqttForward(), true); #else @@ -911,7 +904,7 @@ bool relayStatus(unsigned char id, bool status) { #endif } -bool relayStatus(unsigned char id) { +bool relayStatus(size_t id) { // Check that relay ID is valid if (id >= _relays.size()) return false; @@ -921,35 +914,40 @@ bool relayStatus(unsigned char id) { } -bool relayStatusTarget(unsigned char id) { +bool relayStatusTarget(size_t id) { if (id >= _relays.size()) return false; return _relays[id].target_status; } -void relaySync(unsigned char id) { - - // No sync if none or only one relay - if (_relays.size() < 2) return; +void relaySync(size_t target) { // Do not go on if we are comming from a previous sync - if (_relayRecursive) return; - - // Flag sync mode + if (_relayRecursive) { + return; + } _relayRecursive = true; - bool status = _relays[id].target_status; + // No sync if none or only one relay + auto relays = _relays.size(); + if (relays < 2) { + return; + } + + bool status = _relays[target].target_status; // If RELAY_SYNC_SAME all relays should have the same state if (_relay_sync_mode == RELAY_SYNC_SAME) { - for (unsigned short i=0; i<_relays.size(); i++) { - if (i != id) relayStatus(i, status); + for (decltype(relays) id = 0; id < relays; ++id) { + if (id != target) { + relayStatus(id, status); + } } // If RELAY_SYNC_FIRST all relays should have the same state as first if first changes } else if (_relay_sync_mode == RELAY_SYNC_FIRST) { - if (id == 0) { - for (unsigned short i=1; i<_relays.size(); i++) { - relayStatus(i, status); + if (target == 0) { + for (decltype(relays) id = 1; id < relays; ++id) { + relayStatus(id, status); } } @@ -957,11 +955,11 @@ void relaySync(unsigned char id) { // If NONE_OR_ONE or ONE and setting ON we should set OFF all the others if (status) { if (_relay_sync_mode != RELAY_SYNC_ANY) { - for (unsigned short other_id=0; other_id<_relays.size(); other_id++) { - if (other_id != id) { - relayStatus(other_id, false); - if (relayStatus(other_id)) { - _relaySyncRelaysDelay(other_id, id); + for (decltype(relays) id = 0; id < relays; ++id) { + if (id != target) { + relayStatus(id, false); + if (relayStatus(id)) { + _relaySyncRelaysDelay(id, target); } } } @@ -969,22 +967,21 @@ void relaySync(unsigned char id) { // If ONLY_ONE and setting OFF we should set ON the other one } else { if (_relay_sync_mode == RELAY_SYNC_ONE) { - unsigned char other_id = (id + 1) % _relays.size(); - _relaySyncRelaysDelay(id, other_id); - relayStatus(other_id, true); + auto id = (target + 1) % relays; + _relaySyncRelaysDelay(target, id); + relayStatus(id, true); } } _relayLockAll(); } - // Unflag sync mode _relayRecursive = false; } void relaySave(bool persist) { RelayMaskHelper mask; - for (unsigned char id = 0; id < _relays.size(); ++id) { + for (size_t id = 0; id < _relays.size(); ++id) { mask.set(id, _relays[id].current_status); } @@ -1008,13 +1005,13 @@ void relaySave() { relaySave(false); } -void relayToggle(unsigned char id, bool report, bool group_report) { +void relayToggle(size_t id, bool report, bool group_report) { if (id < _relays.size()) { relayStatus(id, !relayStatus(id), report, group_report); } } -void relayToggle(unsigned char id) { +void relayToggle(size_t id) { #if MQTT_SUPPORT relayToggle(id, mqttForward(), true); #else @@ -1050,7 +1047,7 @@ void _relaySettingsMigrate(int version) { moveSetting("relayDelayInterlock", "relayIlkDelay"); // groups use a new set of keys - for (unsigned char index = 0; index < RelaysMax; ++index) { + for (size_t index = 0; index < RelaysMax; ++index) { auto group = getSetting({"mqttGroup", index}); if (!group.length()) { break; @@ -1072,7 +1069,7 @@ void _relaySettingsMigrate(int version) { "mqttGroup", // migrated to relayTopic "mqttGroupSync", // migrated to relayTopic "relayOnDisc", // replaced with relayMqttDisc - "relayGPIO", // avoid depending on migrate.ino + "relayGpio", // avoid depending on migrate.ino "relayProvider", // different type "relayType", // different type }); @@ -1080,8 +1077,8 @@ void _relaySettingsMigrate(int version) { } } -void _relayBoot(unsigned char index, const RelayMaskHelper& mask) { - const auto boot_mode = getSetting({"relayBoot", index}, _relayBootMode(index)); +void _relayBoot(size_t index, const RelayMaskHelper& mask) { + const auto boot_mode = getSetting({"relayBoot", index}, relay::build::bootMode(index)); auto status = false; auto lock = RelayLock::None; @@ -1154,24 +1151,24 @@ void _relayBootAll() { void _relayConfigure() { auto relays = _relays.size(); for (decltype(relays) id = 0; id < relays; ++id) { - _relays[id].pulse = getSetting({"relayPulse", id}, _relayPulseMode(id)); - _relays[id].pulse_ms = static_cast(1000.0f * getSetting({"relayTime", i}, _relayPulseTime(i))); + _relays[id].pulse = getSetting({"relayPulse", id}, relay::build::pulseMode(id)); + _relays[id].pulse_ms = static_cast(1000.0f * getSetting({"relayTime", id}, relay::build::pulseTime(id))); - _relays[id].delay_on = getSetting({"relayDelayOn", id}, _relayDelayOn(id)); - _relays[id].delay_off = getSetting({"relayDelayOff", id}, _relayDelayOff(id)); + _relays[id].delay_on = getSetting({"relayDelayOn", id}, relay::build::delayOn(id)); + _relays[id].delay_off = getSetting({"relayDelayOff", id}, relay::build::delayOff(id)); } - _relay_flood_window = (1000.0f * getSetting("relayFloodTime", _relayFloodWindow())); - _relay_flood_changes = getSetting("relayFloodChanges", _relayFloodChanges()); + _relay_flood_window = (1000.0f * getSetting("relayFloodTime", relay::build::floodWindow())); + _relay_flood_changes = getSetting("relayFloodChanges", relay::build::floodChanges()); - _relay_delay_interlock = getSetting("relayIlkDelay", _relayInterlockDelay()); - _relay_sync_mode = getSetting("relaySync", _relaySyncMode()); + _relay_delay_interlock = getSetting("relayIlkDelay", relay::build::interlockDelay()); + _relay_sync_mode = getSetting("relaySync", relay::build::syncMode()); #if MQTT_SUPPORT || API_SUPPORT settingsProcessConfig({ - {_relay_rpc_payload_on, "relayPayloadOn", _relayMqttPayloadOn()}, - {_relay_rpc_payload_off, "relayPayloadOff", _relayMqttPayloadOff()}, - {_relay_rpc_payload_toggle, "relayPayloadToggle", _relayMqttPayloadToggle()}, + {_relay_rpc_payload_on, "relayPayloadOn", relay::build::mqttPayloadOn()}, + {_relay_rpc_payload_off, "relayPayloadOff", relay::build::mqttPayloadOff()}, + {_relay_rpc_payload_toggle, "relayPayloadToggle", relay::build::mqttPayloadToggle()}, }); #endif // MQTT_SUPPORT } @@ -1202,17 +1199,17 @@ void _relayWebSocketUpdate(JsonObject& root) { } void _relayWebSocketRelayConfig(JsonArray& relay, size_t id) { - relay.add(static_cast(getSetting({"relayProv", id}, _relayProvider(id)))); + relay.add(static_cast(getSetting({"relayProv", id}, relay::build::provider(id)))); relay.add(getSetting({"relayName", id})); - relay.add(getSetting({"relayBoot", id}, _relayBootMode(id))); + relay.add(getSetting({"relayBoot", id}, relay::build::bootMode(id))); #if MQTT_SUPPORT - relay.add(getSetting({"relayTopicSub", id}, _relayMqttTopicSub(id))); - relay.add(getSetting({"relayTopicPub", id}, _relayMqttTopicPub(id))); + relay.add(getSetting({"relayTopicSub", id}, relay::build::mqttTopicSub(id))); + relay.add(getSetting({"relayTopicPub", id}, relay::build::mqttTopicPub(id))); relay.add(static_cast(getSetting({"relayTopicMode", id}, - _relayMqttTopicMode(id)))); + relay::build::mqttTopicMode(id)))); relay.add(static_cast(getSetting({"relayMqttDisc", id}, - _relayMqttDisconnectionStatus(id)))); + relay::build::mqttDisconnectionStatus(id)))); #endif relay.add(static_cast(_relays[id].pulse)); @@ -1266,8 +1263,8 @@ void _relayWebSocketOnVisible(JsonObject& root) { if (relayCount() > 1) { root["multirelayVisible"] = 1; - root["relaySync"] = static_cast(getSetting("relaySync", _relaySyncMode())); - root["relayIlkDelay"] = getSetting("relayIlkDelay", _relayInterlockDelay()); + root["relaySync"] = static_cast(getSetting("relaySync", relay::build::syncMode())); + root["relayIlkDelay"] = getSetting("relayIlkDelay", relay::build::interlockDelay()); } root["relayVisible"] = 1; @@ -1314,7 +1311,7 @@ void relaySetupWS() { template bool _relayApiTryHandle(ApiRequest& request, T&& callback) { auto id_param = request.wildcard(0); - unsigned char id; + size_t id; if (!_relayTryParseId(id_param.c_str(), id)) { return false; } @@ -1331,7 +1328,7 @@ void relaySetupAPI() { apiRegister(F(MQTT_TOPIC_RELAY), [](ApiRequest&, JsonObject& root) { JsonArray& relays = root.createNestedArray("relayStatus"); - for (unsigned char id = 0; id < relayCount(); ++id) { + for (size_t id = 0; id < relayCount(); ++id) { relays.add(_relays[id].target_status ? 1 : 0); } return true; @@ -1341,13 +1338,13 @@ void relaySetupAPI() { apiRegister(F(MQTT_TOPIC_RELAY "/+"), [](ApiRequest& request) { - return _relayApiTryHandle(request, [&](unsigned char id) { + return _relayApiTryHandle(request, [&](size_t id) { request.send(String(_relays[id].target_status ? 1 : 0)); return true; }); }, [](ApiRequest& request) { - return _relayApiTryHandle(request, [&](unsigned char id) { + return _relayApiTryHandle(request, [&](size_t id) { return _relayHandlePayload(id, request.param(F("value"))); }); } @@ -1355,13 +1352,13 @@ void relaySetupAPI() { apiRegister(F(MQTT_TOPIC_PULSE "/+"), [](ApiRequest& request) { - return _relayApiTryHandle(request, [&](unsigned char id) { + return _relayApiTryHandle(request, [&](size_t id) { request.send(String(static_cast(_relays[id].pulse_ms) / 1000)); return true; }); }, [](ApiRequest& request) { - return _relayApiTryHandle(request, [&](unsigned char id) { + return _relayApiTryHandle(request, [&](size_t id) { return _relayHandlePulsePayload(id, request.param(F("value"))); }); } @@ -1478,7 +1475,7 @@ struct RelayCustomTopic { _mode(base.mode()) {} - unsigned char id() const { + size_t id() const { return _id; } @@ -1508,7 +1505,7 @@ struct RelayCustomTopic { } private: - unsigned char _id; + size_t _id; String _topic; PathParts _parts; RelayMqttTopicMode _mode; @@ -1524,7 +1521,7 @@ void _relayMqttSubscribeCustomTopics() { static std::vector topics; for (size_t id = 0; id < relays; ++id) { - topics.emplace_back(_relayMqttTopicSub(id), _relayMqttTopicMode(id)); + topics.emplace_back(relay::build::mqttTopicSub(id), relay::build::mqttTopicMode(id)); } settings::kv_store.foreach([&](settings::kvs_type::KeyValueResult&& kv) { @@ -1541,22 +1538,21 @@ void _relayMqttSubscribeCustomTopics() { } const auto key = kv.key.read(); - unsigned char id; + size_t id; if (key.startsWith(SubPrefix)) { if (_relayTryParseId(key.c_str() + strlen(SubPrefix), id)) { topics[id] = std::move(kv.value.read()); } } else if (key.startsWith(ModePrefix)) { - using namespace settings::internal; if (_relayTryParseId(key.c_str() + strlen(ModePrefix), id)) { - topics[id] = convert(kv.value.read()); + topics[id] = settings::internal::convert(kv.value.read()); } } }); _relay_custom_topics.clear(); - for (unsigned char id = 0; id < relays; ++id) { + for (size_t id = 0; id < relays; ++id) { RelayCustomTopicBase& topic = topics[id]; auto& value = topic.value(); if (!value.length()) { @@ -1570,15 +1566,15 @@ void _relayMqttSubscribeCustomTopics() { topics.clear(); } -void _relayMqttPublishCustomTopic(unsigned char id) { - const String topic = getSetting({"relayTopicPub", id}, _relayMqttTopicPub(id)); +void _relayMqttPublishCustomTopic(size_t id) { + const String topic = getSetting({"relayTopicPub", id}, relay::build::mqttTopicPub(id)); if (!topic.length()) { return; } auto status = _relayPayloadStatus(id); - auto mode = getSetting({"relayTopicMode", id}, _relayMqttTopicMode(id)); + auto mode = getSetting({"relayTopicMode", id}, relay::build::mqttTopicMode(id)); if (mode == RelayMqttTopicMode::Inverse) { status = _relayInvertStatus(status); } @@ -1588,7 +1584,7 @@ void _relayMqttPublishCustomTopic(unsigned char id) { } // namespace -void _relayMqttReport(unsigned char id) { +void _relayMqttReport(size_t id) { if (id < _relays.size()) { if (_relays[id].report) { _relays[id].report = false; @@ -1608,7 +1604,7 @@ void _relayMqttReportAll() { } } -void relayStatusWrap(unsigned char id, PayloadStatus value, bool is_group_topic) { +void relayStatusWrap(size_t id, PayloadStatus value, bool is_group_topic) { #if MQTT_SUPPORT const auto forward = mqttForward(); #else @@ -1664,7 +1660,7 @@ void _relayMqttHandleDisconnect() { const auto key = kv.key.read(); if (key.startsWith(prefix)) { - unsigned char id; + size_t id; if (_relayTryParseId(key.c_str() + strlen(prefix), id)) { const auto value = kv.value.read(); _relayHandleStatus(id, relayParsePayload(value.c_str())); @@ -1704,7 +1700,7 @@ void relayMQTTCallback(unsigned int type, const char * topic, const char * paylo auto is_relay = t.startsWith(MQTT_TOPIC_RELAY); auto is_pulse = t.startsWith(MQTT_TOPIC_PULSE); if (is_relay || is_pulse) { - unsigned char id; + size_t id; if (!_relayTryParseIdFromPath(t.c_str(), id)) { return; } @@ -1752,8 +1748,8 @@ void relaySetupMQTT() { void _relayInitCommands() { terminalRegisterCommand(F("RELAY"), [](const terminal::CommandContext& ctx) { - auto showRelays = [&](unsigned char start, unsigned char stop, bool full = true) { - for (unsigned char index = start; index < stop; ++index) { + auto showRelays = [&](size_t start, size_t stop, bool full = true) { + for (size_t index = start; index < stop; ++index) { auto& relay = _relays[index]; char pulse_info[64] = ""; @@ -1795,7 +1791,7 @@ void _relayInitCommands() { return; } - unsigned char id; + size_t id; if (!_relayTryParseId(ctx.argv[1].c_str(), id)) { terminalError(ctx, F("Invalid relayID")); return; @@ -1821,7 +1817,7 @@ void _relayInitCommands() { //------------------------------------------------------------------------------ -void _relayReport(unsigned char id [[gnu::unused]], bool status [[gnu::unused]]) { +void _relayReport(size_t id [[gnu::unused]], bool status [[gnu::unused]]) { for (auto& change : _relay_status_change) { change(id, status); } @@ -1873,7 +1869,7 @@ void _relayProcess(bool mode) { // We will trigger a eeprom save only if // we care about current relay status on boot - const auto boot_mode = getSetting({"relayBoot", id}, _relayBootMode(id)); + const auto boot_mode = getSetting({"relayBoot", id}, relay::build::bootMode(id)); const bool save_eeprom = ((RELAY_BOOT_SAME == boot_mode) || (RELAY_BOOT_TOGGLE == boot_mode)); _relay_save_timer.once_ms(RELAY_SAVE_DELAY, relaySave, save_eeprom); @@ -1887,7 +1883,7 @@ void _relayProcess(bool mode) { // Whenever we are using sync modes and any relay had changed the state, check if we can unlock switch (_relay_sync_mode) { case RELAY_SYNC_ONE: - case RELAY_SYNC_NONE_OR_ONE + case RELAY_SYNC_NONE_OR_ONE: if (_relay_sync_locked && changed) { _relaySyncUnlock(); } @@ -1969,14 +1965,14 @@ struct RelayGpioProviderCfg { uint8_t reset; }; -RelayGpioProviderCfg _relayGpioProviderCfg(unsigned char index) { +RelayGpioProviderCfg _relayGpioProviderCfg(size_t index) { return { - gpioBase(getSetting({"relayGPIOType", index}, _relayPinType(index))), - getSetting({"relayGPIO", index}, _relayPin(index)), - getSetting({"relayResetGPIO", index}, _relayResetPin(index))}; + gpioBase(getSetting({"relayGpioType", index}, relay::build::pinType(index))), + getSetting({"relayGpio", index}, relay::build::pin(index)), + getSetting({"relayResetGpio", index}, relay::build::resetPin(index))}; } -std::unique_ptr _relayGpioProvider(unsigned char index, RelayType type) { +std::unique_ptr _relayGpioProvider(size_t index, RelayType type) { auto cfg = _relayGpioProviderCfg(index); if (!cfg.base) { return nullptr; @@ -1992,9 +1988,9 @@ std::unique_ptr _relayGpioProvider(unsigned char index, RelayType return nullptr; } -RelayProviderBasePtr _relaySetupProvider(unsigned char index) { - auto provider = getSetting({"relayProv", index}, _relayProvider(index)); - auto type = getSetting({"relayType", index}, _relayType(index)); +RelayProviderBasePtr _relaySetupProvider(size_t index) { + auto provider = getSetting({"relayProv", index}, relay::build::provider(index)); + auto type = getSetting({"relayType", index}, relay::build::type(index)); RelayProviderBasePtr result; @@ -2026,7 +2022,7 @@ void _relaySetup() { auto relays = _relays.size(); _relays.reserve(relays + _relayAdhocPins()); - for (unsigned char id = relays; id < RelaysMax; ++id) { + for (size_t id = relays; id < RelaysMax; ++id) { auto impl = _relaySetupProvider(id); if (!impl) { break; @@ -2037,7 +2033,7 @@ void _relaySetup() { _relays.emplace_back(std::move(impl)); } - relaySetupDummy(getSetting("relayDummy", DUMMY_RELAY_COUNT)); + relaySetupDummy(getSetting("relayDummy", relay::build::dummyCount())); } void relaySetup() { diff --git a/code/espurna/relay.h b/code/espurna/relay.h index 27b6120e..71ef5aee 100644 --- a/code/espurna/relay.h +++ b/code/espurna/relay.h @@ -70,17 +70,17 @@ public: PayloadStatus relayParsePayload(const char * payload); -bool relayStatus(unsigned char id, bool status, bool report, bool group_report); -bool relayStatus(unsigned char id, bool status); +bool relayStatus(size_t id, bool status, bool report, bool group_report); +bool relayStatus(size_t id, bool status); // gets either current or target status, where current is the status that we are // actually in and target is the status we would be, eventually, unless // relayStatus(id, relayStatus()) is called -bool relayStatus(unsigned char id); -bool relayStatusTarget(unsigned char id); +bool relayStatus(size_t id); +bool relayStatusTarget(size_t id); -void relayToggle(unsigned char id, bool report, bool group_report); -void relayToggle(unsigned char id); +void relayToggle(size_t id, bool report, bool group_report); +void relayToggle(size_t id); size_t relayCount(); @@ -90,8 +90,8 @@ const String& relayPayloadToggle(); const char* relayPayload(PayloadStatus status); -void relayPulse(unsigned char id); -void relaySync(unsigned char id); +void relayPulse(size_t id); +void relaySync(size_t id); void relaySave(bool persist); using RelayStatusCallback = void(*)(size_t id, bool status); diff --git a/code/espurna/relay_config.h b/code/espurna/relay_config.h index e8789f73..7bb19d07 100644 --- a/code/espurna/relay_config.h +++ b/code/espurna/relay_config.h @@ -8,29 +8,36 @@ RELAY MODULE #include "espurna.h" -constexpr int _relaySyncMode() { +namespace relay { +namespace build { + +constexpr size_t dummyCount() { + return DUMMY_RELAY_COUNT; +} + +constexpr int syncMode() { return RELAY_SYNC; } -constexpr float _relayFloodWindow() { +constexpr float floodWindow() { return RELAY_FLOOD_WINDOW; } -static_assert(_relayFloodWindow() >= 0.0f, ""); +static_assert(floodWindow() >= 0.0f, ""); -constexpr unsigned long _relayFloodWindowMs() { - return static_cast(_relayFloodWindow() * 1000.0f); +constexpr unsigned long floodWindowMs() { + return static_cast(floodWindow() * 1000.0f); } -constexpr unsigned long _relayFloodChanges() { +constexpr unsigned long floodChanges() { return RELAY_FLOOD_CHANGES; } -constexpr unsigned long _relayInterlockDelay() { +constexpr unsigned long interlockDelay() { return RELAY_DELAY_INTERLOCK; } -constexpr float _relayPulseTime(unsigned char index) { +constexpr float pulseTime(size_t index) { return ( (index == 0) ? RELAY1_PULSE_TIME : (index == 1) ? RELAY2_PULSE_TIME : @@ -43,16 +50,16 @@ constexpr float _relayPulseTime(unsigned char index) { ); } -static_assert(_relayPulseTime(0) >= 0.0f, ""); -static_assert(_relayPulseTime(1) >= 0.0f, ""); -static_assert(_relayPulseTime(2) >= 0.0f, ""); -static_assert(_relayPulseTime(3) >= 0.0f, ""); -static_assert(_relayPulseTime(4) >= 0.0f, ""); -static_assert(_relayPulseTime(5) >= 0.0f, ""); -static_assert(_relayPulseTime(6) >= 0.0f, ""); -static_assert(_relayPulseTime(7) >= 0.0f, ""); +static_assert(pulseTime(0) >= 0.0f, ""); +static_assert(pulseTime(1) >= 0.0f, ""); +static_assert(pulseTime(2) >= 0.0f, ""); +static_assert(pulseTime(3) >= 0.0f, ""); +static_assert(pulseTime(4) >= 0.0f, ""); +static_assert(pulseTime(5) >= 0.0f, ""); +static_assert(pulseTime(6) >= 0.0f, ""); +static_assert(pulseTime(7) >= 0.0f, ""); -constexpr RelayPulse _relayPulseMode(unsigned char index) { +constexpr RelayPulse pulseMode(size_t index) { return ( (index == 0) ? RELAY1_PULSE_MODE : (index == 1) ? RELAY2_PULSE_MODE : @@ -65,7 +72,7 @@ constexpr RelayPulse _relayPulseMode(unsigned char index) { ); } -constexpr unsigned long _relayDelayOn(unsigned char index) { +constexpr unsigned long delayOn(size_t index) { return ( (index == 0) ? RELAY1_DELAY_ON : (index == 1) ? RELAY2_DELAY_ON : @@ -78,7 +85,7 @@ constexpr unsigned long _relayDelayOn(unsigned char index) { ); } -constexpr unsigned long _relayDelayOff(unsigned char index) { +constexpr unsigned long delayOff(size_t index) { return ( (index == 0) ? RELAY1_DELAY_OFF : (index == 1) ? RELAY2_DELAY_OFF : @@ -91,7 +98,7 @@ constexpr unsigned long _relayDelayOff(unsigned char index) { ); } -constexpr unsigned char _relayPin(unsigned char index) { +constexpr unsigned char pin(size_t index) { return ( (index == 0) ? RELAY1_PIN : (index == 1) ? RELAY2_PIN : @@ -104,7 +111,7 @@ constexpr unsigned char _relayPin(unsigned char index) { ); } -constexpr RelayType _relayType(unsigned char index) { +constexpr RelayType type(size_t index) { return ( (index == 0) ? RELAY1_TYPE : (index == 1) ? RELAY2_TYPE : @@ -117,7 +124,7 @@ constexpr RelayType _relayType(unsigned char index) { ); } -constexpr GpioType _relayPinType(unsigned char index) { +constexpr GpioType pinType(size_t index) { return ( (index == 0) ? RELAY1_PIN_TYPE : (index == 1) ? RELAY2_PIN_TYPE : @@ -130,7 +137,7 @@ constexpr GpioType _relayPinType(unsigned char index) { ); } -constexpr unsigned char _relayResetPin(unsigned char index) { +constexpr unsigned char resetPin(size_t index) { return ( (index == 0) ? RELAY1_RESET_PIN : (index == 1) ? RELAY2_RESET_PIN : @@ -143,7 +150,7 @@ constexpr unsigned char _relayResetPin(unsigned char index) { ); } -constexpr int _relayBootMode(unsigned char index) { +constexpr int bootMode(size_t index) { return ( (index == 0) ? RELAY1_BOOT_MODE : (index == 1) ? RELAY2_BOOT_MODE : @@ -156,7 +163,7 @@ constexpr int _relayBootMode(unsigned char index) { ); } -constexpr RelayProvider _relayProvider(unsigned char index) { +constexpr RelayProvider provider(size_t index) { return ( (index == 0) ? (RELAY1_PROVIDER) : (index == 1) ? (RELAY2_PROVIDER) : @@ -169,7 +176,7 @@ constexpr RelayProvider _relayProvider(unsigned char index) { ); } -constexpr RelayMqttTopicMode _relayMqttTopicMode(unsigned char index) { +constexpr RelayMqttTopicMode mqttTopicMode(size_t index) { return ( (index == 0) ? (RELAY1_MQTT_TOPIC_MODE) : (index == 1) ? (RELAY2_MQTT_TOPIC_MODE) : @@ -182,19 +189,19 @@ constexpr RelayMqttTopicMode _relayMqttTopicMode(unsigned char index) { ); } -constexpr const char* const _relayMqttPayloadOn() { +constexpr const char* const mqttPayloadOn() { return RELAY_MQTT_ON; } -constexpr const char* const _relayMqttPayloadOff() { +constexpr const char* const mqttPayloadOff() { return RELAY_MQTT_OFF; } -constexpr const char* const _relayMqttPayloadToggle() { +constexpr const char* const mqttPayloadToggle() { return RELAY_MQTT_TOGGLE; } -constexpr const char* const _relayMqttTopicSub(unsigned char index) { +constexpr const char* const mqttTopicSub(size_t index) { return ( (index == 0) ? (RELAY1_MQTT_TOPIC_SUB) : (index == 1) ? (RELAY2_MQTT_TOPIC_SUB) : @@ -207,7 +214,7 @@ constexpr const char* const _relayMqttTopicSub(unsigned char index) { ); } -constexpr const char* const _relayMqttTopicPub(unsigned char index) { +constexpr const char* const mqttTopicPub(size_t index) { return ( (index == 0) ? (RELAY1_MQTT_TOPIC_PUB) : (index == 1) ? (RELAY2_MQTT_TOPIC_PUB) : @@ -220,7 +227,7 @@ constexpr const char* const _relayMqttTopicPub(unsigned char index) { ); } -constexpr PayloadStatus _relayMqttDisconnectionStatus(unsigned char index) { +constexpr PayloadStatus mqttDisconnectionStatus(size_t index) { return ( (index == 0) ? (RELAY1_MQTT_DISCONNECT_STATUS) : (index == 1) ? (RELAY2_MQTT_DISCONNECT_STATUS) : @@ -232,3 +239,6 @@ constexpr PayloadStatus _relayMqttDisconnectionStatus(unsigned char index) { (index == 7) ? (RELAY8_MQTT_DISCONNECT_STATUS) : RELAY_MQTT_DISCONNECT_NONE ); } + +} // namespace build +} // namespace relay diff --git a/code/espurna/rfbridge.cpp b/code/espurna/rfbridge.cpp index c605a894..110984ea 100644 --- a/code/espurna/rfbridge.cpp +++ b/code/espurna/rfbridge.cpp @@ -53,33 +53,39 @@ constexpr bool _rfb_transmit { true }; struct RfbRelayMatch { RfbRelayMatch() = default; - RfbRelayMatch(unsigned char id_, PayloadStatus status_) : - id(id_), - status(status_), + RfbRelayMatch(size_t id, PayloadStatus status) : + _id(id), + _status(status), _found(true) {} - bool ok() { - return _found; + void reset(size_t id, PayloadStatus status) { + _id = id; + _status = status; + _found = true; } - void reset(unsigned char id_, PayloadStatus status_) { - id = id_; - status = status_; - _found = true; + size_t id() const { + return _id; } - unsigned char id { 0u }; - PayloadStatus status { PayloadStatus::Unknown }; + PayloadStatus status() const { + return _status; + } - private: + explicit operator bool() const { + return _found; + } +private: + size_t _id { RelaysMax }; + PayloadStatus _status { PayloadStatus::Unknown }; bool _found { false }; }; struct RfbLearn { unsigned long ts; - unsigned char id; + size_t id; bool status; }; @@ -332,7 +338,7 @@ void _rfbWebSocketOnVisible(JsonObject& root) { #if RELAY_SUPPORT -void _rfbWebSocketSendCodeArray(JsonObject& root, unsigned char start, unsigned char size) { +void _rfbWebSocketSendCodeArray(JsonObject& root, size_t start, size_t size) { JsonObject& rfb = root.createNestedObject("rfb"); rfb["size"] = size; rfb["start"] = start; @@ -340,14 +346,14 @@ void _rfbWebSocketSendCodeArray(JsonObject& root, unsigned char start, unsigned JsonArray& on = rfb.createNestedArray("on"); JsonArray& off = rfb.createNestedArray("off"); - for (uint8_t id=start; id std::numeric_limits::max() || id >= relayCount()) { + size_t id; + if (!tryParseId(id_ptr, relayCount, id)) { return; } // when we see the same id twice, we match the opposite statuses - if (matched.ok() && (id == matched.id)) { - matched.status = PayloadStatus::Toggle; + if (matched && (id == matched.id())) { + matched.reset(matched.id(), PayloadStatus::Toggle); return; } - matched.reset(matched.ok() - ? std::min(static_cast(id), matched.id) - : static_cast(id), - status - ); + matched.reset(matched ? std::min(id, matched.id()) : id, status); }); @@ -482,7 +483,7 @@ void _rfbLearnFromString(std::unique_ptr& learn, const char* buffer) { #if WEB_SUPPORT auto id = learn->id; wsPost([id](JsonObject& root) { - _rfbWebSocketSendCodeArray(root, id, 1); + _rfbWebSocketSendCodeArray(root, id, 1ul); }); #endif @@ -493,18 +494,18 @@ bool _rfbRelayHandler(const char* buffer, bool locked = false) { bool result { false }; auto match = _rfbMatch(buffer); - if (match.ok()) { - DEBUG_MSG_P(PSTR("[RF] Matched with the relay ID %u\n"), match.id); - _rfb_relay_status_lock.set(match.id, locked); + if (match) { + DEBUG_MSG_P(PSTR("[RF] Matched with the relay ID %u\n"), match.id()); + _rfb_relay_status_lock.set(match.id(), locked); - switch (match.status) { + switch (match.status()) { case PayloadStatus::On: case PayloadStatus::Off: - relayStatus(match.id, (PayloadStatus::On == match.status)); + relayStatus(match.id(), (PayloadStatus::On == match.status())); result = true; break; case PayloadStatus::Toggle: - relayToggle(match.id); + relayToggle(match.id()); result = true; case PayloadStatus::Unknown: break; @@ -529,13 +530,8 @@ void _rfbLearnStartFromPayload(const char* payload) { std::copy(payload, sep, relay); - char *endptr = nullptr; - const auto id = strtoul(relay, &endptr, 10); - if (endptr == &relay[0] || endptr[0] != '\0') { - return; - } - - if (id >= relayCount()) { + size_t id; + if (!tryParseId(relay, relayCount(), id)) { DEBUG_MSG_P(PSTR("[RF] Invalid relay ID (%u)\n"), id); return; } @@ -580,7 +576,7 @@ bool _rfbEnqueue(const char* code, size_t length, unsigned char repeats = 1u) { return false; } -void _rfbSendRaw(const uint8_t* message, unsigned char size) { +void _rfbSendRaw(const uint8_t* message, size_t size) { Serial.write(message, size); } @@ -1075,6 +1071,20 @@ void _rfbApiSetup() { #if TERMINAL_SUPPORT +void _rfbCommandStatusDispatch(const terminal::CommandContext& ctx, size_t id, const String& payload, RelayStatusCallback callback) { + auto parsed = rpcParsePayload(payload.c_str()); + switch (parsed) { + case PayloadStatus::On: + case PayloadStatus::Off: + callback(id, (parsed == PayloadStatus::On)); + return; + case PayloadStatus::Toggle: + case PayloadStatus::Unknown: + terminalError(ctx, F("Invalid status")); + break; + } +} + void _rfbInitCommands() { terminalRegisterCommand(F("RFB.SEND"), [](const terminal::CommandContext& ctx) { @@ -1093,38 +1103,35 @@ void _rfbInitCommands() { return; } - int id = ctx.argv[1].toInt(); - if (id >= relayCount()) { + size_t id; + if (!tryParseId(ctx.argv[1].c_str(), relayCount, id)) { terminalError(ctx, F("Invalid relay ID")); return; } - rfbLearn(id, (ctx.argv[2].toInt()) == 1); - - terminalOK(ctx); - + _rfbCommandStatusDispatch(ctx, id, ctx.argv[2], rfbLearn); }); terminalRegisterCommand(F("RFB.FORGET"), [](const terminal::CommandContext& ctx) { - if (ctx.argc < 2) { terminalError(ctx, F("RFB.FORGET []")); return; } - int id = ctx.argv[1].toInt(); - if (id >= relayCount()) { + size_t id; + if (!tryParseId(ctx.argv[1].c_str(), relayCount, id)) { terminalError(ctx, F("Invalid relay ID")); return; } if (ctx.argc == 3) { - rfbForget(id, (ctx.argv[2].toInt()) == 1); - } else { - rfbForget(id, true); - rfbForget(id, false); + _rfbCommandStatusDispatch(ctx, id, ctx.argv[2], rfbForget); + return; } + rfbForget(id, true); + rfbForget(id, false); + terminalOK(ctx); }); #endif // if RELAY_SUPPORT @@ -1148,19 +1155,19 @@ void _rfbInitCommands() { // PUBLIC // ----------------------------------------------------------------------------- -void rfbStore(unsigned char id, bool status, const char * code) { +void rfbStore(size_t id, bool status, const char * code) { SettingsKey key { status ? F("rfbON") : F("rfbOFF"), id }; setSetting(key, code); - DEBUG_MSG_P(PSTR("[RF] Saved %s => \"%s\"\n"), key.toString().c_str(), code); + DEBUG_MSG_P(PSTR("[RF] Saved %s => \"%s\"\n"), key.c_str(), code); } -String rfbRetrieve(unsigned char id, bool status) { +String rfbRetrieve(size_t id, bool status) { return getSetting({ status ? F("rfbON") : F("rfbOFF"), id }); } #if RELAY_SUPPORT -void rfbStatus(unsigned char id, bool status) { +void rfbStatus(size_t id, bool status) { // TODO: This is a left-over from the old implementation. Right now we set this lock when relay handler // is called within the receiver, while this is called from either relayStatus or relay loop calling // this via provider callback. This prevents us from re-sending the code we just received. @@ -1173,12 +1180,12 @@ void rfbStatus(unsigned char id, bool status) { _rfb_relay_status_lock[id] = false; } -void rfbLearn(unsigned char id, bool status) { +void rfbLearn(size_t id, bool status) { _rfb_learn.reset(new RfbLearn { millis(), id, status }); _rfbLearnImpl(); } -void rfbForget(unsigned char id, bool status) { +void rfbForget(size_t id, bool status) { delSetting({status ? F("rfbON") : F("rfbOFF"), id}); @@ -1186,7 +1193,7 @@ void rfbForget(unsigned char id, bool status) { // we send these in bulk is at the very start of the connection #if WEB_SUPPORT wsPost([id](JsonObject& root) { - _rfbWebSocketSendCodeArray(root, id, 1); + _rfbWebSocketSendCodeArray(root, id, 1ul); }); #endif diff --git a/code/espurna/rfbridge.h b/code/espurna/rfbridge.h index eac7a667..a6eed9e1 100644 --- a/code/espurna/rfbridge.h +++ b/code/espurna/rfbridge.h @@ -19,13 +19,13 @@ BrokerDeclare(RfbridgeBroker, void(unsigned char protocol, const char* code)); void rfbSend(const char* code); void rfbSend(const String& code); -void rfbStatus(unsigned char id, bool status); -void rfbLearn(unsigned char id, bool status); +void rfbStatus(size_t id, bool status); +void rfbLearn(size_t id, bool status); -String rfbRetrieve(unsigned char id, bool status); -void rfbStore(unsigned char id, bool status, const char * code); +String rfbRetrieve(size_t id, bool status); +void rfbStore(size_t id, bool status, const char* code); -void rfbForget(unsigned char id, bool status); +void rfbForget(size_t id, bool status); void rfbSetup(); #endif // RFB_SUPPORT == 1 diff --git a/code/espurna/settings.cpp b/code/espurna/settings.cpp index cfdcbf12..fd42dcd0 100644 --- a/code/espurna/settings.cpp +++ b/code/espurna/settings.cpp @@ -211,33 +211,39 @@ String settingsQueryDefaults(const String& key) { return String(); } -settings_move_key_t _moveKeys(const String& from, const String& to, unsigned char index) { +settings_move_key_t _moveKeys(const String& from, const String& to, size_t index) { return settings_move_key_t {{from, index}, {to, index}}; } void moveSetting(const String& from, const String& to) { - const auto value = getSetting(from); - if (value.length() > 0) setSetting(to, value); + auto result = settings::kv_store.get(from); + if (result) { + setSetting(to, result.ref()); + } delSetting(from); } void moveSetting(const String& from, const String& to, unsigned char index) { const auto keys = _moveKeys(from, to, index); - const auto value = getSetting(keys.first); - if (value.length() > 0) setSetting(keys.second, value); + + auto result = settings::kv_store.get(keys.first.value()); + if (result) { + setSetting(keys.second, result.ref()); + } delSetting(keys.first); } void moveSettings(const String& from, const String& to) { - unsigned char index = 0; - while (index < 100) { + for (size_t index = 0; index < 100; ++index) { const auto keys = _moveKeys(from, to, index); - const auto value = getSetting(keys.first); - if (value.length() == 0) break; - setSetting(keys.second, value); + auto result = settings::kv_store.get(keys.first.value()); + if (!result) { + break; + } + + setSetting(keys.second, result.ref()); delSetting(keys.first); - ++index; } } @@ -516,7 +522,7 @@ void _settingsInitCommands() { continue; } - ctx.output.printf("> %s => \"%s\"\n", key.c_str(), result.value.c_str()); + ctx.output.printf("> %s => \"%s\"\n", key.c_str(), result.c_str()); } terminalOK(ctx); diff --git a/code/espurna/settings_embedis.h b/code/espurna/settings_embedis.h index 2a1dfb3f..8e3f33d5 100644 --- a/code/espurna/settings_embedis.h +++ b/code/espurna/settings_embedis.h @@ -489,11 +489,9 @@ class KeyValueStore { auto key_result = kv.key.read(); if (key_result == key) { - if (read_value) { - out = std::move(kv.value.read(); - } else { - out = String(); - } + out = read_value + ? std::move(kv.value.read()) + : String(); break; } } while (_state != State::End); diff --git a/code/espurna/thingspeak.cpp b/code/espurna/thingspeak.cpp index 203502bb..d27fa3a8 100644 --- a/code/espurna/thingspeak.cpp +++ b/code/espurna/thingspeak.cpp @@ -50,7 +50,7 @@ const char THINGSPEAK_REQUEST_TEMPLATE[] PROGMEM = bool _tspk_enabled = false; bool _tspk_clear = false; -char * _tspk_queue[THINGSPEAK_FIELDS] = {NULL}; +String _tspk_fields[THINGSPEAK_FIELDS]; String _tspk_data; bool _tspk_flush = false; @@ -136,14 +136,11 @@ void _tspkConfigure() { #endif } -void _tspkClearQueue() { +void _tspkClearFields() { _tspk_tries = THINGSPEAK_TRIES; if (_tspk_clear) { - for (unsigned char id=0; id 0) { + DEBUG_MSG_P(PSTR("[THINGSPEAK] Enqueuing field #%hhu with value %s\n"), index, payload); + _tspk_fields[--index] = payload; + } } void _tspkFlush() { @@ -390,11 +387,11 @@ void _tspkFlush() { _tspk_data.reserve(tspkDataBufferSize); // Walk the fields, numbered 1...THINGSPEAK_FIELDS - for (unsigned char id=0; id 0) _tspk_data.concat("&"); char buf[32] = {0}; - snprintf_P(buf, sizeof(buf), PSTR("field%u=%s"), (id + 1), _tspk_queue[id]); + snprintf_P(buf, sizeof(buf), PSTR("field%u=%s"), (id + 1), _tspk_fields[id].c_str()); _tspk_data.concat(buf); } } @@ -411,14 +408,17 @@ void _tspkFlush() { // ----------------------------------------------------------------------------- -bool tspkEnqueueRelay(unsigned char index, bool status) { - if (!_tspk_enabled) return true; - unsigned char id = getSetting({"tspkRelay", index}, 0); - if (id > 0) { - _tspkEnqueue(id, status ? "1" : "0"); - return true; +bool tspkEnqueueRelay(size_t index, bool status) { + if (_tspk_enabled) { + unsigned char id = getSetting({"tspkRelay", index}, 0); + if (id > 0) { + _tspkEnqueue(id, status ? "1" : "0"); + return true; + } + return false; } - return false; + + return true; } bool tspkEnqueueMeasurement(unsigned char index, const char * payload) { diff --git a/code/espurna/thingspeak.h b/code/espurna/thingspeak.h index 6b8e2c0d..3517548c 100644 --- a/code/espurna/thingspeak.h +++ b/code/espurna/thingspeak.h @@ -12,7 +12,7 @@ Copyright (C) 2019 by Xose Pérez constexpr size_t tspkDataBufferSize { 256ul }; -bool tspkEnqueueRelay(unsigned char index, bool status); +bool tspkEnqueueRelay(size_t index, bool status); bool tspkEnqueueMeasurement(unsigned char index, const char * payload); void tspkFlush(); diff --git a/code/espurna/tuya.cpp b/code/espurna/tuya.cpp index 8f280b1e..b43f26fc 100644 --- a/code/espurna/tuya.cpp +++ b/code/espurna/tuya.cpp @@ -139,7 +139,7 @@ namespace tuya { } } - void channel(unsigned char channel, float value) override { + void channel(size_t channel, float value) override { // XXX: can't handle channel values when OFF, and will turn the lights ON if (!_last_state) { return; @@ -520,7 +520,7 @@ error: namespace build { - constexpr unsigned char channelDpId(unsigned char index) { + constexpr unsigned char channelDpId(size_t index) { return (index == 0) ? TUYA_CH1_DPID : (index == 1) ? TUYA_CH2_DPID : (index == 2) ? TUYA_CH3_DPID : @@ -528,7 +528,7 @@ error: (index == 4) ? TUYA_CH5_DPID : 0u; } - constexpr unsigned char switchDpId(unsigned char index) { + constexpr unsigned char switchDpId(size_t index) { return (index == 0) ? TUYA_SW1_DPID : (index == 1) ? TUYA_SW2_DPID : (index == 2) ? TUYA_SW3_DPID : @@ -551,7 +551,7 @@ error: void setupSwitches() { bool done { false }; - for (unsigned char id = 0; id < RelaysMax; ++id) { + for (size_t id = 0; id < RelaysMax; ++id) { auto dp = getSetting({"tuyaSwitch", id}, build::switchDpId(id)); if (!dp) { break; @@ -577,7 +577,7 @@ error: void setupChannels() { bool done { false }; - for (unsigned char id = 0; id < Light::ChannelsMax; ++id) { + for (size_t id = 0; id < Light::ChannelsMax; ++id) { auto dp = getSetting({"tuyaChannel", id}, build::channelDpId(id)); if (!dp) { break; diff --git a/code/espurna/utils.cpp b/code/espurna/utils.cpp index cd5b6f09..27c64a51 100644 --- a/code/espurna/utils.cpp +++ b/code/espurna/utils.cpp @@ -11,6 +11,18 @@ Copyright (C) 2017-2019 by Xose Pérez #include "board.h" #include "ntp.h" +bool tryParseId(const char* p, TryParseIdFunc limit, size_t& out) { + static_assert(std::numeric_limits::max() >= std::numeric_limits::max(), ""); + + char* endp { nullptr }; + out = strtoul(p, &endp, 10); + if ((endp == p) || (*endp != '\0') || (out >= limit())) { + return false; + } + + return true; +} + void setDefaultHostname() { if (strlen(HOSTNAME) > 0) { setSetting("hostname", F(HOSTNAME)); diff --git a/code/espurna/utils.h b/code/espurna/utils.h index dc485033..5abc5912 100644 --- a/code/espurna/utils.h +++ b/code/espurna/utils.h @@ -52,3 +52,6 @@ double roundTo(double num, unsigned char positions); size_t hexEncode(const uint8_t* in, size_t in_size, char* out, size_t out_size); size_t hexDecode(const char* in, size_t in_size, uint8_t* out, size_t out_size); + +using TryParseIdFunc = size_t(*)(); +bool tryParseId(const char* ptr, TryParseIdFunc limit, size_t& out);