Make Magazin 5/2017
S. 28
Lasershow mit Arduino
Aufmacherbild

Spielen ohne Grenzen

Retrogames wie Tetris, Asteroids, Pong, Quake und viele andere sind wieder in. Sie werden erfolgreich auf alten Röhrenmonitoren, Oszilloskopen oder LED-Displays reaktiviert. Wir gehen noch einen Schritt weiter und lassen einen Laserstrahl die Retro-Vektorgrafiken im ganz großen Stil an die Wand zeichnen.

Beim Bau meiner Laserharfe (Make 1/17, S. 56) habe ich mich intensiv mit Galvo-Scannern beschäftigt: Das sind elektromagnetisch angetriebene Drehachsen, an deren Ende ein Spiegel zur Umlenkung von Laserstrahlen befestigt ist. Für mein elektronisches Musikinstrument brauchte ich allerdings nur einen der zwei Galvo-Scanner, die im Set enthalten waren, das ich seinerzeit gekauft hatte. Da keimte bald in mir der Wunsch, auch den zweiten Scanner in Betrieb zu nehmen und den Laserstrahl in zwei Dimensionen lenken zu können, um damit Grafiken zu zeichnen.

Das Galvo-Scanner-Set, das für diesen Artikel verwendet wurde, wird komplett mit Steuerplatinen geliefert und kostet etwa 120 Euro.
Das verwendete Galvo-Scanner-Paar schafft eine Geschwindigkeit von 20 Kpps (Kilo points per second).

Der Aufbau war schnell bewerkstelligt und schon nach einer knappen Stunde konnte ich die ersten Formen an die Wand werfen. Als ich jedoch versuchte, mit dem Laser schräge Linien zu zeichnen, stieß ich bereits an Grenzen, die ich erst nach und nach überwinden konnte – im Zuge monatelanger Recherchen, Forschungen, Dutzender Lochrasterplatinen und Arduino-Sketche. Immer wieder wurde ich im Internet von Laser-Enthusiasten verunsichert, die es schlichtweg für unmöglich hielten, mit einfachen Galvo-Scannern und einem Arduino Spiele-Klassiker wie Asteroids oder Pong an die Wand zu zaubern. Doch allen Zweifeln zum Trotz kann sich das Endprodukt durchaus sehen lassen – und hier stelle ich es zum Nachbau vor: Licht aus für LaserGambling, den Lasershow-Apparat im Eigenbau, der nicht nur Spiele wie Pong oder Asteroids wandfüllend projiziert, sondern auch selbst programmierte Animationen und Vektorgrafiken, die man mit einem kleinen Windows-Programm schnell zusammenklickt.

Hardware

Grundlage für dieses Projekt sind die Galvo-Scanner, die ich auch schon bei der Laserharfe verwendet habe. Sie sind inklusive der benötigten Ansteuerelektronik, Kabeln und Netzteil bei eBay für etwa 120 Euro zu haben. Die Bezugsquellen für alle verwendeten Teile finden Sie über den Link in der Kurzinfo. Online bekommen Sie auch den Artikel zur Laserharfe aus Make 1/17 (gratis im Volltext), in dem näher erklärt wird, wie die Galvos arbeiten. Bei der Beschreibung ist mir damals jedoch eine kleine Ungenauigkeit unterlaufen. Die Ansteuerelektronik der Galvos erwartet als Signal nicht, wie damals angegeben, eine Spannung von 0–5 V, sondern eine symmetrische Spannung von ±5 V. Die volle Auslenkung des Spiegels wird also erreicht, wenn entweder an beiden Signalleitungen +5 V gegen GND (Masse) oder –5 V gegen GND anliegt. Die gleiche Auslenkung erreicht man jedoch auch, wenn an die Signalleitungen ±10 V angelegt werden. Diese Spannung von ±10 V leitet sich aus dem ILDA-Standard ab (siehe Kasten).

Digital nach analog

