Skip to content

fix: auto-brightness gates too hard — display goes black in dim-but-lit rooms #8

Description

@Ryan4n6

Symptom

At ~5pm on a cloudy day, indoor lights on, the screen drops to effectively zero brightness and looks off. Room is clearly lit but LDR reads in the low range and the curve collapses to ~MIN_BRIGHT_DISPLAY_ON = 4 (i.e. brightness = 4/255).

Root cause (firmware/src/main.cpp::automaticBrightControl)

Three bugs compounding:

  1. No low-side clamp on the LDR reading. map(currentValue, ldrMin, ldrMax, 1, slots) is called when currentValue is below ldrMin. map() extrapolates below the input range — produces slot 0 or negative. Cast to uint8_t produces undefined-behavior brightness levels.
  2. Floor brightness is too low. MIN_BRIGHT_DISPLAY_ON = 4 (~1.5%) is below the threshold where the panel produces useable light in a normally-lit room. Anywhere in the bottom slot of the curve, the panel looks off.
  3. No averaging, no hysteresis on the off path. Single transient low LDR reads (a passing shadow, brief cloud) pin the panel at slot 1. The 10-slot quantization makes the curve jagged.

The MIN_BRIGHT_DISPLAY_OFF = 0 branch (full off when LDR < ldrMin) is also wrong as designed: autoBrightMin is configured by the user as "the LDR reading at which output should be minimum brightness" — not "below this, kill the display." A separate threshold should govern actual lights-out.

Fix plan

  • Average the last N (~8) LDR samples — absorbs cloud/shadow/headlight transients.
  • Clamp the averaged reading into [ldrMin, ldrMax] before mapping.
  • Drop the 10-slot quantization. Map directly to a brightness in [MIN_VISIBLE_BRIGHT, displayBright] where MIN_VISIBLE_BRIGHT is high enough to remain legible in any lit room (~12/255).
  • Replace the < ldrMin → off shortcut with a separate hard cutoff: display goes fully off only when the averaged LDR reading is below DARK_OFF_THRESHOLD (~5/4095) for ≥60s. Means the room is actually pitch black, not just dim.
  • Tighten the apply-only-on-change threshold to ±4 brightness units (continuous, not slot-based) so motion-sensitive displays don't pulse but small sun-cloud movement doesn't drop the panel into the off range.
  • Log under [Bright] prefix at level changes for tunability.

Acceptance

  • At 5pm cloudy, indoor lights on, screen is clearly lit (target ≥ ~25% brightness).
  • At noon direct sun, screen is at full configured brightness.
  • At true bedroom-dark (lights off, blinds closed), screen dims smoothly to MIN_VISIBLE_BRIGHT and only goes fully off after ~60s sustained.
  • No visible flicker / pulsing during slow ambient changes (sunset transition).

Out of scope

  • Exposing the new constants (MIN_VISIBLE_BRIGHT, DARK_OFF_THRESHOLD, sample window) in the web UI. Hardcoded for now; if the fix lands and feels off, follow-up to add prefs.
  • Gamma / log-scale curve on the LDR-to-brightness mapping. Linear with averaging is enough for the stated symptom.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions