Jump to content

[Erledigt]Update LED Strip Bricklet: LED setzen ohne Callbacks


Loetkolben
 Share

Recommended Posts

Edit: Neue LED Bricklet Firmware 2.0.6 verfuegbar mit abschaltbarem Callback

 

Nach der freundlichen Ueberlegung hier "RGB LED Bricklet mit zusaetzlichen LEDs", dass man ggf. das Strip Bricklet erweitert habe ich mal meine Sorgen mit den Callbacks hier beschrieben und einen Loesungsvorschlag hinzugefuegt.

 

Wer sorgfaeltig liest versteht es hoffentlich, aber ansonsten beantworte ich gerne Fragen. Sollte ich Fehler gemacht haben, bitte mal eine Info schreiben.

 

Anbei (unten) das Programm mit dem man eine oder mehrere LED (mit gleicher Farbe) setzen kann.

 

Die Kurzform lautet:

 

#1 WS2812 setzen

#2 LED Wert rausschicken

#3 Redering aktivieren

Kritische Pause

#4 Rendering deaktivieren

 

Die Frameduration ist entscheidend zwischen den Punkten 3 und 4, denn:

Frage: Die Farbwerte werden erst dann auf die LED gebracht, wenn die Renderingzeit abgelaufen ist!?  >:(

 

Also als Beispiel: Rendering 1000ms=1Sekunde

 

- LED Farben uebertragen

- Rendering mit 1000ms aktivieren

- Nach 0,5 Sekunden das Rendering wieder deaktivieren.

->Es wird keine Farbe auf die LED gesetzt.

 

Man muss mindestens 1 Sekunde warten bis man das Rendering wieder deaktiviert, sonst erscheint keine Farbe auf der LED. Da muss man leider viel experimentieren bis man die Anzahl der Callbacks niedrig hinbekommt und das Programm doch noch funktioniert.

 

Das Problem um Callbacks gering zu halten:

Man muss den Renderingwert klein setzen, z.B. 10ms und dann nach ca. 500ms wieder stoppen. (Das shellscript hat ja auch seine Laufzeit) Dann hat man aber noch bis zu 50 Callbacks zu "verdauen".

 

Hilfreich (als Workaround) waere es, wenn nach aktivieren des Rendering sofort gerendert wird und dann erst die Zeit anfaengt zu laufen. Damit koennte man sofort das Rendering wieder deaktivieren und die Farben wuerden trotzdem auf die LED gebracht.

 

Statt:       ......R......R......R
Dieshier:   R......R......R......R

 

Vorschlag zur besseren Loesung:

1. Die Callbacks duerfen nicht automatisch starten, wenn man den ersten Wert gesetzt hat. (Nie automatisch) (Die alten Funktions ID 1 wird genutzt)

2. Es gibt einen API Befehl, der den uebertragenen Wert mit einem "Render-only-once" auf die LED bringt

 

oder

 

1. Einen kombinierten Befehl der die Farbwerte wie Funktions ID 1 annimmt und sofort einmalig auf die LED bringt. Dabei werden keine Callbacks ausgeloest.  ;D

Wenn man in dem neuen Befehl auch gleich noch den Chiptypen setzen kann, erspart man sich die Initialisierung, da man den Chiptyp nicht im EEPROM speichern kann.

 

Hier der Code um eine oder mehrere LED (mit gleicher Farbe) zu setzen:

#!/bin/bash

HOST=192.168.1.99
PORT=4223
tUID=ABC

LEDINDEX=00
LEDLENGTH=04
LEDCOLOR=03,03,03

LOGFILEOUTOUTFORMAT=0
RECEIVEPACKETSTORE=0
RECEIVEPACKETPATH=$(dirname $0)

while [ $# -gt 0 ]       #Solange die Anzahl der Parameter ($#) größer 0
do
  if [ "$1" = "-x" ] ; then LOGFILEOUTOUTFORMAT=1 ;fi
  if [ "$1" = "-h" ] ; then HOST=$2 ;fi
  if [ "$1" = "-p" ] ; then PORT=$2 ;fi
  if [ "$1" = "-u" ] ; then tUID=$2 ;fi
  if [ "$1" = "-r" ] ; then RECEIVEPACKETSTORE=1 ;fi
  if [ "$1" = "-R" ] ; then RECEIVEPACKETPATH=$2 ;fi

  if [ "$1" = "-li" ] ; then LEDINDEX=$2 ;fi
  if [ "$1" = "-ll" ] ; then LEDLENGTH=$2 ;fi
  if [ "$1" = "-lc" ] ; then LEDCOLOR=$2 ;fi

  shift
done

#----------------------------------------------------------------------------------
ALPHABET="123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"
BASECOUNT=`expr length $ALPHABET`
DECODED=0
MULTI=1
for ((i=`expr length $tUID - 1` ; i > -1 ; i--))
  do
    VALUE=${tUID:$i:1}
    DECODEDVALUE=`expr index $ALPHABET $VALUE`
    DECODEDVALUE=`expr $DECODEDVALUE - 1`
    DECODED=`expr $DECODED + $MULTI \* $DECODEDVALUE`
    MULTI=`expr $MULTI \* $BASECOUNT`
  done
HEXDECODEDSTR=""
HEXDECODED=`printf "%08x" $DECODED`
for i in {6,4,2,0}
  do
    DIGIT=${HEXDECODED:$i:2}
    HEXDECODEDSTR=$HEXDECODEDSTR"\x"$DIGIT
  done
#----------------------------------------------------------------------------------
#WS2812 setzen:
SENDPACKET=$HEXDECODEDSTR"\x0a\x09\x10\x00\xfc\x0a"
echo -n -e $SENDPACKET | nc -q0 $HOST $PORT
#----------------------------------------------------------------------------------

SENDPACKET=$HEXDECODEDSTR"\x3b\x01\x10\x00"
SENDPACKET=$SENDPACKET"\x$LEDINDEX\x00\x$LEDLENGTH"

# index -- uint16
# length -- uint8
# r -- uint8[16]
# g -- uint8[16]
# b -- uint8[16]

LEDCOLOR1=`echo $LEDCOLOR|cut -d "," -f1`
LEDCOLOR2=`echo $LEDCOLOR|cut -d "," -f2`
LEDCOLOR3=`echo $LEDCOLOR|cut -d "," -f3`

LEDLENGTHCOUNTDOWN=$LEDLENGTH

for i in {1..16}
    do
      if [ $LEDLENGTHCOUNTDOWN -ge 1 ]
         then
           LEDRED=$LEDRED"\x$LEDCOLOR1"
           LEDGREEN=$LEDGREEN"\x$LEDCOLOR2"
           LEDBLUE=$LEDBLUE"\x$LEDCOLOR3"
           LEDLENGTHCOUNTDOWN=`expr $LEDLENGTHCOUNTDOWN - 1`
         else
           LEDRED=$LEDRED"\x00"
           LEDGREEN=$LEDGREEN"\x00"
           LEDBLUE=$LEDBLUE"\x00"
       fi

    done

SENDPACKET=$SENDPACKET$LEDRED$LEDGREEN$LEDBLUE

# Der "Fram-Duration" Wert muss so sein:
# Bei sehr kleinem Wert kommen viele Callbacks, bis er wieder (in der nachsten Zeile) auf 0 gesetzt wird und die Callbacks aufhoeren zu "senden". Das muellt nur rum.
# Bei grossem Wert (> 1 Sekunde) muss ein Sleep 1 oder Sleep 2 folgen, sonst wird er in der naechsten Zeile wieder auf 0 gesetzt, bevor "gerendert" wurde
# und der Wert auf die LED gebracht wurde
#
#                               "...\xE8\x03" Callback = 1 Sekunde = Wert 1000 = 03E8 hex
# Ist der Wert "so halb", z.B.: "...\x70\x00", dann wird ein rendern mal ausgefuehrt und mal nicht. Warum???
# Ist der Wert                  "...\x30\x00", wird nur jedes 2. mal der RGB Wert auf die LEDs geschrieben. -> also noch kleiner machen
# Bei einem Wert                "...\x10\x00", wird jedesmal die LEDs gesetzt, "aber"/und es werden zwischen 1 und 3 Callbacks ausgeloest.
# Sofern alle anderen Programme mit den zwischenzeitlichen Callbacks klarkommen waere dies ein Workaround.
# 3.7.2016 - Den Wert auf ""...\x07\x00" gesetzt, da die Anzeige nicht ganz zuverlaessig funktioierte. Ab und zu wurde keine LED aktualisiert.
echo -n -e $SENDPACKET                              | nc -q0 $HOST $PORT # LED Wert rausschicken
echo -n -e "$HEXDECODEDSTR\x0a\x03\x10\x00\x07\x00" | nc -q0 $HOST $PORT # Redering aktivieren
echo -n -e "$HEXDECODEDSTR\x0a\x03\x10\x00\x00\x00" | nc -q0 $HOST $PORT # Rendering deaktivieren


echo "LED Start:$LEDINDEX, Laenge:$LEDLENGTH, Color:$LEDCOLOR1,$LEDCOLOR2,$LEDCOLOR3"

Link to comment
Share on other sites

  • 2 weeks later...

Raeusper, rauesper.  ;D

 

gibt es schon ein Workpackage fuer den Praktikanten?  ;)

 

 

Als erste (unabhaengige) Massnahme waere es prima wenn nach dem Aktivieren des Rendering zuerst gerendert wird und dann die Zeit ablaeuft und nicht umgekehrt, also so:

 

Statt:       ......R......R......R
Dieshier:   R......R......R......R

 

Kann man es einfach "mal" machen oder spricht was dagegen? Aus meiner Sicht muesste "nur" vor Eintritt in die Schleife das Rendering zusaetzlich am Anfang aufgerufen werden.

 

Der Rest kann ja spaeter kommen. :)

 

 

Der Loetkolben.

Link to comment
Share on other sites

Raeusper, rauesper.  ;D

 

gibt es schon ein Workpackage fuer den Praktikanten?  ;)

 

 

Als erste (unabhaengige) Massnahme waere es prima wenn nach dem Aktivieren des Rendering zuerst gerendert wird und dann die Zeit ablaeuft und nicht umgekehrt, also so:

 

Statt:       ......R......R......R
Dieshier:   R......R......R......R

 

Kann man es einfach "mal" machen oder spricht was dagegen? Aus meiner Sicht muesste "nur" vor Eintritt in die Schleife das Rendering zusaetzlich am Anfang aufgerufen werden.

 

Der Rest kann ja spaeter kommen. :)

 

 

Der Loetkolben.

 

Das geht so nicht, wir müssen die Daten ja an die LEDs übertragen. Wenn du z.B. das Blinkenlights Kit nutzt und es mit 30 FPS betreibst wird die komplette Zeit zwischen zwei Rendering aufrufen zum übertragen der Daten verwendet. D.h. wir würden die maximale Framerate mit deiner Änderung auf 15 FPS verringern.

 

Eine Option um die Callbacks auszustellen bauen wir rein, das ist kein Problem.

 

Die Änderungen von Lukas kannst du dir bereits angucken: https://github.com/Tinkerforge/led-strip-bricklet/commits/master

 

