Normale Ansicht

GPIO Reloaded III: Kamera

18. Januar 2024 um 20:30

Das ist der dritte Teil einer Mini-Serie zur GPIO-Nutzung am Raspberry Pi 5:

  • GPIO Reloaded I: Python (gpiozero, lgpio, gpiod, rpi-lgpio)
  • GPIO Reloaded II: Bash (gpiod, gpioget, gpioset, pinctrl)
  • GPIO Reloaded III: Kamera (rpicam-xxx, Picamera2)

Genau genommen hat die Kamera-Nutzung nicht unmittelbar etwas mit GPIOs zu tun. Allerdings ist für die Kommunikation mit der Kamera ebenfalls der neu im Pi 5 integrierte RP1-Chip verantwortlich. Der Chip ist der Grund, weswegen alte Kamera-Tools auf dem Raspberry Pi 5 nicht mehr funktionieren. Bevor Sie zu schimpfen beginnen: Der RP1 hat viele Vorteile. Unter anderem können Sie nun zwei Kameras gleichzeitig anschließen und nutzen und höhere Datenmengen übertragen (wichtig für Videos).

Beachten Sie, dass Sie beim Raspberry Pi 5 zum Kamera-Anschluss ein neues, schmaleres Kabel benötigen!

Kamera-Modul 3 mit neuem, FPC-kompatiblen Kabel
Kamera-Modul 3 mit einem neuen Anschlusskabel mit schmaler Kontaktleiste

Veraltet: raspistill, raspivid, picamera2

Im Terminal bzw. in Bash-Scripts funktionieren raspistill, raspivid usw. nicht mehr. Sie müssen stattdessen rpicam-still, rpicam-vid etc. einsetzen.

In Python-Scripts müssen Sie Abschied vom picamera-Modul nehmen. Stattdessen gibt es das vollkommen neue Modul Picamera2. Es bietet (viel) mehr Funktionen, ist aber in der Programmierung komplett inkompatibel. Vorhandene Scripts können nicht portiert werden, sondern müssen neu entwickelt werden.

Sowohl die rpicam-xxx-Kommandos als auch das Picamera2-Modul greifen auf die ebenfalls neue Bibliothek libcamera2 zurück.

Im einfachsten Anwendungsfall erzeugen Sie ein Picamera2-Objekt, machen mit der Methode start_and_capture_file ein Foto und speichern dieses in eine Datei. Dabei kommt die volle Auflösung der Kamera zur Anwendung, beim Camera Module 3 immerhin fast 4600×2600 Pixel.

#!/usr/bin/env python3
# Beispieldatei camera.py
from picamera2 import Picamera2
cam = Picamera2()
# ein Foto machen und speichern
cam.start_and_capture_file("test.jpg")

Anstelle von start_and_capture_file gibt es zwei weitere Methoden, um ebenso unkompliziert Bilderfolgen bzw. Videos aufzunehmen:

# 10 Bilder im Abstand von 0,5 Sekunden aufnehmen
# mit Dateinamen in der Form series-0003.jpg
cam.start_and_capture_files("series-{:0>4d}.jpg", 
                            num_files=10, 
                            delay=0.5)
# Video über 10 Sekunden aufnehmen (640x480 @ 30 Hz, H.264/AVC1)
cam.start_and_record_video("test.mp4", duration=10)

rpicam-xxx-Kommandos

Zum Test der Kamera sowie zur Aufnahme von Bildern und Videos stehen die folgenden neuen Kommandos zur Auswahl:

  • rpicam-hello: zeigt für fünf Sekunden der Preview-Fenster mit dem Bild der Kamera an
  • rpicam-jpeg: nimmt ein Foto auf und speichert es als JPEG-Datei
  • rpicam-still: nimmt ein Foto auf und speichert es (mehr Optionen als rpicam-jpeg, Optionen etwas kompatibler zu raspistill)
  • rpicam-vid: nimmt ein Video auf und speichert es oder gibt den Video-Stream an externe Tools (livav/ffmpeg) weiter
  • rpicam-raw: speichert RAW-Videomaterial in einer Datei

