diff --git a/zephcore/adapters/datastore/ZephyrDataStore.cpp b/zephcore/adapters/datastore/ZephyrDataStore.cpp index 9f46ba0..bd7f60c 100644 --- a/zephcore/adapters/datastore/ZephyrDataStore.cpp +++ b/zephcore/adapters/datastore/ZephyrDataStore.cpp @@ -794,6 +794,19 @@ void ZephyrDataStore::loadPrefs(NodePrefs &prefs) prefs.cad_busycap = 90; } } + + /* Offset 159: adc_multiplier (ZephCore extension, float LE, 0 = board + * DT default). Absent in pre-existing files → keep 0.0 so the DT + * default stays in effect. Same range guard as the repeater CLI path + * (CommonCLI constrains 0..30000); NaN/garbage resets to default. */ + if (off + 4 <= len) { + memcpy(&prefs.adc_multiplier, &buf[off], sizeof(float)); + off += 4; + if (prefs.adc_multiplier != prefs.adc_multiplier || + prefs.adc_multiplier < 0.0f || prefs.adc_multiplier > 30000.0f) { + prefs.adc_multiplier = 0.0f; + } + } } void ZephyrDataStore::savePrefs(const NodePrefs &prefs) @@ -883,7 +896,12 @@ void ZephyrDataStore::savePrefs(const NodePrefs &prefs) buf[off++] = prefs.cad_probe_interval; /* Offset 158: cad_busycap (ZephCore extension, percent) */ buf[off++] = prefs.cad_busycap; - /* Total: 159 bytes */ + /* Offset 159: adc_multiplier (ZephCore extension, float LE, 0 = board + * DT default). Was applied at boot but never serialized before this + * field existed — battery calibration silently reset every reboot. */ + memcpy(&buf[off], &prefs.adc_multiplier, sizeof(float)); + off += 4; + /* Total: 163 bytes */ bool ok = atomicReplaceFile(PREFS_FILE, buf, off); LOG_DBG("savePrefs: wrote %s, ok=%d (%d bytes), name='%.16s'", diff --git a/zephcore/helpers/CommonCLI.cpp b/zephcore/helpers/CommonCLI.cpp index ce46bc5..b613bdb 100644 --- a/zephcore/helpers/CommonCLI.cpp +++ b/zephcore/helpers/CommonCLI.cpp @@ -614,13 +614,26 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch savePrefs(); strcpy(reply, "OK"); } else if (memcmp(config, "int.thresh ", 11) == 0) { - _prefs->interference_threshold = atoi(&config[11]); - savePrefs(); - strcpy(reply, "OK"); + /* Companion runtime never reads this (getInterferenceThreshold is + * only overridden in Repeater/RoomServer) — reject instead of a + * false OK. */ + if (strcmp(_callbacks->getRole(), "companion") == 0) { + strcpy(reply, "Error: not supported on companion"); + } else { + _prefs->interference_threshold = atoi(&config[11]); + savePrefs(); + strcpy(reply, "OK"); + } } else if (memcmp(config, "agc.reset.interval ", 19) == 0) { - _prefs->agc_reset_interval = atoi(&config[19]) / 4; - savePrefs(); - snprintf(reply, CLI_REPLY_SIZE, "OK - interval rounded to %u", ((uint32_t)_prefs->agc_reset_interval) * 4); + /* Companion runtime never reads this (getAGCResetInterval is only + * overridden in Repeater/RoomServer). */ + if (strcmp(_callbacks->getRole(), "companion") == 0) { + strcpy(reply, "Error: not supported on companion"); + } else { + _prefs->agc_reset_interval = atoi(&config[19]) / 4; + savePrefs(); + snprintf(reply, CLI_REPLY_SIZE, "OK - interval rounded to %u", ((uint32_t)_prefs->agc_reset_interval) * 4); + } } else if (memcmp(config, "cad.auto ", 9) == 0) { if (memcmp(&config[9], "on", 2) == 0 || memcmp(&config[9], "off", 3) == 0) { _prefs->cad_auto = (config[9] == 'o' && config[10] == 'n') ? 1 : 0; @@ -818,14 +831,21 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch savePrefs(); strcpy(reply, "OK (ignored: direct.txdelay is now adaptive)"); } else if (memcmp(config, "backoff.multiplier ", 19) == 0) { - float f = atof(&config[19]); - if (f >= 0.0f && f <= 2.0f) { - _prefs->backoff_multiplier = f; - _callbacks->setBackoffMultiplier(f); - savePrefs(); - strcpy(reply, "OK"); + /* Companion's setBackoffMultiplier callback is the base-class + * no-op and the value isn't restored at boot — reject instead of + * a false OK. */ + if (strcmp(_callbacks->getRole(), "companion") == 0) { + strcpy(reply, "Error: not supported on companion"); } else { - strcpy(reply, "Error, range 0.0-2.0"); + float f = atof(&config[19]); + if (f >= 0.0f && f <= 2.0f) { + _prefs->backoff_multiplier = f; + _callbacks->setBackoffMultiplier(f); + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, range 0.0-2.0"); + } } } else if (memcmp(config, "owner.info ", 11) == 0) { config += 11; @@ -848,6 +868,12 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch strcpy(reply, "Error, must be 0,1, or 2"); } } else if (memcmp(config, "loop.detect ", 12) == 0) { + /* Loop detection runs only in the Repeater/RoomServer forward + * path — companions never consult loop_detect. */ + if (strcmp(_callbacks->getRole(), "companion") == 0) { + strcpy(reply, "Error: not supported on companion"); + return; + } config += 12; uint8_t mode; if (memcmp(config, "off", 3) == 0) {