Control brightness of Raspberry Pi 7 inch display with integrated photoresistor

In this article, I will show you how to control the original 7 inch display of the Raspberry Pi with a photoresistor. The photoresistor is integrated directly into the display through a small modification and makes it possible to adjust the brightness of the display to the ambient brightness. This can be particularly useful if you are using the display in an environment where the brightness often varies.

Measuring analog signals with the Raspberry Pi

The simplest way to measure the brightness is using a photoresistor. With a voltage divider you could determine the value of the photoresistor at an analog input and thus determine the brightness. Unfortunately, the Raspberry Pi has no analog inputs. However, there is another way to measure an analog value with a digital input. This method is far from accurate, but it is sufficient for this purpose. 

The trick is to transform the process of "measuring voltage" into a "measuring time", which is easily done with the Raspberry Pi. In addition to the photoresistor, only a capacitor is needed for this. The circuit for this looks like this:

Schaltplan Fotowiderstand

The measurement is performed in two steps:

  1. Discharging the capacitor: To do this, both pins are set to 0. This means that no further charge flows into the capacitor and the existing charge flows out via the Read Pin.
  2. Charging the capacitor: The Read Pin is switched to Input and the Charge Pin is set to 1. Now the capacitor is charged. The speed of charging depends on the current value of the photoresistor. The voltage at the Read Pin will now increase and will eventually exceed the threshold at which the value is recognized by the Raspberry Pi as a logic 1 (about 1.37 volts). This time span can be measured with the Raspberry Pi.

With this method it is possible to indirectly measure the actual value of the photoresistor and thus the brightness. On the oscilloscope you can observe this process very nicely:

The first picture shows the charging process at normal brightness. The yellow line is the charge pin and the blue line is the read pin. The charging time until the threshold value is reached is about 0.5 milliseconds.

RC charging curve fast

The following picture shows the charging process with less light. The charging time here is about 2.8 milliseconds.

RC charging curve slow

The last image shows the charging process in very low light. In this case, the threshold is not reached at all. This is because the internal resistance of the Raspberry Pi plays a role here, which forms a voltage divider together with the photoresistor. This has to be considered in the implementation, because you will need a timeout here. In my case I set this to 15 milliseconds.

RC charging curve too low

Implementation of the brightness control

The brightness control for the Raspberry Pi display is programmed in C and uses the gpiod library to control the ports. First two packages have to be installed:

sudo apt install gpiod
sudo apt install libgpiod-dev

The first package provides some tools for communication with the Linux GPIO ports. The second library contains the necessary libraries and header files for C programming.

With these tools you can for example display the available GPIO devices. With the display connected, the list looks like this for me:

pi@raspberrypi:~ $ gpiodetect
gpiochip0 [pinctrl-bcm2835] (54 lines)
gpiochip1 [raspberrypi-exp-gpio] (8 lines)
gpiochip2 [7inch-touchscreen-p] (2 lines)

All lines can also be displayed for the Devices:

pi@raspberrypi:~ $ gpioinfo
gpiochip0 - 54 lines:
        line   0:     "ID_SDA"       unused   input  active-high
        line   1:     "ID_SCL"       unused   input  active-high
        line   2:       "SDA1"       unused   input  active-high
        line   3:       "SCL1"       unused   input  active-high
        line   4:  "GPIO_GCLK"       unused   input  active-high
        line   5:      "GPIO5"       unused   input  active-high
        line   6:      "GPIO6"       unused   input  active-high
        line   7:  "SPI_CE1_N"       unused   input  active-high
        line   8:  "SPI_CE0_N"       unused   input  active-high
        line   9:   "SPI_MISO"       unused   input  active-high
        line  10:   "SPI_MOSI"       unused   input  active-high
        line  11:   "SPI_SCLK"       unused   input  active-high
        line  12:     "GPIO12"       unused   input  active-high
        line  13:     "GPIO13"       unused   input  active-high
        line  14:       "TXD1"       unused   input  active-high
        line  15:       "RXD1"       unused   input  active-high
        line  16:     "GPIO16"       unused   input  active-high
        line  17:     "GPIO17"       unused   input  active-high
        line  18:     "GPIO18"  "brightness"  output  active-high [used]
        line  19:     "GPIO19"       unused   input  active-high
        line  20:     "GPIO20"       unused   input  active-high
        line  21:     "GPIO21"       unused   input  active-high
        line  22:     "GPIO22"       unused   input  active-high
        line  23:     "GPIO23"       unused   input  active-high
        line  24:     "GPIO24"  "brightness"  output  active-high [used]
        line  25:     "GPIO25"       unused   input  active-high
        line  26:     "GPIO26"       unused   input  active-high
        line  27:     "GPIO27"       unused   input  active-high
        line  28: "HDMI_HPD_N"        "hpd"   input   active-low [used]
        line  29: "STATUS_LED_G" "led0" output active-high [used]
        line  30:       "CTS0"       unused   input  active-high
        line  31:       "RTS0"       unused   input  active-high
        line  32:       "TXD0"       unused   input  active-high
        line  33:       "RXD0"       unused   input  active-high
        line  34:    "SD1_CLK"       unused   input  active-high
        line  35:    "SD1_CMD"       unused   input  active-high
        line  36:  "SD1_DATA0"       unused   input  active-high
        line  37:  "SD1_DATA1"       unused   input  active-high
        line  38:  "SD1_DATA2"       unused   input  active-high
        line  39:  "SD1_DATA3"       unused   input  active-high
        line  40:   "PWM0_OUT"       unused   input  active-high
        line  41:   "PWM1_OUT"       unused   input  active-high
        line  42:    "ETH_CLK"       unused   input  active-high
        line  43:   "WIFI_CLK"       unused   input  active-high
        line  44:       "SDA0"       unused   input  active-high
        line  45:       "SCL0"       unused   input  active-high
        line  46:   "SMPS_SCL"       unused   input  active-high
        line  47:   "SMPS_SDA"       unused  output  active-high
        line  48:   "SD_CLK_R"       unused   input  active-high
        line  49:   "SD_CMD_R"       unused   input  active-high
        line  50: "SD_DATA0_R"       unused   input  active-high
        line  51: "SD_DATA1_R"       unused   input  active-high
        line  52: "SD_DATA2_R"       unused   input  active-high
        line  53: "SD_DATA3_R"       unused   input  active-high
gpiochip1 - 8 lines:
        line   0:      "BT_ON"       unused  output  active-high
        line   1:      "WL_ON"       unused  output  active-high
        line   2:  "PWR_LED_R"       "led1"  output   active-low [used]
        line   3:    "LAN_RUN"       unused   input  active-high
        line   4:         "NC"       unused   input  active-high
        line   5:  "CAM_GPIO0" "cam1_regulator" output active-high [used]
        line   6:  "CAM_GPIO1"       unused  output  active-high
        line   7:         "NC"       unused   input  active-high
gpiochip2 - 2 lines:
        line   0:      unnamed "0.reg_bridge" output active-high [used]
        line   1:      unnamed      "reset"  output   active-low [used]

In the list you can see that I use ports 18 and 24 for the control.

To be able to control the brightness of the display, the corresponding driver must also be activated. This can be checked by the presence of the file /sys/class/backlight/10-0045/brightness. If not, edit the file /boot/config.txt with

sudo nano /boot/config.txt

and add the following line at the end:

dtoverlay=rpi-backlight

After a restart the driver is then active.

Now to the actual control. The code consists of three parts:

Discharging the capacitor

This is done by simply setting the two GPIO ports to 0 and waiting a certain amount of time.

ret = gpiod_line_request_output(gpio_charge_line, CONSUMER, 0);
if (ret != 0)
	goto release_lines;
ret = gpiod_line_request_output(gpio_read_line, CONSUMER, 0);
if (ret != 0)
	goto release_lines;

usleep(10000);

gpiod_line_release(gpio_charge_line);
gpiod_line_release(gpio_read_line);

Charging the capacitor and time measurement

Initially the Read Pin is configured so that an event is triggered on a rising edge. Then the Charge Pin is set to a 1. The function gpiod_line_event_wait then waits until either the event is triggered or a timeout occurs. The time until the event is measured with clock_gettime.

