Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen RevisionVorhergehende Überarbeitung
Nächste Überarbeitung
Vorhergehende Überarbeitung
airdraw [2022/06/22 22:24] – Dokumentation ist fertig studentairdraw [2023/07/03 10:15] (aktuell) – Externe Bearbeitung 127.0.0.1
Zeile 1: Zeile 1:
 +====== AirDraw ======
 +
 +----
 +
 +===== Inspiration =====
 +Das Projekt hat sich von dem Spiel „Paint Splash“ aus der Wii-Konsole inspiriert. Hier
 +ist eine Demonstration: [[https://www.youtube.com/watch?v=Pjhv0I6LShc|Paint Splash]]
 +
 +
 +----
 +
 +===== Einführung =====
 +Im Projekt werden der Sense Hat und drei externe Buttons verwendet. Aus dem Sense Hat sind der Magnetometer, die 8x8 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 8x8 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 ====
 +
 +
 +{{ :fenster.jpg?600 |}}
 +
 +==== VNC-Fenster ====
 +{{ :vnc.jpg?600 |}}
 +
 +==== Externe Buttons ====
 +{{ :4.jpg?250 |}}
 +
 +==== Projektaufbau (Vorderansicht) ====
 +{{ :1.png?500 |}}
 +
 +==== Projektaufbau (Seitenansicht) ====
 +{{ :2.png?500 |}}
 +
 +
 +----
 +
 +===== Bauteile =====
 +  - [[https://www.berrybase.de/raspberry-pi-4-computer-modell-b-8gb-ram|Raspberry Pi 4B]] {{ :rpi4b.jpg?250 |}}
 +  - [[https://www.raspberrypi.com/news/raspberry-pi-400-the-70-desktop-pc/|Raspberry Pi 400]] {{ :rpi400.jpg?250 |}}
 +  - [[https://www.berrybase.de/en/40-pin-gpio-stacking-header-f-252-r-raspberry-pi-farbig-kodiert-13-3mmSense-Hat|Verlängerter 40 PIN GPIO Header]] {{ :header.jpg?250 |}}
 +  - [[https://www.conrad.de/de/p/apem-9633ncdb-drucktaster-30-v-dc-0-1-a-1-x-aus-ein-tastend-1-st-700182.html|Drei Buttons]] {{ :button.jpg?150 |}}
 +  - [[https://www.adafruit.com/product/4301|Offizieller Raspberry Pi 4 Case]] {{ :case.jpg?250 |}}
 +  - [[https://www.berrybase.de/offizielles-sense-hat-f-252-r-raspberry-pi|Sense Hat]] {{ :sense.jpg?250 |}}
 +  - [[https://www.conrad.de/de/p/tru-components-epaha-rl-m-esd-handschuh-mit-beschichtung-an-den-fingerspitzen-kleider-groesse-m-polyamid-1571146.html|Antistatische Handschuhe]] {{ :handschuhe.jpg?250 |}}
 +  - [[https://www.amazon.de/Fesoar-Selbstklebend-Doppelseitig-Klettverschluss-Selbstklebendes/dp/B07TZNXMC9|Klettverband]] {{ :klett.jpg?250 |}}
 +  - [[https://www.pvc-welt.de/Kabelbinder-neutral|Kabelbinder]] {{ :kabelbinder.jpg?250 |}}
 +  - [[https://www.berrybase.de/offizielles-sense-hat-f-252-r-raspberry-pi|Bluetooth Lautsprecher]] {{ :bluetooth.jpg?350 |}}
 +
 +----
 +
 +===== Schaltplan =====
 +{{ :airdraw_steckplatine.png?600 |}}
 +
 +** Fritzing: **
 +  * Raspberry pi 4B [[https://forum.fritzing.org/t/raspberry-pi-4-model-b/8622|RPi4B_Fritzing]]
 +  * Sense Hat [[https://github.com/RafaGS/Fritzing/blob/master/Raspberry%20Pi%20Sense%20Hat.fzpz|Sense-Hat_Fritzing]]
 + 
 +** GPIO PINs vom Sense Hat: **
 +
 +[[https://pinout.xyz/pinout/sense_hat#|Sense-Hat-Pins]]
 +
 +
 +----
 +
 +===== Weitere Informationen =====
 +
 +==== Sense Hat ====
 +  * In raspi-config muss I2C aktiviert sein: [[https://www.rototron.info/using-an-i2c-lcd-display-with-a-raspberry-pi/|Anleitung-I2C]]
 +{{ :i2c.png?350 |}}
 +
 +==== 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: [[https://www.raspberrypi.com/documentation/accessories/sense-hat.html|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: [[https://peppe8o.com/fixed-connect-bluetooth-headphones-with-your-raspberry-pi/|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. [[https://pythonhosted.org/sense-hat/api/|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. [[https://www.pygame.org/docs/|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. [[https://pygame-gui.readthedocs.io/en/latest/|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. [[https://gpiozero.readthedocs.io/en/stable/|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 8x8 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. [[https://github.com/topshed/m8tricks|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. [[https://buyzero.de/en/blogs/news/wie-kann-ich-realvnc-mit-einem-raspberry-pi-nutzen-remote-desktop|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**
 +
 +<file python 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))
 +</file>
 +
 +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**
 +
 +<file python AirDraw.py>
 +# farbeButton erzeugen
 +farbeButton = pygame_gui.elements.UIButton(relative_rect=pygame.Rect((25, 20), (75, 50)),
 +                                           text='FARBE',
 +                                           manager=manager)
 +</file>
 +
 +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 8x8 Bild dargestellt und es folgt eine Pause.
 +
 +** Beispiel eines externen Buttons**
 +
 +<file python 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()
 +</file>
 +
 +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**
 +
 +<file python 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)
 +</file>
 +
 +Am Ende des Malprogramms wird ein Bild vom Malfenster erzeugt und im Verzeichnis „Zeichnungen“ gepeichert. Außerdem wird Pygame heruntergefahren.
 +
 +**Ende des Malprogramms**
 +
 +<file python 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()
 +</file>
 +
 +----
 +
 +===== Gesamter Programmcode =====
 +
 +<file python 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()
 +</file>
 +
 +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?linkonly}}
 +
 +**Button1**
 +
 +{{decidemp.mp3?linkonly}}
 +
 +**Button2**
 +
 +{{ding.mp3?linkonly}}
 +
 +**Button3**
 +
 +{{woosh.mp3?linkonly}}
 +
 +
 +----
 +
 +===== 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/]]
 +
 +