lrg + ledchainarrangement - Graphik-Subsystem für SmartLED-Ketten und Matrizen
Was ist lrg?
lrg ist die Abkürzung für "low resolution graphics", und innerhalb von p44script der Name des globalen Objekts, mit dem das Graphik-Subsystem p44lrgrpahics angesprochen wird. p44lrgrpahics stellt eine Graphik-Umgebung zur Verfügung, mit der komplexe Effekte auf LED-Ketten und Matrizen erstellt und auch animiert werden können. Dazu bietet es eine Hierarchie von Views, die nebeneinander oder auch übereinander (mit variabler Durchsichtigkeit) angeordnet und bewegt werden können.
Ein Lauflicht-Effekt etwa würde in p44lrgrpahics mit einem Hintergrund-View realisiert, über dem das eigentliche Lauflicht als zweiter View bewegt wird.
Was ist ein ledchainarrangement?
SmartLED-Ketten bestehend aus einzeln addressierbaren LEDs (WS281x, SK681x etc.) können verschiedenartig zu Bändern oder Flächen kombiniert werden. Z.B. lassen sich LED-Streifen auf einem Zylinder aufwickeln, oder in Zick-Zack-Anordnung auf einer Fläche.
Ein ledchainarrangement umfasst eine oder mehrere Abschnitte von LED-Ketten, und arrangiert sie logisch innerhalb eines Rechtecks von Pixeln (u.U. mit Lücken). Dieses logische Rechteck bildet dann den rootview für das lrg Graphiksystem. Jede angeschlossene LED erhält damit eine eindeutige Koordinate (x und y). Der rootview (üblicherweise ein stack) kann dann weitere Views beliebig in diesem Rechteck platzieren, verschieben, ein- und ausblenden, etc.
Das ledchainarrangement eines P44-xx-Geräts wird entweder über die Kommandozeile mit der --ledchain
-Option angegeben, oder via p44script mit addledchain()
(s. unten für Details) definiert, üblicherweise aus dem mainscript heraus.
View-Typen
Es gibt verschiedene Typen von Views (Details zur Konfiguration s. unten):
Allgemeine Views
- plain
- Auf diesem einfachsten View sind alle anderen Typen aufgebaut. Ein plain view umfasst ein Rechteck mit einer Hintergrund- und einer Vordergrund-Farbe.
- canvas
- Auf diesem View können programmatisch einzelne Pixel gesetzt (gezeichnet) werden.
- text
- Stellt einen Text in 5*7 Matrixschrift dar.
- image
- Zeigt ein Bild (das im PNG-Format vorliegen muss) an.
- scroller
- Kann einen anderen View mit einstellbarer Geschwindigkeit bewegen (scrollen). Die Bewegungsschritte können kleiner als eine LED sein, dann werden die Zwischenschritte durch Helligkeitsverteilung auf mehrere Pixel simuliert (Anti-Aliasing). Mit dem scroller und einem text-View sind z.B. Laufschriften realisierbar. Der scroller kann einen Inhalt in einer Endlosschlaufe scrollen, oder auch einen Event auszulösen, wenn z.B. eine Laufschrift neuen Inhalt zur Darstellung braucht.
- stack
- Dient dazu, mehrere andere Views in Schichten (Layers) übereinander oder auch nebeneinander anzuordnen.
- sequencer
- Dient dazu, mehrere andere Views (z.B. Bilder) in einer zeitlichen Abfolge (Steps) anzuzeigen, einmalig oder wiederholt. Die einzelnen Schritte können verschiedene Anzeigedauer sowie Ein- und Ausblendzeiten haben.
Spezialisierte Views
- lightspot
- Simuliert einen Licht-"Spot", eine runde oder ovale Fläche mit verschiedenen Möglichkeiten der Farb- und Helligkeitsverteilung rund um die Mitte. Damit sind Farbverläufe, weiche Kanten etc. machbar. Der lightspot ist die Basis des "Moving feature light with effects" das in P44-DSB/LC-Geräten mit SmartLED-Anschluss verfügbar ist.
- epx
- Spielt Animationen ab, die im Expressive pixels IoT animation format-JSON-Format erstellt wurden.
- life
- Stellt Conway's "Game of Life" dar. Das Alter der Zellen kann farblich dargestellt werden.
- torch
- Eine einfach Fackel/Feuer-Simulation, die gut auf rund aufgewickelten LED-Ketten aussieht. Dieser View hat sich aus dem Projekt MessageTorch von 2014 entwickelt.
Aufsetzen der SmartLED-Ketten in LEDChainArrangements
Der Ledchain-Initialisierungsstring
Je nach Hardware verfügt ein Gerät über einen oder mehrere Ausgänge, an die jeweils eine LED-Kette mit einzel addressierbaren LEDs, wie WS28xx und ähnliche, angeschlossen werden können. Wie die LEDs in einer Kette auf die Koordinaten des rootview (Rechteck, das alle LEDs umfasst) abgebildet werden, bestimmt der Initialisierungsstring:
[ledtype:[leddevicename:]]numberOfLeds:[x:dx:y:dy:firstoffset:betweenoffset][XYSA][W#whitecolor]
Hinweis
Wenn der Initialisierungsstring bei der --ledchain
Kommandozeilenoption nur none
lautet, dann wird beim Start nur ein leeres ledchainarrangement angelegt; die LED-Ketten können nachträglich mit der addledchain()-Funktion hinzugefügt werden.
Die einzelnen Elemente sind
- ledtype
-
Gibt den Typ und weitere Parameter für die Ansteuerung der LEDs an. Normalerweise in der Form
chip.layout
, für genauere Kontrolle der Parameter kann es auchchip.layout.tmaxpassive.maxretries
sein.- chip
- Typ des LED-chips. Derzeit unterstützt: WS2811, WS2812, WS2813, WS2815, P9823, SK6812.
- layout
- Kanalzuordnung. Derzeit unterstützt: RGB, GRB, RGBW, GRBW, RBG, GBR, BRG, BGR, RBGW, GBRW, BRGW, BGRW. Das häufigste Layout bei WS28xx-Chips ist GRB.
- tmaxpassive
- maximale Pausenzeit zwischen zwei Bits in µS. Wenn der Wert nicht angegeben wird oder auf 0 gesetzt, wird der Standardwert des jeweiligen Chips verwendet (10..80µS). Bei unvermitteltem Flackern, besonders bei alten WS2812-Chips, kann es helfen, die Pause zu verkürzen.
- maxretries
- Maximale Anzahl von Versuchen, die Daten für ein Update zu senden. Je nach Treiber und Interrupt-Situation auf dem Zielsystem muss ein Update abgebrochen werden, bevor alle LEDs der Kette aktualisiert werden konnten. In diesem Fall wird das Update wiederholt, aber nicht öfters als maxretries (Standardwert: 3).
Aus Rückwärtskompatibilitätsgründen sind auch folgende ledtype-Werte (ohne separate chip/layout-Angabe) noch unterstützt: SK6812, P9823, WS2812, WS2813, WS2815_RGB.
- leddevicename
- Der Name des LED-Devices/Kanals, an dem die LED-Kette angeschlossen ist. Auf MT7688-OpenWrt-Plattformen mit p44-ledchain Kerneltreiber heissen die devices für die maximal 4 PWM-Ausgänge /dev/ledchain0 bis /dev/ledchain3. Auf ESP32 muss der Name des GPIO (bis zu 8 Kanäle möglich) angegeben werden: z.B. gpio22. Auf RaspberryPi ist der leddevicename optional (dann wird GPIO18 via PWM verwendet), kann aber als gpio21 (via PCM) oder gpio10 (via SPI) angegeben werden.
- numberOfLeds
- Die Anzahl LEDs in der Kette, die angesteuert werden sollen. Die Kette kann auch kürzer oder länger sein, aber wenn die Stromverbrauchsberechunng/-Begrenzung verwendet wird, muss die tatsächliche Anzahl LEDs übereinstimmen.
- x, dx, y, dy
- (optional) Diese Werte definieren den Koordinatenbereich im rootview, den diese LED-Kette abdeckt. Die X-Richtung ist die Laufrichtung der Kette, Y kommt zum Zug wenn eine Kette zu einer Fläche angeordnet wird, z.B. aufgerollt auf einen Zylinder, oder im Zickzack auf einer Fläche (s. XYSA-Flags). Fehlen diese Angaben, wird die ganze Kette in X-Richtung ab x=0 und y=0 gemappt.
- firstoffset
- (optional) Gibt die Anzahl nicht angesteuerter LEDs am Anfang der Kette an. Dies kann verwendet werden, wenn z.B. von einer Kette die ersten LEDs aus Montagegründen gar nicht sichtbar sind, und wenn von derselben LED-Kette verschiedene Abschnitte auf verschiedene Koordinaten gelegt werden sollen.
- betweenoffset
- (optional) Bei flächigen Matrix-Anordnungen, wo die LED-Kette zick-zack-förmig geführt wird, sind üblicherweise auch zwischen den Reihen eine oder mehrere LEDs nicht sichtbar. Mit diesem Parameter können diese aus der Matrix ausgenommen werden
- XYSA
-
(optional) Flags für die Anordnung:
- X invertiert die X-Koordinate
- Y invertiert die Y-Koordinate
- S vertauscht X und Y-Koordinate ("Swap")
- A ändert die X-Laufrichtung für jede Y-Reihe ("Alternate") für Zickzack-Anordnungen.
- W#whitecolor
- (optional) Bei Ketten mit weisser vierter LED kann hier die Farbe und Helligkeit, die der Weisskanal erzeugt, angegeben werden (im Webcolor-Format, also etwa
W#AA8
oderW#A5A582
). Damit kann für unterschiedliche RGBW-Ketten korrekte Farbwiedergabe erreicht werden. Ohne Angabe wird der Weisskanal als reinweiss mit doppelter Helligkeit gegenüber den R,G,B-Kanälen angenommen.
Beispiele
Eine übliche WS2813-LED-Kette mit 200 LEDs kann einfach mit Namen und Anzahl LEDs in x-Richtung ab Koordinate x=0, y=0 eingebunden werden:
WS2813.GRB:/dev/ledchain0:200
Auf dem RaspberryPi (nur ein Ausgang, leddevicename irrelevant) reicht sogar:
WS2813.GRB:200
Um die Laufrichtung umzukehren (x=0 für letzte LED in der Kette, nicht für die erste LED):
WS2813.GRB:/dev/ledchain0:200:X
Wenn die Kette aber auf einem Zylinder aufgerollt wird, so dass 20 Windungen à 10 LEDs entstehen, und dies als 10x20-Matrix angesteuert werden soll, z.B. für die Verwendung eines torch-Views (s. Anleitung hier):
WS2813.GRB:/dev/ledchain0:200:0:20:0:10
Nun aber eine etwas anspruchsvollere Anordnung:
Hier sehen wir eine Kette von 106 LEDs, die am Anfang 3 inaktive LEDs hat, nachher eine 14x6-Fläche bildet (mit jeweils zwei inaktiven LEDs in den Knicks), und am Ende noch 9 LEDs in Y-Richtung wie eine Kerze auf den Kuchen setzt.
Die 6x14-Fläche kann so beschrieben werden:
WS2813.GRB:/dev/ledchain0:106:0:14:0:6:3:2:XA
Das X-Flag ist notwendig weil die erste Zeile auf der X-Achse rückwärts geht, das A-Flag, weil die Zeilenrichtung sich jede Zeile ändert.
Um nun die 9 "Kerzen"-LEDs auf die Koordinaten x=6, y=6..14 zu legen, braucht es einen weiteren Initialisierungsstring, der sich auf einen anderen Abschnitt der gleichen LED-Kette (gleicher leddevicename) bezieht:
WS2813.GRB:/dev/ledchain0:106:6:1:6:9:97:S
Hier sind die ersten 97 LEDs inaktiv (diese werden ja von der ersten Definition mit der 6*14-Matrix abgedeckt). Weil die LEDs in diesem Abschnitt in Y-Richtung (statt X) laufen, braucht es das S-Flag.
Verwendung auf der Commandline
Bei Geräten und Installationen mit einem festen LED-Layout, z.B. wenn die LEDs untrennbarer Teil des Geräts sind, kann es sinnvoll sein dass das Layout beim Start des Geräts schon definiert ist. In diesem Fall kann ein LED-Chain Initialisierungsstring und zusätzliche Paramter für die Leistungsbeschränkung oder das Timing direkt auf der command line übergeben werden:
vdcd --ledchain WS2813.GRB:/dev/ledchain0:200 --ledpowerlimit 6000 --ledrefresh 20
Die Option --ledpowerlimit xx
beschränkt den Gesamtverbrauch (ungefähr) auf xx Milliwatt, indem alle LEDs entsprechend gedimmt werden, falls das anzuzeigendene Pixelmuster zuviel Helligkeit verlangt. Das funktioniert nur richtig, wenn sowohl der LED-Typ als auch die effektiv angeschlossene Anzahl LEDs richtig angegeben werden.
Mit der Option --ledrefresh yy
kann das minimale Refreshintervall yy in Millisekunden angegeben werden. Bei sehr langen LED-Ketten muss dieses möglicherweise höher gesetzt werden, damit es nicht flackert.
Auf diese Weise ist in P44-xx-Geräte mit LED-Ausgang üblicherweise eine Default-Anordnung für LED-Ketten vordefiniert. Zum Beispiel ist beim Raspberry-Pi-Image P44-DSB-X standardmässig an GPIO18 eine LED-Kette von 255 LEDs WS2812/13 mit GRB-Layout definiert, was z.B. für einfache Lichtstreifenbeleuchtungen ausreicht.
Falls beim Start aber noch gar keine LED-Chain definiert werden soll (sondern erst später mit p44script, s. unten), ist das möglich durch Angabe von none
:
vdcd --ledchain none
Verwendung in p44script
Sobald das LED-Layout komplexer wird kann die Definition auch über p44script erfolgen. Um die richtige Definition der Abschnitte zuerst auszuprobieren, kann das auch interaktiv und mehrfach hintereinander im REPL geschehen. Ist das korrekte Layout gefunden, kann das Setup ins mainscript kopiert werden, so dass es beim Start des Geräts jeweils ausgeführt wird. Es gibt eine Reihe von Funktionen für das Management von LED-Ketten, s. Script-Referenz.
Eine typische Einricht-Sequenz kann z.B. so aussehen:
// bestehende Definition löschen
removeledchains()
// drei Ketten, die zusammen ein Rechteck von 600*3 Pixel bilden
addledchain("WS2815.RGB:/dev/ledchain0:800:0:600:0:1")
addledchain("WS2815.RGB:/dev/ledchain1:800:0:600:1:1")
addledchain("WS2815.RGB:/dev/ledchain2:800:0:600:2:1")
// root-view in der Grösse automatisch anpassen
lrg.configure(ledchaincover()).set("fullframe",true)
// Konfiguration des rootview ausgeben zur Kontrolle
lrg.status()
Der rootview selber wird durch das Ändern der LED-Chain Anordnungen nicht verändert, auch nicht in der Grösse. Der .configure(...)-Befehl dient dazu, dass die Grösse der neuen Anordnung angepasst wird (d.h. dem Umrissrechteck entspricht, das alle definierten LED-Chain-Abschnitte umfasst)
Um dann zu sehen, ob die LEDs alle funktionieren, kann beispielsweise die Hintergrundfarbe des root view gesetzt werden:
lrg.set('bgcolor','#800') // ganzer Hintergrund rot (halbe Helligkeit)
lrg.set('bgcolor','#000') // wieder aus
View-Konfiguration
Wie oben beschrieben, gibt es verschiedene View-Typen. Allen gemeinsam ist die Konfiguration über JSON. Eine solche Konfiguration (die von einer einzelnen Eigenschaft bis zum Aufbau einer ganzen View-Hierarchie gehen kann) kann entweder von einem JSON-File eingelesen oder in p44script direkt übergeben werden (mit der configure()-Funktion):
lrg.configure({ "bgcolor":"#300" })
Hier wird eine Eigenschaft des rootview, bgcolor, gesetzt.
Mit der status()-Funktion kann der Zustand eines Views abgerufen und inspiziert werden. Da lrg den rootview repräsentiert, kann mit
lrg.status()
die gesamte aktuelle View-Hierarchie abgerufen werden.
Die schon oben verwendete Funktion set macht im Prinzip dasselbe wie configure(), aber ist praktischer für das Setzen einzelner Werte, weil der Wert selber nicht fix im JSON stehen muss, sondern berechnet werden kann:
lrg.set("bgcolor", hsv(120,0.4,0.5))
Hier wird der Farbwert (leichtes grün) mit der hsv-Funktion aus dem Farbradwinkel (hue, 120°), der Sättigung (40%) und der Helligkeit (50%) berechnet.
JSON ist aber praktisch, um kompliziertere Setups als Ganzes zu definieren und einzulesen. Zum Beispiel braucht es für eine Laufschrift einen scroller, in dem ein text eingebunden ist. Auf P44-LC-xx-Geräten gibt es eine entsprechende Konfiguration als Resource namens plan44text.json, also lässt sich ein Laufschriftbeispiel erzeugen mit:
var laufschrift = makeview("plan44text.json")
Um die Laufschrift (oder sonst einen mit makeview erzeugten View) sichtbar zu machen, muss er als Ebene zum rootview (welcher vom Typ stack ist) hinzugefügt werden:
lrg.addview(laufschrift)
Damit wird die Laufschrift sichtbar, oder zumindest der Teil davon der tatsächlich von LEDs abgedeckt ist. Die Beispielkonfiguration hat die Grösse 20 x 10, also braucht es mindestens 10 Reihen a 20 LEDs für diesen Fall.
View-Eigenschaften
Der plain View, auf dem alle anderen aufbauen, hat folgende Eigenschaften:
In Arbeit
Dieser Text ist in Arbeit und daher z.Z. unvollständig.