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

@ -22,10 +22,15 @@ bool ConfigFile::begin(const char* filename, bool readOnly) {
this->_opened = true;
return true;
}
bool ConfigFile::beginRAM(String *buf) {
_ramBuf = buf;
_opened = true;
return true;
}
void ConfigFile::end() {
if(this->isOpen()) {
if(!this->readOnly) this->file.flush();
this->file.close();
if(_ramBuf) { _ramBuf = nullptr; }
else { if(!this->readOnly) this->file.flush(); this->file.close(); }
}
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) {
if(!this->isOpen()) return false;
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(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.
while(slen < len - 1) {
while(slen < (int)len - 1) {
this->file.write(' ');
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) {
if(!this->isOpen()) return false;
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);
if(slen > 0) if(this->file.write((uint8_t *)val, slen) != slen) return false;
this->writeChar(CFG_TOK_QUOTE);
@ -210,6 +228,7 @@ bool ConfigFile::writeVarString(const char *val, const char tok) {
}
bool ConfigFile::writeChar(const char val) {
if(!this->isOpen()) return false;
if(_ramBuf) { _ramBuf->concat(val); return true; }
if(this->file.write(static_cast<uint8_t>(val)) == 1) return true;
return false;
}

View file

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

View file

@ -633,8 +633,10 @@ void SomfyShadeController::commit() {
void SomfyShadeController::writeBackup() {
if(git.lockFS) return;
esp_task_wdt_reset(); // Make sure we don't reset inadvertently.
this->backupData = "";
this->backupData.reserve(16384);
ShadeConfigFile file;
file.begin("/controller.backup", false);
file.beginRAM(&this->backupData);
file.backup(this);
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, 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
// is expected to be called internally when the motor needs commanded.
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) {
if(this->isToggle() || this->shadeType == shade_types::drycontact)
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()) {
this->moveToMyPosition();
this->moveToMyPosition();
Serial.print("Send command end 2\n");
return;
}
else {
@ -2951,6 +2958,7 @@ void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSiz
else {
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, uint8_t repeat, uint8_t stepSize) {

View file

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

View file

@ -9,6 +9,7 @@
#include "Somfy.h"
#include "MQTT.h"
#include "GitOTA.h"
#include "esp_core_dump.h"
ConfigSettings settings;
Web webServer;
@ -20,11 +21,61 @@ MQTTClass mqtt;
GitUpdater git;
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.println();
Serial.println("Startup/Boot....");
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");
else Serial.println("Error mounting file system");
settings.begin();

View file

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