Die Kommandos sind mit all ihren Optionen großartig dokumentiert. Es gibt zwar keine man-Seiten, aber dafür liefern die Kommandos mit der Option -h eine lange Liste aller Optionen (z.B. rpicam-still -h). Ich beschränke mich hier auf einige einfache Anwendungsbeispiele.

# fünf Sekunden lang ein Vorschaufenster anzeigen, dann 
# ein Foto aufnehmen und speichern
rpicam-jpeg -o image.jpg

# ohne Vorschau, Aufnahme nach einer Sekunde (1000 ms)
rpicam-jpeg -n -t 1000 -o image.jpg

# wie oben, aber Debugging-Ausgaben nicht anzeigen
rpicam-jpeg -n -t 1000 -v 0 -o image.jpg 

# Bildgröße 1280x800
rpicam-jpeg --width 1280 --height 800 -o image.jpg

# heller/dunkler (EV Exposure Compensation)
rpicam-jpeg --ev 0.5  -o brighter.jpg
rpicam-jpeg --ev -0.5 -o darker.jpg

# erstellt ein 10 Sekunden langes Video (10.000 ms) 
# 640x480@30Hz, H264-Codec
rpicam-vid -t 10000 -o test.mp4

# wie vorher, aber höhere Auflösung
rpicam-vid --width 1024 --height 768 -t 10000 -o test.mp4

Falls Sie mehr als eine Kamera angeschlossen haben, können Sie diese mit rpicam-hello --list-cameras auflisten. Die bei einer Aufnahme gewünschte Kamera können Sie mit der Option rpicam-xxx --camera <n> festlegen.

picamera2-Modul für Python-Scripts

picamera2 ist ein relativ neues Python-Modul. Es ersetzt das früher gebräuchliche Modul picamera. Der Hauptvorteil von picamera2 besteht darin, dass das Modul zu aktuellen Raspberry-Pi-Modellen kompatibel ist. Beim Raspberry Pi 5 kommt picamera2 auch mit dem Fall zurecht, dass Sie zwei Kameras gleichzeitig an Ihren Minicomputer angeschlossen haben.

Eine umfassende Referenz aller Klassen und Methoden finden Sie in der exzellenten Dokumentation (nur im PDF-Format verfügbar), die allerdings weit mehr technische Details behandelt, als Sie jemals brauchen werden. Eine Menge Beispiel-Scripts finden Sie auf GitHub.

Mit create_still_configuration können Sie in diversen optionalen Parametern Einstellungen vornehmen. Das resultierende Konfigurationsobjekt übergeben Sie dann an die configure-Methode. Wichtig ist, dass Sie das Foto nicht mit start_and_capture_file aufnehmen, sondern dass Sie die Methoden start und capture_file getrennt ausführen. Die folgenden Zeilen zeigen, wie Sie ein Bild in einer Auflösung von 1024×768 Pixel aufnehmen. Die sleep-Aufforderung verbessert die Qualität des Bilds. Sie gibt der Kamera-Software etwas Zeit, um die Aufnahme zu fokussieren und richtig zu belichten.

#!/usr/bin/env python3
from picamera2 import Picamera2, Preview
import time

# ein Foto in reduzierter Auflösung aufnehmen
cam = Picamera2()
myconfig = cam.create_still_configuration(
  main={"size": (1024, 768)} )
cam.configure(myconfig)
cam.start()
time.sleep(0.5)
cam.capture_file("1024x768.jpg")

Mit Transformationen können Sie das aufgenommene Bild vertikal und horizontal spiegeln. Falls Sie die Kamerakonfiguration während der Ausführung eines Scripts ändern möchten, müssen Sie die Kamera vorher stoppen und danach neu starten.

# (Fortsetzung des obigen Listings)
cam.stop()
from libcamera import Transform
mytrans = Transform(hflip=True)
myconfig = cam.create_still_configuration(
  main={"size": (1024, 768)},
  transform=mytrans)  
cam.configure(myconfig)
cam.start()
time.sleep(0.5)
cam.capture_file("1024x768-hflip.jpg")

Massive Video-Probleme

