AirDraw


Inspiration

Das Projekt hat sich von dem Spiel „Paint Splash“ aus der Wii-Konsole inspiriert. Hier ist eine Demonstration: Paint Splash


Einführung

Im Projekt werden der Sense Hat und drei externe Buttons verwendet. Aus dem Sense Hat sind der Magnetometer, die 8×8 LED-Matrix und der Joystick relevant. In Pygame wird ein Malprogramm mit einem x-y Koordinatensystem implementiert, sodass der Malstift anhand des Magnetometers gesteuert wird. Um zu bestimmen, wann der Malstift im Malfenster zeichnen soll oder nicht, muss man mit dem Joystick interagieren. Für die Bestimmung der Koordinaten des Malstiftes wird die Position des Sense Hats regelmäßig abgefragt und im Malfenster gespeichert. Nach der Abfrage wird ein Kreis mit vorher definierten Parametern gezeichnet. Durch die kontinuierliche Abfrage der Position und Zeichnung eines Kreises wird die Bewegung eines Malstiftes simuliert. Zusätzlich bieten drei externe Buttons verschiedene Funktionalitäten an, welche beim einmaligen Drücken eines Buttons beispielsweise die Farbe des Malstiftes wechselt. Nachdem der Button gedrückt wurde, erscheint ein 8×8 Bild aus der LED-Matrix. Nachdem das Malfenster geschlossen wird, wird ein Screenshot von der Zeichnung erstellt und in einem bestimmten Verzeichnis des Raspberry Pis gespeichert.


Projektbilder

Malfenster

VNC-Fenster

Externe Buttons

Projektaufbau (Vorderansicht)

Projektaufbau (Seitenansicht)


Bauteile

Schaltplan

Fritzing:

GPIO PINs vom Sense Hat:

Sense-Hat-Pins


Weitere Informationen

Sense Hat

Magnetometer (Sense Hat)

Da der Magnetometer ursprünglich für einen Kompass gedacht ist und nur einen kleinen Wertebereich nutzt, müssen die rohen Daten des Magnetometers verwendet werden. Dadurch vergrößert sich der Wertebereich, sodass die Bewegung im Malfenster erweitert wird. Von den x-y-z Koordinaten des Sense Hats werden nur die x und y Koordinaten verwendet, weil das Zeichenfenster zweidimensional ist. Hinweis: Falls der Magnetometer unerwünschte Werte anzeigt oder man genauere Koordinaten verwenden möchte, gibt es die Möglichkeit den IMU (Inertial Measurement Unit) zu kalibrieren: IMU-Kalibration

Joystick (Sense Hat)

Um die Zeichenfunktion im Malprogramm zu aktivieren, gibt der Joystick des Sense Hats zwei Zustände an. Der erste Zustand des Stiftes ist als mittlere Position definiert, sodass die Malfunktion beim Drücken aktiviert wird. Der zweite Zustand ist die untere Position definiert, sodass die Malfunktion beim Drücken nach unten gestoppt wird.

Externe Buttons

Die freien GPIO Pins des Raspberry Pis können mithilfe eines verlängerten 40 PIN Headers angesprochen werden. Indem der PIN Header des Sense Hats mit den verlängerten PIN Header vertauscht wird, können die Buttons mit ihrem Kabel verbunden werden. Dabei bleibt die Funktionalität des Sense Hats erhalten. Wichtig ist, nicht die gleichen GPIO Pins wie von den Sense Hats zu verwenden, da sonst Fehler entstehen können.

Bluetooth Lautsprecher

  • Hinweis: Falls die Bluetoothverbindung nicht mit den Raspberry Pi funktioniert, empfiehlt es sich dieser Anleitung zu folgen: Bluetooth-Troubleshooting

Konfiguration

Für das Projekt werden folgende Pakete benötigt:

Python3

  • Hinweis: Im Raspberry Pi 400 wird der Text von den Buttons nicht angezeigt, dies kann an der alten Python Version 3.7.3 liegen, weswegen Version 3.9.4 und darüber empfohlen wird.

