Browse Source

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
mcspr-patch-1
Maxim Prokhorov 3 years ago
parent
commit
cef9547c19
6 changed files with 213 additions and 45 deletions
  1. +50
    -0
      code/espurna/config/defaults.h
  2. +2
    -2
      code/espurna/config/general.h
  3. +7
    -6
      code/espurna/config/types.h
  4. +116
    -37
      code/espurna/relay.cpp
  5. +12
    -0
      code/espurna/relay.h
  6. +26
    -0
      code/espurna/relay_config.h

+ 50
- 0
code/espurna/config/defaults.h View File

@ -837,6 +837,56 @@
#define RELAY8_BOOT_MODE RELAY_BOOT_MODE #define RELAY8_BOOT_MODE RELAY_BOOT_MODE
#endif #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 // LEDs
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------


+ 2
- 2
code/espurna/config/general.h View File

@ -486,14 +486,14 @@
#define RELAY_DELAY_INTERLOCK 0 #define RELAY_DELAY_INTERLOCK 0
#endif #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 #ifndef RELAY_PULSE_MODE
#define RELAY_PULSE_MODE RELAY_PULSE_NONE #define RELAY_PULSE_MODE RELAY_PULSE_NONE
#endif #endif
// Default pulse time in seconds // Default pulse time in seconds
#ifndef RELAY_PULSE_TIME #ifndef RELAY_PULSE_TIME
#define RELAY_PULSE_TIME 1.0
#define RELAY_PULSE_TIME 0.0
#endif #endif
// Relay requests flood protection window - in seconds // Relay requests flood protection window - in seconds


+ 7
- 6
code/espurna/config/types.h View File

@ -109,9 +109,9 @@
#define RELAY_SYNC_SAME 3 #define RELAY_SYNC_SAME 3
#define RELAY_SYNC_FIRST 4 #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_NONE RelayProvider::None
#define RELAY_PROVIDER_DUMMY RelayProvider::Dummy #define RELAY_PROVIDER_DUMMY RelayProvider::Dummy
@ -126,9 +126,10 @@
#define RELAY_GROUP_SYNC_INVERSE 1 #define RELAY_GROUP_SYNC_INVERSE 1
#define RELAY_GROUP_SYNC_RECEIVEONLY 2 #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 // UDP SYSLOG


+ 116
- 37
code/espurna/relay.cpp View File

