Raspberry Pi Projekt: Prozessinformationen auf OLED Display anzeigen
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).
https://youtu.be/nOQnx6_ekVE
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.
AcrylgehĂ€use fĂŒr den Raspberry Pi Zero
Splashscreen des Python Projektes - "pyTop Screen"
Daten aktuellen Prozessen auf dem OLED-Display
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.
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-DisplayRaspberry Pi Zero 2 WHVCC5V (Pin 2)GNDGND (Pin 6)SDASDA (Pin 3)SCLSCL (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 &
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.
Splashscreen des Python Projektes - "pyTop Screen"
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 &
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
cpu_zeit = float(row.replace(',', '.'))
speicherverbrauch = float(row.replace(',', '.'))
if cpu_zeit > 0 and speicherverbrauch > 0:
befehl = row
prozesse_dict = {'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, font=fontNormal, fill="white")
draw.text((5, 40), "CPU Zeit: "+str(prozesse_dict), font=fontNormal, fill="white")
draw.text((5, 50), "MEM: "+str(prozesse_dict), font=fontNormal, fill="white")
Damit ist nun unser kleines Programm fertig und wir erhalten nun die Ausgabe der aktuellen Prozesse auf dem OLED-Display.
Daten aktuellen Prozessen 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.
Programm - "pyTop-Screen" fĂŒr das Anzeigen von Prozessinformationen auf einem OLED-DisplayHerunterladen
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
cpu_zeit = float(row.replace(',', '.'))
speicherverbrauch = float(row.replace(',', '.'))
if cpu_zeit > 0 and speicherverbrauch > 0:
befehl = row
prozesse_dict = {'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, font=fontNormal, fill="white")
draw.text((5, 40), "CPU Zeit: "+str(prozesse_dict), font=fontNormal, fill="white")
draw.text((5, 50), "MEM: "+str(prozesse_dict), 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.
Read the full article