Den PicoBoy kippen
Bisher hast du den PicoBoy nur mit dem Joystick gesteuert. Er spürt aber auch, wie du ihn hältst: Auf der Rückseite sitzt ein Beschleunigungssensor. Und er kann Töne machen. Beides lernst du in diesem letzten Schritt kennen.
Der Sensor misst die Beschleunigung entlang von drei Achsen, und dazu gehört auch die Erdanziehung. Die Befehle pb.xAcc(), pb.yAcc() und pb.zAcc() liefern je eine Kommazahl, gemessen in g. Liegt der PicoBoy flach, steckt die Erdanziehung fast ganz in der z-Achse: pb.zAcc() liefert ungefähr 1 oder -1, die beiden anderen Werte liegen nahe 0. Kippst du ihn, wandert ein Teil davon in die x- oder y-Achse.
Die Messwerte ansehen
from picoboy import PicoBoy
pb = PicoBoy()
# Den Teil oberhalb dieser Zeile nicht verändern
while True:
pb.fill(0)
pb.text("x " + str(round(pb.xAcc(), 2)), 0, 8, 1) # 2 Nachkommastellen
pb.text("y " + str(round(pb.yAcc(), 2)), 0, 24, 1)
pb.text("z " + str(round(pb.zAcc(), 2)), 0, 40, 1)
pb.show()
pb.delay(100)
Die Zeilen mit pb.text() enthalten mehrere Befehle ineinander. Lies sie von innen nach außen: pb.xAcc() misst, round(..., 2) rundet auf zwei Nachkommastellen, str() macht aus der Zahl Text, und + hängt den Text an "x ". pb.fill(0) am Anfang der Schleife löscht die alten Zahlen, sonst lägen die neuen darüber.
Kippe den PicoBoy langsam in alle Richtungen und sieh zu, wie sich die Zahlen ändern. Im Emulator klappst du dafür unter dem Gerät den Bereich „Beschleunigungssensor“ auf und ziehst den Punkt im Kreis. Ein Doppelklick legt das Gerät dort wieder flach.
Eine Kugel, die der Neigung folgt
from picoboy import PicoBoy pb = PicoBoy() # Den Teil oberhalb dieser Zeile nicht verändern x = 60 # Kugel in die Mitte setzen y = 28 while True: x = x + int(pb.xAcc() * 4) # Je stärker die Neigung, desto größer der Schritt y = y + int(pb.yAcc() * 4) x = max(0, min(120, x)) # x bleibt zwischen 0 und 120 y = max(0, min(56, y)) # y bleibt zwischen 0 und 56 pb.fill(0) pb.fill_rect(x, y, 8, 8, 1) pb.show() pb.delay(30)
Anmerkungen
int()schneidet die Nachkommastellen ab. Eine kleine Neigung ergibt deshalb den Schritt 0, und die Kugel bleibt liegen, solange du das Gerät nur leicht bewegst. Den Schritt 1 gibt es erst ab einem Messwert von 0.25.- Läuft die Kugel in die falsche Richtung, ändere das Pluszeichen in ein Minus.
max()undmin()halten den Wert im erlaubten Bereich. Die Grenzen sind 120 und 56, weil die Kugel selbst 8 Pixel groß ist.- Lies
max(0, min(120, x))von innen nach außen:min(120, x)liefert die kleinere der beiden Zahlen und begrenzt x nach oben.max(0, ...)liefert die größere und begrenzt nach unten. Aus 130 wird so 120, aus -4 wird 0, und 50 bleibt 50. - Anders als beim Stift aus dem Kapitel über Schleifen steht
pb.fill(0)hier in der Schleife. So verschwindet die Kugel an ihrer alten Stelle.
Übung: eine Wasserwaage
Der Sensor funktioniert auch ohne Display. Dieses Programm zeigt mit zwei LEDs, zu welcher Seite der PicoBoy kippt:
from picoboy import PicoBoy
pb = PicoBoy()
# Den Teil oberhalb dieser Zeile nicht verändern
while True:
if pb.xAcc() > 0.3: # Nach einer Seite gekippt?
pb.LED_GREEN.on()
else:
pb.LED_GREEN.off()
if pb.xAcc() < -0.3: # Nach der anderen Seite gekippt?
pb.LED_RED.on()
else:
pb.LED_RED.off()
pb.delay(50)
Die Grenze von 0.3 sorgt dafür, dass kleines Wackeln nichts auslöst. Ergänze selbst die gelbe LED: Sie soll leuchten, wenn der PicoBoy ungefähr waagerecht liegt.
Töne erzeugen
Für den Ton gibt es keinen fertigen Befehl, aber ein passendes Werkzeug: PWM (Pulsweitenmodulation). Dabei wird ein Pin sehr schnell ein- und ausgeschaltet. Wie schnell, das bestimmt die Tonhöhe. Der Lautsprecher hängt an Pin 15.
Ein Ton von einer halben Sekunde
from picoboy import PicoBoy from machine import Pin, PWM # beides kommt aus dem Modul "machine" pb = PicoBoy() # Den Teil oberhalb dieser Zeile nicht verändern lautsprecher = PWM(Pin(15)) # PWM auf dem Lautsprecher-Pin einrichten lautsprecher.freq(440) # 440 Schwingungen pro Sekunde: Kammerton A lautsprecher.duty_u16(20000) # Lautstärke: 0 ist aus pb.delay(500) # eine halbe Sekunde lang klingen lassen lautsprecher.duty_u16(0) # Ton ausschalten
Wichtig: Vergisst du
duty_u16(0), läuft der Ton über das Programmende hinaus weiter.
Das macht das Programm:
from machine import Pin, PWMlädt zwei Bausteine aus dem Modulmachine. Fehlt diese Zeile, bricht das Programm mitNameError: name 'PWM' isn't definedab.PWM(Pin(15))richtet PWM auf Pin 15 ein. Die Variablelautsprecherhebt das Ergebnis auf, damit du den Lautsprecher weiter ansprechen kannst.freq()stellt die Frequenz in Hertz ein, also die Schwingungen pro Sekunde. Je mehr Hertz, desto höher der Ton. Die doppelte Frequenz klingt eine Oktave höher.duty_u16()legt fest, wie lange der Pin in jeder Schwingung eingeschaltet ist. Der Wert reicht von 0 bis 65535. Bei 0 ist nichts zu hören, 20000 ist ein guter Wert.- Der Ton läuft von selbst weiter, sobald er eingeschaltet ist.
pb.delay(500)bestimmt also nur, wie lange es dauert, bis das Programm ihn wieder ausschaltet.
Im Emulator kommt der Ton aus den Lautsprechern deines Computers.
Eine Melodie aus einer Liste
Weil ein Ton mehrere Zeilen braucht, lohnt sich eine Funktion. Die Tonhöhen stehen in einer Liste, die Schleife spielt sie nacheinander ab. Hier kommt zusammen, was du in den letzten Schritten gelernt hast.
from picoboy import PicoBoy from machine import Pin, PWM pb = PicoBoy() # Den Teil oberhalb dieser Zeile nicht verändern lautsprecher = PWM(Pin(15)) lautsprecher.duty_u16(0) def ton(hoehe, dauer): # hoehe in Hertz, dauer in Millisekunden lautsprecher.freq(hoehe) lautsprecher.duty_u16(20000) pb.delay(dauer) lautsprecher.duty_u16(0) melodie = [262, 294, 330, 262] # die Töne c, d, e, c for note in melodie: ton(note, 300) pb.delay(50) # kurze Pause, damit die Töne getrennt klingen
Das erste lautsprecher.duty_u16(0) sorgt für Stille, bis der erste Ton an der Reihe ist. Die Funktion ton() bekommt Tonhöhe und Dauer als Parameter und enthält die Zeilen aus dem vorigen Beispiel. Die Schleife holt sich eine Tonhöhe nach der anderen aus der Liste und übergibt sie an ton(). Die Variable lautsprecher entsteht außerhalb der Funktion. Innen wird sie nur benutzt und nicht neu zugewiesen, deshalb ist kein global nötig.
Übung: Alle meine Entchen
Für eigene Melodien brauchst du die Frequenzen der Töne: c 262, d 294, e 330, f 349, g 392, a 440, h 494 und das hohe c 523. So beginnt „Alle meine Entchen“:
melodie = [262, 294, 330, 349, 392, 392, 440, 440, 440, 440, 392]
Ersetze damit die Liste im Programm. Hier sind alle Töne gleich lang. Für unterschiedlich lange Töne legst du eine zweite Liste mit den Längen an und gehst mit dem Index durch beide Listen.
Beides zusammen
Zum Schluss bekommt die Kugel einen Klang: Stößt sie an den Rand, piepst der PicoBoy.
from picoboy import PicoBoy
from machine import Pin, PWM
pb = PicoBoy()
# Den Teil oberhalb dieser Zeile nicht verändern
lautsprecher = PWM(Pin(15))
lautsprecher.duty_u16(0)
def ton(hoehe, dauer):
lautsprecher.freq(hoehe)
lautsprecher.duty_u16(20000)
pb.delay(dauer)
lautsprecher.duty_u16(0)
x = 60
y = 28
while True:
neuX = x + int(pb.xAcc() * 4) # Erst nur berechnen
neuY = y + int(pb.yAcc() * 4)
if neuX < 0 or neuX > 120 or neuY < 0 or neuY > 56: # Wäre die Kugel draußen?
ton(880, 20) # Dann piepsen ...
else:
x = neuX # ... sonst weiterrollen
y = neuY
pb.fill(0)
pb.fill_rect(x, y, 8, 8, 1)
pb.show()
pb.delay(30)
Der Unterschied zur ersten Kugel: Das Programm berechnet die neue Position zuerst in neuX und neuY, ohne sie zu übernehmen. Liegt sie außerhalb, piepst der PicoBoy, und x und y bleiben, wie sie sind. Nur sonst rollt die Kugel weiter. Mit max() und min() würde das Programm den Anstoß gar nicht bemerken.
Die Kugel bleibt dabei manchmal ein paar Pixel vor dem Rand stehen. Liegt x bei 118 und der Schritt ist 4, wäre die neue Position 122, und die Kugel rollt nicht weiter. Kippst du das Gerät schwächer, wird der Schritt kleiner, und sie kommt bis an den Rand.
Übung
Baue ein kleines Geschicklichkeitsspiel: Zeichne ein Ziel an eine Stelle des Displays. Rollt die Kugel hinein, spielt der PicoBoy eine kurze Melodie, und das Ziel springt an eine andere Stelle. Dafür brauchst du noch zwei Bausteine.
Eine neue zufällige Position bekommst du aus dem Modul random:
import random # Ganz oben im Programm zielX = random.randint(0, 120) # Zufällige ganze Zahl von 0 bis 120 zielY = random.randint(0, 56)
Ob die Kugel im Ziel liegt, prüfst du über den Abstand. abs() lässt das Vorzeichen weg, und abs(x - zielX) ist darum der Abstand zwischen Kugel und Ziel, egal welche Zahl größer ist:
if abs(x - zielX) < 4 and abs(y - zielY) < 4: # Kugel fast genau im Ziel? ton(660, 100) # Hier kommt deine Melodie hin zielX = random.randint(0, 120) zielY = random.randint(0, 56)
Zeichne das Ziel mit pb.rect(zielX, zielY, 8, 8, 1) nach dem pb.fill(0) in der Spielschleife. Dieses if gehört ebenfalls in die Spielschleife.
Und weiter?
Damit kennst du die Bausteine, aus denen Programme bestehen: Variablen, Verzweigungen, Schleifen, Listen und Funktionen, dazu Display, Joystick, LEDs, Sensor und Ton. Jetzt heißt es ausprobieren.