[TRACKING] "auto-brightness" aka ambient light sensor in Linux? Details?

All, please remember this is a non-Framework tested/supported project - all bugs/questions best sent to their Github page. Thanks

On Kernel 6.7-rc1 ; with cros_ec patch applied for the amd FW13 and I see :

[    8.849390] hid_sensor_als HID-SENSOR-200041.1.auto: failed to setup attributes
[    8.849397] hid_sensor_als: probe of HID-SENSOR-200041.1.auto failed with error -1

And donā€™t get anything populated in /sys/bus/iio/devices at all.

aenertia@emiemi:~$ modinfo hid_sensor_als
filename:       /lib/modules/6.7.0fw13c-rc1+/kernel/drivers/iio/light/hid-sensor-als.ko
import_ns:      IIO_HID
license:        GPL
author:         Srinivas Pandruvada <srinivas.pandruvada@intel.com>
description:    HID Sensor ALS
rhelversion:    9.99
alias:          platform:HID-SENSOR-LISS-0041
alias:          platform:HID-SENSOR-200041
depends:        hid-sensor-iio-common,hid-sensor-trigger,hid-sensor-hub,industrialio
retpoline:      Y
intree:         Y
name:           hid_sensor_als
vermagic:       6.7.0fw13c-rc1+ SMP preempt mod_unload 
sig_id:         PKCS#7
signer:         Build time autogenerated kernel key
sig_key:        74:33:0F:DC:FA:4D:00:8B:30:C3:08:76:96:56:49:71:39:1F:E3
sig_hashalgo:   sha512
signature:      84:56:72:79:C7:62:DC:16:13:08:19:36:60:88:42:50:A8:93:8A:66:
                A8:FB:AB:2B:3E:6E:74:3E:C3:69:EC:99:85:D6:BF:AA:5E:4A:76:83:
                15:05:0A:99:91:51:8A:C7:B1:CE:DF:85:41:9D:EA:6B:51:97:72:1A:
                6A:3E:60:21:8A:4D:7E:1F:0E:04:07:25:82:6E:95:D6:8E:A8:57:AD:
                99:94:0D:38:62:3D:E7:18:BD:EA:4C:72:81:33:A9:BA:5E:A7:0A:D9:
                E9:12:4C:9E:7D:73:25:AF:72:C3:5B:2D:B1:19:7B:60:04:44:7B:A1:
                37:60:03:6B:4F:CA:35:6E:27:FC:E3:8D:C7:E6:43:A2:7D:DC:1D:BF:
                E7:6B:22:23:16:84:37:20:F0:88:64:FE:17:8A:34:5C:BB:12:83:67:
                DF:7D:7E:7A:0A:D3:E5:61:C6:2E:11:1D:F1:00:0E:E7:5A:B2:00:01:
                73:A9:87:B1:AE:88:C2:2E:28:63:F0:3E:A9:9D:C7:30:1E:B0:BC:05:
                C5:31:C2:88:0D:46:5C:78:A7:EA:D2:D3:B0:A0:C0:17:FD:19:EA:BA:
                FA:BD:C4:22:25:8C:23:FD:2F:CF:43:59:AB:35:99:B3:49:B7:89:80:
                31:15:9C:78:5F:8E:A9:78:18:C2:9C:4D:47:A9:5B:74:B7:F2:35:60:
                3E:3C:F5:EF:B6:59:01:C5:F8:CD:24:50:E8:69:1A:D2:59:86:5D:83:
                EA:E9:22:78:B6:28:7C:47:FD:3F:C1:50:1E:4B:39:9D:82:F7:6A:57:
                3C:F2:36:E4:93:AE:A9:46:41:B4:D2:84:86:ED:8E:8B:03:23:2E:0E:
                FC:5C:9E:60:DC:0E:46:EE:54:6F:C6:3D:B0:BB:70:FE:7F:51:63:78:
                EE:C2:6C:C5:04:2D:3B:E6:A0:5A:AB:39:F4:69:DB:91:77:89:75:DF:
                E5:0C:B3:9C:D3:70:24:0D:49:ED:EF:B9:15:41:F0:A9:8B:4C:A5:B9:
                5B:39:14:52:58:D0:F4:E8:5C:3E:2A:A7:37:97:68:EE:1C:CC:CD:16:
                75:48:6C:1A:E0:C8:9F:98:80:0E:2A:E8:DA:17:5D:D8:84:53:61:45:
                E4:B5:12:53:BF:48:06:20:F7:A3:22:48:84:F7:0B:FF:CD:9E:3D:12:
                8B:36:BE:D6:D2:5E:FC:2B:1C:29:AF:9E:B1:98:15:14:B6:D2:9C:56:
                C1:51:59:CB:4E:BE:82:F9:59:73:87:63:34:3F:EB:74:BF:C9:43:DC:
                94:67:B4:73:1E:3C:17:14:6A:64:6C:F9:CA:87:71:3E:E5:40:73:2F:
                CD:2E:60:C1:AE:AE:BC:65:70:D4:E5:0B
