Skip to content

Safeties and Protections

What the firmware does when voltage, current, temperature, or engine speed leaves the safe range. Each protection below lists its trigger, the action taken, and the variable names to grep for; user-adjustable thresholds persist in non-volatile storage (NVS, via settingWrite() / settingRead()), while a few are deliberately hardcoded.

The protections share a small set of mechanisms (ramp-down paths, immediate cuts, cooldown lockout). Those are defined once below, then referenced by name.


Notation and Shared Mechanisms

Two voltage sensors

  • BatteryV — measured by the ADS1115 analog converter. Used only for the cross-sensor disagreement check.
  • IBV — measured by the INA228 battery monitor. The signal used by all software protections; getBatteryVoltage() returns it.

The INA228 also has a hardware alert pin that operates on the chip's own internally averaged voltage, independent of firmware.

Three shutdown paths

Path Behavior Used for
Immediate cut applyImmediateCut() — field enable line LOW within one control tick, PWM zeroed, control loop reset, system drops to fault state. No cooldown. Hardware overvoltage latch, hard overcurrent, critical temperature, engine speed below minimum
Warning ramp (MODE_WARNING_RAMP_AND_LOCKOUT) Staged ramp-down: slew duty to the tach-keepalive floor at the normal ramp rate, optional hold, then a slow ramp to zero, then a settle period before the enable line is cut. Followed by the cooldown lockout. Software hard overvoltage shutdown, sensor disagreement warning, temperature warning
Critical ramp (MODE_CRITICAL_RAMP) Same machinery but skips the gradual phases — jumps straight to the final settle-and-cut. Slew limiting forced off. Same cooldown after. Critical sensor disagreement, implausible voltage, stale temperature or current data

The ramp shape is user-tunable (DutyRampRate, DutySlowRampRate, ShutdownPhase2HoldMs, SettleTimeBeforeCut). Search 6_functions.ino for runShutdownPath.

Cooldown lockout

After any warning- or critical-ramp shutdown, charging is locked out for a cooldown period (FIELD_COLLAPSE_DELAY, user-settable, default tens of seconds). When it elapses the system retries; if the fault persists, a new lockout begins. Immediate-cut faults do not use the cooldown — they latch or stay cut.

Arming scope of the throttling protections

The fast overvoltage trims (Groups 1–3) and load-dump detection arm only while the voltage-holding loop is engaged (voltageControlActive — absorption, float, and the voltage-holding override modes). Group 1 additionally waits until measured voltage is within a small guard band of the target (PRED_GUARD, hardcoded) so it cannot fire during normal ramp-up far below target. A dashboard test switch (testProtectionsEnabled) can disarm the trim layers for plant-characterization tests; the hard shutdowns are not affected by it.


Overvoltage Protections

Several distinct mechanisms, from gentle to absolute. Groups 1–3 are throttling protections: they keep charging but trim the current setpoint cap (fastOvCurrentCap) so voltage stops rising. Load dump and the hard shutdown ramp the field down. The INA228 hardware alert is the final backup that works even if firmware hangs.

Group 1 — prediction-based trim

Projects where voltage will be a fraction of a second from now using the smoothed rate of rise, and trims the current cap if the projection overshoots.

  • Trigger: projected voltage (IBV plus lookahead horizon TdPred times the smoothed rate-of-rise) exceeds the active target by a margin (OvPredMarginV).
  • Action: lowers fastOvCurrentCap proportionally to the projected excess (slope KHard, amps per volt) and sets fastOvClampActive, which also removes the duty slew limit so the current loop can collapse the field at full speed.
  • Knobs: OvGroup1Enable, TdPred, OvPredMarginV, DvdtAlpha (rate-of-rise smoothing), KHard (shared with Group 2).

Group 2 — measured-voltage trim

Same trim action, but fires on present measured voltage with no prediction: IBV above target by OvMeasMarginV. Knobs: OvGroup2Enable, OvMeasMarginV, KHard.

Group 3 — current-excess supervisor (iExcess)

Catches overshoot before it shows in voltage, by noticing that measured alternator current is running well above the commanded setpoint at a moment when the voltage loop should be trimming back.

  • Trigger: measured current exceeds the setpoint by IExcessK amps for IExcessN consecutive control ticks, while voltage is already near target.
  • Release hysteresis: stays latched until current falls a couple of amps back under the arming threshold (IEXCESS_HYST, hardcoded) — prevents chatter.
  • Action: snaps the voltage-loop integrator (cv_I) to zero (or bleeds it proportionally, IExcessKBleed), lowers fastOvCurrentCap, sets fastOvClampActive. On release, the integrator is re-seeded to a fraction of its pre-event value (IExcessReseedFrac) so it does not bounce straight back through the setpoint.
  • Signal source selectable (IExcessSigSrc): moving average, exponential smoothing, or raw.

Load-dump detection

