backup with RAM

This commit is contained in:
cjkas 2026-03-13 18:40:34 +01:00
parent 9113e58ec4
commit 2509030c49
9 changed files with 121 additions and 19 deletions

View file

@ -82,3 +82,8 @@ Configuration of the Transceiver is done with the ELECHOUSE_CC1101 library which
pio pkg exec -p tool-esptoolpy -- esptool.py --port COM9 read_flash 0x3F0000 0x10000 coredump.bin
esp-coredump info_corefile --core coredump.bin --core-format=raw --gdb C:\Users\oem\.platformio\packages\toolchain-xtensa-esp32\bin\xtensa-esp32-elf-gdb.exe .pio\build\esp32dev\firmware.elf > coredump_report.txt

View file

@ -133,12 +133,12 @@ def minify_svg(text: str) -> str:
MINIFIERS = { MINIFIERS = {
# ".html": minify_html, ".html": minify_html,
# ".htm": minify_html, ".htm": minify_html,
# ".css": minify_css, ".css": minify_css,
# ".js": minify_js, # ".js": minify_js,
# ".json": minify_json, # ".json": minify_json,
# ".svg": minify_svg, ".svg": minify_svg,
# ".xml": minify_svg, # same approach works for generic XML # ".xml": minify_svg, # same approach works for generic XML
} }

View file

@ -11,9 +11,11 @@
[platformio] [platformio]
default_envs = esp32dev default_envs = esp32dev
[env:esp32dev] [env:esp32dev]
platform = espressif32 platform = espressif32
board = esp32-c3-devkitm-1 board = esp32dev
framework = arduino framework = arduino
lib_deps = lib_deps =
bblanchon/ArduinoJson@^7.2.2 bblanchon/ArduinoJson@^7.2.2
@ -23,7 +25,11 @@ lib_deps =
extra_scripts = pre:minify.py extra_scripts = pre:minify.py
board_build.partitions = min_spiffs.csv board_build.partitions = min_spiffs.csv
board_build.filesystem = littlefs board_build.filesystem = littlefs
build_flags =
-DCONFIG_ESP_COREDUMP_ENABLE_TO_FLASH=1
-DCONFIG_ESP_COREDUMP_DATA_FORMAT_ELF=1
-DCONFIG_ESP_COREDUMP_CHECKSUM_CRC32=1
[env:esp32devdbg] [env:esp32devdbg]
build_type = debug build_type = debug
platform = espressif32 platform = espressif32
@ -36,4 +42,17 @@ lib_deps =
knolleary/PubSubClient@^2.8 knolleary/PubSubClient@^2.8
extra_scripts = pre:minify.py extra_scripts = pre:minify.py
board_build.partitions = min_spiffs.csv board_build.partitions = min_spiffs.csv
board_build.filesystem = littlefs
[env:esp32c3dev]
platform = espressif32
board = esp32-c3-devkitm-1
framework = arduino
lib_deps =
bblanchon/ArduinoJson@^7.2.2
links2004/WebSockets@^2.7.3
lsatan/SmartRC-CC1101-Driver-Lib@^2.5.7
knolleary/PubSubClient@^2.8
extra_scripts = pre:minify.py
board_build.partitions = min_spiffs.csv
board_build.filesystem = littlefs board_build.filesystem = littlefs

View file

@ -22,10 +22,15 @@ bool ConfigFile::begin(const char* filename, bool readOnly) {
this->_opened = true; this->_opened = true;
return true; return true;
} }
bool ConfigFile::beginRAM(String *buf) {
_ramBuf = buf;
_opened = true;
return true;
}
void ConfigFile::end() { void ConfigFile::end() {
if(this->isOpen()) { if(this->isOpen()) {
if(!this->readOnly) this->file.flush(); if(_ramBuf) { _ramBuf = nullptr; }
this->file.close(); else { if(!this->readOnly) this->file.flush(); this->file.close(); }
} }
this->_opened = false; this->_opened = false;
} }
@ -187,10 +192,16 @@ bool ConfigFile::readVarString(char *buff, size_t len) {
bool ConfigFile::writeString(const char *val, size_t len, const char tok) { bool ConfigFile::writeString(const char *val, size_t len, const char tok) {
if(!this->isOpen()) return false; if(!this->isOpen()) return false;
int slen = strlen(val); int slen = strlen(val);
if(_ramBuf) {
if(slen > 0) _ramBuf->concat(val);
while(slen < (int)len - 1) { _ramBuf->concat(' '); slen++; }
if(tok != CFG_TOK_NONE) _ramBuf->concat(tok);
return true;
}
if(slen > 0) if(slen > 0)
if(this->file.write((uint8_t *)val, slen) != slen) return false; if(this->file.write((uint8_t *)val, slen) != slen) return false;
// Now we need to pad the end of the string so that it is of a fixed length. // Now we need to pad the end of the string so that it is of a fixed length.
while(slen < len - 1) { while(slen < (int)len - 1) {
this->file.write(' '); this->file.write(' ');
slen++; slen++;
} }
@ -202,6 +213,13 @@ bool ConfigFile::writeString(const char *val, size_t len, const char tok) {
bool ConfigFile::writeVarString(const char *val, const char tok) { bool ConfigFile::writeVarString(const char *val, const char tok) {
if(!this->isOpen()) return false; if(!this->isOpen()) return false;
int slen = strlen(val); int slen = strlen(val);
if(_ramBuf) {
_ramBuf->concat((char)CFG_TOK_QUOTE);
if(slen > 0) _ramBuf->concat(val);
_ramBuf->concat((char)CFG_TOK_QUOTE);
if(tok != CFG_TOK_NONE) _ramBuf->concat(tok);
return true;
}
this->writeChar(CFG_TOK_QUOTE); this->writeChar(CFG_TOK_QUOTE);
if(slen > 0) if(this->file.write((uint8_t *)val, slen) != slen) return false; if(slen > 0) if(this->file.write((uint8_t *)val, slen) != slen) return false;
this->writeChar(CFG_TOK_QUOTE); this->writeChar(CFG_TOK_QUOTE);
@ -210,6 +228,7 @@ bool ConfigFile::writeVarString(const char *val, const char tok) {
} }
bool ConfigFile::writeChar(const char val) { bool ConfigFile::writeChar(const char val) {
if(!this->isOpen()) return false; if(!this->isOpen()) return false;
if(_ramBuf) { _ramBuf->concat(val); return true; }
if(this->file.write(static_cast<uint8_t>(val)) == 1) return true; if(this->file.write(static_cast<uint8_t>(val)) == 1) return true;
return false; return false;
} }

View file

@ -31,12 +31,14 @@ struct config_header_t {
class ConfigFile { class ConfigFile {
protected: protected:
File file; File file;
String *_ramBuf = nullptr;
bool readOnly = false; bool readOnly = false;
bool begin(const char *filename, bool readOnly = false); bool begin(const char *filename, bool readOnly = false);
uint32_t startRecPos = 0; uint32_t startRecPos = 0;
bool _opened = false; bool _opened = false;
public: public:
config_header_t header; config_header_t header;
bool beginRAM(String *buf);
void end(); void end();
bool isOpen(); bool isOpen();
bool seekRecordByIndex(uint16_t ndx); bool seekRecordByIndex(uint16_t ndx);

View file

@ -633,8 +633,10 @@ void SomfyShadeController::commit() {
void SomfyShadeController::writeBackup() { void SomfyShadeController::writeBackup() {
if(git.lockFS) return; if(git.lockFS) return;
esp_task_wdt_reset(); // Make sure we don't reset inadvertently. esp_task_wdt_reset(); // Make sure we don't reset inadvertently.
this->backupData = "";
this->backupData.reserve(16384);
ShadeConfigFile file; ShadeConfigFile file;
file.begin("/controller.backup", false); file.beginRAM(&this->backupData);
file.backup(this); file.backup(this);
file.end(); file.end();
} }
@ -2888,6 +2890,7 @@ void SomfyShade::moveToMyPosition() {
} }
void SomfyShade::sendCommand(somfy_commands cmd) { this->sendCommand(cmd, this->repeats); } void SomfyShade::sendCommand(somfy_commands cmd) { this->sendCommand(cmd, this->repeats); }
void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize) { void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize) {
Serial.print("Send command start\n");
// This sendCommand function will always be called externally. sendCommand at the remote level // This sendCommand function will always be called externally. sendCommand at the remote level
// is expected to be called internally when the motor needs commanded. // is expected to be called internally when the motor needs commanded.
if(this->bitLength == 0) this->bitLength = somfy.transceiver.config.type; if(this->bitLength == 0) this->bitLength = somfy.transceiver.config.type;
@ -2930,9 +2933,13 @@ void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSiz
else if(cmd == somfy_commands::My) { else if(cmd == somfy_commands::My) {
if(this->isToggle() || this->shadeType == shade_types::drycontact) if(this->isToggle() || this->shadeType == shade_types::drycontact)
SomfyRemote::sendCommand(cmd, repeat); SomfyRemote::sendCommand(cmd, repeat);
else if(this->shadeType == shade_types::drycontact2) return; else if(this->shadeType == shade_types::drycontact2){
Serial.print("Send command start 1\n");
return;
}
else if(this->isIdle()) { else if(this->isIdle()) {
this->moveToMyPosition(); this->moveToMyPosition();
Serial.print("Send command end 2\n");
return; return;
} }
else { else {
@ -2951,6 +2958,7 @@ void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSiz
else { else {
SomfyRemote::sendCommand(cmd, repeat, stepSize); SomfyRemote::sendCommand(cmd, repeat, stepSize);
} }
Serial.print("Send command end\n");
} }
void SomfyGroup::sendCommand(somfy_commands cmd) { this->sendCommand(cmd, this->repeats); } void SomfyGroup::sendCommand(somfy_commands cmd) { this->sendCommand(cmd, this->repeats); }
void SomfyGroup::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize) { void SomfyGroup::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize) {

View file

@ -578,6 +578,7 @@ class SomfyShadeController {
void processWaitingFrame(); void processWaitingFrame();
void commit(); void commit();
void writeBackup(); void writeBackup();
String backupData;
bool loadShadesFile(const char *filename); bool loadShadesFile(const char *filename);
#ifdef USE_NVS #ifdef USE_NVS
bool loadLegacy(); bool loadLegacy();

View file

@ -9,6 +9,7 @@
#include "Somfy.h" #include "Somfy.h"
#include "MQTT.h" #include "MQTT.h"
#include "GitOTA.h" #include "GitOTA.h"
#include "esp_core_dump.h"
ConfigSettings settings; ConfigSettings settings;
Web webServer; Web webServer;
@ -20,11 +21,61 @@ MQTTClass mqtt;
GitUpdater git; GitUpdater git;
uint32_t oldheap = 0; uint32_t oldheap = 0;
void setup() {
void inline checkCoreDumpPartition() {
esp_core_dump_init();
esp_core_dump_summary_t *summary =
static_cast<esp_core_dump_summary_t *>(malloc(sizeof(esp_core_dump_summary_t)));
if (summary) {
esp_err_t err = esp_core_dump_get_summary(summary);
if (err == ESP_OK) {
log_i("Getting core dump summary ok.");
} else {
log_e("Getting core dump summary not ok. Error: %d", (int)err);
log_e("Probably no coredump present yet.");
log_e("esp_core_dump_image_check() = %d", esp_core_dump_image_check());
}
free(summary);
}
}
void listDir(const char *dirname, uint8_t levels) {
Serial.printf("Listing: %s\n", dirname);
File root = LittleFS.open(dirname);
if (!root || !root.isDirectory()) {
Serial.println("Failed to open directory");
return;
}
File file = root.openNextFile();
while (file) {
if (file.isDirectory()) {
Serial.printf(" DIR : %s\n", file.name());
if (levels) listDir(file.path(), levels - 1);
} else {
Serial.printf(" FILE: %-30s %d bytes\n", file.name(), file.size());
}
file = root.openNextFile();
}
}
void setup() {
Serial.begin(115200); Serial.begin(115200);
Serial.println(); Serial.println();
Serial.println("Startup/Boot...."); Serial.println("Startup/Boot....");
Serial.println("Mounting File System..."); Serial.println("Mounting File System...");
checkCoreDumpPartition();
if (LittleFS.begin()) {
Serial.printf("\nTotal: %d bytes\n", LittleFS.totalBytes());
Serial.printf("Used: %d bytes\n", LittleFS.usedBytes());
Serial.printf("Free: %d bytes\n", LittleFS.totalBytes() - LittleFS.usedBytes());
Serial.println();
listDir("/", 3);
} else {
Serial.println("LittleFS mount failed!");
}
if(LittleFS.begin()) Serial.println("File system mounted successfully"); if(LittleFS.begin()) Serial.println("File system mounted successfully");
else Serial.println("Error mounting file system"); else Serial.println("Error mounting file system");
settings.begin(); settings.begin();

View file

@ -858,14 +858,11 @@ void Web::handleBackup(WebServer &server, bool attach) {
} }
Serial.println("Saving current shade information"); Serial.println("Saving current shade information");
somfy.writeBackup(); somfy.writeBackup();
File file = LittleFS.open("/controller.backup", "r"); if(somfy.backupData.length() == 0) {
if (!file) { server.send(500, _encoding_text, "backup failed");
Serial.println("Error opening shades.cfg");
server.send(500, _encoding_text, "shades.cfg");
return; return;
} }
server.streamFile(file, _encoding_text); server.send(200, _encoding_text, somfy.backupData);
file.close();
} }
void Web::handleSetPositions(WebServer &server) { void Web::handleSetPositions(WebServer &server) {
webServer.sendCORSHeaders(server); webServer.sendCORSHeaders(server);