Als Mikrocontroller verwendete ich anfangs den Arduino Nano. Aufgrund der geringen Anzahl an Portleitungen und des mit 2 KB zu kleinen Variablenspeichers führten die Experimente damit jedoch schnell ins Aus. Somit besorgte ich mir einen Arduino Mega, der mit 8 KB Variabelspeicher, 54 I/O-Pins und 16 Input-Pins mehr als üppig ausgestattet ist.

Die zahlreichen Portleitungen benötige ich zur Erzeugung der beiden Analog-Signale zur Ansteuerung der Galvos. Die Entscheidung zur Signalerzeugung mit einem R2R-Netzwerk, einer Kaskade aus Widerständen, fiel auch erst nach und nach im Verlauf dieses Projektes. Da ich als Mikrocontroller bei einem Arduino bleiben wollte, musste ich alles versuchen, um möglichst viel Rechenleistung einzusparen. Hierbei zeigte das R2R seine unschlagbare Stärke: Erstens gibt es keine schnellere Methode zur Konvertierung von digitalen zu analogen Signalen (DAC), und zweitens genügt zur Ansteuerung des Netzwerks ein einzelner Befehl. Den Details des R2R-Netzwerks und anderen DAC-Methoden widmet sich in diesem Heft ein eigener Artikel ab Seite 40.

Das R2R-Netzwerk habe ich auf eine eigene Lochrasterplatine gelötet und mit Male-Female-Jumperkabeln mit den Ports PA (Pin 71–78) und PC (Pin 53–60) des Arduino Mega verbunden. Da die Pins am Port PA nach links aufsteigen und am Port PC nach rechts, habe ich auch das R2R-Netzwerk einmal von rechts nach links und das andere von links nach rechts verlegt. Außen auf der Platine liegt jeweils GND, innen befinden sich die Signal-Ausgänge des Netzwerks.

Das R2R-Netzwerk wurde auf eine eigene Lochrasterplatine gelötet.

Prinzipiell kann der Ausgang des R2R-Netzwerks direkt mit der Ansteuer-Elektronik der Galvos verbunden werden. Man ist damit zwar nicht ILDA-konform, kann aber auch so die volle Galvo-Scanner-Auslenkung erreichen. Dazu müssen lediglich die beiden mit „Inputscale“ bezeichneten Trimmpotis auf den Ansteuerplatinen der Galvos nachgeregelt werden. Aber Vorsicht: Werden die Galvos später einmal mit einer ILDA-konformen Steuerung verbunden, so müssen unbedingt vorher die Trimm-Potentiometer wieder zurückgedreht werden. Anderenfalls werden die Galvos zerstört.

Wer lieber gleich (nahezu) ILDA-konform bleiben möchte, kann mit der hier gezeigten Schaltung aus einer 5-V-Spannung eine Spannung im Bereich ±10 V erzeugen. Dafür ist eine synchrone Spannung von ±12 V nötig, wofür sich das Modul SIM2-0512D eignet. An den Trimmpotis meiner Schaltung lassen sich die maximale Auslenkung der Galvos (Gain) und die Lage des Bildes (Offset) einstellen.

Game-Controller

Um Retro-Games wie Asteroids spielen zu können, braucht es den passenden Controller. Dafür eignet sich hervorragend der Wii Nunchuk von Nintendo, der einfach an die I2C-Schnittstelle angeschlossen wird. Die für die Kommunikation benötigte Library für die Arduino IDE gibt es im Netz (siehe Link in der Kurzinfo).

Für Pong hingegen braucht man zwei spezielle Paddles mit einem Drehknopf. Die habe ich aus je einem Handgehäuse von Conrad für 3,50 Euro selber gefertigt. Sie enthalten jeweils lediglich ein 1-KΩ-Potenziometer. Für den Anschluss musste ein Audiokabel herhalten.

