From cef9547c19757b6268c1c87b50d80620a68f2f79 Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Wed, 20 Jan 2021 00:52:53 +0300 Subject: [PATCH] relay: defaults for pulse mode and time - allow strings in mode (off, on, none) - fixup type used for the mode to be am enum instead of int (todo: allow webui to accept strings?) - some cli updates using new funcs --- code/espurna/config/defaults.h | 50 +++++++++++ code/espurna/config/general.h | 4 +- code/espurna/config/types.h | 13 +-- code/espurna/relay.cpp | 153 +++++++++++++++++++++++++-------- code/espurna/relay.h | 12 +++ code/espurna/relay_config.h | 26 ++++++ 6 files changed, 213 insertions(+), 45 deletions(-) diff --git a/code/espurna/config/defaults.h b/code/espurna/config/defaults.h index d584e1a7..fd3b9d56 100644 --- a/code/espurna/config/defaults.h +++ b/code/espurna/config/defaults.h @@ -837,6 +837,56 @@ #define RELAY8_BOOT_MODE RELAY_BOOT_MODE #endif +#ifndef RELAY1_PULSE_MODE +#define RELAY1_PULSE_MODE RELAY_PULSE_MODE +#endif +#ifndef RELAY2_PULSE_MODE +#define RELAY2_PULSE_MODE RELAY_PULSE_MODE +#endif +#ifndef RELAY3_PULSE_MODE +#define RELAY3_PULSE_MODE RELAY_PULSE_MODE +#endif +#ifndef RELAY4_PULSE_MODE +#define RELAY4_PULSE_MODE RELAY_PULSE_MODE +#endif +#ifndef RELAY5_PULSE_MODE +#define RELAY5_PULSE_MODE RELAY_PULSE_MODE +#endif +#ifndef RELAY6_PULSE_MODE +#define RELAY6_PULSE_MODE RELAY_PULSE_MODE +#endif +#ifndef RELAY7_PULSE_MODE +#define RELAY7_PULSE_MODE RELAY_PULSE_MODE +#endif +#ifndef RELAY8_PULSE_MODE +#define RELAY8_PULSE_MODE RELAY_PULSE_MODE +#endif + +#ifndef RELAY1_PULSE_TIME +#define RELAY1_PULSE_TIME RELAY_PULSE_TIME +#endif +#ifndef RELAY2_PULSE_TIME +#define RELAY2_PULSE_TIME RELAY_PULSE_TIME +#endif +#ifndef RELAY3_PULSE_TIME +#define RELAY3_PULSE_TIME RELAY_PULSE_TIME +#endif +#ifndef RELAY4_PULSE_TIME +#define RELAY4_PULSE_TIME RELAY_PULSE_TIME +#endif +#ifndef RELAY5_PULSE_TIME +#define RELAY5_PULSE_TIME RELAY_PULSE_TIME +#endif +#ifndef RELAY6_PULSE_TIME +#define RELAY6_PULSE_TIME RELAY_PULSE_TIME +#endif +#ifndef RELAY7_PULSE_TIME +#define RELAY7_PULSE_TIME RELAY_PULSE_TIME +#endif +#ifndef RELAY8_PULSE_TIME +#define RELAY8_PULSE_TIME RELAY_PULSE_TIME +#endif + // ----------------------------------------------------------------------------- // LEDs // ----------------------------------------------------------------------------- diff --git a/code/espurna/config/general.h b/code/espurna/config/general.h index 5c7f75f6..93282656 100644 --- a/code/espurna/config/general.h +++ b/code/espurna/config/general.h @@ -486,14 +486,14 @@ #define RELAY_DELAY_INTERLOCK 0 #endif -// Default pulse mode: 0 means no pulses, 1 means normally off, 2 normally on +// Default pulse mode / normal mode. Switching from it will start the 'pulse' timer and reset the relay back after it finishes #ifndef RELAY_PULSE_MODE #define RELAY_PULSE_MODE RELAY_PULSE_NONE #endif // Default pulse time in seconds #ifndef RELAY_PULSE_TIME -#define RELAY_PULSE_TIME 1.0 +#define RELAY_PULSE_TIME 0.0 #endif // Relay requests flood protection window - in seconds diff --git a/code/espurna/config/types.h b/code/espurna/config/types.h index eccc7f23..ed8d1039 100644 --- a/code/espurna/config/types.h +++ b/code/espurna/config/types.h @@ -109,9 +109,9 @@ #define RELAY_SYNC_SAME 3 #define RELAY_SYNC_FIRST 4 -#define RELAY_PULSE_NONE 0 -#define RELAY_PULSE_OFF 1 -#define RELAY_PULSE_ON 2 +#define RELAY_PULSE_NONE RelayPulse::None +#define RELAY_PULSE_OFF RelayPulse::Off +#define RELAY_PULSE_ON RelayPulse::On #define RELAY_PROVIDER_NONE RelayProvider::None #define RELAY_PROVIDER_DUMMY RelayProvider::Dummy @@ -126,9 +126,10 @@ #define RELAY_GROUP_SYNC_INVERSE 1 #define RELAY_GROUP_SYNC_RECEIVEONLY 2 -#define RELAY_LOCK_DISABLED 0 -#define RELAY_LOCK_OFF 1 -#define RELAY_LOCK_ON 2 +#define RELAY_LOCK_DISABLED RelayLock::None +#define RELAY_LOCK_NONE RelayLock::None +#define RELAY_LOCK_OFF RelayLock::Off +#define RELAY_LOCK_ON RelayLock::On //------------------------------------------------------------------------------ // UDP SYSLOG diff --git a/code/espurna/relay.cpp b/code/espurna/relay.cpp index 558de7cd..ffc1452e 100644 --- a/code/espurna/relay.cpp +++ b/code/espurna/relay.cpp @@ -96,9 +96,68 @@ private: } // namespace +template +T _relayPayloadToTristate(const char* payload) { + auto len = strlen(payload); + if (len == 1) { + switch (payload[0]) { + case '0': + return T::None; + case '1': + return T::Off; + case '2': + return T::On; + } + } else if (len > 1) { + String cmp(payload); + if (cmp == "none") { + return T::None; + } else if (cmp == "off") { + return T::Off; + } else if (cmp == "on") { + return T::On; + } + } + + return T::None; +} + +template +const char* _relayTristateToPayload(T tristate) { + static_assert(std::is_enum::value, ""); + switch (tristate) { + case T::Off: + return "off"; + case T::On: + return "on"; + case T::None: + break; + } + + return "none"; +} + +const char* _relayPulseToPayload(RelayPulse pulse) { + return _relayTristateToPayload(pulse); +} + +const char* _relayLockToPayload(RelayLock lock) { + return _relayTristateToPayload(lock); +} + namespace settings { namespace internal { +template <> +RelayPulse convert(const String& value) { + return _relayPayloadToTristate(value.c_str()); +} + +template <> +RelayLock convert(const String& value) { + return _relayPayloadToTristate(value.c_str()); +} + template <> RelayProvider convert(const String& value) { auto type = static_cast(value.toInt()); @@ -163,7 +222,7 @@ public: unsigned long delay_on { 0ul }; // Delay to turn relay ON unsigned long delay_off { 0ul }; // Delay to turn relay OFF - unsigned char pulse { RELAY_PULSE_NONE }; // RELAY_PULSE_NONE, RELAY_PULSE_OFF or RELAY_PULSE_ON + RelayPulse pulse { RelayPulse::None }; // Sets up a timer for the opposite mode unsigned long pulse_ms { 0ul }; // Pulse length in millis Ticker* pulseTicker { nullptr }; // Holds the pulse back timer @@ -176,7 +235,7 @@ public: // Status bool current_status { false }; // Holds the current (physical) status of the relay bool target_status { false }; // Holds the target status - unsigned char lock { RELAY_LOCK_DISABLED }; // Holds the value of target status, that cannot be changed afterwards. (0 for false, 1 for true, 2 to disable) + RelayLock lock { RelayLock::None }; // Holds the value of target status that persists and cannot be changed from. // MQTT bool report { false }; // Whether to report to own topic @@ -564,12 +623,12 @@ bool _relayHandlePulsePayload(unsigned char id, const char* payload) { return false; } - if (RELAY_PULSE_NONE != _relays[id].pulse) { + if (RelayPulse::None != _relays[id].pulse) { DEBUG_MSG_P(PSTR("[RELAY] Overriding relayID %u pulse settings\n"), id); } _relays[id].pulse_ms = pulse; - _relays[id].pulse = relayStatus(id) ? RELAY_PULSE_ON : RELAY_PULSE_OFF; + _relays[id].pulse = relayStatus(id) ? RelayPulse::On : RelayPulse::Off; relayToggle(id, true, false); return true; @@ -592,21 +651,21 @@ PayloadStatus _relayStatusTyped(unsigned char id) { void _relayLockAll() { for (auto& relay : _relays) { - relay.lock = relay.target_status ? RELAY_LOCK_ON : RELAY_LOCK_OFF; + relay.lock = relay.target_status ? RelayLock::On : RelayLock::Off; } _relay_sync_locked = true; } void _relayUnlockAll() { for (auto& relay : _relays) { - relay.lock = RELAY_LOCK_DISABLED; + relay.lock = RelayLock::None; } _relay_sync_locked = false; } bool _relayStatusLock(unsigned char id, bool status) { - if (_relays[id].lock != RELAY_LOCK_DISABLED) { - bool lock = _relays[id].lock == RELAY_LOCK_ON; + if (_relays[id].lock != RelayLock::None) { + bool lock = _relays[id].lock == RelayLock::On; if ((lock != status) || (lock != _relays[id].target_status)) { _relays[id].target_status = lock; _relays[id].change_delay = 0; @@ -781,7 +840,7 @@ void relayPulse(unsigned char id) { relay.pulseTicker->detach(); auto mode = relay.pulse; - if (mode == RELAY_PULSE_NONE) { + if (mode == RelayPulse::None) { return; } @@ -796,18 +855,18 @@ void relayPulse(unsigned char id) { // limit is per https://www.espressif.com/sites/default/files/documentation/2c-esp8266_non_os_sdk_api_reference_en.pdf // > 3.1.1 os_timer_arm - // > the timer value allowed ranges from 5 to 0x68D7A3. + // > the timer value allowed ranges from 5 to 0x68D7A3. if ((ms < 5) || (ms >= 0x68D7A3)) { DEBUG_MSG_P(PSTR("[RELAY] Unable to schedule the delay %lums (longer than 114 minutes)\n"), ms); return; } - if ((mode == RELAY_PULSE_ON) != relay.current_status) { + if ((mode == RelayPulse::On) != relay.current_status) { DEBUG_MSG_P(PSTR("[RELAY] Scheduling relay #%d back in %lums (pulse)\n"), id, ms); relay.pulseTicker->once_ms(ms, relayToggle, id); // Reconfigure after dynamic pulse - relay.pulse = getSetting({"relayPulse", id}, RELAY_PULSE_MODE); - relay.pulse_ms = 1000 * getSetting({"relayTime", id}, 0.); + relay.pulse = getSetting({"relayPulse", id}, _relayPulseMode(id)); + relay.pulse_ms = static_cast(1000.0 * getSetting({"relayTime", id}, _relayPulseTime(id))); } } @@ -1045,7 +1104,7 @@ void _relayBoot(unsigned char index, const RelayMaskHelper& mask) { const auto boot_mode = getSetting({"relayBoot", index}, _relayBootMode(index)); auto status = false; - auto lock = RELAY_LOCK_DISABLED; + auto lock = RelayLock::None; switch (boot_mode) { case RELAY_BOOT_SAME: @@ -1059,14 +1118,14 @@ void _relayBoot(unsigned char index, const RelayMaskHelper& mask) { break; case RELAY_BOOT_LOCKED_ON: status = true; - lock = RELAY_LOCK_ON; + lock = RelayLock::On; break; case RELAY_BOOT_OFF: status = false; break; case RELAY_BOOT_LOCKED_OFF: status = false; - lock = RELAY_LOCK_OFF; + lock = RelayLock::Off; break; } @@ -1113,8 +1172,8 @@ void _relayBootAll() { void _relayConfigure() { for (unsigned char i = 0, relays = _relays.size() ; (i < relays); ++i) { - _relays[i].pulse = getSetting({"relayPulse", i}, RELAY_PULSE_MODE); - _relays[i].pulse_ms = 1000 * getSetting({"relayTime", i}, 0.); + _relays[i].pulse = getSetting({"relayPulse", i}, _relayPulseMode(i)); + _relays[i].pulse_ms = static_cast(1000.0 * getSetting({"relayTime", i}, _relayPulseTime(i))); _relays[i].delay_on = getSetting({"relayDelayOn", i}, _relayDelayOn(i)); _relays[i].delay_off = getSetting({"relayDelayOff", i}, _relayDelayOff(i)); @@ -1155,7 +1214,7 @@ void _relayWebSocketUpdate(JsonObject& root) { // Note: we use byte instead of bool to ever so slightly compress json output for (unsigned char i=0; i(_relays[i].target_status); - lock.add(_relays[i].lock); + lock.add(static_cast(_relays[i].lock)); } } @@ -1189,7 +1248,7 @@ void _relayWebSocketSendRelays(JsonObject& root) { relay.add(getSetting({"relayName", id})); relay.add(getSetting({"relayBoot", id}, _relayBootMode(id))); - relay.add(_relays[id].pulse); + relay.add(static_cast(_relays[id].pulse)); relay.add(_relays[id].pulse_ms / 1000.0); #if SCHEDULER_SUPPORT @@ -1534,18 +1593,45 @@ void relaySetupMQTT() { void _relayInitCommands() { terminalRegisterCommand(F("RELAY"), [](const terminal::CommandContext& ctx) { - if (ctx.argc == 1) { - for (unsigned char index = 0; index < _relays.size(); ++index) { + auto showRelays = [&](unsigned char start, unsigned char stop, bool full = true) { + for (unsigned char index = start; index < stop; ++index) { auto& relay = _relays[index]; - ctx.output.printf_P(PSTR("id=%02u provider=%s current=%s target=%s lock=%s\n"), - index, - relay.provider->id(), - relay.current_status ? "ON" : "OFF", relay.target_status ? "ON" : "OFF", - ((relay.lock == RELAY_LOCK_ON) ? "ON" : - (relay.lock == RELAY_LOCK_OFF) ? "OFF" : - "NONE") + + char pulse_info[64] = ""; + if ((relay.pulse != RelayPulse::None) && (relay.pulse_ms)) { + snprintf_P(pulse_info, sizeof(pulse_info), PSTR(" Pulse=%s Time=%u"), + _relayPulseToPayload(relay.pulse), relay.pulse_ms); + } + + char extended_info[64] = ""; + if (full) { + int index = 0; + if (index >= 0 && relay.delay_on) { + index += snprintf_P(extended_info + index, sizeof(extended_info), + PSTR(" DelayOn=%u"), relay.delay_on); + } + if (index >= 0 && relay.delay_off) { + index += snprintf_P(extended_info + index, sizeof(extended_info), + PSTR(" DelayOff=%u"), relay.delay_off); + } + if (index >= 0 && relay.lock != RelayLock::None) { + index += snprintf_P(extended_info + index, sizeof(extended_info), + PSTR(" Lock=%s"), _relayLockToPayload(relay.lock)); + } + } + + ctx.output.printf_P(PSTR("relay%u {Prov=%s Current=%s Target=%s%s%s}\n"), + index, relay.provider->id(), + relay.current_status ? "ON" : "OFF", + relay.target_status ? "ON" : "OFF", + pulse_info, + extended_info ); } + }; + + if (ctx.argc == 1) { + showRelays(0, _relays.size()); terminalOK(ctx); return; } @@ -1566,14 +1652,7 @@ void _relayInitCommands() { _relayHandleStatus(id, status); } - auto& relay = _relays[id]; - - ctx.output.printf_P(PSTR("Status: %s\n"), relay.target_status ? "ON" : "OFF"); - if ((relay.pulse != RELAY_PULSE_NONE) && (relay.pulse_ms)) { - ctx.output.printf_P(PSTR("Pulse: %s\n"), (relay.pulse == RELAY_PULSE_ON) ? "ON" : "OFF"); - ctx.output.printf_P(PSTR("Pulse time: %lu\n"), relay.pulse_ms); - } - + showRelays(id, id + 1, false); terminalOK(ctx); }); diff --git a/code/espurna/relay.h b/code/espurna/relay.h index ecd0dc76..f441b320 100644 --- a/code/espurna/relay.h +++ b/code/espurna/relay.h @@ -13,6 +13,18 @@ Copyright (C) 2016-2019 by Xose PĂ©rez constexpr size_t RelaysMax = 32; +enum class RelayPulse : uint8_t { + None, + Off, + On +}; + +enum class RelayLock : uint8_t { + None, + Off, + On +}; + enum class RelayType : int { Normal, Inverse, diff --git a/code/espurna/relay_config.h b/code/espurna/relay_config.h index 32e47e19..7d6ed61c 100644 --- a/code/espurna/relay_config.h +++ b/code/espurna/relay_config.h @@ -8,6 +8,32 @@ RELAY MODULE #include "espurna.h" +constexpr double _relayPulseTime(unsigned char index) { + return ( + (index == 0) ? RELAY1_PULSE_TIME : + (index == 1) ? RELAY2_PULSE_TIME : + (index == 2) ? RELAY3_PULSE_TIME : + (index == 3) ? RELAY4_PULSE_TIME : + (index == 4) ? RELAY5_PULSE_TIME : + (index == 5) ? RELAY6_PULSE_TIME : + (index == 6) ? RELAY7_PULSE_TIME : + (index == 7) ? RELAY8_PULSE_TIME : RELAY_PULSE_TIME + ); +} + +constexpr RelayPulse _relayPulseMode(unsigned char index) { + return ( + (index == 0) ? RELAY1_PULSE_MODE : + (index == 1) ? RELAY2_PULSE_MODE : + (index == 2) ? RELAY3_PULSE_MODE : + (index == 3) ? RELAY4_PULSE_MODE : + (index == 4) ? RELAY5_PULSE_MODE : + (index == 5) ? RELAY6_PULSE_MODE : + (index == 6) ? RELAY7_PULSE_MODE : + (index == 7) ? RELAY8_PULSE_MODE : RELAY_PULSE_NONE + ); +} + constexpr unsigned long _relayDelayOn(unsigned char index) { return ( (index == 0) ? RELAY1_DELAY_ON :