#9508·firmware

power.adc_multiplier_override runtime changes have no immediate effect

Author: lexfreiCreated Feb 2, 2026Updated Sep 19, 2026
LabelsStale

Device Information

  • Firmware version: 2.7.18.4744010
  • Device: Seeed XIAO ESP32S3 (seeed-xiao-s3)
  • Platform: ESP32-S3

Bug Description

When changing power.adc_multiplier_override at runtime via CLI or Admin API, the setting is saved correctly but has no visible effect on voltage readings for 1-2 minutes. Compile-time ADC_MULTIPLIER in variant.h works correctly and immediately.

Steps to Reproduce

  1. Device has compile-time ADC_MULTIPLIER 2.0 (default)
  2. Battery shows ~4.08V at full charge
  3. Set runtime override: meshtastic --set power.adc_multiplier_override 2.06
  4. Verify setting saved: meshtastic --get power.adc_multiplier_override returns 2.059999...
  5. Reboot device
  6. Wait 2+ minutes
  7. Check voltage: still shows ~4.08V instead of expected ~4.20V

Expected: Voltage should show ~4.20V after multiplier change (or converge quickly) Actual: Voltage remains at ~4.08V for extended period, slowly converges over 1-2 minutes

Root Cause

Found in src/Power.cpp, method AnalogBatteryLevel::getBattVoltage().

The Low Pass Filter (LPF) uses cached last_read_value that was calculated with the old multiplier. The initial_read_done flag is set to true on first reading and never reset when multiplier changes.

cpp
// Line 315-316: Multiplier is correctly read from config
float operativeAdcMultiplier =
    config.power.adc_multiplier_override > 0 ? config.power.adc_multiplier_override : ADC_MULTIPLIER;

// Lines 339-348: But LPF uses old cached value
if (!initial_read_done) {
    if (scaled > last_read_value)
        last_read_value = scaled;
    initial_read_done = true;  // Set to true FOREVER
} else {
    // LPF slowly adapts with 0.5 coefficient - takes 5+ readings to converge
    last_read_value += (scaled - last_read_value) * 0.5;
}

With coefficient 0.5 and minimum read interval of 5 seconds, full convergence takes ~1-2 minutes.

Why compile-time works immediately

When ADC_MULTIPLIER is changed in variant.h and firmware is reflashed, device reboots with initial_read_done = false, so first reading sets last_read_value directly without LPF filtering.

Proposed Fix

Track the current multiplier and reset the filter when it changes:

cpp
// Add member variable:
float last_adc_multiplier = 0;

// In getBattVoltage(), after computing operativeAdcMultiplier:
if (last_adc_multiplier != operativeAdcMultiplier) {
    initial_read_done = false;
    last_adc_multiplier = operativeAdcMultiplier;
}

Additional Context

The comment in AdminModule.cpp (line 702) says:

"Really just the adc override is the only thing that can change without a reboot"

This implies runtime changes should work, but the LPF filter prevents immediate effect.