Der Aufbau in der Gesamtansicht
Die komplette Schaltung mit R2R-Netzwerk und SIM2-0512D für ILDA-Konformität. Der Laseranschluss wurde hier der Übersichtlichkeit halber weggelassen, Details siehe Link in der Kurzinfo.

Zum leichteren Anschließen und Entfernen der Bedienelemente lötete ich die Anschlussbuchsen auf einen Arduino Mega Shield. Hinzu kam noch ein Taster für den Spiel-Neustart. Den Reset-Taster habe ich ebenfalls auf den Shield montiert, da jener des Mega-Boards durch den Shield verdeckt wurde. Später kam noch ein Kodierschalter zur Programmauswahl hinzu. Damit lässt sich bequem zwischen den Spielen, den Animationen und den Testbildern umschalten, ohne jedes Mal einen neuen Sketch in den Arduino übertragen zu müssen.

Da ich die Jumperkabel für das R2R-Netzwerk direkt auf den Arduino Mega aufsteckte, habe ich an der Shield-Platine die entsprechenden Lötpunkte abgesägt. So musste ich nicht immer alle Jumperkabel abziehen, um am Shield etwas einlöten zu können.

Laser an!

Als Laser gönnte ich mit für dieses Projekt einen RGB-Laser, den ich mir ebenfalls für rund 160 Euro aus China schicken ließ. Natürlich funktioniert das Projekt auch mit einem günstigeren und einfarbigen Laser, die farbige Variante ist allerdings ansprechender und auch vielseitiger, denn damit kann man bei Spielen etwa unterschiedliche Objekte in verschiedenen Farben zeichnen.

Das fertige Eigenbau-Shield mit den beiden Paddles und dem Wii-Nunchuk-Controller.

Ist die Anlage komplett aufgebaut, müssen zuerst die Galvoscanner und – sofern RGB-Laser verwendet werden – die Laser eingestellt werden. Dazu leistet das ILDA-Testbild sehr gute Dienste. Eine einfache Variante dieses Bildes habe ich in den Arduino-Sketch integriert. Die Anweisung ShowIldaTestbild(); zeigt es an.

Beim RGB-Laser sind die halbdurchlässigen Dircho-Spiegel deutlich zu erkennen: Sie reflektieren jeweils nur Licht einer spezifische Wellenlänge. Die anderen Farben dringen fast ungehindert hindurch.
Zusätzliche Grafik in der Datei Debugging.ino: Testbild_1();

Hilfreich beim Kalibrieren können auch die weiteren Testbilder sein. Sie wurden in die Datei Debugging.ino integriert. Jedoch ist es praktisch unmöglich, eine Einstellung zu finden, bei der alle Testbilder perfekt angezeigt werden, man muss sich eher an einen zufriedenstellenden Kompromiss herantasten.

Programmierung im Strom

Die Hardware steht, die Lasershow läuft – im Prinzip. Fehlen nur noch die passenden Retro-Spiele, also ran ans Coden!

Beim Programmieren von Asteroids stieß ich allerdings auf ein zentrales Problem: Je umfangreicher die Berechnungen wurden, desto mehr ruckelte das Bild. Somit musste ich das Berechnen der einzelnen Frames unabhängig von der Galvo-Steuerung bewerkstelligen. An einem PC wäre dies in Zeiten von Multitasking sicher kein Problem gewesen. Aber bei einem Arduino?

Anfangs versuchte ich, mit zwei Frame-Arrays zu arbeiten. In das eine werden die berechneten Daten geschrieben, während das zweite zur selben Zeit die Galvos mit Daten versorgt. Anschließend werden die zwei Arrays getauscht. Leider kam ich dabei schnell an die Grenzen des Variablenspeichers des Arduino Mega. 8  KB sind halt auch nicht die Welt.