A three-tier rate-of-change detector on battery current — catches the upward current-slope spike when a big load is suddenly switched off. Each tier pairs a threshold with a persistence count: one very fast sample (hard-switched electronic disconnects), two consecutive fast samples (relay openings), or three consecutive moderate samples (slow contact separations). Two-in-a-row requirements reject the alternating-sign measurement noise of the current sensor.

  • Gate: voltage loop engaged and the battery monitor in its fast sampling mode (field on).
  • Action: integrator, setpoint, and current cap all collapse to zero at once; recovery is driven by the normal anti-windup bleed.
  • Knobs: LoadDumpDtThresh1 / LoadDumpDtThresh / LoadDumpDtThresh3 (amps per second, all user-settable). Search 6_functions.ino for LoadDumpDtThresh1.

Software hard shutdown (absolute trip)

The only software-layer absolute overvoltage shutdown: IBV above AlternatorHardShutdownV triggers the warning ramp plus cooldown. If voltage is well beyond the trip point (a hardcoded extra margin), the slew limit is bypassed and the first ramp phase becomes instant. The threshold default scales with the configured system voltage on first boot; once saved it is absolute — re-set it manually if you change system class. Active in automatic mode only.

INA228 hardware alert (hardware backup)

The fastest and most independent protection — it operates with no firmware involvement.

  • Trigger: the chip's internally averaged bus voltage exceeds a programmed limit (VoltageHardwareLimit, automatically maintained a fixed offset above BulkVoltage — see updateINA228OvervoltageThreshold() in 5_functions.ino).
  • Action: the chip's alert pin (active low, open drain) physically pulls the field enable line down — the gate driver loses power and the field collapses regardless of firmware state.
  • Software latch: CheckAlarms() polls the chip's alert register and sets inaOvervoltageLatched. While latched, the event selector reports the overvoltage reason at top priority and applyImmediateCut() runs every tick. The latch is re-checked on a slow cadence and releases automatically once the chip reports clear.
  • Post-event suppression: for a short window after the latch clears (INA_OV_DISAGREE_SUPPRESS_MS), the sensor-disagreement check is suppressed — the two sensors legitimately diverge while the field collapses.
  • Active in all modes, including manual and Limp Home.

Co-firing behavior

The software hard shutdown and the hardware alert sit at the same nominal threshold by design. The software path reads raw IBV and wins on fast transients; the hardware's averaged value catches slow ramps and firmware hangs. When both fire, applyImmediateCut() notices the enable line is already low and records state without redundant action.


Voltage Sensor Failure Protections

These do not detect a battery problem — they detect that the voltage sensors themselves disagree or read impossible values.

  • Disagreement warning — the two sensors differ by more than VoltageDisagreeThreshold continuously for VoltageDisagreeTimeout. Warning ramp + lockout.
  • Disagreement critical — a much larger difference (auto-scaled with system voltage class) sustained for a short hardcoded debounce, or either sensor returning not-a-number or near zero (those fire with no debounce). Critical ramp + lockout. Search 6_functions.ino for isVoltageDisagreementCritical.
  • Implausibilityboth sensors outside the plausible range for the system class (one bad sensor alone does not trip this). Critical ramp + lockout, no debounce. Search 6_functions.ino for isVoltageSensorPlausible.

All three are active in automatic mode only.


Overvoltage Cascade — Illustrative 12 V Defaults

With factory defaults on a 12 V system, in order of escalation:

Condition Protection Action
Voltage ~0.1 V over target Group 2 Trim current cap
Predicted voltage ~0.15 V over target Group 1 Trim current cap
Current excess while near target Group 3 (iExcess) Snap integrator, trim cap
Voltage over the absolute trip (AlternatorHardShutdownV) Software hard shutdown Warning ramp + lockout
Chip-averaged voltage over VoltageHardwareLimit Hardware alert Physical field cut
Well beyond the absolute trip Slew-bypass accelerator Ramp becomes instant
Both sensors outside plausible range Implausibility Critical ramp + lockout

Note the reference difference: the Group 1–3 margins are relative to the active charging target (bulk, absorption, or float as appropriate), while the hard-shutdown thresholds are absolute.


Temperature Protections

All temperature protections act on TempToUse — the alternator temperature probe by default, or the thermistor input if selected (TempSource). Setting IgnoreTemperature disables T0–T3 below; the staleness and task-health checks (T4, T5) remain.

T0 — continuous thermal derating (the thermal PID)

Not a fault response — normal steady-state thermal management, described in full on Charging Control. It subtracts a current penalty (thermalPenaltyAmps) from the ceiling before the limit is ever reached, working from a predicted temperature (present value plus rate-of-rise times a lookahead horizon). The setpoint is the actual damage limit (TemperatureLimitF).

T1 — warning ramp

Temperature exceeds the limit by a small margin (TempWarnExcess): warning ramp + cooldown lockout. Automatic mode only.

T2 — sustained warning

The T1 condition held continuously for TempSustainedTimeout: the field is cut and stays off — no automatic restart, manual re-enable or reboot required. The escalation exists because repeatedly bouncing off the warning ramp means derating has failed.

T3 — critical immediate cut

