diff --git a/code/espurna/alexa.ino b/code/espurna/alexa.ino index 94ce0150..fc62d666 100644 --- a/code/espurna/alexa.ino +++ b/code/espurna/alexa.ino @@ -8,6 +8,7 @@ Copyright (C) 2016-2019 by Xose Pérez #if ALEXA_SUPPORT +#include "relay.h" #include "broker.h" #include diff --git a/code/espurna/button.ino b/code/espurna/button.ino index 07045fdc..e23413a1 100644 --- a/code/espurna/button.ino +++ b/code/espurna/button.ino @@ -15,6 +15,7 @@ Copyright (C) 2016-2019 by Xose Pérez #include #include +#include "relay.h" #include "light.h" typedef struct { diff --git a/code/espurna/config/general.h b/code/espurna/config/general.h index 510f908d..6cc823f7 100644 --- a/code/espurna/config/general.h +++ b/code/espurna/config/general.h @@ -429,11 +429,6 @@ #define RELAY_MQTT_TOGGLE "2" #endif -// TODO Only single EEPROM address is used to store state, which is 1 byte -// Relay status is stored using bitfield. -// This means that, atm, we are only storing the status of the first 8 relays. -#define RELAY_SAVE_MASK_MAX 8 - // ----------------------------------------------------------------------------- // WIFI // ----------------------------------------------------------------------------- diff --git a/code/espurna/config/prototypes.h b/code/espurna/config/prototypes.h index bbccefb6..84cfd457 100644 --- a/code/espurna/config/prototypes.h +++ b/code/espurna/config/prototypes.h @@ -250,36 +250,6 @@ typedef struct { int16_t rssi; } packet_t; -// ----------------------------------------------------------------------------- -// Relay -// ----------------------------------------------------------------------------- -#include - -enum class RelayStatus : unsigned char { - OFF = 0, - ON = 1, - TOGGLE = 2, - UNKNOWN = 0xFF -}; - -RelayStatus 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(unsigned char id); - -void relayToggle(unsigned char id, bool report, bool group_report); -void relayToggle(unsigned char id); - -unsigned char relayCount(); - -const String& relayPayloadOn(); -const String& relayPayloadOff(); -const String& relayPayloadToggle(); -const char* relayPayload(RelayStatus status); - -void relaySetupDummy(unsigned char size, bool reconfigure = false); - // ----------------------------------------------------------------------------- // Settings // ----------------------------------------------------------------------------- diff --git a/code/espurna/domoticz.ino b/code/espurna/domoticz.ino index f54a3a9c..2f516479 100644 --- a/code/espurna/domoticz.ino +++ b/code/espurna/domoticz.ino @@ -8,7 +8,9 @@ Copyright (C) 2016-2019 by Xose Pérez #if DOMOTICZ_SUPPORT +#include "relay.h" #include "broker.h" + #include bool _dcz_enabled = false; diff --git a/code/espurna/espurna.ino b/code/espurna/espurna.ino index 0206acc3..c5587383 100644 --- a/code/espurna/espurna.ino +++ b/code/espurna/espurna.ino @@ -22,6 +22,8 @@ along with this program. If not, see . #include "config/all.h" #include +#include "utils.h" +#include "relay.h" #include "broker.h" #include "tuya.h" #include "libs/HeapStats.h" diff --git a/code/espurna/ir.ino b/code/espurna/ir.ino index 780dbe7a..ccd6237a 100644 --- a/code/espurna/ir.ino +++ b/code/espurna/ir.ino @@ -48,6 +48,8 @@ Raw messages: #if IR_SUPPORT +#include "relay.h" + #include #if defined(IR_RX_PIN) diff --git a/code/espurna/led.ino b/code/espurna/led.ino index 3eca7371..d743328b 100644 --- a/code/espurna/led.ino +++ b/code/espurna/led.ino @@ -12,6 +12,7 @@ Copyright (C) 2016-2019 by Xose Pérez #if LED_SUPPORT +#include "relay.h" #include "broker.h" typedef struct { diff --git a/code/espurna/relay.h b/code/espurna/relay.h new file mode 100644 index 00000000..b951ee6f --- /dev/null +++ b/code/espurna/relay.h @@ -0,0 +1,72 @@ +/* + +RELAY MODULE + +Copyright (C) 2016-2019 by Xose Pérez + +*/ + +#pragma once + +#include +#include "utils.h" + +constexpr size_t RELAYS_MAX = 32; + +enum class RelayStatus : unsigned char { + OFF = 0, + ON = 1, + TOGGLE = 2, + UNKNOWN = 0xFF +}; + +struct RelayMask { + + explicit RelayMask(const String& string) : + as_string(string), + as_u32(u32fromString(string)) + {} + + explicit RelayMask(String&& string) : + as_string(std::move(string)), + as_u32(u32fromString(as_string)) + {} + + explicit RelayMask(uint32_t value) : + as_string(std::move(u32toString(value, 2))), + as_u32(value) + {} + + explicit RelayMask(std::bitset bitset) : + RelayMask(bitset.to_ulong()) + {} + + RelayMask(String&& string, uint32_t value) : + as_string(std::move(string)), + as_u32(value) + {} + + const String as_string; + uint32_t as_u32; + +}; + +RelayStatus 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(unsigned char id); + +void relayToggle(unsigned char id, bool report, bool group_report); +void relayToggle(unsigned char id); + +unsigned char relayCount(); + +const String& relayPayloadOn(); +const String& relayPayloadOff(); +const String& relayPayloadToggle(); + +const char* relayPayload(RelayStatus status); + +void relaySetupDummy(unsigned char size, bool reconfigure = false); + diff --git a/code/espurna/relay.ino b/code/espurna/relay.ino index bae0f3df..38ee8cf7 100644 --- a/code/espurna/relay.ino +++ b/code/espurna/relay.ino @@ -11,42 +11,77 @@ Copyright (C) 2016-2019 by Xose Pérez #include #include #include +#include +#include "relay.h" #include "broker.h" #include "tuya.h" -typedef struct { +#include "relay_config.h" + +struct relay_t { + + // Default to dummy (virtual) relay configuration + + relay_t(unsigned char pin, unsigned char type, unsigned char reset_pin) : + pin(pin), + type(type), + reset_pin(reset_pin), + delay_on(0), + delay_off(0), + pulse(RELAY_PULSE_NONE), + pulse_ms(0), + current_status(false), + target_status(false), + lock(RELAY_LOCK_DISABLED), + fw_start(0), + fw_count(0), + change_start(0), + change_delay(0), + report(false), + group_report(false) + {} + + relay_t() : + relay_t(GPIO_NONE, RELAY_TYPE_NORMAL, GPIO_NONE) + {} + + // ... unless there are pre-configured values + + relay_t(unsigned char id) : + relay_t(_relayPin(id), _relayType(id), _relayResetPin(id)) + {} // Configuration variables - unsigned char pin; // GPIO pin for the relay - unsigned char type; // RELAY_TYPE_NORMAL, RELAY_TYPE_INVERSE, RELAY_TYPE_LATCHED or RELAY_TYPE_LATCHED_INVERSE - unsigned char reset_pin; // GPIO to reset the relay if RELAY_TYPE_LATCHED - unsigned long delay_on; // Delay to turn relay ON - unsigned long delay_off; // Delay to turn relay OFF - unsigned char pulse; // RELAY_PULSE_NONE, RELAY_PULSE_OFF or RELAY_PULSE_ON - unsigned long pulse_ms; // Pulse length in millis + unsigned char pin; // GPIO pin for the relay + unsigned char type; // RELAY_TYPE_NORMAL, RELAY_TYPE_INVERSE, RELAY_TYPE_LATCHED or RELAY_TYPE_LATCHED_INVERSE + unsigned char reset_pin; // GPIO to reset the relay if RELAY_TYPE_LATCHED + unsigned long delay_on; // Delay to turn relay ON + unsigned long delay_off; // Delay to turn relay OFF + unsigned char pulse; // RELAY_PULSE_NONE, RELAY_PULSE_OFF or RELAY_PULSE_ON + unsigned long pulse_ms; // Pulse length in millis // Status variables - bool current_status; // Holds the current (physical) status of the relay - bool target_status; // Holds the target status - unsigned char lock; // Holds the value of target status, that cannot be changed afterwards. (0 for false, 1 for true, 2 to disable) - unsigned long fw_start; // Flood window start time - unsigned char fw_count; // Number of changes within the current flood window - unsigned long change_start; // Time when relay was scheduled to change - unsigned long change_delay; // Delay until the next change - bool report; // Whether to report to own topic - bool group_report; // Whether to report to group topic + bool current_status; // Holds the current (physical) status of the relay + bool target_status; // Holds the target status + unsigned char lock; // Holds the value of target status, that cannot be changed afterwards. (0 for false, 1 for true, 2 to disable) + unsigned long fw_start; // Flood window start time + unsigned char fw_count; // Number of changes within the current flood window + unsigned long change_start; // Time when relay was scheduled to change + unsigned long change_delay; // Delay until the next change + bool report; // Whether to report to own topic + bool group_report; // Whether to report to group topic // Helping objects - Ticker pulseTicker; // Holds the pulse back timer + Ticker pulseTicker; // Holds the pulse back timer + +}; -} relay_t; std::vector _relays; bool _relayRecursive = false; -Ticker _relaySaveTicker; uint8_t _relayDummy = DUMMY_RELAY_COUNT; unsigned long _relay_flood_window = (1000 * RELAY_FLOOD_WINDOW); @@ -248,7 +283,7 @@ void _relayProviderStatus(unsigned char id, bool status) { lightUpdate(true, true); return; - + } #endif @@ -300,7 +335,7 @@ void _relayProcess(bool mode) { if (target != mode) continue; // Only process if the change delay has expired - if (millis() - _relays[id].change_start < _relays[id].change_delay) continue; + if (_relays[id].change_delay && (millis() - _relays[id].change_start < _relays[id].change_delay)) continue; // Purge existing delay in case of cancelation _relays[id].change_delay = 0; @@ -331,8 +366,8 @@ void _relayProcess(bool mode) { // We will trigger a eeprom save only if // we care about current relay status on boot - unsigned char boot_mode = getSetting("relayBoot", id, RELAY_BOOT_MODE).toInt(); - bool save_eeprom = ((RELAY_BOOT_SAME == boot_mode) || (RELAY_BOOT_TOGGLE == boot_mode)); + const auto boot_mode = getSetting("relayBoot", id, RELAY_BOOT_MODE).toInt(); + 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); } @@ -382,14 +417,42 @@ void setSpeed(unsigned char speed) { // RELAY // ----------------------------------------------------------------------------- -void _relayMaskRtcmem(uint32_t mask) { +// State persistance persistance + +RelayMask INLINE _relayMaskRtcmem() { + return RelayMask(Rtcmem->relay); +} + +void INLINE _relayMaskRtcmem(uint32_t mask) { Rtcmem->relay = mask; } -uint32_t _relayMaskRtcmem() { - return Rtcmem->relay; +void INLINE _relayMaskRtcmem(const RelayMask& mask) { + _relayMaskRtcmem(mask.as_u32); +} + +void INLINE _relayMaskRtcmem(const std::bitset& bitset) { + _relayMaskRtcmem(bitset.to_ulong()); +} + +RelayMask INLINE _relayMaskSettings() { + return RelayMask(getSetting("relayBootMask")); +} + +void INLINE _relayMaskSettings(uint32_t mask) { + setSetting("relayBootMask", u32toString(mask, 2)); +} + +void INLINE _relayMaskSettings(const RelayMask& mask) { + setSetting("relayBootMask", mask.as_string); } +void INLINE _relayMaskSettings(const std::bitset& bitset) { + _relayMaskSettings(bitset.to_ulong()); +} + +// Pulse timers (timer after ON or OFF event) + void relayPulse(unsigned char id) { _relays[id].pulseTicker.detach(); @@ -412,6 +475,8 @@ void relayPulse(unsigned char id) { } +// General relay status control + bool relayStatus(unsigned char id, bool status, bool report, bool group_report) { if (id >= _relays.size()) return false; @@ -563,21 +628,18 @@ void relaySync(unsigned char id) { void relaySave(bool eeprom) { - auto mask = std::bitset(0); - - unsigned char count = relayCount(); - if (count > RELAY_SAVE_MASK_MAX) count = RELAY_SAVE_MASK_MAX; + const unsigned char count = constrain(relayCount(), 0, RELAYS_MAX); - for (unsigned int i=0; i < count; ++i) { - mask.set(i, relayStatus(i)); + auto statuses = std::bitset(0); + for (unsigned int id = 0; id < count; ++id) { + statuses.set(id, relayStatus(id)); } - const uint32_t mask_value = mask.to_ulong(); - - DEBUG_MSG_P(PSTR("[RELAY] Setting relay mask: %u\n"), mask_value); + const RelayMask mask(statuses); + DEBUG_MSG_P(PSTR("[RELAY] Setting relay mask: %s\n"), mask.as_string.c_str()); // Persist only to rtcmem, unless requested to save to the eeprom - _relayMaskRtcmem(mask_value); + _relayMaskRtcmem(mask); // The 'eeprom' flag controls whether we are commiting this change or not. // It is useful to set it to 'false' if the relay change triggering the @@ -586,7 +648,7 @@ void relaySave(bool eeprom) { // Nevertheless, we store the value in the EEPROM buffer so it will be written // on the next commit. if (eeprom) { - EEPROMr.write(EEPROM_RELAY_STATUS, mask_value); + _relayMaskSettings(mask); // We are actually enqueuing the commit so it will be // executed on the main loop, in case this is called from a system context callback eepromCommit(); @@ -651,6 +713,17 @@ RelayStatus relayParsePayload(const char * payload) { // BACKWARDS COMPATIBILITY void _relayBackwards() { + #if defined(EEPROM_RELAY_STATUS) + { + uint8_t mask = EEPROMr.read(EEPROM_RELAY_STATUS); + if (mask != 0xff) { + _relayMaskSettings(static_cast(mask)); + EEPROMr.write(EEPROM_RELAY_STATUS, 0xff); + eepromCommit(); + } + } + #endif + for (unsigned int i=0; i<_relays.size(); i++) { if (!hasSetting("mqttGroupInv", i)) continue; setSetting("mqttGroupSync", i, getSetting("mqttGroupInv", i)); @@ -662,18 +735,14 @@ void _relayBackwards() { void _relayBoot() { _relayRecursive = true; - bool trigger_save = false; - uint32_t stored_mask = 0; - if (rtcmemStatus()) { - stored_mask = _relayMaskRtcmem(); - } else { - stored_mask = EEPROMr.read(EEPROM_RELAY_STATUS); - } + const auto stored_mask = rtcmemStatus() + ? _relayMaskRtcmem() + : _relayMaskSettings(); - DEBUG_MSG_P(PSTR("[RELAY] Retrieving mask: %u\n"), stored_mask); + DEBUG_MSG_P(PSTR("[RELAY] Retrieving mask: %s\n"), stored_mask.as_string.c_str()); - auto mask = std::bitset(stored_mask); + auto mask = std::bitset(stored_mask.as_u32); // Walk the relays unsigned char lock; @@ -687,16 +756,11 @@ void _relayBoot() { lock = RELAY_LOCK_DISABLED; switch (boot_mode) { case RELAY_BOOT_SAME: - if (i < 8) { - status = mask.test(i); - } + status = mask.test(i); break; case RELAY_BOOT_TOGGLE: - if (i < 8) { - status = !mask[i]; - mask.flip(i); - trigger_save = true; - } + mask.flip(i); + status = mask[i]; break; case RELAY_BOOT_LOCKED_ON: status = true; @@ -715,7 +779,11 @@ void _relayBoot() { _relays[i].current_status = !status; _relays[i].target_status = status; + _relays[i].change_start = millis(); + _relays[i].change_delay = status + ? _relays[i].delay_on + : _relays[i].delay_off; #if RELAY_PROVIDER == RELAY_PROVIDER_STM // XXX hack for correctly restoring relay state on boot @@ -725,14 +793,6 @@ void _relayBoot() { _relays[i].lock = lock; - } - - // Save if there is any relay in the RELAY_BOOT_TOGGLE mode - if (trigger_save) { - _relayMaskRtcmem(mask.to_ulong()); - - EEPROMr.write(EEPROM_RELAY_STATUS, mask.to_ulong()); - eepromCommit(); } _relayRecursive = false; @@ -743,32 +803,6 @@ void _relayBoot() { } -constexpr const unsigned long _relayDelayOn(unsigned char index) { - return ( - (index == 0) ? RELAY1_DELAY_ON : - (index == 1) ? RELAY2_DELAY_ON : - (index == 2) ? RELAY3_DELAY_ON : - (index == 3) ? RELAY4_DELAY_ON : - (index == 4) ? RELAY5_DELAY_ON : - (index == 5) ? RELAY6_DELAY_ON : - (index == 6) ? RELAY7_DELAY_ON : - (index == 7) ? RELAY8_DELAY_ON : 0 - ); -} - -constexpr const unsigned long _relayDelayOff(unsigned char index) { - return ( - (index == 0) ? RELAY1_DELAY_OFF : - (index == 1) ? RELAY2_DELAY_OFF : - (index == 2) ? RELAY3_DELAY_OFF : - (index == 3) ? RELAY4_DELAY_OFF : - (index == 4) ? RELAY5_DELAY_OFF : - (index == 5) ? RELAY6_DELAY_OFF : - (index == 6) ? RELAY7_DELAY_OFF : - (index == 7) ? RELAY8_DELAY_OFF : 0 - ); -} - void _relayConfigure() { for (unsigned int i=0; i<_relays.size(); i++) { _relays[i].pulse = getSetting("relayPulse", i, RELAY_PULSE_MODE).toInt(); @@ -1325,18 +1359,14 @@ void _relayLoop() { #endif } -// Dummy relays for AI Light, Magic Home LED Controller, H801, Sonoff Dual and Sonoff RF Bridge -// No delay_on or off for these devices to easily allow having more than -// 8 channels. This behaviour will be recovered with v2. +// Dummy relays for virtual light switches, Sonoff Dual, Sonoff RF Bridge and Tuya void relaySetupDummy(unsigned char size, bool reconfigure) { - size = constrain(size, 0, RELAY_SAVE_MASK_MAX); + size = constrain(size + _relays.size(), _relays.size(), RELAYS_MAX); if (size == _relays.size()) return; - _relayDummy = size; - _relays.assign(size, { - GPIO_NONE, RELAY_TYPE_NORMAL, GPIO_NONE - }); + _relayDummy = size; + _relays.resize(size); if (reconfigure) { _relayConfigure(); @@ -1348,34 +1378,48 @@ void relaySetupDummy(unsigned char size, bool reconfigure) { } -void relaySetup() { +void _relaySetupAdhoc() { + + size_t relays = 0; - // Ad-hoc relays #if RELAY1_PIN != GPIO_NONE - _relays.push_back((relay_t) { RELAY1_PIN, RELAY1_TYPE, RELAY1_RESET_PIN }); + ++relays; #endif #if RELAY2_PIN != GPIO_NONE - _relays.push_back((relay_t) { RELAY2_PIN, RELAY2_TYPE, RELAY2_RESET_PIN }); + ++relays; #endif #if RELAY3_PIN != GPIO_NONE - _relays.push_back((relay_t) { RELAY3_PIN, RELAY3_TYPE, RELAY3_RESET_PIN }); + ++relays; #endif #if RELAY4_PIN != GPIO_NONE - _relays.push_back((relay_t) { RELAY4_PIN, RELAY4_TYPE, RELAY4_RESET_PIN }); + ++relays; #endif #if RELAY5_PIN != GPIO_NONE - _relays.push_back((relay_t) { RELAY5_PIN, RELAY5_TYPE, RELAY5_RESET_PIN }); + ++relays; #endif #if RELAY6_PIN != GPIO_NONE - _relays.push_back((relay_t) { RELAY6_PIN, RELAY6_TYPE, RELAY6_RESET_PIN }); + ++relays; #endif #if RELAY7_PIN != GPIO_NONE - _relays.push_back((relay_t) { RELAY7_PIN, RELAY7_TYPE, RELAY7_RESET_PIN }); + ++relays; #endif #if RELAY8_PIN != GPIO_NONE - _relays.push_back((relay_t) { RELAY8_PIN, RELAY8_TYPE, RELAY8_RESET_PIN }); + ++relays; #endif + _relays.reserve(relays); + for (unsigned char id = 0; id < relays; ++id) { + _relays.emplace_back(id); + } + +} + +void relaySetup() { + + // Ad-hoc relays + _relaySetupAdhoc(); + + // Dummy (virtual) relays relaySetupDummy(getSetting("relayDummy", DUMMY_RELAY_COUNT).toInt()); _relaySetupProvider(); diff --git a/code/espurna/relay_config.h b/code/espurna/relay_config.h new file mode 100644 index 00000000..5254b5da --- /dev/null +++ b/code/espurna/relay_config.h @@ -0,0 +1,73 @@ +/* + +RELAY MODULE + +*/ + +#pragma once + +constexpr const unsigned long _relayDelayOn(unsigned char index) { + return ( + (index == 0) ? RELAY1_DELAY_ON : + (index == 1) ? RELAY2_DELAY_ON : + (index == 2) ? RELAY3_DELAY_ON : + (index == 3) ? RELAY4_DELAY_ON : + (index == 4) ? RELAY5_DELAY_ON : + (index == 5) ? RELAY6_DELAY_ON : + (index == 6) ? RELAY7_DELAY_ON : + (index == 7) ? RELAY8_DELAY_ON : 0 + ); +} + +constexpr const unsigned long _relayDelayOff(unsigned char index) { + return ( + (index == 0) ? RELAY1_DELAY_OFF : + (index == 1) ? RELAY2_DELAY_OFF : + (index == 2) ? RELAY3_DELAY_OFF : + (index == 3) ? RELAY4_DELAY_OFF : + (index == 4) ? RELAY5_DELAY_OFF : + (index == 5) ? RELAY6_DELAY_OFF : + (index == 6) ? RELAY7_DELAY_OFF : + (index == 7) ? RELAY8_DELAY_OFF : 0 + ); +} + +constexpr const unsigned char _relayPin(unsigned char index) { + return ( + (index == 0) ? RELAY1_PIN : + (index == 1) ? RELAY2_PIN : + (index == 2) ? RELAY3_PIN : + (index == 3) ? RELAY4_PIN : + (index == 4) ? RELAY5_PIN : + (index == 5) ? RELAY6_PIN : + (index == 6) ? RELAY7_PIN : + (index == 7) ? RELAY8_PIN : GPIO_NONE + ); +} + +constexpr const unsigned char _relayType(unsigned char index) { + return ( + (index == 0) ? RELAY1_TYPE : + (index == 1) ? RELAY2_TYPE : + (index == 2) ? RELAY3_TYPE : + (index == 3) ? RELAY4_TYPE : + (index == 4) ? RELAY5_TYPE : + (index == 5) ? RELAY6_TYPE : + (index == 6) ? RELAY7_TYPE : + (index == 7) ? RELAY8_TYPE : RELAY_TYPE_NORMAL + ); +} + +constexpr const unsigned char _relayResetPin(unsigned char index) { + return ( + (index == 0) ? RELAY1_RESET_PIN : + (index == 1) ? RELAY2_RESET_PIN : + (index == 2) ? RELAY3_RESET_PIN : + (index == 3) ? RELAY4_RESET_PIN : + (index == 4) ? RELAY5_RESET_PIN : + (index == 5) ? RELAY6_RESET_PIN : + (index == 6) ? RELAY7_RESET_PIN : + (index == 7) ? RELAY8_RESET_PIN : GPIO_NONE + ); +} + diff --git a/code/espurna/rfbridge.ino b/code/espurna/rfbridge.ino index 4d01cefa..10e321e1 100644 --- a/code/espurna/rfbridge.ino +++ b/code/espurna/rfbridge.ino @@ -8,6 +8,8 @@ Copyright (C) 2016-2019 by Xose Pérez #if RF_SUPPORT +#include "relay.h" + #include #include diff --git a/code/espurna/rpnrules.ino b/code/espurna/rpnrules.ino index 96f9b270..5e275f44 100644 --- a/code/espurna/rpnrules.ino +++ b/code/espurna/rpnrules.ino @@ -8,7 +8,9 @@ Copyright (C) 2019 by Xose Pérez #if RPN_RULES_SUPPORT -#include "rpnlib.h" +#include "relay.h" + +#include // ----------------------------------------------------------------------------- // Custom commands diff --git a/code/espurna/scheduler.ino b/code/espurna/scheduler.ino index b106af44..5dd7e03c 100644 --- a/code/espurna/scheduler.ino +++ b/code/espurna/scheduler.ino @@ -9,6 +9,8 @@ Adapted by Xose Pérez #if SCHEDULER_SUPPORT +#include "relay.h" + #include int _sch_restore = 0; diff --git a/code/espurna/sensor.ino b/code/espurna/sensor.ino index 07b228ec..1190feb9 100644 --- a/code/espurna/sensor.ino +++ b/code/espurna/sensor.ino @@ -8,6 +8,7 @@ Copyright (C) 2016-2019 by Xose Pérez #if SENSOR_SUPPORT +#include "relay.h" #include "broker.h" #include diff --git a/code/espurna/terminal.ino b/code/espurna/terminal.ino index d57818ab..bc460169 100644 --- a/code/espurna/terminal.ino +++ b/code/espurna/terminal.ino @@ -8,12 +8,14 @@ Copyright (C) 2016-2019 by Xose Pérez #if TERMINAL_SUPPORT -#include +#include "utils.h" #include "libs/EmbedisWrap.h" -#include #include "libs/StreamInjector.h" #include "libs/HeapStats.h" +#include +#include + StreamInjector _serial = StreamInjector(TERMINAL_BUFFER_SIZE); EmbedisWrap embedis(_serial, TERMINAL_BUFFER_SIZE); diff --git a/code/espurna/thermostat.ino b/code/espurna/thermostat.ino index 4ad5c543..82c212da 100644 --- a/code/espurna/thermostat.ino +++ b/code/espurna/thermostat.ino @@ -8,6 +8,8 @@ Copyright (C) 2017 by Dmitry Blinov #if THERMOSTAT_SUPPORT +#include "relay.h" + #include #include diff --git a/code/espurna/tuya.ino b/code/espurna/tuya.ino index d25ec2c8..39418203 100644 --- a/code/espurna/tuya.ino +++ b/code/espurna/tuya.ino @@ -10,6 +10,9 @@ Copyright (C) 2019 by Maxim Prokhorov #if TUYA_SUPPORT +#include "relay.h" +#include "light.h" + #include #include #include diff --git a/code/espurna/utils.h b/code/espurna/utils.h new file mode 100644 index 00000000..0a8e5e6d --- /dev/null +++ b/code/espurna/utils.h @@ -0,0 +1,57 @@ +/* + +UTILS MODULE + +Copyright (C) 2017-2019 by Xose Pérez + +*/ + +#pragma once + +extern "C" uint32_t _SPIFFS_start; +extern "C" uint32_t _SPIFFS_end; + +String getIdentifier(); +void setDefaultHostname(); + +void setBoardName(); +String getBoardName(); +String getAdminPass(); + +const String& getCoreVersion(); +const String& getCoreRevision(); + +unsigned char getHeartbeatMode(); +unsigned char getHeartbeatInterval(); +void heartbeat(); + +String getEspurnaModules(); +String getEspurnaOTAModules(); +String getEspurnaSensors(); + +String getEspurnaWebUI(); + +String buildTime(); +unsigned long getUptime(); +bool haveRelaysOrSensors(); + +void infoMemory(const char * name, unsigned int total_memory, unsigned int free_memory); +void info(); + +bool sslCheckFingerPrint(const char * fingerprint); +bool sslFingerPrintArray(const char * fingerprint, unsigned char * bytearray); +bool sslFingerPrintChar(const char * fingerprint, char * destination); + +bool eraseSDKConfig(); + +char * ltrim(char * s); +char * strnstr(const char * buffer, const char * token, size_t n); +bool isNumber(const char * s); + +void nice_delay(unsigned long ms); + +double roundTo(double num, unsigned char positions); + +uint32_t u32fromString(const String& string, int base); +uint32_t u32fromString(const String& string); +String u32toString(uint32_t bitset, int base); diff --git a/code/espurna/utils.ino b/code/espurna/utils.ino index ea627187..b5eedd54 100644 --- a/code/espurna/utils.ino +++ b/code/espurna/utils.ino @@ -6,9 +6,11 @@ Copyright (C) 2017-2019 by Xose Pérez */ +#include "utils.h" +#include "libs/HeapStats.h" + #include #include -#include "libs/HeapStats.h" String getIdentifier() { char buffer[20]; @@ -64,7 +66,7 @@ const String& getCoreRevision() { #ifdef ARDUINO_ESP8266_GIT_VER revision = String(ARDUINO_ESP8266_GIT_VER, 16); #else - revision = ""; + revision = "(unspecified)"; #endif } return revision; @@ -192,12 +194,7 @@ namespace Heartbeat { return defaultValue(); } - // invalidate the whole string when invalid chars are detected - char *value_endptr = nullptr; - const auto value = strtoul(cfg.c_str(), &value_endptr, 10); - if (value_endptr == cfg.c_str() || value_endptr[0] != '\0') { - return defaultValue(); - } + const auto value = u32fromString(cfg); // because we start shifting from 1, we could use the // first bit as a flag to enable all of the messages @@ -250,7 +247,7 @@ void heartbeat() { #if MQTT_SUPPORT if (!serial && (_heartbeat_mode == HEARTBEAT_REPEAT || systemGetHeartbeat())) { if (hb_cfg & Heartbeat::Interval) - mqttSend(MQTT_TOPIC_INTERVAL, String(getHeartbeatInterval() / 1000).c_str()); + mqttSend(MQTT_TOPIC_INTERVAL, String(getHeartbeatInterval()).c_str()); if (hb_cfg & Heartbeat::App) mqttSend(MQTT_TOPIC_APP, APP_NAME); @@ -677,3 +674,50 @@ char* strnstr(const char* buffer, const char* token, size_t n) { return nullptr; } + +// TODO: force getSetting return type to handle settings +uint32_t u32fromString(const String& string, int base) { + + const char *ptr = string.c_str(); + char *value_endptr = nullptr; + + // invalidate the whole string when invalid chars are detected + const auto value = strtoul(ptr, &value_endptr, base); + if (value_endptr == ptr || value_endptr[0] != '\0') { + return 0; + } + + return value; + +} + +uint32_t u32fromString(const String& string) { + if (!string.length()) { + return 0; + } + + int base = 10; + if (string.length() > 2) { + if (string.startsWith("0b")) { + base = 2; + } else if (string.startsWith("0o")) { + base = 8; + } else if (string.startsWith("0x")) { + base = 16; + } + } + + return u32fromString((base == 10) ? string : string.substring(2), base); +} + +String u32toString(uint32_t value, int base) { + String result; + result.reserve(32 + 2); + result += "0b"; + + char buffer[33] = {0}; + ultoa(value, buffer, base); + result += buffer; + + return result; +} diff --git a/code/espurna/web.ino b/code/espurna/web.ino index 3c80fd2c..3620a721 100644 --- a/code/espurna/web.ino +++ b/code/espurna/web.ino @@ -8,6 +8,8 @@ Copyright (C) 2016-2019 by Xose Pérez #if WEB_SUPPORT +#include "utils.h" + #include #include #include