ret = gpiod_line_request_rising_edge_events(gpio_read_line, CONSUMER);
if (ret != 0)
	goto release_lines;

ret = gpiod_line_request_output(gpio_charge_line, CONSUMER, 1);
if (ret != 0)
	goto release_lines;
		
clock_gettime(CLOCK_MONOTONIC, &start);
ret = gpiod_line_event_wait(gpio_read_line, &ts); // Wait until the signal changes from 0 to 1
clock_gettime(CLOCK_MONOTONIC, &end);

if (ret > 0 && end.tv_nsec > start.tv_nsec) {
	y = end.tv_nsec - start.tv_nsec;
} else {
	y = READ_TIMEOUT;
}

gpiod_line_release(gpio_charge_line);
gpiod_line_release(gpio_read_line);

Adjusting the brightness

Since the measured values have a rather high scatter, the values are first smoothed. For this I use a method called exponential smoothing.

// Smooth values 
y_stern = 0.1 * y + (1 - 0.1) * y_stern__prev;
y_stern__prev = y_stern;

// Map timing values to brightness values
brightness = (int)map_to_brightness( y_stern / 100000.0 ); // Map to 0 - 150
		
// Set brightness of display
set_brightness(brightness);

Then the time values are converted into brightness values. Since the relationship between the two values is not linear, I chose a pragmatic way. For this purpose, I have manually selected a brightness for some values, which seems to me to be suitable. I entered these values in a diagram and created a straight line equation from it for the individual ranges. This approximation worked well. 

brightness mapping

Finally, the calculated value is written to the file /sys/class/backlight/10-0045/brightness to set the brightness.

The source code for the control is available on GitHub.

Set up brightness control as a service

The brightness control is to be started automatically after booting. To do this, first copy the file created with the make command into the directory /usr/local/bin:

sudo cp brightness /usr/local/bin

Then a unit file is created with this command:

sudo nano /lib/systemd/system/brightness.service

and this content inserted:

[Unit]
 Description=Brightness
 After=multi-user.target

 [Service]
 Type=idle
 ExecStart=/usr/local/bin/brightness

 [Install]
 WantedBy=multi-user.target

The permissions for this file need to be adjusted:

sudo chmod 644 /lib/systemd/system/brightness.service

Finally, the service is activated:

sudo systemctl daemon-reload
sudo systemctl enable brightness.service

This sets up the service. After a restart, it should now start automatically.

Mounting the photoresistor in the display

Of course, the photoresistor does not necessarily have to be built into the display. But since my case for the weather station is already finished and I didn't want to print a new case, I decided for this variant. But you have to be aware that you lose all warranty claims!

To control the brightness of the display optimally, the photoresistor should be as close as possible to the display. The Raspberry Pi 7 inch display has a relatively wide frame, which is well suited for this. To do this, you have to carefully scrape off the black paint on the back of the front glass on the narrow side of the display with a Stanley knife so that a small area the size of the photoresistor becomes transparent. You have to be very careful, since this paint can be scraped off very easily.

ldr_raspbeery_pi_display_DSCF8105.jpg

The next step is to glue the photoresistor onto the hole. I used a 2-component adhesive for this.

ldr_raspbeery_pi_display_DSCF8111.jpg

To prevent light shining through the scratched opening from behind, I masked the area with black tape. The rest of the "electronics", i.e. the capacitor, is soldered onto a female connector. Also the connecting wires for the photoresistor are soldered here. I used a 2x20 pin female connector, from which I just cut off a part, because the right pins are already occupied by the display.

ldr_raspbeery_pi_display_DSCF8115.jpg

Here is another picture of the female connector.

ldr_raspbeery_pi_display_DSCF8116.jpg

In the end, the display should look like this from the front:

ldr_raspbeery_pi_display_DSCF8113.jpg

This also completes the modification.

ldr_raspbeery_pi_display_DSC_7163.jpg

The Raspberry Pi display now automatically adjusts to the brightness of the environment. This is not only energy efficient, but also easy on the eyes. Have fun building your own!

Loading Conversation