Bei der Aufnahme von Videos haben Sie die Wahl zwischen drei Encodern, die die aufgenommenen Bilder in Video-Dateien umzuwandeln:

  • H264Encoder (Hardware-Encoder für H.264, kommt per Default zum Einsatz, max. 1080p@30 Hz)
  • MJPEGEncoder (Hardware-Encoder für Motion JPEG = MJPEG)
  • JpegEncoder (Software-Encoder für MJPEG)

Hardware-Encoding steht nur auf dem Raspberry Pi 4 (H.264 und MJPEG) und dem Raspberry Pi 5 (nur H.264) zur Verfügung. Beim Raspberry Pi 5 läuft der MJPEGEncoder also per Software.

Das folgenden Script soll ein Video im Format 720p aufnehmen und gleichzeitig ein Vorschaubild anzeigen. Dabei soll der H.264-Codec eingesetzt werden.

#!/usr/bin/env python3
from picamera2 import Picamera2, Preview
from picamera2.encoders import H264Encoder, Quality
import time
cam = Picamera2()
myconfig = cam.create_video_configuration(
    main={"size": (1280, 720)})        # Auflösung 720p
myencoder = H264Encoder()
cam.configure(myconfig)
cam.start_preview(Preview.QTGL)
time.sleep(0.5)
cam.start_recording(myencoder, 
                    "test-720p.mp4", 
                    quality=Quality.MEDIUM)
time.sleep(10)
cam.stop_recording()
cam.stop_preview()

Das Script zeigt zwar keine Fehlermeldungen an, allerdings lässt sich die Video-Datei nicht abspielen, weder mit VLC am Raspberry Pi noch mit anderen Video-Playern auf anderen Rechnern. Ich habe tagelang mit den Video-Funktionen von Picamera2 experimentiert, aber die resultierenden Videos waren meist schwarz oder enthielten nur ein Bild, das am Beginn der Aufnahme entstanden ist. Auch die auf der folgenden Seite gesammelten Beispiel-Scripts zum Video-Recording funktionierten bei meinen Tests entweder gar nicht oder nur mit Einschränkungen:

https://github.com/raspberrypi/picamera2/tree/main/examples

Fazit: Die Video-Funktionen von Picamera2 sind aktuell (Anfang 2024) ebenso ambitioniert wie unausgereift. Es ist zu hoffen, dass neue Versionen von libcamera und Picamera2 und eine bessere Dokumentation der Grundfunktionen in Zukunft Abhilfe schaffen. Was nützen coole Spezial-Features, wenn es schon bei den einfachsten Grundfunktionen Probleme gibt?

Quellen/Links

Anteil erneuerbarer Energien bei der Stromerzeugung stark gestiegen

09. Januar 2024 um 19:54

Wärmepumpen sind ökologisch umso sinnvoller, je größer der Anteil erneuerbarer Energie bei der Stromerzeugung ist. In unserem Wärmepumpenbuch gibt es eine Tabelle mit Zahlen von 2021/2022. In den letzten Tagen wurden aktualisierte Zahlen für das vergangene Jahr 2023 veröffentlicht — und die sind sehr erfreulich!

Anteil erneuerbarer Energien an der Stromerzeugung

Quellen/Links

Anmerkung: Es gibt unterschiedliche Zahlen für den Anteil der erneuerbaren Energien (EE) am Strom, je nachdem, ob der Anteil relativ zur Erzeugung oder zur Nutzung des Stroms berechnet wird, ob Import/Export aus dem bzw. in das Ausland mit berücksichtigt wird und ob die betriebseigene Stromerzeugung durch eigene Kraftwerke in Bergbau, Industrie und Zugverkehr miteinberechnet wird oder nicht (dieser Strom zählt nicht zum offiziellen »Strommix«). Außerdem versorgen sich immer mehr Betriebe und Haushalte zumindest stundenweise selbst mit PV-Strom. Diese Strommengen können nicht genau erfasst werden, was eine korrekte Berechnung noch schwieriger macht. Dementsprechend variiert der EE-Anteil je nach Berechnungsmethode und Quelle ein wenig (ein bis zwei Prozent auf oder ab).

Für die Schweiz habe ich noch keine 2023er-Zahlen gefunden.

