Wie du das kleine Spiel „Pixel Chaser“ für den Raspberry PI Pico programmierst, möchte ich dir in diesem Beitrag zeigen.
Was ist Pixel Chaser?
Das Spiel „Pixel Chaser“ funktioniert recht einfach. Du musst lediglich eine Taste betätigen, wenn zwei NeoPixel übereinander liegen. Die Schwierigkeit dabei liegt daran dass, der eine Pixel feststeht und der andere sich im Kreis dreht.
Aufbau der Schaltung
Für den Aufbau der Schaltung benötigst du:
- Raspberry PI Pico*,
- ein 8bit NeoPixel Ring* (besser noch ein 16bit* oder 24bit*)
- ein Piezo Buzzer*,
- ein Taster für die Printmontage* inkl. Kappe,
- ein 10 kOhm Widerstand*,
- diverse Breadboardkabel*,
- ein 170 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!
Achtung!
Der Raspberry PI Pico kann nur maximal 30 NeoPixel gleichzeitig betreiben, d.h. alle NeoPixel sind aktiviert. Wenn du mehr als 30 an einem Pi Pico steuern möchtest, so musst du eine externe 5V Stromquelle einbinden.
Das kannst du zbsp. mit einem Power Shield für die 400 bzw. 720 Pin Breadboards* erledigen.
Das Modul „Power Supply for Breadboards“ kann 3,3V und 5V liefern, welches mit einem Jumper gesetzt werden kann.
Wenn du dieses Modul in deine Schaltung integrierst, dann musst du zusätzlich noch den Minus-Pol des Power Supply Moduls mit einem GND des Raspberry PI Pico verbinden!
Anschluss des NeoPixel Ring
Zunächst widmen wir uns dem Pixel Ring, dieser verfügt auf der Rückseite über 3 Lötpunkte an welche Breadboardkabel angelötet werden können.
ABER diese Kabel sind recht dünn und daher reißen diese bei mehrmaliger Bewegung recht schnell ab.
Da an diesem Pixel Ring ein NeoPixel defekt ist, habe ich mir schnell ein neuen, etwas größeren Ring (16bit = 16 NeoPixel) bei Roboter-Bausatz.de für knapp 7 € inkl. Versandkosten bestellt.
Dieser NeoPixel Ring hat vier Pins.
Beschriftung | Beschreibung |
---|---|
DI | digital IN |
5V | Versorgungsspannung |
GND | Minus-Pol |
DO | digital OUT |
Über den DO Pin kann ein oder mehrere NeoPixel Ringe kaskadierend angeschlossen werden.
Schaltbild
Programmieren in CircuitPython
Für die Programmierung des NeoPixel Rings für den Raspberry PI Pico gibt es diverse Bibliotheken. Jedoch habe ich mit der Adafruit Bibliothek deutlichen erfolg gehabt. Diese Bibliothek findest du als Download unter https://circuitpython.org/libraries bzw. zum direkten Download der knapp 4 MB großen ZIP Datei.
Der Download hat mit meiner 100MBit Leitung knapp 4 Minuten gedauert, also bitte etwas Zeit beim Download mitbringen.
Download & kopieren der NeoPixel Bibliothek
Wenn die knapp 4 MB große ZIP Datei heruntergeladen und entpackt wurde, muss die Datei „neopixel.mpy“ nach X:\CIRCUPY\lib kopiert werden.
Erstellen des Programmes im MU Editor
Für die Programmierung nutze ich die kostenfreie, kleine IDE „MU Editor“ welche du von der Seite https://codewith.mu/en/download herunterladen kannst. Diesen Editor bekommst du für Microsoft Windows, MacOS sowie als Python Package (für Linux).
Programmieren des Spieles „Pixel Chaser“ in X Schritten
Wie du das Spiel „Pixel Chaser“ programmierst, möchte ich dir in x einfachen Schritten zeigen.
Schritt 1 – steuern eines NeoPixel Rings
Im ersten Schritt wollen wir einen NeoPixel Ring ansteuern. Im Detail heißt dass, das wir zunächst einmal prüfen, wie wir die einzelnen Pixel eines NeoPixel Rings ansteuern können.
# importieren der Funktion sleep aus dem Modul time from time import sleep # Modul "board" für die Ansteuerung der GPIOs import board # Modul zum Ansteuern eines NeoPixel import neopixel #Anzahl der verfügbaren NeoPixel numPixels = 12 #initialisieren der NeoPixel am GPO0 und der Anzahl "numPixels" pixels = neopixel.NeoPixel(board.GP0, numPixels) #Helligkeit auf 50% setzen pixels.brightness = 0.5 #Endlosschleife while True: #von 0 bis Anzahl "numPixels" mache... for pixel in range(num_pixels): #NeoPixel an Position "pixel" in die Farbe grün färben pixels[pixel] = (0, 255, 0) #eine Pause von 0,03 Sekunden sleep(0.03) #NeoPixel an Position "pixel" in die Farbe schwarz färben #quasi den Pixel deaktivieren pixels[pixel] = (0, 0, 0)
steuern der Geschwindigkeit mit einem Drehpotentiometer
Die Geschwindigkeit des Umschaltens steuern wir im Code mit einer kleinen Pause, in diesem Fall sind es 0,03 Sekunden. Wir können diese Pause auch über einen 50 kOhm Drehpotentiometer manipulieren.
Quellcode
# importieren der Funktion sleep aus dem Modul time from time import sleep # Modul "board" für die Ansteuerung der GPIOs import board # Modul zum Ansteuern eines NeoPixel import neopixel from analogio import AnalogIn #Anzahl der verfügbaren NeoPixel numPixels = 12 #initialisieren der NeoPixel am GPO0 und der Anzahl "numPixels" pixels = neopixel.NeoPixel(board.GP0, numPixels) #Helligkeit auf 50% setzen pixels.brightness = 0.5 poti = AnalogIn(board.GP27) #Funktion zum Mappen eines Werte zu einer Range #https://stackoverflow.com/questions/1969240/mapping-a-range-of-values-to-another def translate(value, leftMin, leftMax, rightMin, rightMax): leftSpan = leftMax - leftMin rightSpan = rightMax - rightMin valueScaled = float(value - leftMin) / float(leftSpan) return rightMin + (valueScaled * rightSpan) #berechnen eines Wertes für die Pause def pauseValue(): value = poti.value return translate(value, 0,65520, 0,5) #Endlosschleife while True: #von 0 bis Anzahl "numPixels" mache... for pixel in range(numPixels): #NeoPixel an Position "pixel" in die Farbe grün färben pixels[pixel] = (0, 255, 0) #eine Pause von 0,03 Sekunden sleep(pauseValue()) #NeoPixel an Position "pixel" in die Farbe schwarz färben #quasi den Pixel deaktivieren pixels[pixel] = (0, 0, 0)
Video
Über diesen Drehpotentiometer können wir nun den Schwierigkeitsgrad des Spieles einstellen. Oder wir lassen diesen vom Mikrocontroller einstellen. Beide Varianten sind möglich.
Schritt 2 – einbau des Tasters
Wie du einen Taster an einem Raspberry PI Pico anschließt und auslesen kannst, habe ich dir bereits im Beitrag Raspberry PI Pico #4 – Taster mit PullDown & PullUp abfragen ausführlich erläutert.
# Modul "board" für die Ansteuerung der GPIOs import board # Modul um den Status eines Buttons einzulegen import digitalio # Taster am GPIO1 angeschlossen button = digitalio.DigitalInOut(board.GP1) button.direction = digitalio.Direction.INPUT
Schritt 3 – Piezo Buzzer für die Ausgabe eines Tones
Damit wir eine akustische Rückmeldung haben, ob wir gewonnen oder verloren haben, bauen wir nun einen Piezo Buzzer ein.
import time import board import pulseio tones = [ 200, 800] buzzer = pulseio.PWMOut(board.GP2, variable_frequency=True) buzzer.frequency = tones[0] buzzer.duty_cycle = 2**15 while True: buzzer.frequency = tones[0] time.sleep(0.5) buzzer.frequency = tones[1] time.sleep(0.5)
fertiges Spiel „Pixel Chaser“
Hier nun der fertige Code.
# Modul für den Import einer Funktion zum einlegen einen Pause im Code from time import sleep # Modul "board" für die Ansteuerung der GPIOs import board # Modul um den Status eines Buttons einzulegen import digitalio # Modul zum Ansteuern eines NeoPixel import neopixel # Modul um eine Zufallszah lzu generieren from random import randint # Modul um ein PWM Signal zu erzeugen (wird für den Piezo Buzzer benötigt) import pulseio # Taster am GPIO1 angeschlossen button = digitalio.DigitalInOut(board.GP1) button.direction = digitalio.Direction.INPUT # initialisieren des Piezo Buzzers am GPIO2 Pin buzzer = pulseio.PWMOut(board.GP2, variable_frequency=True) # zunächst die Frequenz auf 1 und die Wellenlänge auf 0 buzzer.frequency = 1 buzzer.duty_cycle = 0 # Anzahl der verfügbaren NeoPixel numPixels = 16 # aktiver Pixel activePixel = -1 # initialisieren der NeoPixel am GPO0 und der Anzahl "numPixels" pixels = neopixel.NeoPixel(board.GP0, numPixels) # Helligkeit auf 10% setzen pixels.brightness = 0.1 # Töne welche wiedergegeben werden sollen tone = [ 200, # Ton für verloren 800 # Ton für gewonnen ] # erzeugen einer Zufallszahl & aktivieren des Pixels # dieser aktivierte Pixel wird in der globalen Variable # activePixel gespeichert def activateRandomPixel(): global activePixel activePixel = randint(0, numPixels-1) pixels[activePixel] = (255, 0, 0) # löscht alle aktiven NeoPixel def clearPixels(): for pixel in range(numPixels): pixels[pixel] = (0, 0, 0) # deaktivieren des Piezo Buzzers def turnPiezoBuzzerOff(): buzzer.duty_cycle = 0 # aktivieren des Piezo Buzzers def turnPiezoBuzzerOn(): buzzer.duty_cycle = 2**15 activateRandomPixel() # Endlosschleife while True: # von 0 bis Anzahl "numPixels" mache... for pixel in range(numPixels): # wenn der aktive Pixel nicht der gleiche aus der Schleife ist, # dann mache... if pixel != activePixel: # NeoPixel an Position "pixel" in die Farbe grün färben pixels[pixel] = (0, 255, 0) # eine Pause von 0,03 Sekunden sleep(0.25) # NeoPixel an Position "pixel" in die Farbe schwarz färben # quasi den Pixel deaktivieren pixels[pixel] = (0, 0, 0) # Wenn der Button gedrückt ist, dann... if not button.value: # aktivieren des Piezo Buzzers turnPiezoBuzzerOn() # ausgeben der Pixel auf der Console print(activePixel, pixel, sep="|") # Wenn der aktive Pixel gleich der Pixel aus der # Schleife ist, dann... if (activePixel-1) == pixel: # ausgeben der Zeichenkette "gewonnen" print("gewonnen") # ausgeben eines Tones buzzer.frequency = tone[1] # eine kleine Pause sleep(0.1) buzzer.frequency = tone[1] sleep(0.1) buzzer.frequency = 1 # löschen aller Pixel clearPixels() # ermitteln eines neuen Pixels welcher gesucht werden soll activateRandomPixel() else: # Wenn die beiden Pixel nicht übereinander liegen dann... buzzer.frequency = tone[0] sleep(0.25) buzzer.frequency = 1 # deaktivieren des Piezo Buzzers turnPiezoBuzzerOff()