In Zuge eines 8-wöchigen Betriebspraktikum im Sommer 2017, haben ein Mitschüler und ich an der Hochschule Bochum, einen RGB-LED-Tisch gebaut. In diesem Projekt ging es darum, dass ein RGB-LED-Matrix-Tisch mit 10 Pixeln in der Breite und 20 Pixelnin der Länge gebaut werden sollte,um später ehemalige 8-bit-Klassiker wie Tetris auf dem Tisch zu spielen. Dafür wurde ein IKEA-Tisch verwendet,um die RGB-LEDs einzubauen. Der Tisch wurde so umgebaut, dass ein Rechteck aus dem Tisch geschnitten und in den Tische in Gitter aus finnischer Holzpappe eingelegt wurde. Zur Verfügung wurden 200 RGB-LEDs und ein Raspberry Pi Zero W gestellt, mit dem die Steuerung der RGB-LEDs übernommen wurde. Auf die RGB-LEDs wurde ein überfang aus weißem Milchglas aufgelegt,um das Licht der RGB LEDs zu streuen, womit es besser dargestellt wird. Außerdem wurde eine Android-App geschrieben, die als Controller für die Spiele verwendet wird. Die Verbindung der Steuerung zu dem Tisch wurde über Bluetooth hergestellt. Als Programmiersprache wurde Java eingesetzt. Als LED-Typ wurden die APA102 Chips verwendet.
In dem Beitrag werden ich auf mein Programmcode eingehen. Das gesamte Projekt für den Raspberry Pi sowie für die Android App ist auf Github zu finden.
RPi Anwendung https://github.com/Gurkengewuerz/ledmatrix/
Android App https://github.com/Gurkengewuerz/ledmatrix-android
Raspberry Pi Zero W einrichten
Nach der Standardmäßigen Installation eines Raspbian Betriebssystem ist es wichtig den Pi direkt nach dem ersten Start ins WLAN zu hängen, um diesen Headless zu betreiben. Dafür direkt nach der Installation auf der Boot Partition die Datei wpa_suplicant.conf anlegen un konfigurieren bspw. nach diesem Tutorial.
Wichtig: Bitte das Root Passwort ändern!
$ sudo passwd root |
Anschließend für das Projekt Java installieren.
$ echo „deb http://ppa.launchpad.net/webupd8team/java/ubuntu xenial main“ | tee /etc/apt/sources.list.d/webupd8team-java.list
$ echo „deb-src http://ppa.launchpad.net/webupd8team/java/ubuntu xenial main“ | tee -a /etc/apt/sources.list.d/webupd8team-java.list $ apt-key adv –keyserver hkp://keyserver.ubuntu.com:80 –recv-keys EEA14886 $ apt-get update $ apt-get install oracle-java8-installer |
Um an den GPIO-Pins BSM 9-11 den SPI-Bus für die APA102 LEDs zu aktivieren Um den SPI Bus zu aktivieren, über das Programm „raspi-config“ in dem Unterpunkt „Interfacing“ den SPI- Bus aktivieren.
Programmablauf
Bezüglich des Programmablaufs zwischen dem Client und Server habe ich mir überlegt, dass der Server mit der laufenden Java-Applikation ständig in einer Schleife läuft und auf neue Client Bluetooth-Verbindungsanfragen wartet. Die zu übertragenden Daten, die zwischen dem Client und dem Server gesendet werden, werden in JSON-Pakete gepackt, damit sie auf beiden Seiten einfach verarbeitet werden können.
An den Nutzer werden in einem Fünf-Sekunden-Takt über Bluetooth die aktuellen Statusmeldungen, beispielsweise den Spielstatus (Läuft, Pause, Game Over etc.) oder den Punktestand gesendet. Die Statusmeldungen, die der Client auf seinem Smartphone empfängt, werden dann bei Veränderungen in der Oberfläche des Nutzers aktualisiert. Da mehrere Spiele auf dem Server laufen sollen, kann der Nutzer beim Spielstatus „Warten“ ein Spiel auf dem Gerät auswählen. Die getroffene Auswahl wird dann dem Server mitgeteilt, welcher das jeweilige Spiel startet.
Solange ein Spiel läuft und nicht beendet ist, sei es durch manuelles Unterbrechen oder ein Game Over, liest der Server die Tastendrücke aus, die von dem Controller aus der Smartphone-App gesendet werden. Sobald in den Spielmodus Game Over geschaltet wird, werden die aktuellen Daten mit dem Punktestand in eine SQLite- Datenbank geschrieben. Erkennt das Smartphone ein Game Over, so bekommt der Nutzer über die App ein haptisches Feedback. Sollte der Client seine Verbindung trennen, sei es durch den Verlust der Verbindung oder durch ein beabsichtigtes Trennen, startet die Applikation seinen Bluetooth-Server neu und erlaubt wieder die Verbindung neuer Geräte.
Dieser Aufbau ist stark an eine UDP-Verbindung angelehnt. Wie bei UDP gibt bei dieser Verbindung keine Flusskontrolle zwischen dem Server und dem Client. Des Weiteren werden keine Daten bestätigt. Hätte ich dies eingebaut, wäre die Verbindung deutlich getakteter. Von dieser Eigenschaft hätte ich nicht profitiert, da man für eine Spielsteuerung schon fast in Echtzeit mit der Anwendung kommunizieren muss.
APA102 Ansteuerung
Jeder APA102 Chip besitzt insgesamt acht Pins. Die Betriebsspannung sowie die Masse werden durchgeschleift, um die Spannung an jede LED im Bus zu bringen. Die Datenpins (D) sowie die Taktgeschwindigkeit (C) werden in Input (DI/CI) sowie Output aufgeteilt (DO/CO).
LED-Pin | Beschreibung | Raspberry Pi Pin (BCM) |
DI | Data Input | GPIO Pin 10 |
CI | Clock Input | GPIO Pin 11 |
Do | Data output | – |
CO | Clock output | – |
GND | Ground | GND |
VCC | +5V | 5V |
DI ist der Datenbus. Er nimmt die Datenpakete an und schleift sie zu DO durch, um die gesendeten Daten an die folgenden RGB-LEDs weiterzuleiten. CI ist für die Taktfrequenz zuständig. Das binäre Signal wird von dem Master (RPi) vorgegeben. Das Signal regelt unter anderem die Datenübertragung.
Um die Datenpakete zu verschicken, wird die SPI-Schnittstelle verwendet. Die RGB-LED empfängt ein gültiges SPI-Signal und leitet dieses der nächsten RGB-LED weiter. In dem Datenblatt ist dies so beschrieben, dass die Datenleitung nur während der Anstiegsflanke des Taktsignals beansprucht wird. Dies führt zu dem Problem, dass die Daten, die an die erste RGB-LED geschickt werden, möglicherweise nicht so schnell verarbeitet werden können. Um dieses Problem zu umgehen, verzögert der APA102 die Daten auf der Ausgabe um circa einen halben Zyklus. Dieser Entwurf bewirkt, dass die RGB-LEDs keine interne Taktquelle benötigen.
Ein Datenpaket für den LED-Strip benötigt u.a. ein Start Frame. Dies hat die Auswirkung, dass die RGB-LEDs aktualisiert werden. Dieses Paket muss mindestens 32 Bits groß sein und mit führenden Nullen beginnen. Der Frame mit den Eigenschaften in dem Paket für die RGB-LED wird anhand der drei führenden Einsen von der RGB-LED erkannt. Auf die Füllbits folgt die Helligkeit. Die Helligkeit kann einen Wert von 0 bis 31 Bits annehmen, wobei 0 für nicht aktiv steht. Nach der Helligkeit folgen die drei Farben Blau, Grün und Rot. Diese kann man mischen, sodass man über sechzehn Millionen mögliche Farben erzeugen kann. Die Farben nehmen jeweils acht-Bits ein. Für jede angeschlossene LED wird dies wiederholt (Füllbit, Helligkeit, Blau, Grün, Rot) gesendet. Am Ende des Pakets folgen 32 Endbits.
Bluetooth-Kommunikation
Für den Controller wurde die Serielle Bluetooth Schnittstelle RFCOMM verwendet. Für dieses Projekt habe ich die Bluetooth Library bluecov verwendet. Für den ARM-Prozessor musste ich eine zusätzliche C-Library sowie das BlueCove Modul GPL installieren. Dies wird unter Linux benötigt, um mit bluez zu kommunizieren.
BlueCove nutzt unter Linux die Schnittstelle bluez, die letzten Endes für die Bluetooth-Kommunikation zwischen Betriebssystem und Software zuständig ist. Für die Nutzung von Bluetooth auf dem RPi habe ich aus dem Packet-Manager alle benötigten Programme heruntergeladen.
$ apt-get install bluetooth bluez-tools blueman libbluetooth-dev |
Als ich ein Testprogramm laufen ließ, ist das Programm mit einem Fehler abgestürzt. Es konnte keine Verbindung zu dem SDP-Socket aufgebaut werden. Dies lag an der Startoption des Bluetooth-Services unter Linux. Um diesen zu nutzen, muss die Startoption –c mit übergeben werden. Dafür die Datei /etc/systemd/system/bluetooth.target.wants/bluetooth.service bearbeiten und den Parameter ExecStart verändern.
#ExecStart=/usr/lib/bluetooth/bluetoothd
ExecStart=/usr/lib/bluetooth/bluetoothd -C |
Außerdem musste noch den eigebauten Bluetooth-Controller starten.
$ hciconfig hci0 up |
Für das pairen mit dem Smartphone kann man den Assistenten bluetoothctl auf dem RPi in der Kommandozeile nutzen.
pairable on
discoverable on |
Eine erfolgreiche Koppelung bedeutet aber noch nicht, dass man den Datenverkehr von dem Gerät auf Softwareebene mitlesen kann. Dafür muss für Bluetooth eine neue Konfigurationsdatei für RFCOMM angelegt werden. In /etc/bluetooth/rfcomm.conf wird für jedes Gerät, das sich verbinden soll, ein neuer Eintrag erstellt. Dafür wird die eindeutige Hardware-Adresse von dem Gerät benötigt, ob der Konfigurationseintrag bei dem Start des Betriebssystems geladen werden soll, und der Kanal auf der RFCOMM-Schnittstelle für das Gerät.
rfcomm0 {
bind yes; device xx:xx:xx:xx:xx:xx; channel 1; comment „Smartphone“; } |
Sollte bei einem Gerät der Eintrag „bind auf yes“ gestellt sein, wird dieses nach einem Neustart automatisch eingebunden. Dadurch wird für jedes Gerät, das sich verbindet und in der Konfigurationsdatei steht, eine serielle Schnittstelle auf dem Betriebssystem unter „/dev/rfcomm0“ verfügbar. Diese Schnittstelle wird von BlueCove genutzt, um mit einer aktiven Verbindung zu kommunizieren.
Zusammenbau des Tisches
Um den LED-Matrix-Tisch zu bauen, hatte ich vor den Tisch in einer Schreinerei anfertigen zu lassen. Das bedeutete großen Aufwand, folglich wählte ich einen Ikea Tisch und baute ihn um, indem ein Rechteck im Tisch ausgesägt wurde. Der Tisch war anfangs zur Stabilität mit Pappe gefüllt, welche ich zur Seite gedrückt habe.
Der Ikea-Tisch ist 90×55 cm groß, es handelt sich um einen weißen Lack-Tisch. Das ausgesägte Rechteck ist 70×40 cm groß. In das ausgeschnittene Rechteck habe ich ein Gitter aus finnischer Holzpappe gelegt, um die Pixel voneinander zu trennen. Hier habe ich mich für 2mm dicke finnische Holzpappe entschieden, da diese mit einem Teppichmesser schneidbar ist. Außerdem ist sie günstig im Internet zu bestellen.
Als nächstes habe ich die RGB-LEDs gelötet. Hierbei sollte man unbedingt Zeit und viel Geduld mitbringen.
Der Draht, welchen ich für die Lötungen verwendet habe, hatte einen Durchmesser von 0.5 mm. Wegen der hohen Stromstärke musste ich auf einen etwas dickeren Draht zurückgreifen, da dieser sonst ziemlich heiß werden könnte und ein Sicherheitsrisiko darstellen würde. Der Draht für die Stromleitungen hatte einen Durchmesser von 0.8 mm. Ich habe den Strom so verteilt, dass er an vier Stellen von den LEDs eingespeist wird. Das hat den Grund, dass die LEDs sonst zu wenig Strom bekommen und somit nicht hell leuchten oder sogar gar nicht funktionieren.
Im Nachhinein habe ich bemerkt, dass ich nur eine Masse-Leitung verlegt hatte. Das hatte zur Folge, dass es in der Masse zu Engpässen kommen kann.
Als nächstes habe ich ein Netzteil gesucht, welches den RPI Zero W und die RGB-LEDs betreiben soll. Von vorherigen Tests mit 50 LEDs, hatte ich bei voller Helligkeit und der Farbe Weiß einen Strom von 2,5 Ampere gemessen. Das bedeutet bei 200 LEDs einen Stromanteil von zirka 10 Ampere. Ich habe mich jedoch für ein Netzteil mit 5V bei 4A entschieden, da es sehr preiswert war und es zum Testen reichte. Eine Buchse für das Netzteil habe ich in den Tisch an der rechten Seite eingebaut. Nach ein paar mehr Tests sollte man schon auf ein größeres Netzteil gehen!
Um das Licht zu brechen, musste ich noch eine Plexiglas-Platte oder eine Glasplatte besorgen. Um das Aussehen des Tisches zu verschönern, habe ich mich für eine Glasplatte entschieden. Damit das Licht gebrochen wird, wurde ein Milchüberfang weiß Glas ausgewählt. Dieses wird häufig für Tischplatten verwendet und ist mit einer Keramikschicht beschichtet. Da das Glas jedoch 4 mm dick war, konnte es nicht mehr in das ausgeschnittene Rechteck gelegt werden. Deshalb habe ich mich dazu entschieden, das Glas auf den gesamten Tisch zu legen und dieses mit Alu-Leisten an der Seite zu befestigen.
Installation des Projektes via Skript
Für eine einfachere Installation liegt ein Bash Skript im Github Repository bei.
Weiterentwicklung
Mittlerweile besitzt das Projekt eine zusätzliche API-Schnittstelle um den Tisch per Websocket Commands anstuern zu können. Zudem können nicht nur Spiele auf dem Tisch gespielt werden, sondern nun auch als „Ambilight“ auf Parties o.ä. benutzt werden. Dafür habe ich den Tisch um ein Webinterface erweitert über die man Animationen starten kann.