sense_hat

enthält alle Funktionen des Sense Hats. Sense-Hat-Doku

Pygame

Mithilfe Pygame wird ein interaktives Malfenster erzeugt. In Pygame gibt es die Möglichkeit, Formen zu zeichnen, dabei ist das Zeichnen von Kreisen relevant. Dazu kann mithilfe einer Methode der Mauscursor bewegt werden, welcher zu den Positionen der Buttons springt. Pygame-Doku

Pygame GUI

ist ein Graphical User Interface, mit dem Buttons im Malfenster angezeigt werden kann. Im Programm dient sie nur zur Orientierung der Funktionalität der externen Buttons. Pygame-GUI-Doku

gpiozero

vereinfacht die Implementation der externen Buttons, da sie Funktionen aus RPI.GPIO in eigene Methoden übernimmt und dadurch den Programmcode verkürzt. gpiozero-doku


Zusätzliche Programme

m8tricks

vereinfacht die Implementation der LED-Matrix, sodass wenn eine LED im Programm eine andere Farbe als Schwarz ausgewählt wird, auch im Sense Hat angezeigt wird. Dadurch kann leicht überprüft werden, ob ein Bild in 8×8 Format gut oder schlecht aussieht. Dazu kann die aktuelle LED-Matrix in einem Ordner gespeichert werden und später mit Thonny im Hauptprogramm gespeichert werden. M8tricks ist nur eine Hilfsanwendung, es wird nicht mit den Hauptprogramm gleichzeitig ausgeführt. m8tricks

  • Hinweis: In der Dokumentation von m8tricks steht, dass das Programm im Terminal mit „m8tricxks“ ausgeführt wird. Dies ist falsch und wird stattdessen mit „m8tricks“ ausgeführt.

VNC

VNC (Virtual Network Computing) übertragt den Bildschirm des Raspberry Pis mit den Malprogramm auf einen zweiten Raspberry Pi, sodass während der Übertragung keine Maus, Keyboard und HDMI-Kabel benötigt wird. Dies verbessert die Bewegung des Sense Hats mit der Hand. Um VNC zu nutzen, muss im zweiten Raspberry Pi VNC-Viewer installiert werden und beide Raspberry Pis müssen im gleichen Netzwerk sein. Um das Malprogramm zu beenden, muss mit den zweiten Raspberry Pi das Malfenster geschlossen werden. VNC-Anleitung


Programmcode

In Pygame werden Parameter wie Name, Größe und Hintergrundfarbe des Fensters angegeben. Dazu wird für Pygame GUI ein Bereich definiert, welcher später die Buttons im Fenster verarbeitet.

Malfenster

AirDraw.py
# Fenstergröße und Hintergrundfarbe bestimmen
pygame.display.set_caption('AirDraw')
screen = pygame.display.set_mode((screenX, screenY))
screen.fill(WE)
pygame.display.update()
 
# GUI-Bereich definieren
manager = pygame_gui.UIManager((screenX, screenY))

Wenn ein Button im Fenster erzeugt werden soll, müssen bestimmte Parameter angegeben werden. In der Dokumentation von Pygame GUI werden weitere Möglichkeiten für Parameter angegeben.

Beispiel eines Buttons im GUI

AirDraw.py
# farbeButton erzeugen
farbeButton = pygame_gui.elements.UIButton(relative_rect=pygame.Rect((25, 20), (75, 50)),
                                           text='FARBE',
                                           manager=manager)

Falls ein externer Button gedrückt wird, wird eine Sounddatei abgespielt, der Mauszeiger springt zum jeweiligen Button im Malfenster und beispielsweise die Funktion „setze die Stiftfarbe auf Weiß“ wird ausgeführt. Zusätzlich wird in der LED-Matrix ein 8×8 Bild dargestellt und es folgt eine Pause.

Beispiel eines externen Buttons

AirDraw.py
if button2.is_pressed:
    sound_2.play()
    position2()
    aktuelleFarbe = WE
    sense.set_pixels(
        [(0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
         (255, 0, 255), (255, 0, 255), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
         (255, 0, 255), (255, 0, 255), (255, 0, 255), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
         (255, 0, 255), (255, 0, 255), (255, 0, 255), (255, 0, 255), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
         (0, 0, 0), (255, 0, 255), (255, 0, 255), (255, 0, 255), (255, 0, 255), (0, 0, 0), (0, 0, 0), (0, 0, 0),
         (0, 0, 0), (0, 0, 0), (255, 0, 255), (255, 0, 255), (255, 0, 255), (0, 0, 0), (0, 0, 0), (0, 0, 0),
         (0, 0, 0), (0, 0, 0), (0, 0, 0), (255, 0, 255), (255, 0, 255), (0, 0, 0), (0, 0, 0), (0, 0, 0),
         (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0)])
    pygame.time.delay(2500)
    sense.clear()

Um die Koordinaten vom Sense Hat in Pygame korrekt anzeigen zu können, müssen die Sensordaten des Sense Hats formatiert und gerundet werden. Danach wird im Malfenster mit den Koordinaten des Sense Hats ein Kreis gezeichnet.

Kreis an der richtigen Position zeichnen

AirDraw.py
raw = sense.get_compass_raw()
# Button für das Zeichnen wird gedrückt
if event.type == Draw and zeichnen is True:
    # Formatierung und Berechnung der Position des Stiftes
    xsense = "{x}".format(**raw)
    ysense = "{y}".format(**raw)
 
    xsense = float(xsense)
    ysense = float(ysense)
 
    xsense = round(xsense)
    ysense = round(ysense)
 
    # Multiplikation mit negativer Zahl, weil der SenseHat in Normalposition negative Zahlen anzeigt
    lastPos = ((ysense * -10) + 350, (xsense * -10) - 200)
    pygame.draw.circle(screen, aktuelleFarbe, lastPos, radius)
    pygame.display.update()
    pygame.time.delay(100)

Am Ende des Malprogramms wird ein Bild vom Malfenster erzeugt und im Verzeichnis „Zeichnungen“ gepeichert. Außerdem wird Pygame heruntergefahren.

Ende des Malprogramms

AirDraw.py
except StopIteration:
    pass
sense.clear()
screenshot.blit(sub, (0, 0))
 
# Ordner "Zeichnungen" wurde vorher für die Ablage erstellt
pygame.image.save(screenshot, "/home/raspberry/Dokumente/Zeichnungen/Zeichnung.bmp")
print("Die Zeichnung wurde erfolgreich gespeichert.")
# Programm wird beendet
pygame.mixer.music.stop()
pygame.quit()

Gesamter Programmcode

AirDraw.py
# Pakete importieren
import pygame
import pygame_gui
from sense_hat import SenseHat
from gpiozero import Button
 
# Buttons definieren
button1 = Button("BOARD11")
button2 = Button("BOARD13")
button3 = Button("BOARD29")
 
# Pygame und Sense Hat starten
pygame.mixer.pre_init(44100, -16, 2, 2048)
pygame.init()
pygame.mixer.init()
sense = SenseHat()
 
# LED-Matrix in Normalzustand versetzen
sense.clear()
 
# Hintergrundmusik starten
pygame.mixer.music.load('noodleSoup.mp3')
pygame.mixer.music.play(-1)
pygame.mixer.music.set_volume(0.3)
 
# Fenstergröße bestimmen
screenX = 800
screenY = 600
 
# Farben mit RGB-Werten bestimmen
WE = (255, 255, 255)
SCHW = (0, 0, 0)
ROT = (255, 0, 0)
GR = (0, 255, 0)
BL = (0, 0, 255)
 
# Liste für FarbeButton
Farben = [ROT, GR, BL]
 
aktuelleFarbe = SCHW
 
# Zähler für Farben_Button
counter = 0
 
# Bedingung für while-Schleife
durchlaufen = True
 
# Fenstergröße und Hintergrundfarbe bestimmen
pygame.display.set_caption('AirDraw')
screen = pygame.display.set_mode((screenX, screenY))
screen.fill(WE)
pygame.display.update()
 
# GUI-Bereich definieren
manager = pygame_gui.UIManager((screenX, screenY))
 
# farbeButton erzeugen
farbeButton = pygame_gui.elements.UIButton(relative_rect=pygame.Rect((25, 20), (75, 50)),
                                           text='FARBE',
                                           manager=manager)
# loeschButton erzeugen
loeschButton = pygame_gui.elements.UIButton(relative_rect=pygame.Rect((125, 20), (83, 50)),
                                            text='LÖSCHEN',
                                            manager=manager)
 
# resetButton erzeugen
resetButton = pygame_gui.elements.UIButton(relative_rect=pygame.Rect((225, 20), (75, 50)),
                                           text='RESET',
                                           manager=manager)
 
# Uhr für Bildwiederholrate und Abfrage der Buttons
clock = pygame.time.Clock()
 
# boolean zur Bestimmung des Zustandes des Stiftes
zeichnen = False
 
# Endposition des Stiftzeigers
lastPos = (0, 0)
 
# Radius vom Kreis, also Stiftdicke am Anfang bestimmen
radius = 10
 
# boolean, um Loeschzustand zu bestimmen
radierer = 0
 
# Bildwiederholrate auf 60 FPS, um Leistung zu minimieren
timeDelta = clock.tick(60) / 1000.0
 
# Definition eines eignen Pygame-Events, um in der Methode pygame.event.get() zu erscheinen
Draw = pygame.USEREVENT + 0
pygame.time.set_timer(Draw, 100)
 
# Positionen des Mauscursors, um Buttons zu erreichen (zur Veranschaulichung)
 
def startPosition(): {
    pygame.mouse.set_pos([screenX / 2, screenY / 2])
}
 
 
def position1(): {
    pygame.mouse.set_pos([screenX - 742, screenY - 555])
}
 
 
def position2(): {
    pygame.mouse.set_pos([screenX - 637, screenY - 555])
}
 
 
def position3(): {
    pygame.mouse.set_pos([screenX - 542, screenY - 555])
}
 
# Definition eines Teilfensters, welches als später als Zeichunung.bmp gespeichert wird
rect = pygame.Rect(0, 70, 800, 500)
sub = screen.subsurface(rect)
screenshot = pygame.Surface((screenX, 500))
 
# Soundeffekte initialisieren
sound_1 = pygame.mixer.Sound('decidemp.mp3')
sound_1.set_volume(0.4)
sound_2 = pygame.mixer.Sound('ding.mp3')
sound_2.set_volume(0.4)
sound_3 = pygame.mixer.Sound('woosh.mp3')
sound_3.set_volume(0.4)
 
# Mauszeiger in der Mitte des Malfensters
startPosition()
 
# Programm wird gestartet
try:
    while durchlaufen:
        # alle externe Button-Events
        if button1.is_pressed:
            sound_1.play()
            position1()
            if aktuelleFarbe == WE:
                aktuelleFarbe = SCHW
                # set_pixels() zeigt im SenseHat die LEDS an
                sense.set_pixels(
                    [(0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
                     (0, 0, 0), (255, 0, 0), (255, 0, 0), (0, 0, 0), (0, 0, 0), (0, 255, 0), (0, 255, 0),
                     (0, 0, 0), (0, 0, 0), (255, 0, 0), (255, 0, 0), (0, 0, 0), (0, 0, 0), (0, 255, 0),
                     (0, 255, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
                     (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
                     (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 255),
                     (0, 0, 255), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 255),
                     (0, 0, 255), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
                     (0, 0, 0), (0, 0, 0)])
                pygame.time.delay(2500)
                sense.clear()
            else:
                counter = counter + 1
                if counter == 1:
                    aktuelleFarbe = Farben[0]
                    sense.set_pixels(
                        [(255, 255, 255), (255, 255, 255), (255, 255, 255), (255, 255, 255), (0, 0, 0), (0, 0, 0),
                         (0, 0, 0), (0, 0, 0), (255, 255, 255), (255, 0, 0), (255, 0, 0), (255, 255, 255),
                         (0, 0, 0), (0, 255, 0), (0, 255, 0), (0, 0, 0), (255, 255, 255), (255, 0, 0), (255, 0, 0),
                         (255, 255, 255), (0, 0, 0), (0, 255, 0), (0, 255, 0), (0, 0, 0), (255, 255, 255),
                         (255, 255, 255), (255, 255, 255), (255, 255, 255), (0, 0, 0), (0, 0, 0), (0, 0, 0),
                         (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
                         (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 255), (0, 0, 255),
                         (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 255), (0, 0, 255),
                         (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
                         (0, 0, 0)])
                    pygame.time.delay(2500)
                    sense.clear()
                if counter == 2:
                    aktuelleFarbe = Farben[1]
                    sense.set_pixels([(0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (255, 255, 255), (255, 255, 255),
                                      (255, 255, 255), (255, 255, 255), (0, 0, 0), (255, 0, 0), (255, 0, 0),
                                      (0, 0, 0), (255, 255, 255), (0, 255, 0), (0, 255, 0), (255, 255, 255),
                                      (0, 0, 0), (255, 0, 0), (255, 0, 0), (0, 0, 0), (255, 255, 255), (0, 255, 0),
                                      (0, 255, 0), (255, 255, 255), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
                                      (255, 255, 255), (255, 255, 255), (255, 255, 255), (255, 255, 255), (0, 0, 0),
                                      (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
                                      (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 255),
                                      (0, 0, 255), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
                                      (0, 0, 255), (0, 0, 255), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
                                      (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0)])
                    pygame.time.delay(2500)
                    sense.clear()
                if counter == 3:
                    aktuelleFarbe = Farben[2]
                    sense.set_pixels(
                        [(0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
                         (0, 0, 0), (255, 0, 0), (255, 0, 0), (0, 0, 0), (0, 0, 0), (0, 255, 0), (0, 255, 0),
                         (0, 0, 0), (0, 0, 0), (255, 0, 0), (255, 0, 0), (0, 0, 0), (0, 0, 0), (0, 255, 0),
                         (0, 255, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
                         (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (255, 255, 255),
                         (255, 255, 255), (255, 255, 255), (255, 255, 255), (0, 0, 0), (0, 0, 0), (0, 0, 0),
                         (0, 0, 0), (255, 255, 255), (0, 0, 255), (0, 0, 255), (255, 255, 255), (0, 0, 0),
                         (0, 0, 0), (0, 0, 0), (0, 0, 0), (255, 255, 255), (0, 0, 255), (0, 0, 255),
                         (255, 255, 255), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (255, 255, 255),
                         (255, 255, 255), (255, 255, 255), (255, 255, 255)])
                    pygame.time.delay(2500)
                    sense.clear()
                if counter == 4:
                    aktuelleFarbe = SCHW
                    sense.set_pixels(
                        [(0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
                         (0, 0, 0), (255, 0, 0), (255, 0, 0), (0, 0, 0), (0, 0, 0), (0, 255, 0), (0, 255, 0),
                         (0, 0, 0), (0, 0, 0), (255, 0, 0), (255, 0, 0), (0, 0, 0), (0, 0, 0), (0, 255, 0),
                         (0, 255, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
                         (0, 0, 0), (0, 0, 0), (255, 255, 255), (255, 255, 255), (255, 255, 255), (255, 255, 255),
                         (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (255, 255, 255), (0, 0, 0), (0, 0, 0),
                         (255, 255, 255), (0, 0, 0), (0, 0, 255), (0, 0, 255), (0, 0, 0), (255, 255, 255),
                         (0, 0, 0), (0, 0, 0), (255, 255, 255), (0, 0, 0), (0, 0, 255), (0, 0, 255), (0, 0, 0),
                         (255, 255, 255), (255, 255, 255), (255, 255, 255), (255, 255, 255), (0, 0, 0), (0, 0, 0),
                         (0, 0, 0), (0, 0, 0)])
                    pygame.time.delay(2500)
                    sense.clear()
                    counter = 0
 
        if button2.is_pressed:
            sound_2.play()
            position2()
            aktuelleFarbe = WE
            sense.set_pixels(
                [(0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
                 (255, 0, 255), (255, 0, 255), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
                 (255, 0, 255), (255, 0, 255), (255, 0, 255), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
                 (255, 0, 255), (255, 0, 255), (255, 0, 255), (255, 0, 255), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
                 (0, 0, 0), (255, 0, 255), (255, 0, 255), (255, 0, 255), (255, 0, 255), (0, 0, 0), (0, 0, 0), (0, 0, 0),
                 (0, 0, 0), (0, 0, 0), (255, 0, 255), (255, 0, 255), (255, 0, 255), (0, 0, 0), (0, 0, 0), (0, 0, 0),
                 (0, 0, 0), (0, 0, 0), (0, 0, 0), (255, 0, 255), (255, 0, 255), (0, 0, 0), (0, 0, 0), (0, 0, 0),
                 (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0)])
            pygame.time.delay(2500)
            sense.clear()
 
        if button3.is_pressed:
            sound_3.play()
            position3()
            screen.fill(WE)
            aktuelleFarbe = SCHW
            pygame.display.update()
            sense.set_pixels(
                [(0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
                 (0, 0, 0), (255, 255, 255), (255, 255, 255), (255, 255, 255), (255, 255, 255), (255, 255, 255),
                 (255, 255, 255), (0, 0, 0), (0, 0, 0), (255, 255, 255), (255, 255, 255), (255, 255, 255),
                 (255, 255, 255), (255, 255, 255), (255, 255, 255), (0, 0, 0), (0, 0, 0), (255, 255, 255),
                 (255, 255, 255), (255, 255, 255), (255, 255, 255), (255, 255, 255), (255, 255, 255), (0, 0, 0),
                 (0, 0, 0), (255, 255, 255), (255, 255, 255), (255, 255, 255), (255, 255, 255), (255, 255, 255),
                 (255, 255, 255), (0, 0, 0), (0, 0, 0), (255, 255, 255), (255, 255, 255), (255, 255, 255),
                 (255, 255, 255), (255, 255, 255), (255, 255, 255), (0, 0, 0), (0, 0, 0), (255, 255, 255),
                 (255, 255, 255), (255, 255, 255), (255, 255, 255), (255, 255, 255), (255, 255, 255), (0, 0, 0),
                 (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0)])
            pygame.time.delay(2500)
            sense.clear()
        # Joystick-Events
        for event in sense.stick.get_events():
            if event.action == 'pressed' and event.direction == 'middle':
                zeichnen = True
            elif event.action == 'pressed' and event.direction == 'down':
                zeichnen = False
        # allgemeine Events
        for event in pygame.event.get():
            raw = sense.get_compass_raw()
            # Button für das Zeichnen wird gedrückt
            if event.type == Draw and zeichnen is True:
                # Formatierung und Berechnung der Position des Stiftes
                xsense = "{x}".format(**raw)
                ysense = "{y}".format(**raw)
 
                xsense = float(xsense)
                ysense = float(ysense)
 
                xsense = round(xsense)
                ysense = round(ysense)
 
                # Multiplikation mit negativer Zahl, weil der SenseHat in Normalposition negative Zahlen anzeigt
                lastPos = ((ysense * -10) + 350, (xsense * -10) - 200)
                pygame.draw.circle(screen, aktuelleFarbe, lastPos, radius)
                pygame.display.update()
                pygame.time.delay(100)
 
            # Bedingung wird erfüllt, wenn Malfenster geschlossen wird
            if event.type == pygame.QUIT:
                raise StopIteration
            # führt die Events aus
            manager.process_events(event)
        # aktualisiert den Zustand der Buttons
        manager.update(timeDelta)
 
        # die Änderungen im Fenster werden in Pygame verarbeitet
        screen.blit(screen, (0, 0))
        manager.draw_ui(screen)
        clock.tick(60)
 
    pygame.display.update()
 
except StopIteration:
    pass
sense.clear()
screenshot.blit(sub, (0, 0))
 
# Ordner "Zeichnungen" wurde vorher für die Ablage erstellt
pygame.image.save(screenshot, "/home/raspberry/Dokumente/Zeichnungen/Zeichnung.bmp")
print("Die Zeichnung wurde erfolgreich gespeichert.")
# Programm wird beendet
pygame.mixer.music.stop()
pygame.quit()

Um die Sounddateien im Malprogramm abspielen zu können, müssen sie sich im gleichen Verzeichnis neben der Python-Datei sich befinden.


Sounddateien

Hintergrundmusik

noodlesoup.mp3

Button1

decidemp.mp3

Button2

ding.mp3

Button3

woosh.mp3


Quellen

Fritzing-Quellen

https://forum.fritzing.org/t/raspberry-pi-4-model-b/8622/13

https://github.com/RafaGS/Fritzing/blob/master/Raspberry%20Pi%20Sense%20Hat.fzpz

Dokumentationen

https://www.pygame.org/docs/

https://pygame-gui.readthedocs.io/en/latest/

https://gpiozero.readthedocs.io/en/stable/

https://pythonhosted.org/sense-hat/api/

https://www.raspberrypi.com/documentation/accessories/sense-hat.html

Programme

https://github.com/topshed/m8tricks

https://buyzero.de/en/blogs/news/wie-kann-ich-realvnc-mit-einem-raspberry-pi-nutzen-remote-desktop

Codebeispiele

https://stackoverflow.com/questions/7746263/how-can-i-play-an-mp3-with-pygame

https://tousu.in/qa/?qa=920344/mouse-how-to-create-ms-paint-clone-with-python-and-pygame

Soundquellen

http://dig.ccmixter.org

https://pixabay.com/sound-effects/

Andere Quellen

http://paintsplash.knapnokgames.com/?p=story

https://www.youtube.com/watch?v=Pjhv0I6LShc

https://pinout.xyz/pinout/sense_hat#

https://www.berrybase.de/raspberry-pi-4-computer-modell-b-8gb-ram

https://www.raspberrypi.com/news/raspberry-pi-400-the-70-desktop-pc/

https://www.berrybase.de/en/40-pin-gpio-stacking-header-f-252-r-raspberry-pi-farbig-kodiert-13-3mmSense-Hat

https://www.conrad.de/de/p/apem-9633ncdb-drucktaster-30-v-dc-0-1-a-1-x-aus-ein-tastend-1-st-700182.html

https://www.adafruit.com/product/4301

https://www.berrybase.de/offizielles-sense-hat-f-252-r-raspberry-pi

https://www.conrad.de/de/p/tru-components-epaha-rl-m-esd-handschuh-mit-beschichtung-an-den-fingerspitzen-kleider-groesse-m-polyamid-1571146.html

https://www.amazon.de/Fesoar-Selbstklebend-Doppelseitig-Klettverschluss-Selbstklebendes/dp/B07TZNXMC9

https://www.pvc-welt.de/Kabelbinder-neutral

https://www.amazon.de/-/en/PL7-Portable-Bluetooth-Protection-sdLighting/dp/B086LST81G

https://www.rototron.info/using-an-i2c-lcd-display-with-a-raspberry-pi/