Skip to content

[BUG] Spurious ERR_ELECTRO_ACF_AT_INIT (power panic at boot) #5253

Description

@boinger

Printer model

Core One L (error surfaces on Core One as well — see analysis below)

Firmware version

6.5.3+12780 (current). Originally seen on 6.5.2 (factory firmware on the Core One L, exact build unknown); updated to 6.5.3+12780 hoping to fix it, still reproduces. Bug present in code on all POWER_PANIC-enabled printers since 0050cb43a (2025-02-12).

Upgrades and modifications

N/A — issue is in stock firmware

Printing from

N/A — fault fires at boot, before any print

Describe the bug

On boot, the printer intermittently displays the orange "Power Panic" error screen with ERR_ELECTRO_ACF_AT_INIT ("Power panic detected on startup. Check power panic cable.") — on Core One L this maps to help article #35338 "AC Controller Fault" (prusa.io/35338). Multiple resets (sometimes 6-7) are needed to clear the condition. No crash dump is generated.

Your help article acknowledges sporadic occurrences may be firmware-related. The analysis below confirms it.

How to reproduce

Power cycle the printer repeatedly. On some boots (intermittent, ~1 in every few to every few dozen depending on unit) the error fires instead of reaching the home screen.


TL;DR

check_ac_fault_at_startup() reads the acFault GPIO pin once with no debounce or retry, triggering ERR_ELECTRO_ACF_AT_INIT on transient power-on glitches. The pin is configured Pull::none, meaning it floats until the AC monitoring circuit drives it. On Core One, this causes intermittent false-positive "Power Panic" errors at boot, sometimes requiring 6-7 resets.

I'd like to propose adding a retry loop with settling delay as a workaround. Happy to send a PR — wanted to flag the pin configuration and history first, since the root cause may warrant Pull::up instead of (or in addition to) software debounce.

All file references in this issue are permalinks pinned to b91eeda0c so they survive line-number drift.

The Pull::up removal argument

The acFault pin originally had a pull-up resistor configured in software. In c9b365bc4 (2024-06-20, "iX: Enable Power Panic", BFW-4751), the pin was converted from InputPin + Pull::up to InterruptPin + Pull::none (schematic — macro arguments elided for readability):

-MACRO_FUNCTION(buddy::hw::InputPin, acFault, ..., IMode::input COMMA Pull::up, buddy::hw::noHandler)
+MACRO_FUNCTION(buddy::hw::InterruptPin, acFault, ..., IMode::IT_falling COMMA Pull::none COMMA ISR_PRIORITY_POWER_PANIC COMMA 0, power_panic::ac_fault_isr)

The stated intent was enabling interrupt-driven power panic on iX. The Pull::up removal was a side effect of the pin type change. With Pull::up, a floating pin reads HIGH (no fault) — safe during power-on transients. With Pull::none, a floating pin is indeterminate and may read LOW (fault).

Eight months later, 0050cb43a (2025-02-12, "Show nice ACF_AT_INIT for all relevant printers", BFW-6768) removed the printer-type guard that limited the check to XL/MK4/MK3.5, extending it to all POWER_PANIC printers including Core One. Neither debounce nor Pull::up was considered.

The two-commit interaction: one removed the hardware safety net, the other expanded the check to printers with potentially different monitoring circuit timing. Neither introduced compensation.

History — how the false positive was introduced

The startup check was established in 2e4ee6679 (2023-05-26, v5.0.0-alpha1). It was a single is_ac_fault_active() call guarded by if constexpr to fire only on XL/MK4/MK3.5. The code included a TODO: "AC-fault during initialization //TODO: IXL Remove if after PP is ready".

c9b365bc4 (2024-06-20) changed the pin from Pull::up to Pull::none. The commit's stated intent was enabling power panic on iX. The pull-up change was not discussed in the commit message. This is the commit that removed the protection against transient reads.

0050cb43a (2025-02-12) removed the printer-type conditional. BFW-6768's stated intent was showing the error on all relevant printers. The commit is -8/+2 lines — purely removing the guard. No debounce was added.

git log -S 'check_ac_fault_at_startup' since then shows only 0050cb43a itself. No one has touched the debounce gap in the 14 months since the pull-up was removed.

Why the current design fails

Three concrete failure surfaces with the current code:

  1. Floating pin at power-on. hwio_pindef.h:389. Pin PG0 configured Pull::none, IMode::IT_falling. Between GPIO init (main.cpp:239) and AC monitoring circuit stabilization, the pin state is indeterminate. A Pull::none pin with no external pull-up may read LOW (fault) on noise alone.

  2. Single read, immediate fatal_error. power_panic.cpp:812-817. One GPIO read → fatal_error(). No second opinion, no settling delay. A transient LOW on a Pull::none pin is treated identically to a persistent AC fault.

  3. Confirmed by users and Prusa. Your own help article (#35338) states: "if the error occurs sporadically and resolves itself without user intervention, it may point to a firmware-related issue." Users report needing 6-7 resets to clear the condition, confirming the transient window is wide.

Caller trace

Two production call sites for is_ac_fault_active():

  • power_panic.cpp:813check_ac_fault_at_startup(). Called once at boot from main.cpp:501. Only when POWER_PANIC is enabled (MK4, MK3.5, iX, XL, COREONE). IWDG watchdog is not yet running at this point — verified in wdt.cpp (IWDG init happens later in Marlin's setup()).

  • modular_bed.cpp:130 — runtime guard in ModularBed::read_general_status(). Only on modular-bed printers (XL/iX), not Core One. Well after boot, GPIO long settled. Not affected.

The ac_fault_isr() at power_panic.cpp:825 is armed during GPIO init but gated by ac_fault_enabled (set true only after the startup check). The ISR provides ongoing protection after boot — it is not affected by the startup transient issue.

If there's a caller I missed — especially one where the pin is read before GPIO initialization is complete — I'd want to hear about it.

Proposal

Software workaround (ready to PR): Add a retry loop to check_ac_fault_at_startup() — 5 retries × 200ms = 1s max settling window. Genuine faults persist; transients clear. Uses delay_ms() from timing.h — already included in power_panic.cpp (line 67), same family as the delay_us_precise() already used elsewhere in this file (line 798). Busy-wait on the project's ticks_ms timer, no FreeRTOS scheduler dependency. Net diff: +6 lines. The IWDG watchdog is not running at this point in boot, so the 1s delay carries no watchdog risk.

void check_ac_fault_at_startup() {
    for (int i = 0; i < 5 && power_panic::is_ac_fault_active(); ++i) {
        delay_ms(200);
    }
    if (power_panic::is_ac_fault_active()) {
        fatal_error(ErrCode::ERR_ELECTRO_ACF_AT_INIT);
    }
    ac_fault_enabled = true;
}

Hardware-level question for your team: Would restoring Pull::up on the acFault pin be appropriate? The pull-up was present before c9b365bc4 and would keep the pin HIGH (non-fault) during the transient window without needing software debounce. I don't know whether the AC monitoring circuit's output stage is compatible with an internal pull-up, so this is a question rather than a recommendation.

What this is NOT

This is not a root cause fix. Without an oscilloscope trace of PG0 during Core One boot, the exact cause of the transient LOW is undiagnosed. The retry loop tolerates the symptom. Possible root causes include: floating Pull::none pin before the monitoring circuit drives it, active fault assertion during monitoring circuit initialization, or power sequencing differences between printers. The software debounce works for all of these.

This is not a boot sequence reorder. The acFault pin is direct GPIO on the main STM32, not managed by a puppy controller. Moving the check after puppy init (main.cpp:508) would not help.


PR: #5254

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions