@@ -85,6 +85,15 @@ static bool s_user_controlled_state;
8585//! For temporary disabling backlight (ie: low power mode)
8686static bool s_backlight_allowed = false;
8787
88+ //! Cached ambient light level captured when backlight turns on (to avoid feedback from backlight illuminating sensor)
89+ static uint32_t s_cached_ambient_light_level = 0 ;
90+
91+ //! Starting intensity for fade-out (captured when fade begins)
92+ static uint16_t s_fade_start_intensity = 0 ;
93+
94+ //! Fade step size (calculated once at start of fade to avoid rounding jitter)
95+ static uint16_t s_fade_step_size = 0 ;
96+
8897//! Mutex to guard all the above state. We have a pattern of taking the lock in the public functions and assuming
8998//! it's already taken in the prv_ functions.
9099static PebbleMutex * s_mutex ;
@@ -100,11 +109,54 @@ static void light_timer_callback(void *data) {
100109static uint16_t prv_backlight_get_intensity (void ) {
101110 // low_power_mode backlight intensity (25% of max brightness)
102111 const uint16_t backlight_low_power_intensity = (BACKLIGHT_BRIGHTNESS_MAX * (uint32_t )25 ) / 100 ;
103- return low_power_is_active () ? backlight_low_power_intensity : backlight_get_intensity ();
112+
113+ if (low_power_is_active ()) {
114+ return backlight_low_power_intensity ;
115+ }
116+
117+ #if CAPABILITY_HAS_DYNAMIC_BACKLIGHT && !defined(RECOVERY_FW )
118+ // Dynamic backlight: adjust intensity based on ambient light sensor
119+ if (backlight_is_dynamic_intensity_enabled ()) {
120+ // Use cached light level to avoid feedback from backlight illuminating the sensor
121+ uint32_t light_level = s_cached_ambient_light_level ;
122+ uint16_t user_max_intensity = backlight_get_intensity ();
123+
124+ // Low intensity is always 5% (the "Low" setting)
125+ const uint16_t low_intensity = (BACKLIGHT_BRIGHTNESS_MAX * (uint32_t )5 ) / 100 ;
126+
127+ // Get thresholds from board config
128+ const uint32_t min_light_threshold = BOARD_CONFIG .dynamic_backlight_min_threshold ;
129+ const uint32_t max_light_threshold = BOARD_CONFIG .dynamic_backlight_max_threshold ;
130+
131+ // If below minimum threshold, return low intensity
132+ if (light_level < min_light_threshold ) {
133+ return low_intensity ;
134+ }
135+
136+ // Clamp light level to max threshold
137+ if (light_level > max_light_threshold ) {
138+ light_level = max_light_threshold ;
139+ }
140+
141+ // Scale linearly from low_intensity to user_max_intensity based on ambient light
142+ // Adjusted to start scaling from min_light_threshold
143+ uint32_t dynamic_intensity = low_intensity +
144+ ((user_max_intensity - low_intensity ) * (light_level - min_light_threshold )) /
145+ (max_light_threshold - min_light_threshold );
146+
147+ return (uint16_t )dynamic_intensity ;
148+ }
149+ #endif
150+
151+ return backlight_get_intensity ();
104152}
105153
106154static void prv_change_brightness (int32_t new_brightness ) {
107- const uint16_t HALF_BRIGHTNESS = (prv_backlight_get_intensity () - BACKLIGHT_BRIGHTNESS_OFF ) / 2 ;
155+ // Use fade start intensity during fading, otherwise get current intensity
156+ uint16_t reference_intensity = (s_light_state == LIGHT_STATE_ON_FADING && s_fade_start_intensity > 0 )
157+ ? s_fade_start_intensity
158+ : prv_backlight_get_intensity ();
159+ const uint16_t HALF_BRIGHTNESS = (reference_intensity - BACKLIGHT_BRIGHTNESS_OFF ) / 2 ;
108160
109161 // update the debug stats
110162 if (new_brightness > HALF_BRIGHTNESS && s_current_brightness <= HALF_BRIGHTNESS ) {
@@ -126,10 +178,19 @@ static void prv_change_brightness(int32_t new_brightness) {
126178}
127179
128180static void prv_change_state (BacklightState new_state ) {
181+ BacklightState old_state = s_light_state ;
129182 s_light_state = new_state ;
130183
184+ // Capture ambient light level when transitioning from OFF to ON states
185+ // This prevents feedback from the backlight illuminating the sensor
186+ if ((new_state == LIGHT_STATE_ON || new_state == LIGHT_STATE_ON_TIMED ) &&
187+ s_current_brightness == BACKLIGHT_BRIGHTNESS_OFF ) {
188+ s_cached_ambient_light_level = ambient_light_get_light_level ();
189+ }
190+
131191 // Calculate the new brightness and reset any timers based on our state.
132192 int32_t new_brightness = 0 ;
193+
133194 switch (new_state ) {
134195 case LIGHT_STATE_ON :
135196 new_brightness = prv_backlight_get_intensity ();
@@ -143,7 +204,12 @@ static void prv_change_state(BacklightState new_state) {
143204 light_timer_callback , NULL , 0 /* flags */ );
144205 break ;
145206 case LIGHT_STATE_ON_FADING :
146- new_brightness = s_current_brightness - (prv_backlight_get_intensity () / LIGHT_FADE_STEPS );
207+ // Capture the starting intensity only when we first enter fading state
208+ if (old_state != LIGHT_STATE_ON_FADING ) {
209+ s_fade_start_intensity = s_current_brightness ;
210+ s_fade_step_size = s_fade_start_intensity / LIGHT_FADE_STEPS ;
211+ }
212+ new_brightness = s_current_brightness - s_fade_step_size ;
147213
148214 if (new_brightness <= BACKLIGHT_BRIGHTNESS_OFF ) {
149215 // Done fading!
@@ -193,6 +259,9 @@ void light_init(void) {
193259 s_timer_id = new_timer_create ();
194260 s_num_buttons_down = 0 ;
195261 s_user_controlled_state = false;
262+ s_cached_ambient_light_level = 0 ;
263+ s_fade_start_intensity = 0 ;
264+ s_fade_step_size = 0 ;
196265 s_mutex = mutex_create ();
197266}
198267
0 commit comments