Die neue Firmware gibt es bereits nächste Woche.

Link to comment
Share on other sites

Hmm.

 

Ich habe den Eindruck, dass wir aneinander vorbeireden. Von einer Halbierung der Rate habe ich nicht gesprochen. Vielleicht verstehe ich auch den Ansatz nicht.

 

Frage: Wenn ich "set_frame_duration" von 0 auf x ms setze, was wird beim ersten mal, nach dem setzen auf x ms, zuerst ausgefuehrt?

a) die Wartezeit von x ms?

b) das Anzeigen der Farben auf die LEDs?

 

Ich denke es geht so:  a b a b

Besser faende ich :    b a b a b

 

Mal sehen wie die neue FW ist.  ;)

 

Danke

 

 

Der Loetkolben

 

Link to comment
Share on other sites

Loetkolben, du willst eigentlich einen Single-Shot Modus haben oder nicht?

 

Die API des LED Strip Bricklets ist im Moment auf Animationen ausgelegt. Sprich, du stellst über die Frame Duration die Frame Rate ein. Dann wird alle X ms der Frame auf die LEDs geschrieben und der Frame Rendered Callback ausgelöst. Auf den Callback hin hat dein Programm dann bis zu X ms Zeit den nächsten Frame an das Bricklet zu schicken.

 

