Wie schon letztes Jahr steht urplötzlich Weihnachten vor der Tür. Was liegt da für einen Geek näher, als in die Grabbelkiste zu greifen und eine eigene Weihnachtsspieluhr zu basteln.
Spieluhren und Weihnachtspyramiden zählen in der Adventszeit zu den Verkaufsrennern. Teure, handgeschnitzte Exemplare gehen für einen vierstelligen Betrag über die Theke, billige Imitate aus China für 20 Euro aufwärts. Die Pyramiden werden oft von Kerzen angetrieben, und gerade die billigen Vertreter der Gattung besitzen ein so ungünstiges Verhältnis von Kerzen und Flügeln, dass sich die Pyramide ohne mechanische Hilfe dann doch nicht dreht. Fragile Federn setzen wiederum Spieluhren in Gang, das ständige Aufziehen macht jedoch auch keinen Spaß.
Aus der Frustration daraus entstand die Idee, eine eigene Spieluhr zu bauen. Viele Komponenten auf der Elektronikseite braucht es dazu nicht. Die folgenden Abschnitte sollen Anregungen für eigene Ideen geben; die hier verbauten Teile lassen sich problemlos durch das ersetzen, was die Grabbelkiste hergibt. Unsere Bastelei soll die Elemente Licht, Bewegung und Musik enthalten.
Steuerung
Für die Steuerung verwendet das Bastelprojekt einen Mikrocontroller von Adafruit, den Trinket-M0 (Abbildung 1). Darauf läuft CircuitPython, ein minimalistisches Python, das mit seinen vielen Bibliotheken eine umfassende Palette von Peripherie unterstützt. Ein früherer Artikel ging bereits im Detail auf den Trinket-M0 ein [1]. Preislich liegt der Controller in derselben Größenordnung wie ein Pi Zero samt SD-Karte, er ist aber leichter in Betrieb zu nehmen, da die Installation und Konfiguration des Betriebssystems entfällt.

