Raspberry PI Pico #9 – kleines Spiel “Pixel Chaser”

Wie du das kleine Spiel “Pixel Chaser” für den Raspberry PI Pico programmierst, möchte ich dir in diesem Beitrag zeigen.

Raspberry PI Pico - Pixel Chaser
Dieses Video ansehen auf YouTube.

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:

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.

Power Shield für ein 400 / 720 Pin Breadboard
Power Shield für ein 400 / 720 Pin Breadboard

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.

Anschluss eines NeoPixel Rings
Anschluss eines NeoPixel Rings

Dieser NeoPixel Ring hat vier Pins.

BeschriftungBeschreibung
DIdigital IN
5VVersorgungsspannung
GNDMinus-Pol
DOdigital OUT

Über den DO Pin kann ein oder mehrere NeoPixel Ringe kaskadierend angeschlossen werden.

Schaltbild

Schaltung – Raspberry PI Pico “Pixel Chaser”

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.

Inhalt vom Laufwerk “CIRCUITPY”

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).

Editor Mu - wählen des Modus
Editor Mu – wählen des Modus

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.

Aufbau der Schaltung “Drehpotentiometer am Raspberry PI Pico”
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()

Kommentar hinterlassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.