Temperature exceeds the limit by a larger margin (TempCritExcess): applyImmediateCut(), no ramp. Active in all modes including manual — it is evaluated at top priority in the event selector (selectFieldEventReason()), ahead of the manual-mode branch. Manual does not bypass it.

T4 — temperature task hang

The temperature probe runs in its own task on the second CPU core; a heartbeat monitor (checkTempTaskHealth(), deadline TEMP_TASK_TIMEOUT) raises the alarm condition if the task goes silent. This raises the buzzer/latch condition; the actual field cut comes from T5, which fires on the same silence.

T5 — temperature data stale

The primary field-cut for any temperature monitoring failure: no validated reading for the staleness window (about twenty seconds), or readings that are not-a-number or wildly out of range. Critical ramp + lockout; recovers automatically on the first valid reading after the lockout clears. Covers sensor disconnects, CRC failures, and hung tasks alike.


Overcurrent Protections

Hard overcurrent (electronic fuse)

Measured alternator current above HardOCTripAmps for a short debounce (HardOCDebounceMs): applyImmediateCut(). The trip point is auto-derived a fixed margin above the user's maximum table current (MaxTableValue) and recomputed whenever that changes — search 3_functions.ino for HardOCTripAmps. Active in all modes including manual, at top priority.

Current alarms (buzzer only)

Two thresholds drive the buzzer but never touch the field: alternator current above CurrentAlarmHigh, and net battery current magnitude above MaximumAllowedBatteryAmps. Both via CheckAlarms() when alarms are armed.


Engine Speed Gate (RPM gate)

Engine speed below MinRPMForField (with IgnoreRPM off) cuts the field immediately — there is no reason to excite the field on a stopped or stalling engine. Active in all modes; no cooldown, recovers as soon as RPM returns.


System-Level Safeties

Hardware watchdog

A hardware watchdog timer (initialized via esp_task_wdt_init in Xregulator.ino) reboots the processor if the main loop stops feeding it for sixteen seconds. On reboot all GPIO pins reset low, which itself cuts the field driver — a hang fails safe.

Alarm Buzzer (GPIO21)

Driven by CheckAlarms() on a fixed cadence, aggregating every alarm condition into one output on GPIO21. The buzzer is informational only — it has no authority over the field.

Electrical output

The alarm output is a switched 5 V supply driven through a current-limited high-side switch (folds back at roughly a quarter amp, so shorts and oversized loads cannot damage the board) with a built-in flyback clamp diode, so relay coils and magnetic buzzers connect directly. Full circuit details and supported-load table: Warning Buzzer.

Alarm conditions

Condition Threshold setting
High alternator temperature TempAlarm
Low alternator temperature TempAlarmLow (zero disables)
High battery voltage VoltageAlarmHigh
Low battery voltage VoltageAlarmLow
High alternator current CurrentAlarmHigh
High battery current MaximumAllowedBatteryAmps
Hardware overvoltage latch any INA228 alert event
Temperature sensor not responding staleness window
Temperature task hung heartbeat deadline

The temperature-task-hang condition is raised regardless of the arm switch (console message and latch), but the physical output stays silent unless alarms are armed (AlarmActivate).

Alarm logic

How the three user toggles, the live condition, and the latch combine:

AlarmTest AlarmActivate AlarmLatchEnabled Condition Latch Output
on any any any ON (auto-clears after the test period)
off off any any any OFF
off on off none OFF
off on off tripped ON
off on on tripped set ON
off on on cleared after a trip still set ON until latch reset
off on on never tripped / latch reset clear OFF

A latched alarm can be silenced by disarming, but returns on re-arm unless the latch is reset (ResetAlarmLatch). New conditions arriving while silenced still set the latch.

Dashboard controls

The Alarms panel exposes all of the above:

Alarm panel in the web dashboard

Dashboard control Firmware toggle
Alarm Enable (Off / Armed) AlarmActivate
Alarm Status indicator Alarm_Status
Temperature / voltage / current thresholds the settings in the conditions table
Alarm Latch Mode (Momentary / Latched) AlarmLatchEnabled
Test Buzzer AlarmTest
Reset Latch ResetAlarmLatch

Mode-by-Mode Active Protections

Protection AUTO MANUAL LIMP HOME
Groups 1 / 2 / 3 (overvoltage trims) Active (voltage loop engaged)
Load dump Active
Software hard shutdown Active
INA228 hardware alert (physical) Active Active Active
INA228 software latch Active Active Active
Sensor disagreement (warning + critical) Active
Voltage implausible Active
T0 thermal derating Active
T1 / T2 temperature warning Active
T3 critical temperature cut Active Active
T4 temperature task hang (alarm) Active Active Active
T5 temperature data stale Active
Hard overcurrent Active Active
RPM gate Active Active
Watchdog reboot Active Active Active
Buzzer When armed Same Same

"—" means the priority chain in selectFieldControlMode() resolves the mode before that protection is evaluated, or Limp Home deliberately bypasses it. Limp Home keeps only the hardware-level protections — it exists for getting home on failed sensors.