Whenever I would adjust my desktop monitor's brightness as the day progressed, I opened a window, or I turned on/off a light in the room, I would think: if only this happened automatically... I'm manually changing the monitor brightness to a value directly related to the room's level of light!
I was already changing my monitor's brightness via its DDC/CI interface using ddcutil on Fedora, so I figured this could be achieved. I set out to build a software monitor brightness controller in Python that uses data from an Arduino-powered light sensor to correlate ambient light and backlight brightness. Now, as the system regularly reads the sensor data, my monitor will automagically change its brightness to reflect the room's level of ambient light—I call it: lighten.
Here I will share how to go about setting up the hardware and software for this project, how it works, and what I learned along the way.
For any of this to work, a ddcutil-controllable monitor must be used.
The hardware-side of this project is composed of an Arduino board, a light sensor, and a means of connecting them.
I chose these components:
- Adafruit QT Py ESP32-S2
- This board has built-in support for the TinyUSB library
- Adafruit TSL2591 Light Sensor
- This sensor is precise, allowing for exact lux calculations
- STEMMA QT Cable
- This cable is super convenient!
Simply connect the QT Py board to the light sensor with the STEMMA QT cable and position them behind your monitor with masking tape!
Remember to keep the light sensor exposed.
Power and receive data from the Arduino with a USB-C to USB cable plugged into your computer.
The Arduino code to read the light sensor data and the system-side controller are open-source.
This process requires a working Arduino IDE, basic knowledge of uploading sketches, and experience with the Linux command line.
The Arduino code can be found here: arduino-lighten. It requires the following libraries:
- Adafruit TSL2591 library
- Adafruit Unified Sensor
- Adafruit TinyUSB (The board used should have built-in support for this library)
This tutorial explains how to set up the first two.
The TinyUSB library can be installed similarly:
In the Arduino IDE menus, go to Sketch -> Include Library -> Manage Libraries, then search for and install Adafruit TinyUSB.
Now clone the arduino-lighten repo:
git clone https://github.com/jcrd/arduino-lighten.git
arduino-lighten.ino file in the Arduino IDE and upload it to the board.
This tool requires read/write access to
/dev/i2c video card devices. In order to use it without root permissions:
Add user to
sudo usermod <user-name> -aG i2c
Copy ddcutil's udev rule into place:
sudo cp /usr/share/ddcutil/data/45-ddcutil-i2c.rules /etc/udev/rules.d
Reload and trigger the new rule:
sudo udevadm control --reload sudo udevadm trigger
See this document for more information.
Now, let's set up lighten. It's currently available as an RPM package on Fedora, but it should be compatible with any Linux distro if installed from source!
Install with copr:
dnf copr enable jcrd/lighten dnf install lighten
lighten requires the product and vendor ID of the Arduino HID device. Use
lsusbto determine these IDs:
> lsusb Bus 005 Device 002: ID 239a:8111 Adafruit QT Py ESP32-S2
Here the vendor ID is
239aand the product ID is
Create a new file at
~/.config/lighten/lightend.confwith this content:
[sensor] vendor_id=239a product_id=8111
Enable the daemon's
systemctl --user enable --now lightend
If the command above succeeds, everything should be operational!
How it works
lighten makes no assumptions about what monitor brightness value corresponds to an ambient light reading. The daemon,
lightend, runs in the background and records manual changes to monitor brightness made with the client,
lighten, until it's able to guess which values are appropriate.
lighten is used to adjust monitor brightness like this:
lighten set - 10 # decrease brightness by 10 lighten set + 20 # increase brightness by 20 lighten set = 100 # set brightness to max
Over time, lighten builds up a database of the ideal monitor brightness in relation to the ambient light level based on your adjustments, and restores brightness:
- when ambient light changes significantly
- on demand
- at startup
- upon wakeup from sleep
- at regular intervals as time passes
What I learned
I learned how to implement and interface with a HID device using TinyUSB and
python-hid after trying and failing to maintain a serial connection to the Arduino board upon the computer waking up from suspend.
I also learned how to use
PyGObject to run a main loop with custom
GSources alongside a DBus server all in Python. I found only scattered documentation about this, so I wrote about building a D-Bus service in Python here!