GPIO Reloaded II: Bash

06. Januar 2024 um 16:42

Das ist der zweite Teil einer Mini-Serie zur GPIO-Nutzung am Raspberry Pi 5:

  • GPIO Reloaded I: Python (gpiozero, lgpio, gpiod, rpi-lgpio)
  • GPIO Reloaded II: Bash (gpiod, gpioget, gpioset, pinctrl)
  • GPIO Reloaded III: Kamera (rpicam-xxx, Picamera2)

Zu den wichtigsten Neuerungen beim Raspberry Pi 5 zählt nicht nur der viel schnellere SoC (System-on-a-Chip), sondern auch ein eigener I/O-Controller, der als eigener Chip realisiert ist (RP1). Dieser I/O-Chip bringt mit sich, dass etablierte Mechanismen zur GPIO-Steuerung nicht mehr funktionieren. Besonders stark betroffen sind Kommandos, die im Terminal oder in Bash-Scripts aufgerufen werden.

Veraltet: WiringPi, »gpio«, »raspi-gpio« und »pigpiod/pigs«

Im Verlauf eines Jahrzehnts haben sich diverse Kommandos etabliert, die mittlerweile veraltet sind. Dazu zählt das Kommando gpio aus dem WiringPi-Projekt, das bereits 2019 eingestellt wurde. Ebenfalls verabschieden müssen Sie sich von dessen Nachfolger-Kommando raspi-gpio: Das Kommando ist nicht mit dem neuen I/O-Chip RP1 kompatibel. Glücklicherweise lässt sich das Kommando relativ einfach durch pinctrl ersetzen.

Deutlich ärgerlicher ist, dass auch der beliebte Dämon pigpiod und das dazugehörende Kommando pigs der Kompatibilität zu RP1 zum Opfer gefallen ist. Absurderweise kann der Dienst Anfang 2024 im Raspberry-Pi-Konfigurationsprogramm als GPIO-Fernzugriff scheinbar weiterhin aktiviert werden.

Der »GPIO-Fernzugriff« kann scheinbar weiterhin aktiviert werden. Er ist aber inkompatibel zum Pi 5!

journalctl -u pigpiod beweist aber, dass der Dienst nicht funktioniert:

journalctl -u pigpiod

systemd[1]: Starting pigpiod.service - Daemon required to control GPIO pins via pigpio...
systemd[1]: Started pigpiod.service - Daemon required to control GPIO pins via pigpio.
pigpiod[88161]: 2023-12-29 11:02:24 gpioHardwareRevision: unknown rev code (d04170)
pigpiod[88161]: 2023-12-29 11:02:24 initCheckPermitted:
pigpiod[88161]: +---------------------------------------------------------+
pigpiod[88161]: |Sorry, this system does not appear to be a raspberry pi. |
pigpiod[88161]: |aborting.                                                |
pigpiod[88161]: +---------------------------------------------------------+
pigpiod[88161]: Can't initialise pigpio library
systemd[1]: pigpiod.service: Main process exited, code=exited, status=1/FAILURE
systemd[1]: pigpiod.service: Failed with result 'exit-code'.

Das Problem ist bekannt, aber es sieht nicht so aus, als könnte es behoben werden: https://github.com/joan2937/pigpio/issues/589

gpioget und gpioset

Welche Kommandos funktionieren dann noch? Sie haben die Wahl zwischen den gpioxxx-Kommandos aus dem Paket gpiod sowie pinctrl (siehe den folgenden Abschnitt). Das Paket gpiod ist standardmäßig installiert. Die darin enthaltenen Kommandos nutzen zur Kommunikation mit dem Kernel die Device-Dateien /dev/gpiochip<n> und die Bibliothek libgpiod2.

Der größte Nachteil der Kommandos gpioget, gpioset usw. besteht darin, dass Sie als ersten Parameter die GPIO-Chip-Nummer angeben müssen. Diese variiert je nach Raspberry-Pi-Modell. Bei den Modellen der Serie 1 bis 4 müssen Sie die Nummer 0 angeben, ab Modell 5 die Nummer 4.