Wenn ich dich aber richtig verstehe dann willst du die LEDs nicht in dem Sinne animieren. Sondern nur einmal setzen. Du brauchst also keine Frame Duration, die ist dir nur im Weg. Du willst eigentlich Frame Duration auf 0 setzen, aber dann wird nichts angezeigt.

 

Wie wäre es hier mit:

 

Setup:

- set_frame_duration(0), weil sie initial 100 ms ist

 

LEDs setzen:

- set_rgb_values(...)

- render_frame(), neue Funktion, erzwingt das sofortige Senden der Daten an die LEDs

 

Damit hättest du dann volle Kontrolle wann die Daten anzeigt werden. Das ist es was du eigentlich haben möchtest, oder?

Link to comment
Share on other sites

Loetkolben, du willst eigentlich einen Single-Shot Modus haben oder nicht?

 

Die API des LED Strip Bricklets ist im Moment auf Animationen ausgelegt. Sprich, du stellst über die Frame Duration die Frame Rate ein. Dann wird alle X ms der Frame auf die LEDs geschrieben und der Frame Rendered Callback ausgelöst. Auf den Callback hin hat dein Programm dann bis zu X ms Zeit den nächsten Frame an das Bricklet zu schicken.

Ja, so funktioniert diese Firmware zu dem Bricklet. Fuer den Einsatzzweck macht das auch Sinn.

 

Wenn ich dich aber richtig verstehe dann willst du die LEDs nicht in dem Sinne animieren. Sondern nur einmal setzen. Du brauchst also keine Frame Duration, die ist dir nur im Weg. Du willst eigentlich Frame Duration auf 0 setzen, aber dann wird nichts angezeigt.

 

Genau!  ;)  Deshalb muss ich nach dem Uebertragen der Daten via ID 1 mal kurz die FrameDuration > 0 setzen um sie dann nach einer selbst bestimmten Zeit wieder auf 0 zu setzen, damit die Werte auf die LEDs kommen. Um die in dieser Zeit anfallenden Callbacks moeglich klein zu halten, muss man ein wenig beim berechnen der Zeit tricksen. Siehe auch Ausgangsposting.

 

Um diese Zeit moeglichst kein zu halten hatte ich eine kleine Aenderung vorgeschlagen. Siehe letzer Beitrag von mir.

 

Eine grosse Loesung mit eigener ID nehme ich natuerlich gerne an.  ;D

 

 

Wie wäre es hier mit:

 

Setup:

- set_frame_duration(0), weil sie initial 100 ms ist

 

LEDs setzen:

- set_rgb_values(...)

- render_frame(), neue Funktion, erzwingt das sofortige Senden der Daten an die LEDs

 

Damit hättest du dann volle Kontrolle wann die Daten anzeigt werden. Das ist es was du eigentlich haben möchtest, oder?

 

Ja, so ungefaehr. Wobei ich noch eine Anregung geben darf:

 

Um eine LED am Bricklet aus der Ferne VON SERVER A zu setzen, muss man dann IMMER "nur" 4 Befehle absetzen:

1. WS2812 Init (Da nicht speicherbar und Bricklet Zustand unbekannt)

2. FrameDuration = 0 (Sicherheitshalber)

3. LED Werte uebermitteln

4. RenderFrame ausloesen

 