@ -96,9 +96,68 @@ private:
} // namespace } // namespace
template <typename T>
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 <typename T>
const char* _relayTristateToPayload(T tristate) {
static_assert(std::is_enum<T>::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 settings {
namespace internal { namespace internal {
template <>
RelayPulse convert(const String& value) {
return _relayPayloadToTristate<RelayPulse>(value.c_str());
}
template <>
RelayLock convert(const String& value) {
return _relayPayloadToTristate<RelayLock>(value.c_str());
}
template <> template <>
RelayProvider convert(const String& value) { RelayProvider convert(const String& value) {
auto type = static_cast<RelayProvider>(value.toInt()); auto type = static_cast<RelayProvider>(value.toInt());
@ -163,7 +222,7 @@ public:
unsigned long delay_on { 0ul }; // Delay to turn relay ON unsigned long delay_on { 0ul }; // Delay to turn relay ON
unsigned long delay_off { 0ul }; // Delay to turn relay OFF 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 unsigned long pulse_ms { 0ul }; // Pulse length in millis
Ticker* pulseTicker { nullptr }; // Holds the pulse back timer Ticker* pulseTicker { nullptr }; // Holds the pulse back timer
@ -176,7 +235,7 @@ public:
// Status // Status
bool current_status { false }; // Holds the current (physical) status of the relay bool current_status { false }; // Holds the current (physical) status of the relay
bool target_status { false }; // Holds the target status 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 // MQTT
bool report { false }; // Whether to report to own topic bool report { false }; // Whether to report to own topic
@ -564,12 +623,12 @@ bool _relayHandlePulsePayload(unsigned char id, const char* payload) {
return false; 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); DEBUG_MSG_P(PSTR("[RELAY] Overriding relayID %u pulse settings\n"), id);
} }
_relays[id].pulse_ms = pulse; _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); relayToggle(id, true, false);
return true; return true;
@ -592,21 +651,21 @@ PayloadStatus _relayStatusTyped(unsigned char id) {
void _relayLockAll() { void _relayLockAll() {
for (auto& relay : _relays) { 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; _relay_sync_locked = true;
} }
void _relayUnlockAll() { void _relayUnlockAll() {
for (auto& relay : _relays) { for (auto& relay : _relays) {
relay.lock = RELAY_LOCK_DISABLED;
relay.lock = RelayLock::None;
} }
_relay_sync_locked = false; _relay_sync_locked = false;
} }
bool _relayStatusLock(unsigned char id, bool status) { 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)) { if ((lock != status) || (lock != _relays[id].target_status)) {
_relays[id].target_status = lock; _relays[id].target_status = lock;
_relays[id].change_delay = 0; _relays[id].change_delay = 0;
@ -781,7 +840,7 @@ void relayPulse(unsigned char id) {
relay.pulseTicker->detach(); relay.pulseTicker->detach();
auto mode = relay.pulse; auto mode = relay.pulse;
if (mode == RELAY_PULSE_NONE) {
if (mode == RelayPulse::None) {
return; 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 // 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 // > 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)) { if ((ms < 5) || (ms >= 0x68D7A3)) {
DEBUG_MSG_P(PSTR("[RELAY] Unable to schedule the delay %lums (longer than 114 minutes)\n"), ms); DEBUG_MSG_P(PSTR("[RELAY] Unable to schedule the delay %lums (longer than 114 minutes)\n"), ms);
return; 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); DEBUG_MSG_P(PSTR("[RELAY] Scheduling relay #%d back in %lums (pulse)\n"), id, ms);
relay.pulseTicker->once_ms(ms, relayToggle, id); relay.pulseTicker->once_ms(ms, relayToggle, id);
// Reconfigure after dynamic pulse // 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<unsigned long>(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)); const auto boot_mode = getSetting({"relayBoot", index}, _relayBootMode(index));
auto status = false; auto status = false;
auto lock = RELAY_LOCK_DISABLED;
auto lock = RelayLock::None;
switch (boot_mode) { switch (boot_mode) {
case RELAY_BOOT_SAME: case RELAY_BOOT_SAME:
@ -1059,14 +1118,14 @@ void _relayBoot(unsigned char index, const RelayMaskHelper& mask) {
break; break;
case RELAY_BOOT_LOCKED_ON: case RELAY_BOOT_LOCKED_ON:
status = true; status = true;
lock = RELAY_LOCK_ON;
lock = RelayLock::On;
break; break;
case RELAY_BOOT_OFF: case RELAY_BOOT_OFF:
status = false; status = false;
break; break;
case RELAY_BOOT_LOCKED_OFF: case RELAY_BOOT_LOCKED_OFF:
status = false; status = false;
lock = RELAY_LOCK_OFF;
lock = RelayLock::Off;
break; break;
} }
@ -1113,8 +1172,8 @@ void _relayBootAll() {
void _relayConfigure() { void _relayConfigure() {
for (unsigned char i = 0, relays = _relays.size() ; (i < relays); ++i) { 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<unsigned long>(1000.0 * getSetting({"relayTime", i}, _relayPulseTime(i)));
_relays[i].delay_on = getSetting({"relayDelayOn", i}, _relayDelayOn(i)); _relays[i].delay_on = getSetting({"relayDelayOn", i}, _relayDelayOn(i));
_relays[i].delay_off = getSetting({"relayDelayOff", i}, _relayDelayOff(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 // Note: we use byte instead of bool to ever so slightly compress json output
for (unsigned char i=0; i<relayCount(); i++) { for (unsigned char i=0; i<relayCount(); i++) {
status.add<uint8_t>(_relays[i].target_status); status.add<uint8_t>(_relays[i].target_status);
lock.add(_relays[i].lock);
lock.add(static_cast<uint8_t>(_relays[i].lock));
} }
} }
@ -1189,7 +1248,7 @@ void _relayWebSocketSendRelays(JsonObject& root) {
relay.add(getSetting({"relayName", id})); relay.add(getSetting({"relayName", id}));
relay.add(getSetting({"relayBoot", id}, _relayBootMode(id))); relay.add(getSetting({"relayBoot", id}, _relayBootMode(id)));
relay.add(_relays[id].pulse);
relay.add(static_cast<uint8_t>(_relays[id].pulse));
relay.add(_relays[id].pulse_ms / 1000.0); relay.add(_relays[id].pulse_ms / 1000.0);
#if SCHEDULER_SUPPORT #if SCHEDULER_SUPPORT
@ -1534,18 +1593,45 @@ void relaySetupMQTT() {
void _relayInitCommands() { void _relayInitCommands() {
terminalRegisterCommand(F("RELAY"), [](const terminal::CommandContext& ctx) { 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]; 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); terminalOK(ctx);
return; return;
} }
@ -1566,14 +1652,7 @@ void _relayInitCommands() {
_relayHandleStatus(id, status); _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); terminalOK(ctx);
}); });


+ 12
- 0
code/espurna/relay.h View File

@ -13,6 +13,18 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
constexpr size_t RelaysMax = 32; 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 { enum class RelayType : int {
Normal, Normal,
Inverse, Inverse,


+ 26
- 0
code/espurna/relay_config.h View File

@ -8,6 +8,32 @@ RELAY MODULE
#include "espurna.h" #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) { constexpr unsigned long _relayDelayOn(unsigned char index) {
return ( return (
(index == 0) ? RELAY1_DELAY_ON : (index == 0) ? RELAY1_DELAY_ON :


Loading…
Cancel
Save