Wie du am Raspberry Pi ein OLED Display anschließt und programmierst, habe ich dir bereits gezeigt. Im heutigen Beitrag soll es darum gehen wie du Prozessinformationen auf dem OLED Display visualisierst. Es ist hier empfehlenswert ein großes Display zu verwenden, da die Daten, welche angezeigt werden sollen, doch recht umfangreich sind (oder man muss Abstriche bei den Daten machen).
Für diesen Beitrag verwende ich den Raspberry Pi Zero 2 WH, da mein eigentlich für solche Beiträge verwendete Raspberry Pi 3b+ nun in ein RetroPi verwandelt wurde. Jedoch steht der kleine Pi Zero 2 dem großen in nichts nach und kann für solche Projekte ebenso verwendet werden und du kannst dieses auch auf dein Pi nachbauen.
Die Idee zu diesem Beitrag kommt aus einem Kommentar zu meinem YouTube-Video OLED Display am Raspberry Pi anschließen und per I2C steuern: So geht’s!. Ich bin immer froh, wenn solch Input aus meiner Community kommt und ich damit euch helfen kann und ich ebenso neuen Content generieren kann.
Inhaltsverzeichnis
- Benötigte Ressourcen für dieses Projekt
- Was soll dargestellt werden?
- Aufbau der Schaltung – Raspberry Pi mit OLED Display
- Python Skript im Hintergrund laufen lassen
- Programmieren des Skriptes zum anzeigen der laufenden Prozesse
- Schritt 1 – Willkommensnachricht anzeigen
- Schritt 2 – exportieren der laufenden Prozesse als CSV Datei
- Schritt 3 – Parsen der CSV Datei in ein Dictionary
- Schritt 4 – Anzeigen der Prozessinformationen auf dem OLED Display am Raspberry Pi
- Das fertige Python Skript zum anzeigen der Prozessinformationen auf einem OLED-Display
- Fazit & Ausblick zu Raspberry Pi Prozessinformationen am OLED Display anzeigen
Benötigte Ressourcen für dieses Projekt
Wenn du dieses kleine Projekt nachbauen möchtest, dann benötigst du:
- einen Raspberry Pi* inkl. Netzteil, SD-Karte etc.,
- ein OLED Display mit I2C Anschluss*,
- ein paar Breadboardkabel*,
- ein 400 Pin Breadboard*
Hinweis von mir: Die mit einem Sternchen (*) markierten Links sind Affiliate-Links. Wenn du über diese Links einkaufst, erhalte ich eine kleine Provision, die dazu beiträgt, diesen Blog zu unterstützen. Der Preis für dich bleibt dabei unverändert. Vielen Dank für deine Unterstützung!
Was soll dargestellt werden?
Auf dem Display möchte ich die laufenden Prozesse darstellen, diese kann man sich recht einfach über den Befehl top anzeigen lassen.
pi@raspberrypi:~ $ top
top - 10:27:00 up 1:27, 1 user, load average: 0,07, 0,06, 0,09
Tasks: 194 total, 1 running, 193 sleeping, 0 stopped, 0 zombie
%CPU(s): 0,5 us, 0,6 sy, 0,0 ni, 98,8 id, 0,1 wa, 0,0 hi, 0,0 si, 0,0 st
MiB Spch: 426,7 total, 102,6 free, 132,7 used, 191,5 buff/cache
MiB Swap: 100,0 total, 7,6 free, 92,4 used. 229,0 avail Spch
PID USER PR NI VIRT RES SHR S %CPU %MEM ZEIT+ BEFEHL
17628 mysql 20 0 614080 67592 9212 S 1,3 15,5 3:49.26 mariadbd
22393 pi 20 0 11476 3204 2612 R 1,3 0,7 0:01.13 top
839 zabbix 20 0 128416 3552 2988 S 0,3 0,8 0:01.97 zabbix_server
857 zabbix 20 0 132984 2092 1808 S 0,3 0,5 0:00.36 zabbix_server
1 root 20 0 34948 5260 3568 S 0,0 1,2 0:28.14 systemd
2 root 20 0 0 0 0 S 0,0 0,0 0:00.03 kthreadd
3 root 0 -20 0 0 0 I 0,0 0,0 0:00.00 rcu_gp
4 root 0 -20 0 0 0 I 0,0 0,0 0:00.00 rcu_par_gp
5 root 0 -20 0 0 0 I 0,0 0,0 0:00.00 slub_flushwq
6 root 0 -20 0 0 0 I 0,0 0,0 0:00.00 netns
10 root 0 -20 0 0 0 I 0,0 0,0 0:00.00 mm_percpu_wq
Jedoch interessieren mich hier nur die Werte, PID, %CPU, %MEM sowie der BEFEHL.
Da wir diese Daten in Python weiterverarbeiten möchten, exportieren wir diese Daten mit einem Befehl in eine Semikolonseparierte CSV-Datei. (Das Komma wird in den Daten für die Gelitkommazahlen verwendet!)
echo "PID;Befehl;CPU Zeit;Speicherverbrauch" > prozesse.csv && top -b -n 1 | awk 'NR > 7 { print $1 ";" $12 ";" $9 ";" $10 }' >> prozesse.csv
Damit erhalten wir wie gewünscht lediglich die Daten für die Spalten PID, %CPU, %MEM, BEFEHL.
PID;Befehl,CPU Zeit,Speicherverbrauch
22545;top;111;0,7
1;systemd;0,0;1,2
2;kthreadd;0,0;0,0
3;rcu_gp;0,0;0,0
4;rcu_par_gp;0,0;0,0;
Aufbau der Schaltung – Raspberry Pi mit OLED Display
Wie bereits erwähnt baue ich die Schaltung am Raspberry Pi Zero 2 WH auf, du kannst diese aber ebenso am normalen Raspberry Pi nachbauen (solltest jedoch zusätzlich auf das ggf. geänderte Pinout achten).
OLED-Display | Raspberry Pi Zero 2 WH |
---|---|
VCC | 5V (Pin 2) |
GND | GND (Pin 6) |
SDA | SDA (Pin 3) |
SCL | SCL (Pin 5) |
Python Skript im Hintergrund laufen lassen
Ich möchte das kleine Skript einmal starten und dann über die Tasten Funktionen ausführen können, während ich gleichzeitig weiterhin im geöffneten Terminalfenster arbeite. Deshalb soll das Skript im Hintergrund laufen.
Es gibt hier mehrere Möglichkeiten, in diesem Beitrag möchte ich die einfachste nutzen und hänge lediglich ein & Zeichen an den Befehl und als Rückgabe erhalte ich eine PID. Mit dieser Prozess-ID kann ich dieses Skript auch jederzeit mit dem Befehl kill beenden.
pi@raspberrypi:~/Python/DisplayTop $ python3 displayHello.py &
[1] 23467
pi@raspberrypi:~/Python/DisplayTop $ kill 23467
pi@raspberrypi:~/Python/DisplayTop $
Programmieren des Skriptes zum anzeigen der laufenden Prozesse
Starten wir nun und lassen die laufenden Prozesse auf dem OLED Display am Raspberry Pi anzeigen. Das Skript erstellen wir iterativ und nach jedem Schritt hast du quasi ein kleines lauffähiges Skript mit einer zusätzlichen Funktion.
Schritt 1 – Willkommensnachricht anzeigen
Im ersten Schritt lassen wir auf dem Display zunächst einen Text anzeigen, welcher kurz erläutert welche Daten angezeigt werden.
import time from luma.core.interface.serial import i2c, spi, pcf8574 from luma.core.interface.parallel import bitbang_6800 from luma.core.render import canvas from luma.oled.device import sh1106 from PIL import ImageFont serial = i2c(port=1, address=0x3C) device = sh1106(serial) fontBig = ImageFont.truetype('FreeSans.ttf', 14) fontNormal = ImageFont.truetype('FreeSans.ttf', 10) while True: with canvas(device) as draw: draw.rectangle(device.bounding_box, outline="white", fill="black") draw.text((25, 3), "pyTop Screen", font=fontBig, fill="white") draw.text((5, 20), "Anzeige fuer PID, Befehl,", font=fontNormal, fill="white") draw.text((5, 30), "CPU Zeit sowie", font=fontNormal, fill="white") draw.text((5, 40), "Speicherverbrauch", font=fontNormal, fill="white")
In meinem Fall nutze ich ein 1.3″ OLED Display und musste die Schriftgröße auf 14 & 10 setzen. Hier gilt wie zuvor ein möglichst großes Display zu verwenden.
Schritt 2 – exportieren der laufenden Prozesse als CSV Datei
In einem Abschnitt zuvor habe ich dir gezeigt, wie der Befehl lautet, um die benötigten Daten der laufenden Prozesse in eine CSV Datei zu schreiben. Diesen wollen wir jetzt alle 2 Minuten ausführen.
Damit wir eine Funktion im Hintergrund laufen lassen können, benötigen wir zusätzlich das Modul “schedule” dieses muss über den Befehl “python3 -m pip install schedule” installiert werden.
pi@raspberrypi:~/Python/DisplayTop $ python3 -m pip install schedule
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting schedule
Downloading https://www.piwheels.org/simple/schedule/schedule-1.2.1-py3-none-any.whl (11 kB)
Installing collected packages: schedule
Successfully installed schedule-1.2.1
pi@raspberrypi:~/Python/DisplayTop $
import subprocess import schedule import time ... def loadProcessInformations(): command = 'echo "PID;Befehl;CPU Zeit;Speicherverbrauch" > prozesse.csv && top -b -n 1 | awk \'NR > 7 { print $1 ";" $12 ";" $9 ";" $10 }\' >> prozesse.csv' subprocess.run(command, shell=True) loadProcessInformations() schedule.every(2).minutes.do(loadProcessInformations) ... while True: schedule.run_pending()
Wenn wir das Skript ausgeführt wird, dann wird im aktuellen Verzeichnis die Datei “prozesse.csv” mit einem auszug der Daten von top angezeigt.
pi@raspberrypi:~/Python/DisplayTop $ python3 pyTopScreen.py &
[1] 24037
pi@raspberrypi:~/Python/DisplayTop $ ls -ll
insgesamt 12
-rw-r--r-- 1 pi pi 4987 21. Apr 11:43 prozesse.csv
-rw-r--r-- 1 pi pi 1242 21. Apr 11:38 pyTopScreen.py
pi@raspberrypi:~/Python/DisplayTop $
Schritt 3 – Parsen der CSV Datei in ein Dictionary
Für einen einfachen Zugriff auf die exportierten Daten, parsen wir die CSV Datei in ein Dictionary.
Es interessieren mich jedoch nur laufende Prozesse, diese haben eine CPU Zeit und einen Speicherverbrauch größer 0.
import csv ... def loadProcessInformations(): command = 'echo "PID;Befehl;CPU Zeit;Speicherverbrauch" > prozesse.csv && top -b -n 1 | awk \'NR > 7 { print $1 ";" $12 ";" $9 ";" $10 }\' >> prozesse.csv' subprocess.run(command, shell=True) prozesse_dict = {} with open('prozesse.csv', 'r') as file: csv_reader = csv.DictReader(file, delimiter=';') for row in csv_reader: pid = row['PID'] cpu_zeit = float(row['CPU Zeit'].replace(',', '.')) speicherverbrauch = float(row['Speicherverbrauch'].replace(',', '.')) if cpu_zeit > 0 and speicherverbrauch > 0: befehl = row['Befehl'] prozesse_dict[pid] = {'Befehl': befehl, 'CPU Zeit': cpu_zeit, 'Speicherverbrauch': speicherverbrauch} print(prozesse_dict)
Immer wenn die neuen Daten geladen werden, werden diese zusätzlich im Terminalfenster ausgegeben.
pi@raspberrypi:~/Python/DisplayTop $ python3 pyTopScreen.py
{
'24261': {'Befehl': 'python3', 'CPU Zeit': 100.0, 'Speicherverbrauch': 3.2},
'24530': {'Befehl': 'top', 'CPU Zeit': 5.9, 'Speicherverbrauch': 0.7}
}
Schritt 4 – Anzeigen der Prozessinformationen auf dem OLED Display am Raspberry Pi
Kommen wir nun zum letzten Schritt und lassen die Prozessinformationen auf dem OLED Display anzeigen. Dazu müssen wir unseren bisherigen Code etwas anpassen, denn das Dictionary muss global abgelegt werden.
prozesse_dict = {} def loadProcessInformations(): ... global prozesse_dict ...
In der Endlosschleife in welchem der Code zum anzeigen der Daten wiederholt wird, legen wir dann eine For-Each-Schleife an welche über das Dictionary mit den Daten läuft.
while True: schedule.run_pending() total = len(prozesse_dict) index = 1 for entry in prozesse_dict: showProcessEntry(entry, index, total) index += 1 time.sleep(2)
Als Nächstes legen wir dann eine neue Funktion an welche einen Eintrag aus diesem Dictionary behandelt, bzw. wir übergeben lediglich die ID (die Prozess-ID / PID), zusätzlich wird noch die länge des Dictionarys sowie der aktuelle Index als Parameter übergeben (diese beiden Daten werden oben rechts im Display angezeigt).
def showProcessEntry(entry, index, total): with canvas(device) as draw: draw.rectangle(device.bounding_box, outline="white", fill="black") draw.text((25, 3), "pyTop Screen", font=fontBig, fill="white") draw.text((105, 20), str(index) + "/"+ str(total), font=fontNormal, fill="white") draw.text((5, 20), "PID: "+ str(entry), font=fontNormal, fill="white") draw.text((5, 30), "Befehl: "+prozesse_dict[entry]['Befehl'], font=fontNormal, fill="white") draw.text((5, 40), "CPU Zeit: "+str(prozesse_dict[entry]['CPU Zeit']), font=fontNormal, fill="white") draw.text((5, 50), "MEM: "+str(prozesse_dict[entry]['Speicherverbrauch']), font=fontNormal, fill="white")
Damit ist nun unser kleines Programm fertig und wir erhalten nun die Ausgabe der aktuellen Prozesse auf dem OLED-Display.
Das fertige Python Skript zum anzeigen der Prozessinformationen auf einem OLED-Display
Hier nun das fertige Python Skript als ZIP-Datei zum download.
import csv import subprocess import schedule import time from luma.core.interface.serial import i2c, spi, pcf8574 from luma.core.interface.parallel import bitbang_6800 from luma.core.render import canvas from luma.oled.device import sh1106 from PIL import ImageFont serial = i2c(port=1, address=0x3C) device = sh1106(serial) fontBig = ImageFont.truetype('FreeSans.ttf', 14) fontNormal = ImageFont.truetype('FreeSans.ttf', 10) fontSmall = ImageFont.truetype('FreeSans.ttf', 8) prozesse_dict = {} def loadProcessInformations(): command = 'echo "PID;Befehl;CPU Zeit;Speicherverbrauch" > prozesse.csv && top -b -n 1 | awk \'NR > 7 { print $1 ";" $12 ";" $9 ";" $10 }\' >> prozesse.csv' subprocess.run(command, shell=True) global prozesse_dict prozesse_dict.clear() with open('prozesse.csv', 'r') as file: csv_reader = csv.DictReader(file, delimiter=';') for row in csv_reader: pid = row['PID'] cpu_zeit = float(row['CPU Zeit'].replace(',', '.')) speicherverbrauch = float(row['Speicherverbrauch'].replace(',', '.')) if cpu_zeit > 0 and speicherverbrauch > 0: befehl = row['Befehl'] prozesse_dict[pid] = {'Befehl': befehl, 'CPU Zeit': cpu_zeit, 'Speicherverbrauch': speicherverbrauch} print(prozesse_dict) def showProcessEntry(entry, index, total): with canvas(device) as draw: draw.rectangle(device.bounding_box, outline="white", fill="black") draw.text((25, 3), "pyTop Screen", font=fontBig, fill="white") draw.text((105, 20), str(index) + "/"+ str(total), font=fontNormal, fill="white") draw.text((5, 20), "PID: "+ str(entry), font=fontNormal, fill="white") draw.text((5, 30), "Befehl: "+prozesse_dict[entry]['Befehl'], font=fontNormal, fill="white") draw.text((5, 40), "CPU Zeit: "+str(prozesse_dict[entry]['CPU Zeit']), font=fontNormal, fill="white") draw.text((5, 50), "MEM: "+str(prozesse_dict[entry]['Speicherverbrauch']), font=fontNormal, fill="white") loadProcessInformations() schedule.every(15).seconds.do(loadProcessInformations) with canvas(device) as draw: draw.rectangle(device.bounding_box, outline="white", fill="black") draw.text((25, 3), "pyTop Screen", font=fontBig, fill="white") draw.text((5, 20), "Anzeige fuer PID, Befehl,", font=fontNormal, fill="white") draw.text((5, 30), "CPU Zeit sowie", font=fontNormal, fill="white") draw.text((5, 40), "Speicherverbrauch", font=fontNormal, fill="white") draw.text((15, 52), "https://draeger-it.blog", font=fontNormal, fill="white") time.sleep(3) while True: schedule.run_pending() total = len(prozesse_dict) index = 1 for entry in prozesse_dict: showProcessEntry(entry, index, total) index += 1 time.sleep(2)
Fazit & Ausblick zu Raspberry Pi Prozessinformationen am OLED Display anzeigen
Als Fazit ziehe ich ich jedoch das, dass kleine 1,3″ OLED Display zu klein ist für die Informationen hier muss ich mal schauen ob es deutlich größere gibt um dieses kleine Projekt deutlich aufzuwerten.
Die Nächste Ausbaustufe zu diesem Projekt könnte nun sein, drei Taster anzuschließen mit welchen man durch die aktiven Prozesse blättern kann. Zusätzlich mit einem roten Taster könnte man den Befehl kill auf einen Prozess anstoßen um den angezeigten Prozess hart zu beenden.