# LED ein- und ausschalten, die über den GPIO 7 gesteuert wird
# (= Pin 26 des J8-Headers)

# gpioset auf dem Raspberry Pi 5
gpioset 4 7=1; sleep 3; gpioset 4 7=0

# gpioset auf dem Raspberry Pi 1 bis 4
gpioset 0 7=1; sleep 3; gpioset 0 7=0

Warum variiert die GPIO-Chip-Nummer? Weil beim Raspberry Pi 4 die Kernel-Schnittstelle /dev/gpiochip0 für die GPIO-Steuerung verantwortlich ist (das sind in den BCM 2711 integrierte Funktionen), beim Pi 5 aber der RP1 (ein externer Chip) mit der Kernel-Schnittstelle /dev/gpiochip4. Informationen darüber, welche GPIO-Schnittstellen es gibt und welche GPIO-Funktion wie »verdrahtet« ist, geben die Kommandos gpiodetect und gpioinfo. Die folgenden Ausgaben gelten für den Raspberry Pi 5:

gpiodetect  

  gpiochip0 [gpio-brcmstb@107d508500] (32 lines)
  gpiochip1 [gpio-brcmstb@107d508520] ( 4 lines)
  gpiochip2 [gpio-brcmstb@107d517c00] (17 lines)
  gpiochip3 [gpio-brcmstb@107d517c20] ( 6 lines)
  gpiochip4 [pinctrl-rp1]             (54 lines)


gpioinfo    

  gpiochip0 - 32 lines:
    line   0:   "-"              unused   input  active-high 
    line   1:   "2712_BOOT_CS_N" "spi10 CS0" output active-low
    line   2:   "2712_BOOT_MISO" unused   input active-high 
    ...
  gpiochip1 - 4 lines:
    line   0: "WIFI_SDIO_D0"     unused   input active-high 
    line   1: "WIFI_SDIO_D1"     unused   input active-high 
    ...
  gpiochip2 - 17 lines:
    line   0: "RP1_SDA"          unused   input  active-high 
    line   1: "RP1_SCL"          unused   input  active-high 
    line   2: "RP1_RUN" "RP1 RUN pin"     output active-high 
    ...
  gpiochip3 - 6 lines:
    line   0: "HDMI0_SCL"        unused   input  active-high 
    line   1: "HDMI0_SDA"        unused   input  active-high 
    ...
  gpiochip4 - 54 lines:
    line   0: "ID_SD"            unused   input  active-high 
    line   1: "ID_SC"            unused   input  active-high 
    line   2: "PIN3"             unused   input  active-high 
    line   3: "PIN5"             unused   input  active-high 
    line   4: "PIN7"        "onewire@0"   output active-high
    line   5: "PIN29"       "onewire@0"   output active-low
    line   6: "PIN31"            unused   input  active-high 
    line   7: "PIN26"            unused   input  active-high 
    line   8: "PIN24"            unused   input  active-high 
    line   9: "PIN21"            unused   input  active-high 
    line  10: "PIN19"            unused   input  active-high 
    ...
    line  28: "PCIE_RP1_WAKE"    unused   input  active-high 
    line  29: "FAN_TACH"         unused   input  active-high 
    line  30: "HOST_SDA"         unused   input  active-high 
    line  31: "HOST_SCL"         unused   input  active-high 
    line  32: "ETH_RST_N"   "phy-reset"  output  active-low 
    ...

Um Scripts zu programmieren, die universell funktionieren, können Sie die folgenden Zeilen in den Code einbauen:

# chip=4 für RPi5, chip=0 für ältere Modelle
if gpiodetect | grep -q "pinctrl-rp"; then
  chip=4
else
  chip=0
fi

In der einfachsten Form schalten Sie mit gpioset einen GPIO-Ausgang auf High oder Low. In den folgenden Beispielen bezieht sich der erste Parameter auf die gpiochip-Nummer. 7 gibt die GPIO-Nummer in BCM-Nomenklatur an, 1 oder 0 den gewünschten Zustand:

gpioset $chip 7=1   # GPIO 7 (Pin 26) auf High stellen
gpioset $chip 7=0   # GPIO 7 (Pin 26) auf Low stellen