Gelöst habe ich das Problem schließlich mit einer Art Stream: Die Berechnungsroutinen zu Positionen von Objekten und Ähnlichem legen ihre Ergebnisse in einem Array ab. Genaugenommen handelt es sich dabei um drei Arrays, die parallel befüllt werden, eines für die X-Koordinaten, eines für die Y-Koordinaten und das dritte für die Laserfarbe. Daraus werden Timer-Interrupt-gesteuert die Galvos und die Laser mit Daten versorgt. Das Befüllen (PointerSet) mit Daten kann dabei sowohl vor dem Anzeigezeiger (PointerShow) als auch hinter dem Anzeigezeiger erfolgen. Man muss beim Programmieren lediglich darauf achten, dass die Berechnung eines Frames weniger Zeit in Anspruch nimmt als seine Anzeige. Anderenfalls beginnt das Bild wieder zu ruckeln, denn der Anzeigezeiger kann den Berechnungszeiger nicht überholen. Der möglichen Komplexität von Spielen und anderen Arduino-Anwendungen sind also Grenzen gesetzt.

Zeichnen mit Trägheit

Nachdem diese technischen Voraussetzungen geschaffen wurden, geht es nun ans Zeichnen. Auch hier gilt es jede Menge zu beachten. Leider genügt es nicht, die Galvo-Spiegel auf die Startposition zu bewegen, den Laser einzuschalten und dann die Spiegel auf die Endposition zu dirigieren. Anders als ein Elektronenstrahl in einer Fernsehröhre oder einem Oszilloskop (siehe auch Seite 16) kommt bei Galvo-Spiegeln die Trägheit ins Spiel, denn auch die besten und schnellsten Galvos besitzen eine Masse, die beschleunigt und am Ende wieder abgebremst werden muss. Dafür ist Zeit nötig, die bei der Programmierung berücksichtigt werden muss, um ein sauberes Bild zu erhalten.

Zusätzliche Grafik in der Datei Debugging.ino: Testbild_2()
An den Trimmpotenziometern der Ansteuerplatine wird justiert, bis das ILDA-Testbild korrekt erscheint.

Um eine gleichmäßige Linie zeichnen zu können, muss man diese in einzelne Teile zerlegen. Befindet sich der Laser auf der Startposition, wird er eingeschaltet. Dann bewegt man die Spiegel in festgelegten Zeitintervallen immer um einen kleinen Teilbereich weiter (Parameter maxDistDrawn). Ist die rechnerische Zielposition erreicht, wird der Laser jedoch nicht sofort ausgeschaltet, sondern noch für eine festgelegte Zeit (post Draw) gewartet, bis die Spiegel tatsächlich am Ziel angelangt sind und abgebremst haben.

Mit Hilfe der beiden Innensechskantschrauben und der Madenschraube kann der Spiegel ausgerichtet werden.

Anschließend bewegen wir die Spiegel bei ausgeschaltetem Laser wieder zur Startposition zurück. Diesen Vorgang nennt man Blanking. Hierbei spielt es keine Rolle, welchen Weg der Laser dabei nimmt, er sollte nur möglichst kurz sein, um nicht unnötig Zeit für das Blanking zu verschwenden. Jedoch muss auch beim Blanking dem Laser genügend Zeit fürs Schalten eingeräumt (preBlanking) und am Ende gewartet werden, damit die Zielposition sicher erreicht werden kann (postBlanking).

Pong mit sichtbarem Blanking

Es kann übrigens sehr interessant sein, sich einmal die Wege des Lasers beim Blanking anzeigen zu lassen. Dazu müssen Sie in der Datei Draw.ino in der Routine blanking(byte x, byte y) lediglich in der Zeile DrawFastLine(x, y, 0); die 0 durch eine 1 ersetzen. Zum einen lässt sich dann erkennen, welche interessanten Bahnen der Laser beim Blanking beschreibt. Zum anderen bietet es auch die Chance, den Programmablauf zu optimieren und unnötige Blanking-Strecken einzusparen.

Programmablauf

Der prinzipielle Programmablauf sieht nun folgendermaßen aus:

