Field Control System Architecture¶
System Overview¶
This is an alternator field controller for marine/vehicle applications featuring: - Cascaded PID control with outer voltage loop and inner current loop - Adaptive learning tables that adjust current limits based on thermal feedback - Multi-tier setpoint caps (MaxTableValue, RPM-dependent mechanical limits, MaximumAllowedBatteryAmps) - Multi-tier fault protection (temperature, voltage, sensor validation) - Two-phase field shutdown protecting LM2907 tachometer circuitry from rapid changes in stator waveform - Dual charging stages (Bulk and Float) with automatic transitions - Persistent learning tables stored in NVS - Web-configurable parameters stored in LittleFS
Main Loop Architecture¶
┌─────────────────────────────────────────────────────────────────────┐
│ MAIN LOOP (every tick) │
├─────────────────────────────────────────────────────────────────────┤
│ 1. Build TickSnapshot (immutable state capture) │
│ 2. Update sustained timers (temp warning, voltage disagreement) │
│ 3. Update charging stage (bulk ↔ float transitions) │
│ 4. Administrative actions (table reset, clear history) │
│ 5. Select mode via pure function (priority-based) │
│ 6. Detect mode transitions, handle state resets │
│ 7. Execute mode: │
│ ├─ NON-NORMAL: Two-phase shutdown │
│ │ a) Check immediate cut (critical temp only) │
│ │ b) Ramp duty to 0% with rate limiting │
│ │ c) Cut GPIO4 after settle (or immediately for critical) │
│ │ d) Start lockout timer if WARNING/CRITICAL │
│ └─ NORMAL: Cascaded PID control │
│ a) Get target from learning table (RPM-based lookup) │
│ b) Apply corrections (ambient temp, overheat penalty) │
│ c) Apply modifiers (HiLow, ForceFloat) │
│ d) Apply setpoint caps (MaxTableValue, RPM cap, battery cap)│
│ e) Voltage outer loop (float stage only): cap current │
│ f) Rate-limit setpoint changes │
│ g) Inner PID compute → duty cycle │
│ h) Soft limiters (temp, voltage) │
│ i) Process learning logic (if enabled and conditions met) │
│ 8. Apply duty with rate limiting + RPM minimum enforcement │
│ 9. Update telemetry │
│ 10. Calculate diagnostics (1Hz) │
└─────────────────────────────────────────────────────────────────────┘
Mode Priority Hierarchy¶
Modes are selected via pure functions based on immutable TickSnapshot. Higher priority wins.
| Priority | Mode | Trigger Conditions | GPIO4 | Alarm | Lockout |
|---|---|---|---|---|---|
| 1 | MODE_CRITICAL_RAMP |
Temp stale, temp critical (+30°F), voltage implausible, voltage disagree critical | Cut (immediate for temp critical) | ON | No |
| 2 | MODE_WARNING_RAMP_AND_LOCKOUT |
Voltage spike, voltage disagree warning, temp warning (+10°F) | Cut after settle | ON | Yes |
| 3 | MODE_LOCKOUT_RAMP |
Auto-zero active, lockout timer active | Cut after settle | ON | N/A |
| 4 | MODE_DISABLED_RAMP |
Charging disabled (ignition off, OnOff=0, BMS signal, weather mode) | Cut after settle | OFF | No |
| 5 | MODE_NORMAL_MANUAL |
Manual mode enabled | HIGH | OFF | No |
| 6 | MODE_NORMAL_AUTO_PID |
Default normal operation | HIGH | OFF | No |
Two-Phase Shutdown Mechanism¶
Purpose: Protect LM2907 tachometer IC's AC coupling capacitor (τ = RC = 100kΩ × 20µF = 2.0s) from sending stator pulse waveform (RPM input) outside measurable range
┌──────────────────────────────────────────────────────────────────┐
│ PHASE 1: Controlled Ramp-Down │
│ ─────────────────────────────────────────────────────────────── │
│ • GPIO4 remains HIGH (field driver active) │
│ • Duty ramped to 0% at DutyRampRate (default 50%/sec) │
│ • Takes ~2 seconds from 100% to 0% │
│ • Prevents voltage transients that charge coupling cap │
└──────────────────────────────────────────────────────────────────┘
↓
After SettleTimeBeforeCut (500ms) at 0%
↓
┌──────────────────────────────────────────────────────────────────┐
│ PHASE 2: Field Disconnect │
│ ─────────────────────────────────────────────────────────────── │
│ • GPIO4 goes LOW (field driver disabled) │
│ • PWM output electrically irrelevant │
│ • Rate limiter continues tracking for smooth restart │
└──────────────────────────────────────────────────────────────────┘
EXCEPTION: TEMP_CRITICAL skips Phase 1 entirely (immediate GPIO4 cut)
Why This Matters: - Sudden duty changes cause stator voltage transients - Transients charge the AC coupling capacitor - Charged cap affects tach signal for 4.6τ ≈ 9.2 seconds - Rate-limited field ramp prevents this problem
Cascaded Control Architecture¶
The system uses cascaded control loops during the Float charging stage to prevent PID/voltage-limiter fighting:
┌─────────────────────────────────────────────────────────────────┐
│ CASCADED CONTROL (Float Stage) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ OUTER LOOP (Voltage Control) - 500ms update rate │ │
│ │ ────────────────────────────────────────────────────── │ │
│ │ Input: ChargingVoltageTarget - BatteryVoltage (error) │ │
│ │ Output: voltageCapAmps = vError × VoltageKp │ │
│ │ Bounds: 0 to finalLearningTarget (thermal limit) │ │
│ │ Effect: Caps current setpoint to maintain target voltage│ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ uTargetAmps = min(uTargetAmps, voltageCapAmps)
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ INNER LOOP (Current Control) - 100ms update rate │ │
│ │ ────────────────────────────────────────────────────── │ │
│ │ Input: uTargetAmps - measuredCurrent (error) │ │
│ │ Output: dutyCycle (0-100%) │ │
│ │ Type: PID controller │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
BULK STAGE: Outer loop inactive, inner PID targets learning table current
Voltage soft limiter provides backup protection
FLOAT STAGE: Outer loop active, smoothly reduces current to maintain voltage
No PID/limiter fighting, no integral windup
Key Design Points:
- Outer loop can only reduce current, never exceed thermal limit
- Upper bound is finalLearningTarget (includes ambient correction and penalty)
- P-only controller sufficient for voltage maintenance
- Rate limiting on duty output protects LM2907 regardless of loop
Setpoint Cap Chain¶
Before the voltage outer loop and PID compute, uTargetAmps passes through a chain of setpoint caps. This is the correct pattern: cap the setpoint, not the duty cycle. Capping duty causes the PID to fight the limiter; capping the setpoint lets the PID settle cleanly to the constrained target.
┌─────────────────────────────────────────────────────────────────┐
│ SETPOINT CAP CHAIN │
├─────────────────────────────────────────────────────────────────┤
│ │
│ uTargetAmps (from learning table + corrections + modifiers) │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 1. MaxTableValue cap (absolute table ceiling) │ │
│ │ if uTargetAmps > MaxTableValue → clamp │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 2. RPM Cap Table (mechanical limits: belt/shaft/mount) │ │
│ │ Interpolated from rpmCapCurrentTable[] │ │
│ │ if uTargetAmps > rpmCapAmps → clamp │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 3. MaximumAllowedBatteryAmps (global battery ceiling) │ │
│ │ if uTargetAmps > MaximumAllowedBatteryAmps → clamp │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ uTargetAmps (capped) │
│ │ │
│ ▼ │
│ Voltage outer loop (float stage only) │
│ │ │
│ ▼ │
│ Inner PID compute │
│ │
└─────────────────────────────────────────────────────────────────┘
If ANY cap binds → learning is suppressed for that tick
(system is not thermally limited, so learning data would be invalid)
RPM Cap Table:
- Always enabled; uses same RPM breakpoints as learning table (rpmTableRPMPoints[])
- Interpolated between breakpoints for smooth behavior
- To disable a cap point, set its value to 99999 (effectively unlimited)
- Protects against mechanical limits (belt slip, shaft stress, mounting fatigue)
Charging Stage State Machine¶
┌─────────────────────────────────────────────────────────────────┐
│ BULK STAGE │
│ ─────────────────────────────────────────────────────────────── │
│ ChargingVoltageTarget = BulkVoltage │
│ Control: Inner PID targets learning table current │
│ Voltage soft limiter: Active (backup protection) │
│ Learning: Active (thermal feedback valid) │
│ │
│ Exit condition: │
│ voltage >= BulkVoltage for bulkCompleteTime │
└──────────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ FLOAT STAGE │
│ ─────────────────────────────────────────────────────────────── │
│ ChargingVoltageTarget = FloatVoltage │
│ Control: Cascaded - outer voltage loop caps inner current PID │
│ Voltage soft limiter: Disabled (outer loop handles it) │
│ Learning: Suppressed (voltage-limited, not thermally-limited) │
│ │
│ Exit conditions (return to bulk): │
│ • Time in float > FLOAT_DURATION, OR │
│ • Voltage drops below FloatVoltage - 0.5V │
└─────────────────────────────────────────────────────────────────┘
Fault Response Matrix¶
Temperature Faults¶
| Severity | Condition | Mode | Action | Learning |
|---|---|---|---|---|
| Soft | temp > limit |
NORMAL_AUTO_PID |
Soft limiter: reduce duty | Continues |
| Warning | temp > limit + TempWarnExcess |
WARNING_RAMP_AND_LOCKOUT |
Two-phase shutdown, lockout | Paused |
| Sustained | Warning for > TempSustainedTimeout | WARNING_RAMP_AND_LOCKOUT |
GPIO4 cut after settle | Triggers penalty |
| Critical | temp > limit + TempCritExcess |
CRITICAL_RAMP |
Immediate GPIO4 cut | Triggers penalty |
| Stale | No fresh temp data > 30s (after 60s boot) | CRITICAL_RAMP |
Two-phase shutdown | Paused |
Voltage Faults¶
| Severity | Condition | Mode | Action |
|---|---|---|---|
| Soft | voltage > ChargingVoltageTarget (bulk only) |
NORMAL_AUTO_PID |
Soft limiter: reduce duty |
| Spike | voltage > BulkVoltage + VoltageSpikeMargin |
WARNING_RAMP_AND_LOCKOUT |
Two-phase shutdown, lockout |
| Disagree Warning | |BatteryV - IBV| > threshold for > timeout |
WARNING_RAMP_AND_LOCKOUT |
Two-phase shutdown, lockout |
| Disagree Critical | |BatteryV - IBV| > 1/2/4V (12/24/48V systems) |
CRITICAL_RAMP |
Two-phase shutdown |
| Implausible | Either sensor NaN, zero, or out of range | CRITICAL_RAMP |
Two-phase shutdown |
Soft Limiters (Duty-Based Backstops)¶
These remain as duty-based "nudges" rather than setpoint caps because they are rare emergency backstops, not expected to bind during normal operation. Immediate duty reduction provides fastest relief.
| Limiter | Condition | Action |
|---|---|---|
| Temperature | temp > TemperatureLimitF |
Reduce duty by 2 * dutyStep |
| Voltage (bulk only) | voltage > ChargingVoltageTarget |
Reduce duty by 3 * dutyStep |
Note: Battery current limiting is handled by the MaximumAllowedBatteryAmps setpoint cap, not a duty-based soft limiter. This prevents PID/limiter fighting during normal high-current operation.
Lockout (Cooldown) System¶
After WARNING or CRITICAL faults, a mandatory cooldown period prevents immediate restart.
┌─────────────────────────────────────────────────────────────────┐
│ LOCKOUT BEHAVIOR │
├─────────────────────────────────────────────────────────────────┤
│ Trigger: WARNING or CRITICAL mode entered │
│ Duration: FIELD_COLLAPSE_DELAY (configurable) │
│ │
│ During lockout: │
│ • Mode forced to MODE_LOCKOUT_RAMP │
│ • GPIO4 remains LOW │
│ • Charging cannot resume regardless of fault clearing │
│ │
│ After lockout expires: │
│ • If fault cleared → return to normal operation │
│ • If fault persists → remain in fault mode │
│ │
│ Console message on lockout start: │
│ "Field lockout started: Xs cooldown before restart allowed" │
└─────────────────────────────────────────────────────────────────┘
Learning System¶
RPM-Based Tables (Shared X-Axis)¶
All tables share the same RPM breakpoints (rpmTableRPMPoints[]) with length RPM_TABLE_SIZE:
| Table | Purpose | Interpolation |
|---|---|---|
rpmCurrentTable[] |
Thermal learning targets (A) | Stepped (uses index) |
rpmMinDutyTable[] |
Minimum duty for tach signal (%) | Linear interpolation |
rpmCapCurrentTable[] |
Mechanical current ceiling (A) | Linear interpolation |
|
Learning Events¶
| Event | Trigger | Table Action | Side Effects |
|---|---|---|---|
| Overheat | TempToUse > TemperatureLimitF |
Reduce current at RPM index by LearningDownStep |
Penalty applied, neighbors reduced if enabled |
| Safe Operation | Temp below (limit - LearningTempHysteresis) for SafeOperationThreshold |
Increase current at RPM index by LearningUpStep |
Cumulative time tracked |
Learning Upward Clamp¶
When learning increases a table entry, the result is clamped to the minimum of:
- MaxTableValue (absolute ceiling)
- rpmCapCurrentTable[index] (mechanical limit at that RPM)
- MaximumAllowedBatteryAmps (battery ceiling)
This prevents learning from writing values that would immediately be capped at runtime.
Learning Suppression Conditions¶
Learning is blocked when ANY of these conditions are true:
| Condition | Reason |
|---|---|
LearningMode == 0 |
Globally disabled |
LearningPaused == 1 |
User-paused |
HiLow == 0 |
Half-power mode active |
ForceFloat == 1 |
SOC maintenance mode active |
voltageControlActive == true |
Float stage (voltage-limited, not thermally-limited) |
| Any setpoint cap binding | MaxTableValue, RPM cap, or MaximumAllowedBatteryAmps is limiting |
| Penalty active | Recently overheated |
Within LearningSettlingPeriod of RPM change |
RPM transition settling |
Within MinLearningInterval of last update |
Rate limiting |
Temp within LearningTempHysteresis of limit |
Hysteresis band |
| Non-normal mode | Fault or shutdown in progress |
Design Principle: Learning only runs when the system is thermally limited under full power with no overrides or caps binding. This ensures table adjustments reflect true thermal limits.
Special Operating Modes¶
ForceFloat (SOC Maintenance Mode)¶
Note: "ForceFloat" is a historical misnomer. This mode maintains current battery state-of-charge by targeting zero battery current, not a specific float voltage.
┌─────────────────────────────────────────────────────────────────┐
│ FORCEFLOAT BEHAVIOR │
├─────────────────────────────────────────────────────────────────┤
│ Trigger: ForceFloat == 1 (user-activated) │
│ │
│ Behavior: │
│ • uTargetAmps = 0 (target zero battery current) │
│ • Uses getBatteryCurrent() as feedback source │
│ • Learning suppressed (learningSuppressedByMode = true) │
│ • All safety limits still active (temp, voltage) │
│ │
│ Use Case: Battery is fully charged, minimize alternator │
│ contribution while allowing other loads to draw from alternator │
│ │
│ Future Enhancement: Could target present battery voltage │
│ instead of zero current for true maintenance charging. │
└─────────────────────────────────────────────────────────────────┘
Weather Mode (Solar Priority)¶
Weather mode cleanly disables charging via the mode system when solar forecast indicates sufficient renewable generation.
┌─────────────────────────────────────────────────────────────────┐
│ WEATHER MODE BEHAVIOR │
├─────────────────────────────────────────────────────────────────┤
│ Trigger: weatherModeEnabled == 1 && currentWeatherMode == 1 │
│ │
│ Mechanism: │
│ • Folds into chargingEnabled in buildTickSnapshot() │
│ • Results in MODE_DISABLED_RAMP (clean shutdown) │
│ • No alarm (GPIO21 LOW) - this is intentional, not a fault │
│ • Two-phase ramp-down protects LM2907 │
│ │
│ Decision Logic (in analyzeWeatherMode): │
│ • Counts days with UV/solar above UVThresholdHigh │
│ • If 2+ of 3 days above threshold → disable alternator │
└─────────────────────────────────────────────────────────────────┘
Hardware Interface¶
| GPIO | Function | Active State | Notes |
|---|---|---|---|
| GPIO4 | Field driver enable | HIGH = enabled | Hard enable for PWM output |
| GPIO21 | Alarm output | HIGH = fault | Active during non-normal modes (except DISABLED) |
| GPIO36 | BMS input | Configurable | Polarity set by bmsLogicLevelOff |
Key Parameters Reference¶
Timing Parameters¶
| Parameter | Example Default | Units | Purpose |
|---|---|---|---|
PidSampleTime |
100 | ms | Inner PID update interval |
VoltageLoopInterval |
500 | ms | Outer voltage loop update interval |
MinLearningInterval |
30000 | ms | Min time between learning updates |
SafeOperationThreshold |
30000 | ms | Time for upward learning credit |
LearningSettlingPeriod |
30000 | ms | Post-RPM-change settling delay |
SettleTimeBeforeCut |
500 | ms | Duty at 0% before GPIO4 cut |
TempSustainedTimeout |
120000 | ms | Warning → sustained escalation |
VoltageDisagreeTimeout |
10000 | ms | Sensor disagreement warning threshold |
MaxPenaltyDuration |
60000 | ms | Overheat penalty duration |
FIELD_COLLAPSE_DELAY |
(define) | ms | Lockout/cooldown duration |
bulkCompleteTime |
(configurable) | ms | Time at bulk voltage before float transition |
FLOAT_DURATION |
(define) | sec | Duration of float stage before returning to bulk |
Threshold Parameters¶
| Parameter | Example Default | Units | Purpose |
|---|---|---|---|
TempWarnExcess |
10.0 | °F | Above limit → WARNING mode |
TempCritExcess |
30.0 | °F | Above limit → CRITICAL mode (immediate cut) |
LearningTempHysteresis |
10 | °F | Below limit required for upward learning |
VoltageSpikeMargin |
0.2 | V | Above bulk → voltage spike warning |
VoltageDisagreeThreshold |
0.15 | V | Sensor disagreement detection |
Control Parameters¶
| Parameter | Example Default | Units | Purpose |
|---|---|---|---|
VoltageKp |
25.0 | A/V | Voltage loop gain (P-only) |
PidKp |
0.5 | - | Inner PID proportional gain |
PidKi |
0.0 | - | Inner PID integral gain |
PidKd |
0.0 | - | Inner PID derivative gain |
SetpointRampRate |
5.0 | A/sec | PID setpoint change rate limit |
DutyRampRate |
50.0 | %/sec | PWM duty change rate limit |
Setpoint Cap Parameters¶
| Parameter | Example Default | Units | Purpose |
|---|---|---|---|
MaxTableValue |
120.0 | A | Absolute ceiling for table entries and setpoint |
MaximumAllowedBatteryAmps |
(configurable) | A | Global battery current ceiling |
rpmCapCurrentTable[] |
99999 (all) | A | RPM-dependent mechanical ceiling (99999 = disabled) |
Voltage Plausibility Ranges (Auto-Detected)¶
| System | BulkVoltage Range | Min Plausible | Max Plausible |
|---|---|---|---|
| 12V | < 18V | 4.5V | 15.5V |
| 24V | 18V - 36V | 9.0V | 30.5V |
| 48V | > 36V | 18V | 60.5V |
Data Storage¶
NVS Namespaces (Array/Blob Data)¶
| Namespace | Key | Contents |
|---|---|---|
learning |
rpmTable |
Current limit table (float[10]) |
learning |
rpmPoints |
RPM breakpoints (int[10]) |
learning |
overheatCount |
Per-RPM overheat counts |
learning |
lastOverheat |
Per-RPM overheat timestamps |
learning |
cumulativeTime |
Per-RPM safe operation time |
learning |
totalEvents |
Total learning events (u32) |
learning |
totalOverheats |
Total overheats (u32) |
learning |
totalSafeMs |
Total safe operation time (u64 blob) |
minduty |
minDutyTable |
RPM-minimum-duty table (float[10]) |
capcurrent |
capTable |
RPM-cap-current table (float[10]) |
LittleFS Files (Scalar Parameters)¶
All user-adjustable parameters stored as /ParameterName.txt files.
Loaded in initSystemSettings(), written by web handler on change.
Telemetry Variables¶
| Variable | Type | Purpose |
|---|---|---|
dutyCycle |
float | Current duty cycle (%) |
vvout |
float | Calculated field voltage (V) |
iiout |
float | Calculated field current (A) |
fieldActiveStatus |
int | 1 if field energized, 0 if not |
uTargetAmps |
float | Current PID setpoint (A) |
learningTargetFromRPM |
float | Raw table lookup result (A) |
finalLearningTarget |
float | After corrections applied (A) |
currentRPMTableIndex |
int | Which table entry is active |
overheatPenaltyEndMs |
uint32_t | When penalty expires (0 = none) |
overheatingPenaltyAmps |
float | Current penalty amount (A) |
voltageControlActive |
bool | True when in float stage |
voltageCapAmps |
float | Current output of voltage loop |
inBulkStage |
bool | True during bulk charging |
State Machine Diagram¶
┌─────────────────┐
┌──────────────│ NORMAL_AUTO_PID │◄────────────────────┐
│ │ or MANUAL │ │
│ └────────┬────────┘ │
│ │ │
│ Fault detected │ Fault cleared + lockout │
│ │ expired │
│ ▼ │
│ ┌────────────────────────────────────────────┐ │
│ │ FAULT MODES │ │
│ ├────────────────────────────────────────────┤ │
│ │ │ │
│ │ ┌─────────────┐ ┌──────────────────┐ │ │
│ │ │ CRITICAL │ │ WARNING_LOCKOUT │ │ │
│ │ │ RAMP │ │ │ │ │
│ │ │ │ │ • Starts │ │ │
│ │ │ • Temp crit │ │ lockout │ │ │
│ │ │ (immed) │ │ timer │ │ │
│ │ │ • V implaus │ │ • Temp warning │ │ │
│ │ │ • V disagree│ │ • V spike │ │ │
│ │ │ critical │ │ • V disagree │ │ │
│ │ │ • Temp stale│ │ warning │ │ │
│ │ └──────┬──────┘ └────────┬─────────┘ │ │
│ │ │ │ │ │
│ │ │ GPIO4 cut │ │ │
│ │ ▼ ▼ │ │
│ │ ┌────────────────────────────────────┐ │ │
│ │ │ LOCKOUT_RAMP │ │ │
│ │ │ (waiting for cooldown to expire) │───┼──────┘
│ │ └────────────────────────────────────┘ │
│ │ │
│ └────────────────────────────────────────────┘
│
│ ┌─────────────────┐
└──────────────│ DISABLED_RAMP │
│ (charging off) │
│ (weather mode) │
└─────────────────┘
Future Work / TODOs¶
1. Temperature Rate-of-Rise Detection¶
Opportunity: Proactively reduce current when temperature is climbing rapidly toward limit, before soft limiter engages.
Blocker: Current OneWire sensor updates every ~5 seconds with Core0 scheduling variability. Derivative calculation is too coarse for reliable dT/dt detection.
Solution Path: Add faster temperature sensor (filtered thermistor or TMP235A4DCKR) with ~100ms response time.
2. ForceFloat Voltage Maintenance¶
Current: ForceFloat targets zero battery current, which can allow voltage drift over time.
Enhancement: Use voltage control loop to maintain present battery voltage instead of targeting zero current. This would provide true maintenance charging behavior.
3. Adaptive VoltageKp Tuning¶
Current: VoltageKp is a fixed user parameter.
Enhancement: Could auto-tune based on observed system response, or scale with battery capacity / alternator rating.
Console Messages Reference¶
Mode Transitions¶
"Entering normal mode""Exiting normal mode: <reason>""Field lockout expired - normal operation resumed""Field lockout started: Xs cooldown before restart allowed"
GPIO4 Events¶
"GPIO4 immediate cut: TEMP_CRITICAL""GPIO4 cut after settle: <reason>"
Learning Events¶
"Learning: Overheat at X RPM, reduced to YA""Learning: Safe operation at X RPM, increased to YA""Learning: RPM transition detected (X RPM) - settling for Ys""Learning: Overheat penalty expired""Learning: Table reset to factory defaults"
Charging Stage¶
"CHARGING: Bulk stage complete, switching to float""CHARGING: Returning to bulk stage"
Soft Limiters¶
"Temp limit reached, backing off...""Voltage limit reached, backing off...""Battery current limit reached, backing off..."
Sensor Faults¶
"OneWire sensor stale, sensor dead or disconnected""Battery Voltage disagreement! BatteryV=X V, IBV=Y V. Field shut off for safety!"