Man muss nur aufpassen, dass nicht NICHT AUCH SERVER B zur gleichen Zeit eine andere LED setzen will. Die ersten beiden Befehle sind ja egal, wenn sie wiederholt werden, aber wenn die Schritte 3 und 4 verschachtelt reinkommen gibt es ein Problem:

 

3. LED Werte uebermitteln von Server A

3. LED Werte uebermitteln von Server B

4. RenderFrame von Server A

4. RenderFrame von Server B

 

Ich Moment halte ich dies auseinander indem ich zeitversetzt arbeite.

 

Wenn ihr schon schon eine neue ID machen moechtet, dann "clont" doch einfach die ID 1 (Werte annehmen) und rendert die Werte dann sofort auf die LEDs IN EINEM ZUG.

 

Wenn man in diesem Zug auch noch in einem Byte den LED Typ mit angeben koennte waere es prima!

 

Danke.  :)

 

 

Der Loetkolben

 

 

PS: Warum das Ganze? Ich habe auf diversen Servern und NAS Buechsen ein kleines Script am laufen, dass Werte ausliest und diese Werte als Farben auf die LEDs des weit enferten LED-Strip-Bricklets sendet.

Somit muss keine weitere Programmlogik (Wertesammler / Monitoring Tools) vorhanden sein.

Z.B.:

- EMail vorhanden: LED 1 gelb, bei mir aus 10 eMails LED 1 rot.

- NAS HDD Spindown check: Schlaeft die Platte, dann LED 2 Gruen, sonst LED 2 Rot.

- HDD Fuellgrad auf dem vServer: LED 3 verschiedenen Farben.

- Kellerfenster auf: LED 10 tuerkis.

u.s.w. Man kann das auch per Textdisplay machen, aber so ist es dezenter die Infos in der LED Wohnraumbeleutung unterzubringen. ;-)

Link to comment
Share on other sites

Dein ursprüngliches Problem war doch, dass dein kleiner Linux PC nicht mit der Menge an Frame Rendered Callbacks parat kommt, wenn du das Bricklet "normal" benutzt, oder nicht? Das hast du versucht zu umgehen, in dem du die Frame Duration passend änderst in der richtigen zeitlichen Abfolge.

 

Das können wir verbessern indem wir z.B. den Frame Rendered Callback abstellbar machen oder eine Render Frame Funktion hinzufügen. Wir haben das gerade noch mal besprochen und denken, das ein abstellbarere Frame Rendered Callback die einfachere und bessere Lösung ist.

 

Dein Skript würde einfach den Chip Type setzen, den Frame Rendered Callback abstellen und die zu setzenden LEDs setzen.

 

Dein Server A/B Problem kann ich nicht nachvollziehen, weder mit abschaltbarem Frame Rendered Callback noch mit extra Render Frame Funktion.

 

Das Bricklet speichert sich welche Farben du für die LEDs gesetzt hast und rendert diese dann jede Frame Duration auf die LEDs, sofern seit dem letzten Rendern die Farben geändert wurden. Es spielt also keine Rolle in welcher Reihenfolge das Rendern und das Setzen passieren, solange nicht Server A und B versuchen die gleiche LED zu setzen.

 

Ablauf 1:

 

1. Server A setzt LED 1 auf grün -> Bricklet Speicher [grün, schwarz, ...]

2. Frame Duration abgelaufen / Server A ruft Render Frame auf -> LEDs [grün, schwarz, ...]

3. Server B setzt LED 2 auf rot -> Bricklet Speicher [grün, rot, ...]

4. Frame Duration abgelaufen / Server B ruft Render Frame auf -> LEDs [grün, rot, ...]

 

Ablauf 2:

 

1. Server A setzt LED 1 auf grün -> Bricklet Speicher [grün, schwarz, ...]

2. Server B setzt LED 2 auf rot -> Bricklet Speicher [grün, rot, ...]

3. Frame Duration abgelaufen / Server A ruft Render Frame auf -> LEDs [grün, rot, ...]

4. Frame Duration abgelaufen / Server B ruft Render Frame auf -> keine Änderung

 

Beides führt zum gleichen Ergebnis. Was übersehe ich?

