Browse Source

relay: replace eeprom 8bit with 32bit bitmask stored in settings

- mask is defined as (relay# status << number)
- transparently handle base-2 and base-10 numbers
- if the relay mode requires us to save the mask, it will be saved as base-2
master
Maxim Prokhorov 5 years ago
parent
commit
9f4329dc1a
20 changed files with 229 additions and 74 deletions
  1. +1
    -0
      code/espurna/alexa.ino
  2. +1
    -0
      code/espurna/button.ino
  3. +0
    -5
      code/espurna/config/general.h
  4. +0
    -30
      code/espurna/config/prototypes.h
  5. +2
    -0
      code/espurna/domoticz.ino
  6. +2
    -0
      code/espurna/espurna.ino
  7. +2
    -0
      code/espurna/ir.ino
  8. +1
    -0
      code/espurna/led.ino
  9. +38
    -0
      code/espurna/relay.h
  10. +46
    -28
      code/espurna/relay.ino
  11. +2
    -0
      code/espurna/rfbridge.ino
  12. +3
    -1
      code/espurna/rpnrules.ino
  13. +2
    -0
      code/espurna/scheduler.ino
  14. +1
    -0
      code/espurna/sensor.ino
  15. +4
    -2
      code/espurna/terminal.ino
  16. +2
    -0
      code/espurna/thermostat.ino
  17. +3
    -0
      code/espurna/tuya.ino
  18. +56
    -0
      code/espurna/utils.h
  19. +61
    -8
      code/espurna/utils.ino
  20. +2
    -0
      code/espurna/web.ino

+ 1
- 0
code/espurna/alexa.ino View File

@ -8,6 +8,7 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#if ALEXA_SUPPORT
#include "relay.h"
#include "broker.h"
#include <fauxmoESP.h>


+ 1
- 0
code/espurna/button.ino View File

@ -15,6 +15,7 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#include <DebounceEvent.h>
#include <vector>
#include "relay.h"
#include "light.h"
typedef struct {


+ 0
- 5
code/espurna/config/general.h View File

@ -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
// -----------------------------------------------------------------------------


+ 0
- 30
code/espurna/config/prototypes.h View File

@ -250,36 +250,6 @@ typedef struct {
int16_t rssi;
} packet_t;
// -----------------------------------------------------------------------------
// Relay
// -----------------------------------------------------------------------------
#include <bitset>
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
// -----------------------------------------------------------------------------


+ 2
- 0
code/espurna/domoticz.ino View File

@ -8,7 +8,9 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#if DOMOTICZ_SUPPORT
#include "relay.h"
#include "broker.h"
#include <ArduinoJson.h>
bool _dcz_enabled = false;


+ 2
- 0
code/espurna/espurna.ino View File

@ -22,6 +22,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "config/all.h"
#include <vector>
#include "utils.h"
#include "relay.h"
#include "broker.h"
#include "tuya.h"
#include "libs/HeapStats.h"


+ 2
- 0
code/espurna/ir.ino View File

@ -48,6 +48,8 @@ Raw messages:
#if IR_SUPPORT
#include "relay.h"
#include <IRremoteESP8266.h>
#if defined(IR_RX_PIN)


+ 1
- 0
code/espurna/led.ino View File

@ -12,6 +12,7 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#if LED_SUPPORT
#include "relay.h"
#include "broker.h"
typedef struct {


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

@ -0,0 +1,38 @@
/*
RELAY MODULE
Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
*/
#pragma once
constexpr size_t RELAYS_MAX = 32;
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);

+ 46
- 28
code/espurna/relay.ino View File

@ -11,7 +11,9 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#include <ArduinoJson.h>
#include <vector>
#include <functional>
#include <bitset>
#include "relay.h"
#include "broker.h"
#include "tuya.h"
@ -390,6 +392,18 @@ uint32_t _relayMaskRtcmem() {
return Rtcmem->relay;
}
void _relayMaskSettings(const String& string) {
setSetting("relayBootMask", string);
}
void _relayMaskSettings(uint32_t mask) {
_relayMaskSettings(bitsetToString(mask));
}
uint32_t _relayMaskSettings() {
return bitsetFromString(getSetting("relayBootMask"));
}
void relayPulse(unsigned char id) {
_relays[id].pulseTicker.detach();
@ -563,18 +577,16 @@ void relaySync(unsigned char id) {
void relaySave(bool eeprom) {
auto mask = std::bitset<RELAY_SAVE_MASK_MAX>(0);
unsigned char count = relayCount();
if (count > RELAY_SAVE_MASK_MAX) count = RELAY_SAVE_MASK_MAX;
auto mask = std::bitset<RELAYS_MAX>(0);
for (unsigned int i=0; i < count; ++i) {
mask.set(i, relayStatus(i));
const unsigned char count = constrain(relayCount(), 0, RELAYS_MAX);
for (unsigned int id = 0; id < count; ++id) {
mask.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 String mask_string = bitsetToString(mask_value);
DEBUG_MSG_P(PSTR("[RELAY] Setting relay mask: %s\n"), mask_string.c_str());
// Persist only to rtcmem, unless requested to save to the eeprom
_relayMaskRtcmem(mask_value);
@ -586,7 +598,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_string);
// 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 +663,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<uint32_t>(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));
@ -668,12 +691,13 @@ void _relayBoot() {
if (rtcmemStatus()) {
stored_mask = _relayMaskRtcmem();
} else {
stored_mask = EEPROMr.read(EEPROM_RELAY_STATUS);
stored_mask = _relayMaskSettings();
}
DEBUG_MSG_P(PSTR("[RELAY] Retrieving mask: %u\n"), stored_mask);
const String string_mask(bitsetToString(stored_mask));
DEBUG_MSG_P(PSTR("[RELAY] Retrieving mask: %s\n"), string_mask.c_str());
auto mask = std::bitset<RELAY_SAVE_MASK_MAX>(stored_mask);
auto mask = std::bitset<RELAYS_MAX>(stored_mask);
// Walk the relays
unsigned char lock;
@ -687,16 +711,12 @@ 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;
}
status = !mask[i];
mask.flip(i);
trigger_save = true;
break;
case RELAY_BOOT_LOCKED_ON:
status = true;
@ -727,12 +747,12 @@ void _relayBoot() {
}
const auto mask_value = mask.to_ulong();
_relayMaskRtcmem(mask_value);
// 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();
_relayMaskSettings(mask_value);
}
_relayRecursive = false;
@ -1325,12 +1345,10 @@ 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 + _relays.size(), _relays.size(), RELAY_SAVE_MASK_MAX);
size = constrain(size + _relays.size(), _relays.size(), RELAYS_MAX);
if (size == _relays.size()) return;
_relayDummy = size;


+ 2
- 0
code/espurna/rfbridge.ino View File

@ -8,6 +8,8 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#if RF_SUPPORT
#include "relay.h"
#include <queue>
#include <Ticker.h>


+ 3
- 1
code/espurna/rpnrules.ino View File

@ -8,7 +8,9 @@ Copyright (C) 2019 by Xose Pérez <xose dot perez at gmail dot com>
#if RPN_RULES_SUPPORT
#include "rpnlib.h"
#include "relay.h"
#include <rpnlib.h>
// -----------------------------------------------------------------------------
// Custom commands


+ 2
- 0
code/espurna/scheduler.ino View File

@ -9,6 +9,8 @@ Adapted by Xose Pérez <xose dot perez at gmail dot com>
#if SCHEDULER_SUPPORT
#include "relay.h"
#include <TimeLib.h>
int _sch_restore = 0;


+ 1
- 0
code/espurna/sensor.ino View File

@ -8,6 +8,7 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#if SENSOR_SUPPORT
#include "relay.h"
#include "broker.h"
#include <vector>


+ 4
- 2
code/espurna/terminal.ino View File

@ -8,12 +8,14 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#if TERMINAL_SUPPORT
#include <vector>
#include "utils.h"
#include "libs/EmbedisWrap.h"
#include <Stream.h>
#include "libs/StreamInjector.h"
#include "libs/HeapStats.h"
#include <vector>
#include <Stream.h>
StreamInjector _serial = StreamInjector(TERMINAL_BUFFER_SIZE);
EmbedisWrap embedis(_serial, TERMINAL_BUFFER_SIZE);


+ 2
- 0
code/espurna/thermostat.ino View File

@ -8,6 +8,8 @@ Copyright (C) 2017 by Dmitry Blinov <dblinov76 at gmail dot com>
#if THERMOSTAT_SUPPORT
#include "relay.h"
#include <ArduinoJson.h>
#include <float.h>


+ 3
- 0
code/espurna/tuya.ino View File

@ -10,6 +10,9 @@ Copyright (C) 2019 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
#if TUYA_SUPPORT
#include "relay.h"
#include "light.h"
#include <functional>
#include <queue>
#include <StreamString.h>


+ 56
- 0
code/espurna/utils.h View File

@ -0,0 +1,56 @@
/*
UTILS MODULE
Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
*/
#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 bitsetFromString(const String& string);
String bitsetToString(uint32_t bitset);

+ 61
- 8
code/espurna/utils.ino View File

@ -6,9 +6,11 @@ Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
*/
#include "utils.h"
#include "libs/HeapStats.h"
#include <Ticker.h>
#include <limits>
#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;
@ -132,6 +134,22 @@ bool haveRelaysOrSensors() {
return result;
}
// TODO: force getSetting return type to handle settings
uint32_t u32fromString(const String& string, int base = 10) {
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;
}
// -----------------------------------------------------------------------------
// Heartbeat helper
// -----------------------------------------------------------------------------
@ -192,12 +210,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
@ -677,3 +690,43 @@ char* strnstr(const char* buffer, const char* token, size_t n) {
return nullptr;
}
// Note:
// - when using standard base-2 literal syntax, parse that
// to keep backwards compatibility
// - otherwise, fallback to base-10 numbers
uint32_t bitsetFromString(const String& string) {
if (!string.length()) {
return 0;
}
if (string.startsWith("0b") && (string.length() > 2)) {
return u32fromString(string.substring(2), 2);
}
return u32fromString(string);
}
// Note:
// - bitset::to_string() will return std::string
// - itoa accepts int, so it will cut the sign bit
String bitsetToString(uint32_t value) {
String result;
result.reserve(34);
result += "0b";
const uint32_t _value { value };
size_t bits = 0;
do {
value >>= 1;
bits++;
} while (value);
int bit = bits - 1;
do {
result += ((_value & (1 << bit)) ? '1' : '0');
} while (--bit >= 0);
return result;
}

+ 2
- 0
code/espurna/web.ino View File

@ -8,6 +8,8 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#if WEB_SUPPORT
#include "utils.h"
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <Hash.h>


Loading…
Cancel
Save