Abbildung 1: Der Trinket-M0 kostet weniger als 10 US-Dollar. Ein Mikrocontroller bietet den Vorteil, dass er nicht booten muss und auch hart abgeschaltet werden darf.
Außerdem bietet ein Mikrocontroller den Vorteil, dass Sie ihn einfach ausschalten dürfen, ohne dass die Installation Schaden nimmt. Der größte Vorteil liegt jedoch in der Unterstützung von CircuitPython. Die Beispiele hier zeigen, mit wie wenig Code man seine Ideen damit umsetzen kann. Unabhängig davon ließe sich das Projekt auch auf Basis des Pi Zero implementieren, an der einen oder anderen Stelle wie der Stromversorgung wären dann aber Anpassungen notwendig.
Es werde Licht
Unsere Lichteffekte erzeugen wir mit LEDs, genauer Neopixel-LEDs. Die gibt es in allen möglichen Konfektionen, von langen Streifen über Ringe und Arrays bis hin zu einzelnen LEDs. Die Bauteile enthalten schon die Treiberchips, was sie etwas teurer macht als normale LEDs, die Ansteuerung der LEDs vereinfacht sich dadurch aber ganz wesentlich.
Neopixel verlangen normalerweise eine Spannung von 5 Volt. Dabei genügen drei Anschlüsse für Spannung, Masse und Daten. Sind die Kabel kurz und die Anzahl der Pixel gering, dann reichen für die Spannungsversorgung und Ansteuerung bereits 3,3 Volt aus. Die maximale Helligkeit sinkt dadurch zwar etwas; da die LEDs aber sowieso sehr hell leuchten, spielt das in der Praxis keine Rolle. Unabhängig von der gewählten Spannung gilt es, zu prüfen, ob die Neopixel die Spannungsquelle nicht überfordern. Insbesondere die 3,3V-Pins des Raspberry Pi sind nicht dafür ausgelegt, hohe Stromstärken zu liefern. Die 3,3V-Schiene des Trinket hat hier etwas weniger Probleme, der eingebaute Spannungskonverter liefert 500 mA.
Beim Beispielprojekt fiel die Wahl auf einen RGBW-Pixelring mit 24 Pixeln (Abbildung 2). Er ließ sich problemlos über den Trinket-M0 mit 3,3 Volt Spannung versorgen und direkt ansteuern. Die Leistungsaufnahme bei heruntergeregelten LEDs lag bei ungefähr 60 mA im Maximum. Ganz billig ist der Spaß nicht, der LED-Ring stellt die teuerste Einzelkomponente im Projekt dar (21 Euro). Beim Anlöten der Kabel gilt es, darauf zu achten, dass keine Lötbrücke zu benachbarten LEDs entsteht.
Abbildung 2: Der Neopixel-Ring mit 24 RGBW-LEDs bietet im Vergleich zu Einzelkomponenten den Vorteil, dass sich die Ansteuerung ganz wesentlich vereinfacht.
Ein minimales Anwendungsprogramm zeigt Listing 1. Die Neopixel-Bibliothek für CircuitPython (Import in Zeile 3) erwartet der Code dabei im Unterverzeichnis lib/. Allerdings besitzt die Bibliothek einen Bug: Beim Initialisieren des Objekts (Zeilen 8 bis 10) müssen Sie zwingend das Argument pixel_order=neopixel.GRBW angeben, obwohl der übergebene Wert eigentlich der Voreinstellung entspricht.
Listing 1
import time
import board
import neopixel
pixel_pin = board.D2
num_pixels = 24
pixels = neopixel.NeoPixel(pixel_pin,
num_pixels,
pixel_order=neopixel.GRBW,
brightness=0.05,
auto_write=False)
def farbrad(pos):
if pos < 0 or pos > 255:
return (0,0,0,0)
if pos < 85:
return (255 -- pos * 3,pos * 3,0,0)
if pos < 170:
pos -= 85
return (0,255 -- pos * 3,pos * 3,0)
pos -= 170
return (pos * 3,0,255 -- pos * 3,0)
def regenbogen(wait):
for j in range(255):
for i in range(num_pixels):
rc_index = (i * 256 // num_pixels) + j
pixels[i] = farbrad(rc_index & 255)
pixels.show()
time.sleep(wait)
while True:
regenbogen(0.1)
Das Beispielprogramm erzeugt über alle Pixel hinweg die Regenbogenfarben und rotiert je nach wait-Parameter mehr oder weniger langsam. Möchten Sie zusätzlich einen Button an den Trinket-M0 anschließen, etwa für An/Aus oder um zwischen Effekten umzuschalten, dann müssen Sie den Schalter an geeigneter Stelle abfragen: CircuitPython kennt keine Interrupts. Im Beispiel wäre das entweder innerhalb der While-Schleife oder vor Zeile 29.
In Bewegung
Damit sich die Spieluhr dreht, kommt ein sogenannter Continuous-Rotation-Servomotor zum Einsatz. Im Unterschied zu Steppermotoren, die das exakte Ansteuern einer Position erlauben, lässt sich bei Servomotoren lediglich die Drehzahl sowie die Drehrichtung vorgeben. Im Projekt kommt ein FS90R von Fitec für 6 Euro zum Einsatz (Abbildung 3). Der Motor lässt sich zum Beispiel über Pimoroni ordern.

Abbildung 3: Bei einem Continuous-Rotation-Servomotor wie dem Fitec FS90R lassen sich Drehrichtung und -geschwindigkeit regeln.
Für die Steuerung genügen wieder wenige Zeilen Code (Listing 2). Zuerst erzeugen wir in Zeile 6 ein PWM-Objekt und dann anschließend das Steuerobjekt für den Motor. Der Parameter throttle steuert die Richtung (Vorzeichen) sowie die Geschwindigkeit und nimmt Werte zwischen -1 und 1 entgegen. Je nach Exemplar und Spannungsversorgung steht der Motor allerdings nicht erst bei einem Wert von 0 still. Beim Autor stoppte er schon bei Werten zwischen 0.05 und 0.10.