Sie können auch mehrere Ausgänge auf einmal steuern (hier GPIO 7, 8 und 25):

gpioset $chip 7=0 8=1 25=0 

Durch diverse Optionen können Sie weitere Funktionen steuern (siehe auch man gpioset):

  • --bias=as-is|disable|pull-down|pull-up aktiviert die internen Pull-up- oder Pull-down-Widerstände.
  • --mode=exit|wait|time|signal gibt an, wie lange das Kommando laufen soll. Standardmäßig gilt exit, das Kommando wird also sofort beendet. Mit wait wartet das Programm, bis der Benutzer [Return] drückt. Bei der Einstellung time können Sie mit --sec=<n> oder --usec=<n> die gewünschte Wartezeit einstellen. signal bedeutet, dass das Programm weiterläuft, bis es mit [Strg]+[C] beendet wird.

  • --background führt das Kommando als Hintergrunddienst weiter.

gpioget funktioniert analog zu gpioset: Sie übergeben im ersten Parameter die gpiochip-Nummer (in aller Regel 0), im zweiten Parameter die BCM-Nummer des GPIOs, dessen Input Sie auswerten wollen. Das Ergebnis des Kommandos lautet 0 oder 1, je nachdem, welchen Zustand der Eingang hat.

gpioget $chip 9   # Zustand von GPIO 9 (Pin 21) auslesen
0

pinctrl

Auch mit pinctrl aus dem Paket raspi-utils können Sie GPIO-Funktionen steuern. Der Vorteil von pinctrl besteht darin, dass das Kommando zur Zeit mit allen Raspberry-Pi-Modellen kompatibel ist. Eine Fallunterscheidung, ob das Script auf einem alten oder neuen Modell mit RP1-Chip läuft, entfällt. Außerdem ist das Kommando syntaktisch weitestgehend zu raspi-gpio kompatibel.

Gegen den Einsatz des Kommandos spricht der Umstand, dass das Kommando laut pinctrl -h (der einzigen mir bekannten Dokumentation) nur für Debugging-Zwecke gedacht ist.

Die folgende Aufzählung fasst die wichtigsten Anwendungen des Kommandos zusammen:

  • pinctrl get [gpionr] ermittelt den aktuellen Status aller GPIOs bzw. des angegebenen GPIOs.
  • pinctrl funcs [gpionr] ermittelt, welche alternativen Funktionen der angegebene GPIO bzw. alle GPIOs erfüllen können.

  • pinctrl set gpionr options verändert den Status des angegeben GPIOs. Mögliche Optionen sind:

    • ip = Input
    • op = Output
    • dl = Zustand Low (Drive Low)
    • dh = Zustand High (Drive High)
    • pu = Pull-up-Widerstand aktiv
    • pd = Pull-down-Widerstand aktiv
    • pn = keine Pull-up/down-Funktion
    • a0 bis a7 = alternative Funktion n aktivieren
    • no = Deaktivieren (no function)

Soweit sich sinnvolle Kombinationen ergeben, dürfen mehrere der obigen Optionen auf einmal übergeben werden, jeweils getrennt durch Leerzeichen. Welche alternative Funktionen ein GPIO unterstützt und wie sie nummeriert sind, geht aus pinctrl funcs hervor.

Das folgende Kommando ermittelt, welche Funktionen der GPIO mit der BCM-Nummer 23 unterstützt. Auf dem Raspberry Pi ist dieser GPIO mit Pin 16 des J8-Headers verbunden. GPIO23 kann diverse Funktionen übernehmen:

pinctrl funcs 23

  23, PIN16/GPIO23, SD0_CMD, DPI_D19, I2S0_SDO1, SCL3, 
  I2S1_SDO1, SYS_RIO023, PROC_RIO023, PIO23

Wenn Sie über Pin 26 (BCM-Nummer 07) eine Leuchtdiode angeschlossen haben, dann können Sie die LED wie folgt ein- und ausschalten:

pinctrl set 7 op dh   # LED an Pin 26 ein
pinctrl set 7 op dl   # LED an Pin 26 aus

Quellen/Links

❌