Skip to content

Commit

Permalink
Initial implementation of RTCMEM storage (#1420)
Browse files Browse the repository at this point in the history
- store system crash counter and reset reason in rtcmem instead of eeprom
- store relay state mask in rtc in addition to the eeprom
- store relay state in eeprom only when boot mode requires it
- simplify relay state mask calculation / reading using std::bitset
- light state save and restore
- energy total save and restore
  • Loading branch information
mcspr authored May 8, 2019
1 parent 193cebd commit 820d8c4
Show file tree
Hide file tree
Showing 13 changed files with 365 additions and 144 deletions.
5 changes: 5 additions & 0 deletions code/espurna/config/general.h
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,11 @@
#define RELAY_MQTT_OFF "0"
#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
// -----------------------------------------------------------------------------
Expand Down
20 changes: 19 additions & 1 deletion code/espurna/config/prototypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,19 @@

extern "C" {
#include "user_interface.h"
extern struct rst_info resetInfo;
}

#define UNUSED(x) (void)(x)

// -----------------------------------------------------------------------------
// System
// -----------------------------------------------------------------------------

uint32_t systemResetReason();
uint8_t systemStabilityCounter();
void systemStabilityCounter(uint8_t);

// -----------------------------------------------------------------------------
// API
// -----------------------------------------------------------------------------
Expand Down Expand Up @@ -134,6 +143,11 @@ typedef struct {
int16_t rssi;
} packet_t;

// -----------------------------------------------------------------------------
// Relay
// -----------------------------------------------------------------------------
#include <bitset>

// -----------------------------------------------------------------------------
// Settings
// -----------------------------------------------------------------------------
Expand Down Expand Up @@ -214,7 +228,6 @@ typedef std::function<void(justwifi_messages_t code, char * parameter)> wifi_cal
void wifiRegister(wifi_callback_f callback);
bool wifiConnected();

// -----------------------------------------------------------------------------
// THERMOSTAT
// -----------------------------------------------------------------------------
#if THERMOSTAT_SUPPORT
Expand All @@ -224,3 +237,8 @@ bool wifiConnected();
#define thermostat_callback_f void *
#endif

// -----------------------------------------------------------------------------
// RTC MEMORY
// -----------------------------------------------------------------------------
#include "rtcmem.h"

44 changes: 44 additions & 0 deletions code/espurna/config/rtcmem.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#pragma once

// Base address of USER RTC memory
// https://github.com/esp8266/esp8266-wiki/wiki/Memory-Map#memmory-mapped-io-registers
#define RTCMEM_ADDR_BASE (0x60001200)

// RTC memory is accessed using blocks of 4 bytes.
// Blocks 0..63 are reserved by the SDK, 64..192 are available to the user.
// Blocks 64..96 are reserved by the eboot 'struct eboot_command' (128 -> (128 / 4) -> 32):
// https://github.com/esp8266/Arduino/blob/master/bootloaders/eboot/eboot_command.h
#define RTCMEM_OFFSET 32u
#define RTCMEM_ADDR (RTCMEM_ADDR_BASE + (RTCMEM_OFFSET * 4u))

#define RTCMEM_BLOCKS 96u

// Change this when modifying RtcmemData
#define RTCMEM_MAGIC 0x45535075

// XXX When using bitfields / inner structs / etc:
// ...
// uint32_t a : 8;
// uint32_t b : 8;
// uint32_t c : 8;
// uint32_t d : 8;
// ...
// mem->d = 4;
// At the same time writes 4 to the a, b and c

// TODO replace with custom memory segment in ldscript
struct RtcmemData {
uint32_t magic;
uint32_t sys;
uint32_t relay;
uint32_t mqtt;
uint64_t light;
double energy;
};

static_assert(sizeof(RtcmemData) <= (RTCMEM_BLOCKS * 4u), "RTCMEM struct is too big");
constexpr uint8_t RtcmemSize = (sizeof(RtcmemData) / 4u);

auto Rtcmem = reinterpret_cast<volatile RtcmemData*>(RTCMEM_ADDR);

bool rtcmemStatus();
3 changes: 3 additions & 0 deletions code/espurna/espurna.ino
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ void setup() {
debugSetup();
#endif

// Init RTCMEM
rtcmemSetup();

// Init EEPROM
eepromSetup();

Expand Down
56 changes: 51 additions & 5 deletions code/espurna/light.ino
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,47 @@ void _lightProviderUpdate() {
// PERSISTANCE
// -----------------------------------------------------------------------------

void _lightColorSave() {
union light_rtcmem_t {
struct {
uint8_t channels[5];
uint8_t brightness;
uint16_t mired;
} packed;
uint64_t value;
};

#define LIGHT_RTCMEM_CHANNELS_MAX sizeof(light_rtcmem_t().packed.channels)

void _lightSaveRtcmem() {
if (lightChannels() > LIGHT_RTCMEM_CHANNELS_MAX) return;

light_rtcmem_t light;

for (unsigned int i=0; i < lightChannels(); i++) {
light.packed.channels[i] = _light_channel[i].inputValue;
}

light.packed.brightness = _light_brightness;
light.packed.mired = _light_mireds;

Rtcmem->light = light.value;
}

void _lightRestoreRtcmem() {
if (lightChannels() > LIGHT_RTCMEM_CHANNELS_MAX) return;

light_rtcmem_t light;
light.value = Rtcmem->light;

for (unsigned int i=0; i < lightChannels(); i++) {
_light_channel[i].inputValue = light.packed.channels[i];
}

_light_brightness = light.packed.brightness;
_light_mireds = light.packed.mired;
}

void _lightSaveSettings() {
for (unsigned int i=0; i < _light_channel.size(); i++) {
setSetting("ch", i, _light_channel[i].inputValue);
}
Expand All @@ -479,7 +519,7 @@ void _lightColorSave() {
saveSettings();
}

void _lightColorRestore() {
void _lightRestoreSettings() {
for (unsigned int i=0; i < _light_channel.size(); i++) {
_light_channel[i].inputValue = getSetting("ch", i, i==0 ? 255 : 0).toInt();
}
Expand Down Expand Up @@ -699,9 +739,11 @@ void lightUpdate(bool save, bool forward, bool group_forward) {
if (group_forward) mask += 2;
_light_comms_ticker.once_ms(LIGHT_COMMS_DELAY, _lightComms, mask);

_lightSaveRtcmem();

#if LIGHT_SAVE_ENABLED
// Delay saving to EEPROM 5 seconds to avoid wearing it out unnecessarily
if (save) _light_save_ticker.once(LIGHT_SAVE_DELAY, _lightColorSave);
if (save) _light_save_ticker.once(LIGHT_SAVE_DELAY, _lightSaveSettings);
#endif

};
Expand All @@ -712,7 +754,7 @@ void lightUpdate(bool save, bool forward) {

#if LIGHT_SAVE_ENABLED == 0
void lightSave() {
_lightColorSave();
_lightSaveSettings();
}
#endif

Expand Down Expand Up @@ -1166,7 +1208,11 @@ void lightSetup() {
DEBUG_MSG_P(PSTR("[LIGHT] Number of channels: %d\n"), _light_channel.size());

_lightConfigure();
_lightColorRestore();
if (rtcmemStatus()) {
_lightRestoreRtcmem();
} else {
_lightRestoreSettings();
}

#if WEB_SUPPORT
wsOnSendRegister(_lightWebSocketOnSend);
Expand Down
44 changes: 1 addition & 43 deletions code/espurna/mqtt.ino
Original file line number Diff line number Diff line change
Expand Up @@ -267,48 +267,6 @@ void _mqttBackwards() {
}
}

unsigned long _mqttNextMessageId() {

static unsigned long id = 0;

// just reboot, get last count from EEPROM
if (id == 0) {

// read id from EEPROM and shift it
id = EEPROMr.read(EEPROM_MESSAGE_ID);
if (id == 0xFF) {

// There was nothing in EEPROM,
// next message is first message
id = 0;

} else {

id = (id << 8) + EEPROMr.read(EEPROM_MESSAGE_ID + 1);
id = (id << 8) + EEPROMr.read(EEPROM_MESSAGE_ID + 2);
id = (id << 8) + EEPROMr.read(EEPROM_MESSAGE_ID + 3);

// Calculate next block and start from there
id = MQTT_MESSAGE_ID_SHIFT * (1 + (id / MQTT_MESSAGE_ID_SHIFT));

}

}

// Save to EEPROM every MQTT_MESSAGE_ID_SHIFT
if (id % MQTT_MESSAGE_ID_SHIFT == 0) {
EEPROMr.write(EEPROM_MESSAGE_ID + 0, (id >> 24) & 0xFF);
EEPROMr.write(EEPROM_MESSAGE_ID + 1, (id >> 16) & 0xFF);
EEPROMr.write(EEPROM_MESSAGE_ID + 2, (id >> 8) & 0xFF);
EEPROMr.write(EEPROM_MESSAGE_ID + 3, (id >> 0) & 0xFF);
eepromCommit();
}

id++;
return id;

}

// -----------------------------------------------------------------------------
// WEB
// -----------------------------------------------------------------------------
Expand Down Expand Up @@ -628,7 +586,7 @@ void mqttFlush() {
root[MQTT_TOPIC_IP] = getIP();
#endif
#if MQTT_ENQUEUE_MESSAGE_ID
root[MQTT_TOPIC_MESSAGE_ID] = _mqttNextMessageId();
root[MQTT_TOPIC_MESSAGE_ID] = (Rtcmem->mqtt)++;
#endif

// Send
Expand Down
Loading

0 comments on commit 820d8c4

Please sign in to comment.