Hallo Zusammen,
ich möchte auf einem Orange Pi Zero ein SW schreiben die Daten per UDP
entgegennimmt (Helligkeitsstufen von LEDs) und diese entsprechend auf an
die LED-Treiber weiterleitet. Jetzt dachte ich mir, dass es am besten
wäre das Empfangen von neuen Helligkeitsstufen über UDP entkoppelt ist
vom schreiben der Helligkeitsstufen auf dem LED-Treiber.
Dafür wollte ich Threads benutzen. Also vllt. in einem Thread das
empfangen der Daten über UDP und in einem anderen die Ausgabe auf den
LED-Treiber.
Macht das Sinn? Wie tauscht man die Daten am besten zwischen den Threads
aus? FIFO-Buffer?
> Macht das Sinn?
Kommt auf die Rahmenbedingungen an. Kann Overkill sein, kann aber auch
nötig sein.
> Wie tauscht man die Daten am besten zwischen den Threads aus? FIFO-Buffer?
Zum Bleistift, ja. Aber auf Fredsicherheit achten.
Patrick L. schrieb:> Jetzt dachte ich mir, dass es am besten> wäre das Empfangen von neuen Helligkeitsstufen über UDP entkoppelt ist> vom schreiben der Helligkeitsstufen auf dem LED-Treiber.
Warum?
Musst du neue Helligkeits-Stufen schreiben, wenn nichts per UDP gekommen
ist?
Klar, wenn du am Netzwerk furchtbar viel Jitter hast (Wlan mit
Auslastung am Anschlag), und trotzdem Ruckelfrei animieren willst, kann
das Sinn machen. Aber: Es macht die Sache gleich um Größenordnungen
komplizierter.
Beispiel in LUA auf einem NodeMCU: Per UDP (von einem Hyperion-Server)
RGB-Daten empfangen und auf einen RGB-LED-Stripe schieben:
1
ws2812.init()
2
srv=net.createServer(net.UDP)
3
srv:listen(12345)
4
srv:on("receive", function(sck, pl)
5
ws2812.write(pl)
6
end)
Ohne Threads und Multitasking, auf einer CPU die vielleicht grad mal 1%
der Performance des OrangePI bringt, in einer Scriptsprache, über
Wlan.... Problemlos.
Patrick L. schrieb:> Wie tauscht man die Daten am besten zwischen den Threads aus?
Des Sinn von Threads (statt verschiedenen Prozessen) ist es doch
gerade, dass sie das Datensegment des Prozesses gemeinsam nutzen
können. Daher kannst du, wenn es schon Threads sein müssen, auch
einfache Variablen nehmen. Vermutlich musst du dir bei dieser
Anwendung noch nicht einmal großartig Gedanken um eine Synchronisation
per Mutex oder dergleichen zu machen.
Wenn du einen FIFO nimmst, dann kannst du auch gleich getrennte
Prozesse benutzen. Hat unter unixoiden Betriebssystemen kaum
Mehr„kosten“ im Vergleich zu Threads; läuft oft intern auf einem
gemeinsamen Interface zusammen (weiß gerade nicht, ob das bei Linux
auch so ist).
Patrick L. schrieb:> Dafür wollte ich Threads benutzen. Also vllt. in einem Thread das> empfangen der Daten über UDP und in einem anderen die Ausgabe auf den> LED-Treiber.>> Macht das Sinn?
Jein. Eigentlich sollte ein einfacher Server reichen, der nicht
concurrent (Threads, Prozesse) arbeitet. Denn ein paar LEDs steuern
sollte nicht so lange dauern, dass man UDP-Pakete verliert.
> Wie tauscht man die Daten am besten zwischen den Threads> aus? FIFO-Buffer?
So in etwa. Bei zwei Threads ist das als das Producer-Consumer Problem
bekannt. Bei mehreren Threads zur Abarbeitung als Reader-Writer Problem.
Die Lösungen sind wohl bekannt, eigentlich sind das keine großen
Probleme. Kannst du dir alles zusammengoogeln.
Ok, danke für die Rückmeldungen. Ich höre raus, dass es wohl keinen Sinn
macht Threads zu benutzen. Dann werde ich das nochmal überdenken.
Ich habe halt 12x TLC5940 an denen 192 LEDs hängen und ich hatte jetzt
intuitiv gedacht, dass die Ausgabe auf allen LEDs länger dauern wird als
das neue UDP Pakete reinkommen. Vllt. probiere ich es erstmal ohne
Threads aus!
Patrick L. schrieb:> ... und ich hatte jetzt intuitiv gedacht, dass die Ausgabe auf allen> LEDs länger dauern wird als das neue UDP Pakete reinkommen ...
Tja, dann hättest Du wohl ein anderes Problem oder wie würdest Du das
dann machen wenn Du Threads hast? Nur weil Du Threads hast verlierst Du
vielleicht keine Pakete - aber ausgeben kannst Du sie eben auch nicht
alle also was solls, wenn Pakete verloren gehen, kannst sie auch mit
Threads nicht bearbeiten!!
Sowas macht man besser mit Event Queues bzw. select oder ähnlichem. Bei
dir wird aber auch das nicht notwendig sein, da du sonst nichts nebenher
machen möchtest. Sockets haben einen Puffer den man konfigurieren kann,
da werden dann keine Pakete verworfen. Wenn du das auch für den Sender
transparent machen möchtest nimm besser TCP, das ist einfacher zu
handhaben.
Wie wäre es mit dem Ansatz, das Empfangen der UDP-Datenpakete einerseits
und das Schreiben der Helligkeitsstufen auf den LED-Treiber andererseits
in zwei komplett getrennte Programme zu platzieren, und diese beiden
Programme dann einfach per Pipe zu verbinden?
Also z.B. ein Programm "set_leds", das nichts weiter tut, als
Eingabezeilen von STDIN einzulesen (z.B. als durch Kommata getrennte
Liste von Float-Werten, die die Helligkeit beschreiben), und diese
Helligkeitswerte dann an die LED-Treiber zu übertragen.
Und ein anderes Programm, das UDP-Pakete auf einem bestimmten Port
empfängt und einfach 1:1 auf STDOUT ausgibt.
Wobei man dieses Programm nicht mal extra schreiben muss, dafür würde
der Befehl genügen:
1
netcat -u -l <Port-Nummer>
Um die LEDs dann bspw. wie von Dir gewünscht per UDP-Paket an die
Port-Nummer 50123 zu steuern, würde man folgendes Kommando eingeben
(oder in ein eigenes Shell-Script packen):
1
netcat -u -l 50123 | set_leds
...und falls Du zu dem Schluss kommst, dass TCP doch sinnvoller als UDP
wäre, dann änderst Du das einfach in:
1
netcat -l 50123 | set_leds
Auf diese Weise hast Du das Setzen der LEDs komplett vom UDP-Empfang
getrennt, beides läuft in zwei unterschiedlichen Prozessen.
Und falls Du beispielsweise (z.B. zur Fehlersuche oder so) mal mitloggen
möchtest, was empfangen wird, dann setzt Du einfach noch ein "tee"
dazwischen, ohne irgendetwas am Programmcode ändern zu müssen:
1
netcat -u -l 50123 | tee led_log.txt | set_leds
Meiner Meinung nach wäre das der einfachste, unixoide Ansatz: getrennte
Prozesse für UDP-Empfang und Setzen der LEDs, obwohl Du Dich gar nicht
um Prozesse, Threads etc. kümmern musst, und den gesamten UDP-Empfang
brauchst Du gar nicht erst zu programmieren.
Jörg W. schrieb:> Wenn du einen FIFO nimmst, dann kannst du auch gleich getrennte> Prozesse benutzen.
Dann kann man sich einen der Prozesse aber gleich ganz sparen. Ob der
LED-Prozess nun aus einem Socket oder einem FIFO liest, ist Jacke wie
Hose.
Michael S. schrieb:> Tja, dann hättest Du wohl ein anderes Problem oder wie würdest Du das> dann machen wenn Du Threads hast? Nur weil Du Threads hast verlierst Du> vielleicht keine Pakete - aber ausgeben kannst Du sie eben auch nicht> alle also was solls, wenn Pakete verloren gehen, kannst sie auch mit> Threads nicht bearbeiten!!
Ja, das stimmt da hatte ich auch schon dran gedacht.. ;-)
Sven schrieb:> Wenn du das auch für den Sender> transparent machen möchtest nimm besser TCP, das ist einfacher zu> handhaben.
Das geht leider nicht. Zur UDP Kommunikation nehme ich das tpm2.net
Protokoll und die SW Jinx! Die gibt die Daten nur per UDP raus.
Joachim S. schrieb:> Wie wäre es mit dem Ansatz, das Empfangen der UDP-Datenpakete einerseits> und das Schreiben der Helligkeitsstufen auf den LED-Treiber andererseits> in zwei komplett getrennte Programme zu platzieren, und diese beiden> Programme dann einfach per Pipe zu verbinden?
Die Idee finde ich eigenlich sehr charmant. Ich habe noch nie Programme
geschrieben die sachen raus-pipen. Aber da kann man sich ja einlesen.
Darum einen UDP-Server zu schreiben komme ich nicht herum, weil ich ja
das tpm2.net Protokoll interpretieren muss. Das ist aber nicht so
schwierig. Die Implementierung habe ich schon fertig ;-)
Patrick L. schrieb:> Darum einen UDP-Server zu schreiben komme ich nicht herum, weil ich ja> das tpm2.net Protokoll interpretieren muss. Das ist aber nicht so> schwierig. Die Implementierung habe ich schon fertig ;-)
Von "tpm2.net" hatte ich bis eben noch nie gehört. Eine kurzes googlen
machte etwas schlauer - dabei fand ich allerdings folgende Aussage:
> Das Protokoll sollte nicht auf eine spezielle HW oder Übertragungskanal> angewiesen sein - "genormt" wird also nur, wie der Sender die Daten> verschicken muss, so dass sie der Empfänger "versteht".
Sofern ich da nicht gerade einen Denkfehler habe, ist tpm2.net also doch
gar nicht auf UDP beschränkt, sondern der Übertragungskanal soll ja
vielmehr egal sein, und tpm2.net quasi nur das Datenformat definieren?
Dementsprechend wäre für mich der naheliegende Ansatz, dass das (weiter
oben von mir beispielhaft "set_leds" genannte) Programm einen
tmp2.net-Datenstrom als Eingabe von STDIN erwartet.
Dann wäre der Übertragungskanal wie bei tpm2.net vorgesehen völlig
transparent, das von mir oben angesprochene pipen der Ausgabe von
"netcat -u -l 50123" für UDP als Übertragungskanal sollte genauso
funktioneren wie z.B. eine Überträgung z.B. per UART oder das Abspielen
einer vordefinierten Animation aus einer Datei, die tmp2.net-Daten
enthält.