Link to comment
Share on other sites

Hallo photron,

 

das zweite zuerst. Ja, du hast Recht, die Reihenfolge wie die Befehle eingehen ist egal, da die Werte ja zwischengespeichert werden. Dieser Logikschritt war mir nicht so klar. Alles ok.

 

Das Problem mit den Callbacks mit einer kleinen Zeichnung.

 

[LED-Strip---Master---USB---MiniPC]---LAN---SWITCH---LAN---PC1
                                                        +--PC2
                                                        +--PC3
                                                        |
                                                        +---Router---VPN---Internet----vServer1
                                                                               |  |  +-vServer2
                                                                               |  |
                                                                               |  +----Hamburg-PC1
                                                                               |     +-Hamburg-PC2
                                                                               |
                                                                               +-------Paderborn-NAS1
                                                                                     +-Paderborn-NAS2
                                                                                     +-Paderborn-NAS3

 

Wenn nun alle 10 Devices je eine LED "steuern" und dort ihren Status abliefern, bekommen doch alle 10 Devices die Callbacks presentiert. Da gibt es nun 2-ein-halb Probleme:

 

1. Wenn man die Callbacks nicht sauber abfaengt, dann koennen sie einem den Rechner zumuellen und zum Absturz bringen. (Endlos wartender Prozess und naechster Prozess per Cron gestartet) Das ist mir passiert und ein vServer ist kollabiert. Jetzt kann man sagen, dass ich besser programmieren sollte. OK, stimmt ein wenig. ???

2. Nur ein halbes Problem: Der Netztraffik im LAN ist hoch und so ziemlich alle Switchports sind beschaeftigt. Da kann man an den LED des Switches auch nichts mehr sinnvolles erkennen. (Flashing LAN)

3. Grosses Problem: Wie hoch ist der Traffic der Callbacks? Wenn jetzt nun, wie in meinem Beispiel, Callbacks zu 7 Devices per DSL rausgeht dann ist doch der Upload arg belegt.

 

Deshalb: Vermeide Calbacks! Immer? Nicht immer, aber dann wenn man sie nicht braucht!  ;D

 

 

Danke

 

Der Loetkolben

Link to comment
Share on other sites

Okay, hier für dich eine Version des LED Strip Bricklet Plugins 2.0.5 ohne den Frame Rendered Callback.

 

Die in Kürze erscheinende neue Version des Plugins wird wahrscheinlich erst mit Master Brick Firmware 2.4.1 funktionieren, wegen des Bugs der in 2.4.1 behoben wurde.

 

Die neue Version wird dann aber auch eine Option haben den Frame Rendered Callback abzustellen.

led-strip-bricklet-no-callback.bin

Link to comment
Share on other sites

Och.  ;D  Laeuft wie ein Doeppchen!

 

Nur zur Info: Das Timing beim Rendering (in dieser temporaeren FW 2.0.5) ist nicht veraendert worden. Man kann nur einfach eine LED setzten ohne das Callbacks loslaufen.

 

Wenn man mit der ID 3 das Rendering aus- und einschaltet werden, wie bisher, die uebertragenen Werte nur nach Ablauf der Zeit auf die LEDs transferiert.

 

Also die Brickletfirmware tut exact das was der Name verspricht!  ;D

 

DANKE!

 

 

Der Loetkolben

 

Link to comment
Share on other sites

  • 2 weeks later...

Nun gibt es die neue FW fuer das LED Strip Bricklet: 2.0.6

 

Wenn man die neue ID 16 (disable_frame_rendered_callback) setzt kann man problemlos mit der ID 1 LED Farben setzen die nach der eingestellten Zeit in "set_frame_duration" gesetzt werden, aber ohne das ein Callback gesendet wird.

 

Der Workaround mit "set_frame_duration=0" zum temporaeren unterdruecken der Callbacks ist nicht mehr notwendig.

 

Damit ist also eine "grosse" Loesung gefunden worden die ich absolut gut finde.

 

Danke

 

 

Der Loetkolben

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...