aenertia@emiemi:~$ lsmod |grep hid_sen
hid_sensor_als         16384  0
hid_sensor_trigger     20480  1 hid_sensor_als
hid_sensor_iio_common    20480  2 hid_sensor_trigger,hid_sensor_als
industrialio_triggered_buffer    12288  1 hid_sensor_trigger
industrialio          131072  4 industrialio_triggered_buffer,hid_sensor_trigger,kfifo_buf,hid_sensor_als
hid_sensor_hub         32768  3 hid_sensor_trigger,hid_sensor_iio_common,hid_sensor_als

1 Like

Has anyone reliably been able to figure out the case of the disapearing ALS sensor entry?

Iā€™ve never seen it at all on my fw13 7840U ; despite numerous kernels and distro swaps Iā€™ve created a thread/poll here: [POLL/HELP] AMD FW 13 als/Illuminance sensor - present/populated?

If the ALS is the same chip as per the wiki itā€™s not connected directly via i2c as reports from users with the IIO bus entry donā€™t show the cm module loaded; I assume this must be something provided by the EC. So am wondering if the 3.03 update broke something?

I have not, but do keep me posted.

So an update - more details in the above thread. But basically ; try wiping the Disk (including the EFI partition) and starting again with a known working distro (Fedora 39 Workstation (Gnome) ā€¦

1 Like

As Iā€™ve shared in the other ā€œhelp/pollā€ topic that was created, I believe the cm32181, or any other Capella-specific, module should not be required.

The generic hid_sensor_als should be able to pick up the sensor.

Iā€™m leaving this just as a forward ref to the other post with some more details for anyone who may have come here first.

I own a Framework 13 intel 13th gen. The backlight sensor is working correctly and I can adjust the backlight with the brightness keys as well. The module hid_sensor_als is loaded. Fedora 39 kernel 6.6.2-201.fc39.x86_64

The keys and the sensor work nicely together.

custom initramfs
/etc/dracut.conf.d/myflags.conf:
hostonly=ā€œyesā€
add_drivers+=" hid_sensor_hub "
omit_dracutmodules+=" network iscsi ifcfg "

Iā€™ve tracked it to a regression introduced in 6.7rc series with the hid_sensor_als init sequence

Specifically, it seems to be caused by commit kernel/git/torvalds/linux.git - Linux kernel source tree with kernel/git/torvalds/linux.git - Linux kernel source tree and kernel/git/torvalds/linux.git - Linux kernel source tree also having to be reverted due to dependency.

I reported the bug upstream: https://bugzilla.kernel.org/show_bug.cgi?id=218223

2 Likes

Iā€™ve just patched my 6.7-rc4 test tree with a revert and will report back.

Also there is an open upstream KDE bugtrack for incorporating iio-sensor-proxy als bits into powerdevil/KCM here:

The revert is now in 6.7-rc7 and works for me.

Many thanks @Steel99xl !

I have an amd laptop 13 running ubuntu 23.10 with gnome DE. Gnome provides a checkbox for ā€œautomatic screen brightnessā€ but with no effect at all. This script seems to work quite nicely and adjust screen brightness to adjustable and useful levels (extended testing for different ambient light situations still to come).

However, I had to find out first that
(1) brightnessctl now works only with sudo (this might have changed since previous versions)
(2) SensorToDisplayScale on my amd needs to be about 0.4 which is a decimal number. Hence the calculation of TempLight further down the script needs to be changed to account for the float:

# TempLight=$(($Light * $SensorToDisplayScale))
# now $SensorToDisplayScale is a floating-point number
TempLight=$(echo "scale=2; $Light * $SensorToDisplayScale" | bc)
# rounding to smaller integer 
TempLight=$(LANG=C printf "%.0f" $TempLight)

With these small changes it appears to work quite nicely!

I would be interested whether I should try to commit this modification to the github repo. I donā€™t have experience with such system, however ā€¦

Many thanks again
Matthias

1 Like

Hi All, I tried to improve on Monster_userā€™s script (above) by making the ramping super-smooth and integrating support for manually adjusting brightness using the system controls. (the manual adjustment can interfere with the timing of a brightness change, so it may glitch a little). If you decrease brightness to the lowest setting, it resets the adjustment to 0.

Iā€™m really happy with it, and it works about as well as MacOSā€™s implementation as far as I can tell. It should work on both Intel and AMD Framework laptops, but I havenā€™t tested it on Intel (feedback welcome). Iā€™ve integrated it with Stikeā€™s systemd service (above).

Let me know if you run into an issue. (to test, run with ā€˜sudoā€™)

#
# Bash script to automatically control the backlight brightness using the illumination sensor
# Written because the Gnome 45.4 auto brightness is not smooth unlike my previous MacOS laptop
# which was driving me insane.
#
# Features:
#  * smooth, flicker-free ramping
#  * sensitivity and delay to prevent constant adjustments
#  * manual adjustment though backlight keys or software(*)
#  * should work on all Framework 13 AMD and Intel laptops (**)
#
#  (*) manual adjustment can be a bit glitchy at times
#  (**) Intel has not yet been tested
#
#  Michiel Toneman 2024
#  Released under the Apache License V2.0

# Define the appropriate devices for Intel and AMD systems
if [ -f /sys/class/backlight/intel_backlight/brightness ]
then
  screen_brightness=/sys/class/backlight/intel_backlight/brightness
  screen_actual_brightness=/sys/class/backlight/intel_backlight/brightness
  screen_max_brightness=/sys/class/backlight/intel_backlight/max_brightness
else
  screen_brightness=/sys/class/backlight/amdgpu_bl1/brightness
  screen_actual_brightness=/sys/class/backlight/amdgpu_bl1/actual_brightness
  screen_max_brightness=/sys/class/backlight/amdgpu_bl1/max_brightness
fi

# Set some constants
max=$(cat $screen_max_brightness)
sensitivity=$((max/10))
min=$((max/10))
delay=5 # Check every 5 seconds
debug=0 # Set to 1 for debug output

# Variables
manual_adjust=0 # Start with manual adjustment set to 0
last_target=$(cat $screen_actual_brightness) # Start the target brightness at the current screen brightness

# Loop
while [ 1 ]
do
    doupdate=0

    # Get the current state of the backlight and illuminance sensor
    sensor=$(cat /sys/bus/iio/devices/iio:device0/in_illuminance_raw)
    backlight=$(cat $screen_actual_brightness)

    # If the backlight has been manually changed since the last
    # change by the script, we take the delta and add it to the manual
    # adjustment.
    # Reset the manual adjustment by manually dimming to minimum
    if [ $((backlight - last_target)) -ne 0 ]
    then
      if [ $backlight -lt 4 ] # Backlight controls don't appear to go below 2 
      then
        manual_adjust=0
      else
        manual_adjust=$((manual_adjust + (backlight - last_target)))
        last_target=$backlight
      fi
    fi

    # The target brightness is the sensor reading plus the user's (positive or negative) adjustment
    target=$(( sensor + manual_adjust ))

    # Check if delta between ambient and current backlight exceeds the sensitivity threshold
    # so that we don't keep changing brightness all the time
    if [ $backlight -gt $target ]
    then
        if [ $((backlight - target)) -gt $sensitivity ]
        then
          doupdate=1
        fi
    fi
    if [ $backlight -lt $target ]
    then
        if [ $((backlight - target)) -lt $sensitivity ]
        then
          doupdate=1
        fi
    fi

    # Check that we don't exceed the min and max brightness values
    if [ $target -gt $max ]
    then
      target=$max
    fi
    if [ $target -lt $min ]
    then
      target=$min
    fi

    if [ $doupdate -eq 1 ]
    then
      # Debug logging
      if [ $debug -eq 1 ]
      then
        echo ā€œStarting brightness: $backlightā€
        echo ā€œAmbient light: $sensorā€
        echo ā€œManual adjustment: $manual_adjustā€
        echo ā€œAdjusted brightness: $targetā€
        echo ā€œ-------------------------------ā€
        echo ā€œSensitivity: $sensitivityā€
        echo ā€œMin: $min Max: $maxā€
        echo
      fi

      # Now change the brightness smoothly in single value in/decrements per 20ms 
      intermediate=$backlight

      while [ $intermediate -ne $target ]
      do
        if [ $intermediate -lt $target ]
        then
          ((intermediate++))
        else
          ((intermediate--))
        fi
        echo $intermediate > $screen_brightness
        sleep 0.02
      done

      # Remember what the target brightness was
      # If the next time round the last target brightness differs from the current backlight value
      # then the assumption is that someone has manually adjusted the brightness and we can use that delta
      last_target=$target
    fi
  sleep $delay
done
3 Likes

Work in progress modification of @Michiel_Toneman 's version:

Major Changes:

  • Adjusted constants to be more applicable to 13th gen Intel
  • Refactored while loop to re-calculate target more often
  • Uses exponential ramp (more adjustment early, then slows down as it approaches the target)
  • Disabled ā€œmanualā€ adjustment factor calculation
  • More debug lines

I am sure this is not at all CPU cycles efficient, and could again be refactored to be more elegant. I am firing the script as a system wide systemd service wanted by multi-user.target, so that it is active at the greeter screen as well.

I will keep testing and update this post / pastebin URL if I make any signifcant changes.

1 Like

Iā€™m thinking about how to get a ā€œmanualā€ correction factor back into the script. Think Iā€™ll change up the loop to be roughly:

  • read brightness value from brightnessctl
  • compare against what it should be from the last loop adjusted it to
  • if different, adjust illumination sensor to target multiplier ratio in the same direction (i.e. if user commanded brighter while loop slept, increase multiplier)
  • get current illumination sensor value, compare to last loopā€™s illumination sensor value
  • calculate target, compare to last loopā€™s target
  • adjust towards new target using brightnessctl
  • save adjustment step, target, and illumination sensor results
  • sleep variable length of time based on how far off illumination, target, or step results are from last loop values (the more any of those change, the sooner we loop)

havenā€™t clicked into this thread a while, Iā€™ve already written a similar program that does just that:

The repo is kind of bare right now, but it has been functioning quite nicely for me.

Iā€™ll hopefully get around to working on documentation this weekend.

1 Like

First stab at brightnessctl based script + update 1:

UPDATE: Fixed ipeak going negative, substituted ā€œsnoreā€ function instead of sleep for better memory / pid consumption.

Its jank and needs a ton of cleanup, but so far it works. I would be happy to have feedback, regarding more elegant ways to do this code (even in other languages), or ways to make it more performant / lower overhead.

Script does everything I explained in last post, but still finding little errors and invalid values that need trappedā€¦

I set it up so that there is a linear increase in brightness with illumination_raw to some peak value, in which case brightness will be full from there on up. Manually adjusting brightness just pulls that peak value up or down to bias the calculation. So it takes more than a few button presses of brightness up or brightness down before the auto adjust loop will settle on the value that you like.

Might want to edit the initial value of the ipeak variable to suit your preferences.

Oh and donā€™t run two copies of the script at once by accident, as they will fight each other can give weird results!

So I want to give some feedback to whoever chose the illumination sensor hardware, and/or programs its output values. If yā€™all are seeing different behavior in sensor values, let me know. It is possible I got a not so great one, but I only have a sample size of one to test against at the moment. If mine is in fact way out in left field, disregard the below.

My sensor has way too little granularity at low illumination values, and way too much granularity at high values. The sensor on my machine varies from 0-3355 . 0 is reported until far from a zero lux room, in fact it does not turn to 1 until there is nearly enough light to read a book by. I would love to see values under 1000 or so corresponding to much dimmer actual illumination. Values above 1000 or so should be shifted in the opposite direction, because values between 1000 and 3355 are all basically in full direct sun. In other words, with illumination sensor values in the low triple digits, its already time to have full backlight brightness, which is wasting ~3000 values worth of granularity in the sensor.

The fact that the values seem to be inconsistent, and somewhat ā€œstickyā€ (put a blocker over the sensor, leave it for a few seconds, then remove it, and notice the value does not go all the way to its previous value even after more than a minute of ā€œhysteresis smoothingā€ has elapsedā€¦ same with shining a flashlight straight into it).

Iā€™ve tried cleaning the sensor cover with IPA (does not change anything). No it does not have the plastic peel layer on it that they are delivered with. If anyone else has any suggestions about remapping illuminance sensor values to change the granularity, please let me know.

This tracks with my experience. There seems to be a lot of other potential values to expose, or at least the iio framework supports them but maybe the hardware doesnā€™t. Or the driver is simply lacking.

Iā€™ve yet to see a light sensor with a good low light sensitivity, all Iā€™ve used so far (i. e. the TEMT6000 or various Tuya devices) have varying degrees of resolution, but all of them have the behavior @D.H describes.

1 Like