fix: swap restored radio config under the radio lock

Restoring a backup wrote the transceiver config in place from the async
task while the radio task could be transmitting with it.

Read the record into a copy and swap it in through the new
Transceiver::setConfig, which Transceiver::fromJSON now uses as well.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
cjkas 2026-10-11 12:46:40 +02:00
parent 352f36e478
commit 00a084048f
3 changed files with 15 additions and 7 deletions

View file

@ -608,7 +608,10 @@ bool ShadeConfigFile::restoreFile(SomfyShadeController *s, const char *filename,
if(opts.shades) s->commit(); if(opts.shades) s->commit();
if(opts.transceiver) if(opts.transceiver)
{ {
this->readTransRecord(s->transceiver.config); // Read into a copy so the radio task never transmits on a half restored config.
transceiver_config_t cfg = s->transceiver.config;
this->readTransRecord(cfg);
s->transceiver.setConfig(cfg);
s->transceiver.save(); s->transceiver.save();
} }
if(opts.settings || opts.network) settings.save(); if(opts.settings || opts.network) settings.save();

View file

@ -4812,17 +4812,21 @@ bool Transceiver::toJSON(JsonObject& obj) {
bool Transceiver::fromJSON(JsonObject& obj) { bool Transceiver::fromJSON(JsonObject& obj) {
if (!obj["config"].isNull()) { if (!obj["config"].isNull()) {
JsonObject objConfig = obj["config"]; JsonObject objConfig = obj["config"];
// Parse into a copy so the radio task never sees a half updated config, // Parse into a copy so the radio task never sees a half updated config.
// then swap it in once any transmission in flight has stopped. Queued
// jobs are kept and go out with the new settings.
transceiver_config_t cfg = this->config; transceiver_config_t cfg = this->config;
cfg.fromJSON(objConfig); cfg.fromJSON(objConfig);
RadioLock lock(portMAX_DELAY, true); this->setConfig(cfg);
if(this->config.enabled && this->config.TXPin != cfg.TXPin) pinMode(this->config.TXPin, INPUT);
this->config = cfg;
} }
return true; return true;
} }
void Transceiver::setConfig(const transceiver_config_t &cfg) {
// Swap the whole config in once any transmission in flight has stopped so the
// radio task never sees a half updated one. Queued jobs are kept and go out
// with the new settings.
RadioLock lock(portMAX_DELAY, true);
if(this->config.enabled && this->config.TXPin != cfg.TXPin) pinMode(this->config.TXPin, INPUT);
this->config = cfg;
}
bool Transceiver::usesPin(uint8_t pin) { bool Transceiver::usesPin(uint8_t pin) {
if(this->config.enabled) { if(this->config.enabled) {
if(this->config.SCKPin == pin || if(this->config.SCKPin == pin ||

View file

@ -559,6 +559,7 @@ class Transceiver {
bool printBuffer = false; bool printBuffer = false;
//bool toJSON(JsonObject& obj); //bool toJSON(JsonObject& obj);
bool fromJSON(JsonObject& obj); bool fromJSON(JsonObject& obj);
void setConfig(const transceiver_config_t &cfg);
bool save(); bool save();
bool begin(); bool begin();
void loop(); void loop();