Voraussetzung für das Ausführen meines „LaserGambling-Frameworks“ sind die Libraries TimerOne.h, Time.h und WiiNunchuck. Diese müssen vorher heruntergeladen und in die Arduino IDE eingebunden werden (siehe Link in der Kurzinfo).

Asteroids mit sichtbarem Blanking
Eine funktionierende analoge Uhr als Hingucker

Im setup()-Teil des Arduino-Codes wird mit init_DAC(25) das R2R-Netzwerk vorbereitet und das Timer-Intervall eingestellt. Je kleiner der Wert in der Klammer, desto höher ist die Scanner-Geschwindigkeit. Meine Galvos waren mit einer Geschwindigkeit von 20 Kpps angegeben. Dafür erwies sich ein Timer-Intervall von 22 bis 25 Mikrosekunden als recht gut passend.

Auf beginFrame() folgen Zeichenanweisungen, wie sie in der Tabelle aufgelistet sind. Den Abschluss eines Frames löst der Aufruf von endFrame() aus.

Schon während der Stream mit Daten gefüllt wird, bekommen die Galvo-Scanner Timer-gesteuert Anzeigedaten geliefert. Wurde ein Frame komplett angezeigt, wird der nächste abgespielt, sofern der in der Zwischenzeit berechnet wurde. Soll – für ein stehendes Bild – immer wieder der gleiche Frame wiedergegeben werden, so setzt man einfach PointerSet= -1. Damit wird dem Anzeigezeiger vorgegaukelt, dass sich der Befüllzeiger hinter ihm befindet und damit Daten zum Anzeigen vorliegen.

Recycling

Die Struktur des Streams mit den zwei Zeigern erlaubt einen Trick, um die Berechnung einzelner Frames deutlich zu verkürzen, wenn sich nur Teile des Bilds ändern sollen. Denn der Stream muss nicht jedes Mal komplett neu gefüllt werden.

Tabelle: Zeichenbefehle

Als Beispiel habe ich eine analoge Uhr in den Sketch eingebaut. Dabei wird das Zifferblatt nur einmal berechnet und in den Stream gefüllt. Die Zeigerposition vom Ende des Ziffernblattes speichere ich und positioniere den Befüllzeiger (PointerSet) zum Berechnen der Zeiger jedes Mal an diese Position. Dadurch wird jede Menge Rechenzeit eingespart und das Bild flüssiger dargestellt. Soll die Uhr wirklich die korrekte Uhrzeit anzeigen, braucht der Arduino natürlich noch eine DCF- oder RTC-Uhr.

Vektorkunst

Um die Grafiken für die Lasershow nicht mühsam aus Linien, Rechtecken und Kreisen zusammenprogrammieren zu müssen, habe ich noch ein kleines Windows-Vektorzeichenprogramm namens VectorEdit geschrieben, das als Ergebnis fertigen Arduino-Code bereitstellt (Download siehe Link in der Kurzinfo). Dort hinein kann man BMP- und JPG-Grafiken als Hintergrundbild laden, die man dann einfach durch Linien nachzeichnet. Das Programm speichert die Vektordaten als TXT-Dateien oder gibt sie als PROGMEM-Arrays aus. Diese nutzen den üppigen Programmspeicher des Arduino Mega und belegen damit keinen Platz im Speicher für die Variablen, den ich ja für den Stream reserviert habe.

Die Enterprise – nach einer Vorlage aus VectorEdit mit dem Laser gezeichnet
Das selbstgeschriebene Zeichenprogramm VectorEdit wandelt den Make-Schriftzug in Laservektoren um.

Alle in diesem Artikel dargestellten Laserbilder und weitere Laserbilder sind im Arduino-Sketch im Download-Paket zu diesem Artikel integriert. Wie immer freuen wir uns aber auf Ihre eigenen Lasergrafiken und Spiele – wenn Sie also Ihre eigene Lasershow zum Laufen gebracht haben und selbst kreativ geworden sind, freuen wir uns über Bild- und Codezuschriften an mail@make-magazin.de. —pek