Helligkeit des Raspberry Pi 7 Zoll Displays mit integriertem Fotowiderstand regeln
In diesem Artikel zeige ich euch, wie man das Original 7 Zoll Display des Raspberry Pi mit einem Fotowiderstand automatisch steuern kann. Der Fotowiderstand ist durch einen kleinen Umbau direkt in das Display integriert und ermöglicht es, die Helligkeit des Displays an die Umgebungshelligkeit anzupassen.
Analoge Signale mit dem Raspberry Pi messen
Die einfachste Möglichkeit, die Helligkeit zu messen, ist mit einem Fotowiderstand. Mit einem Spannungsteiler könnte man so an einem analogen Eingang den Wert des Fotowiderstands bestimmen und damit auf die Helligkeit schließen. Leider ist es aber so, dass der Raspberry Pi keine analogen Eingänge hat. Es gibt jedoch eine andere Möglichkeit, einen analogen Wert mit einem digitalen Eingang zu messen. Diese Methode ist zwar alles andere als genau, aber für diese Anwendung reicht es.
Der Trick besteht darin, den Vorgang "Messen von Spannung" in ein "Messen von Zeit" zu transformieren, was mit dem Raspberry Pi problemlos möglich ist. Dazu ist neben dem Fotowiderstand nur noch ein Kondensator nötig. Die Schaltung dazu sieht folgendermaßen aus:
Die Messung erfolgt nun in zwei Schritten:
- Entladen des Kondensators: Dazu werden beide Pins auf 0 gesetzt. Dadurch fließt keine weitere Ladung in den Kondensator und die vorhandene Ladung fließt über das Read Pin ab.
- Laden des Kondensators: Das Read Pin wird auf Input geschaltet und das Charge Pin wird auf 1 gesetzt. Jetzt wird der Kondensator geladen. Die Geschwindigkeit der Ladung hängt vom aktuellen Wert des Fotowiderstands ab. Die Spannung am Read Pin steigt nun an und wird irgendwann den Schwellwert übersteigen, bei dem der Wert vom Raspberry Pi als logische 1 (ca. 1,37 Volt) erkannt wird. Diese Zeitspanne kann mit dem Raspberry Pi gemessen werden.
Mit dieser Methode ist also möglich, indirekt den aktuellen Wert des Fotowiderstands und damit die Helligkeit zu messen. Am Oszilloskop kann man diesen Vorgang sehr schön beobachten:
Das erste Bild zeigt den Ladevorgang bei normaler Helligkeit. Die gelbe Linie ist das Charge Pin und die blaue Linie das Read Pin. Die Ladezeit bis der Schwellwert erreicht ist, beträgt hier etwas 0,5 Millisekunden
Das folgende Bild zeigt den Ladevorgang bei weniger Licht. Die Ladezeit beträgt hier etwa 2,8 Millisekunden.
Das letzte Bild zeigt den Ladevorgang bei sehr wenig Licht. In diesem Fall wird der Schwellwert gar nicht erreicht. Das liegt daran, dass hier der Innenwiderstand des Raspberry Pi eine Rolle spielt, der gemeinsam mit dem Fotowiderstand einen Spannungsteiler bildet. Das muss bei der Implementierung berücksichtigt werden, da man hier einen Timeout brauchen wird. In meinem Fall habe ich diesen auf 15 Millisekunden festgelegt.
Implementierung der Helligkeitssteuerung
Die Steuerung der Helligkeit für das Raspberry Pi Display ist in C programmiert und verwendet die gpiod Bibliothek, um die Ports anzusteuern. Zunächst müssen zwei Pakete installiert werden:
sudo apt install gpiod sudo apt install libgpiod-dev
Das erste Paket bietet einige Werkzeuge für die Kommunikation mit den Linux GPIO Ports. Die zweite Bibliothek enthält die nötigen Bibliotheken und Header Files für die C-Programmierung.
Mit diesen Werkzeugen kann man sich beispielsweise die vorhandenen GPIO Devices anzeigen lassen. Bei angeschlossenem Display sieht die Liste bei mir folgendermaßen aus:
pi@raspberrypi:~ $ gpiodetect gpiochip0 [pinctrl-bcm2835] (54 lines) gpiochip1 [raspberrypi-exp-gpio] (8 lines) gpiochip2 [7inch-touchscreen-p] (2 lines)
Für die Devices können auch alle Leitungen angezeigt werden:
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 der Liste sieht man, dass ich die Ports 18 und 24 für die Steuerung verwende.
Um die Helligkeit des Displays steuern zu können, muss auch der entsprechende Treiber aktiviert sein. Das sieht man daran, ob die Datei /sys/class/backlight/10-0045/brightness vorhanden ist. Wenn nicht, editiert man die Datei /boot/config.txt mit
sudo nano /boot/config.txt
und fügt am Ende folgende Zeile an:
dtoverlay=rpi-backlight
Nach einem Neustart ist der Treiber dann aktiv.
Nun zur eigentlichen Steuerung. Der Code besteht aus drei Teilen:
Entladen des Kondensators
Dazu werden einfach die beiden GPIO Ports auf 0 gesetzt und eine gewisse Zeit gewartet.
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);
Laden des Kondensators und Zeitmessung
Zunächst wird das Read Pin so konfiguriert, dass bei einer steigenden Flanke ein Event ausgelöst wird. Anschließend wird das Charge Pin auf ein 1 gesetzt. Die Funktion gpiod_line_event_wait wartet dann, bis entweder das Event ausgelöst wird oder ein Timeout auftritt. Mittels clock_gettime wird die Zeit bis zu dem Event gemessen.
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);
Einstellen der Helligkeit
Da die Messwerte eine recht hohe Streuung haben, werden die Werte zunächst geglättet. Dazu verwende ich ein Verfahren, das man exponentielle Glättung nennt.
// 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);
Anschließend werden die Zeitwerte in Helligkeitswerte umgerechnet. Da der Zusammenhang zwischen den beiden Größen nicht linear ist, habe ich einen pragmatischen Weg gewählt. Dazu habe ich für einige Werte manuell eine Helligkeit gewählt, die mir passend erscheint. Diese Werte habe ich in ein Diagramm eingetragen und daraus für die einzelnen Bereiche eine Geradengleichung erstellt. Diese Näherung hat sich bewährt.
Zuletzt wird noch der errechnete Wert in die Datei /sys/class/backlight/10-0045/brightness geschrieben und damit die Helligkeit eingestellt.
Der Quellcode für die Steuerung ist auf GitHub verfügbar.
Helligkeitssteuerung als Service einrichten
Die Steuerung der Helligkeit soll nach dem Booten automatisch gestartet werden. Dazu wird zunächst die mit dem make-Kommando erstelle Datei in das Verzeichnis /usr/local/bin kopiert:
sudo cp brightness /usr/local/bin
Dann wird eine Unit Datei mit diesem Befehl angelegt:
sudo nano /lib/systemd/system/brightness.service
und dieser Inhalt eingefügt:
[Unit] Description=Brightness After=multi-user.target [Service] Type=idle ExecStart=/usr/local/bin/brightness [Install] WantedBy=multi-user.target
Die Berechtigungen für diese Datei müssen noch angepasst werden:
sudo chmod 644 /lib/systemd/system/brightness.service
Abschließend wird der Service aktiviert:
sudo systemctl daemon-reload sudo systemctl enable brightness.service
Damit ist der Service eingerichtet. Nach einem Neustart sollte er jetzt automatisch starten.
Fotowiderstand in das Display einbauen
Der Fotowiderstand muss natürlich nicht unbedingt in das Display eingebaut werden. Da mein Gehäuse für die Wetterstation aber schon fertig ist und ich nicht extra ein neues Gehäuse drucken wollte, habe ich mich für diese Variante entschieden. Es muss einem aber bewusst sein, dass man dadurch sämtliche Garantieansprüche verliert!
Damit die Helligkeit des Displays optimal geregelt wird, sollte der Fotowiderstand auch möglichst nah am Display sein. Das Raspberry Pi 7 Zoll Display hat einen relativ breiten Rahmen, der dafür gut geeignet ist. Dazu muss man auf der schmalen Seite des Displays die schwarze Lackierung auf der Rückseite des Frontglases vorsichtig mit einem Stanleymesser abkratzen, so dass ein kleiner Bereich in der Größe des Fotowiderstandes durchsichtig wird. Dabei muss man sehr behutsam vorgehen, da sich diese Lackierung sehr leicht abkratzen lässt.
Im nächsten Schritt wird der Fotowiderstand auf das Loch geklebt. Ich habe dazu einen 2-Komponentenkleber verwendet.
Damit von hinten kein Licht durch die aufgekratzte Öffnung scheint, habe ich den Bereich noch mit einem schwarzen Klebeband abgeklebt. Die restliche "Elektronik", also der Kondensator, wird auf eine Buchsenleiste aufgelötet. Auch die Anschlussdrähte für den Fotowiderstand werden hier angelötet. Ich habe hier eine 2x20 polige Buchsenleiste verwendet, von der ich einfach einen Teil abgezwickt habe, da die rechten Pins schon vom Display belegt sind.
Hier noch eine weitere Aufnahme der Buchsenleiste.
Am Ende sollte das Display dann von vorne so aussehen:
Damit ist der Umbau auch schon fertig.
Das Raspberry Pi Display passt sich jetzt automatisch an die Helligkeit der Umgebung an. Das ist nicht nur energieeffizient, sondern schont auch die Augen. Viel Spaß beim Nachbauen!