Angepinnt tpm2 - Protokoll zur Matrix-/Lichtsteuerung

    tpm2 - Protokoll zur Matrix-/Lichtsteuerung

    Hier im Forum gibt es ja in letzter Zeit mehrere LED-Matrizen, "Pixeltische", etc. und nun auch 3 Freewares zur Ansteuerung von sowas.

    Dabei benutzt jeder irgendwie ein eigenes Protokoll - OK, Artnet können alle drei Steuer-SWs, aber das ist schon wieder etwas komplizierter - daher kam der Gedanke auf, hier im Forum ein einfaches Protokoll zu entwickeln, um mit solcher SW einfach über USB/Seriell-Adapter Eigenbau-HW ansteuern zu können. Wunschvorstellung ist dabei, dass möglichst viele mitmachen, so dass der Anwender/Bastler vielseitig diese Steuer-SW mit jener HW kombinieren kann, ohne für jede Kombination wieder extra was programmieren zu müssen.

    Dieses Protokoll, das mehrere User zusammen auf die Beine gestellt haben (McGyver, MichuNeo, Pepe_1981, T64, turi und ich) soll nun hier vorgestellt werden. Da dies ein reiner Vorstellungsthread ist, bleibt er geschlossen - bei Fragen und Anregungen bitte PN, wird dann hier eingepflegt. Wenn es was neues gibt, wird es ebenfalls hier hinzugefügt.

    als Name wurde "tpm2" gewählt, das sind die Anfangsbuchstaben der Nicknames der Entwickler, t, p, m, je 2x - man kann das auch als "Transport Protocol (for) Matrices (generation) 2" lesen... ;)

    Ziele/Vorgaben
    • 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".
    • in erster Linie zur Datenübertragung gedacht, aber mit der Option, es auch für Steuerbefehle nutzen zu können
    • ausreichend für Bastler-übliche Matrixgrößen - eine Videowand mit 800x600 Pixeln muss damit nicht angesteuert werden können, da gibt es andere Möglichkeiten
    • einfach umzusetzen (die Sende- und Empfangs-SW)
    • dabei aber trotzdem recht vielseitig
    • Grundsätzlich "Fire and Forget", also der Sender muss nicht auf eine Bestätigung warten (wie bei DMX z.B. ja auch) - vorgesehen ist aber eine einfache Rückmeldung, um z.B. einen Port automatisch finden zu können, oder festzustellen, ob der Empfänger überhaupt noch "anwesend" ist
    • "Ausreichend sicher" - Es sollen ja keine Kernkraftwerke damit gesteuert werden, wenn auf einer Matrix alle 20 Minuten mal ein Pixel für einen Frame ne falsche Farbe hat, kann dies toleriert werden.


    Protokoll/Umsetzung
    Es werden Daten blockweise übertragen, also in "Frames", wie bei DMX z.B. auch - ein Frame ist z.B. ein Bild auf einer Matrix, oder eine Lichtszene o.ä.

    Die Blöcke werden - wie bei vielen solcher Protokolle - am Anfang und Ende gekennzeichnet, hier nur mit je einem Byte. Eingebettet die Nutzdaten, davor noch ein paar Steuerbytes. Es gibt keine feste Größe für einen Frame, diese wird mit übertragen. Das macht das Ganze recht flexibel, das Protokoll reicht - bei entsprechend schnellem Kanal - für eine RGB-Matrix mit 21.845 Pixeln, aber wenn man nur einen RGBW-Controller damit steuern will, dann reichen auch 9 Bytes/Frame.

    im einzelnen kommen in so einem Frame:

    Quellcode

    1. Blockstart-Byte: 0xC9
    2. Block-Art: 0xDA = Datenframe (DAta) *oder*
    3. 0xC0 = Befehl (Command) *oder*
    4. 0xAA = Angeforderte Antwort (vom Datenempfänger an den Sender)
    5. Framegöße in 16 Bit: High-Byte zuerst, dann
    6. Low-Byte
    7. Nutzdaten: 1 - 65.535 Bytes Daten oder Befehle mit Parametern
    8. Blockende-Byte: 0x36


    Das schöne ist hier eben die variable Framegröße - dadurch gibt es keinen Overhead und immer maximal mögliche Übertragungsgeschwindigkeit. Weiterhin ist damit z.B. auch eine "automatische Adressierung" per Daisy-Chain möglich: der Sender schickt z.B. 2.048 Bytes raus, der erste Empfänger nimmt sich die ersten 512 Bytes raus, setzt die Framegröße um 512 runter und schickt den Rest weiter - usw., bis nichts mehr übrig ist.

    Unabhängig davon ist es natürlich auch hier möglich (wie bei DMX auch), dass mehrere Empfänger "parallel" (an einem Bus o.ä.) die selben Daten empfangen, und jeder sich nur die rausnimmt, die für ihn interessant sind (per Adress-Einstellung).

    Die Befehle sind zum jetzigen Zeitpunkt noch nicht genormt, ebensowenig wie mögliche Antworten darauf - denkbar z.B. Einstellungen aus der Ferne zu machen, oder Automatik-Einstellung, die Steuer-SW fragt die Matrix ab, wie groß sie ist, wie verkabelt, etc. und stellt sich dann automatisch darauf ein.

    Hier ist i.M. noch nichts implementiert, wenn solche Befehle benutzt werden (z.B. auch Sachen wie "folgende Frames auf SD-Karte speichern"), sollten sie natürlich ebenfalls einheitlich sein, damit auch hier SW von User x mit HW von User y zusammenarbeitet.

    Handshake/Flow-Control
    vorgesehen ist, dass der Empfänger nach jedem korrekt empfangenen Frame (also beim Blockende-Byte) als Antwort 0xAC ("ACknowledge) zurück sendet. So weiß der Sender, dass das Gerät empfangen hat, es ist z.B. auch möglich, dass die Sende-SW auf allen möglichen Ports einen Frame rausschickt, wo dann 0xAC zurück kommt, hängt der Empfänger dran (Pixelcontroller macht das z.B. so).

    Diese Rückmeldung soll aber keine "Pflicht" sein, der Sender soll trotzdem weiter senden, auch wenn keine Rückmeldung kommt (vermeidet Verzögerungen), und in der Sende-SW sollte der (z.B. bei USB VCP-)-Port auch manuell einstellbar sein.

    Hardware/unterliegende Schicht/Baudrate etc.
    Für dieses Protokoll sind keine speziellen HW-Voraussetzungen (Übertragungsweg, Stecker, etc.) "genormt" - Es geht wie gesagt nur um die Definition der Datenblöcke, ob diese dann über RS232, TTL, USB, RS485, Ethernet oder Funk übertragen werden, spielt dafür keine Rolle - dies bleibt jedem Anwender selbst überlassen.

    Ebenso die benutzte Baudrate, diese kann ja je nach Anforderung höchst unterschiedlich sein: Für die 128x128-Pixel-Matrix mit 30 fps sind schon eher 100 Mbit (Ethernet o.ä.) angesagt, für den 16x16-Pixel-Tisch reichen 250-500 kBaud über USB, für ne Gebäudelichtsteuerung, wo hier und da mal eine Leuchte ein- oder ausgeschaltet wird, reichen auch 2.400 Baud...

    Für die Übertragung über UDP wird noch ein Port genormt.

    Das soll erst mal zur Definition reichen, wenn ich was wichtiges vergessen habe -> PN
    It's only light - but we like it!

    Da es sich in letzter Zeit häuft: Ich beantworte keine PNs mit Fragen, die sich auch im Forum beantworten lassen!
    Insbesondere solche von Mitgliedern mit 0 Beiträgen, die dann meist auch noch Sachen fragen, die bereits im entsprechenden Thread beantwortet wurden.
    Ich bin keine private Bastler-Hotline, technische Tipps etc. sollen möglichst vielen Lesern im Forum helfen!

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Pesi“ ()

    Unterstützende HW/SW, Links

    Hier werden Projekte, HW und SW aufgelistet, die dieses Protokoll unterstützen - ausserdem soll hier noch Beispielcode/Routinen veröffentlicht werden für div. Plattformen (Java, Python, Processing, Ardunio, SEDU bzw. AVR allgemein), so dass man das Protokoll leicht implementieren kann, ohne selbst von Grund auf neue Routinen programmieren zu müssen.

    Steuer-Softwares (tpm2-Sender)

    "tpm2-Hardware"/Firmware (Empfänger)
    It's only light - but we like it!

    Da es sich in letzter Zeit häuft: Ich beantworte keine PNs mit Fragen, die sich auch im Forum beantworten lassen!
    Insbesondere solche von Mitgliedern mit 0 Beiträgen, die dann meist auch noch Sachen fragen, die bereits im entsprechenden Thread beantwortet wurden.
    Ich bin keine private Bastler-Hotline, technische Tipps etc. sollen möglichst vielen Lesern im Forum helfen!

    Dieser Beitrag wurde bereits 13 mal editiert, zuletzt von „Pesi“ ()

    Hier folgen oft gestellte Fragen und Antworten - Fragen bitte in diesem Thread stellen, Fragen und Antworten von allgemeinem Interesse werden dann hier eingepflegt.
    It's only light - but we like it!

    Da es sich in letzter Zeit häuft: Ich beantworte keine PNs mit Fragen, die sich auch im Forum beantworten lassen!
    Insbesondere solche von Mitgliedern mit 0 Beiträgen, die dann meist auch noch Sachen fragen, die bereits im entsprechenden Thread beantwortet wurden.
    Ich bin keine private Bastler-Hotline, technische Tipps etc. sollen möglichst vielen Lesern im Forum helfen!

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Pesi“ ()

    Wie im ersten Post beschrieben, ist tpm2 ja nur ein Protokoll zur Datenübertragung (speziell für LED-Anwendungen), von der unterliegenden Schicht/Hardware unabhängig.

    Das gilt bei Ethernet/Wlan/UDP nicht *ganz*, da dort ja Pakete verschickt werden, die i.A. nicht größer als 1.500 Byte Nutzdaten sein sollten (Ethernet-Framegröße). Daher ist es sinnvoll, auch die tpm2-Frames bei größeren Datenmengen auf entsprechende "Portionen" aufzuteilen.

    damit diese Portionen dann wieder richtig zusammen gesetzt werden können, werden hier also die einzelnen Pakete nummeriert. Dazu wurde bei tpm2.net ein zusätzliches "Paketnummer"-Byte direkt nach den Framesize-Bytes eingeführt.

    Zur Unterscheidung "normales tpm2" oder tpm2.net wurde ebenfalls der Startcode geändert, und zwar auf 0x9C. Ein Frame sieht also so aus:

    Quellcode

    1. Blockstart-Byte: 0x9C
    2. Block-Art: 0xDA = Datenframe (DAta) *oder*
    3. 0xC0 = Befehl (Command) *oder*
    4. 0xAA = Angeforderte Antwort (vom Datenempfänger an den Sender)
    5. Framegöße in 16 Bit: High-Byte zuerst, dann
    6. Low-Byte
    7. Paketnummer: 1-255
    8. Anzahl Pakete: 1-255
    9. Nutzdaten: 1 - 65.535 Bytes Daten oder Befehle mit Parametern
    10. Blockende-Byte: 0x36

    Und wird per UDP an den Port 65506 / 0xFFE2 verschickt. Der Port für Antworten vom Empfänger an den Sender ist 65442 / 0xFFA2

    Die Empfangsroutine kann anhand dessen (Startcode 0xC9 oder 0x9C) also unterscheiden, entweder es ist "normales tpm2", dann kommen nach den Framesize-Bytes gleich die Nutzdaten, oder es ist tpm2.net, dann folgt noch das Paketnummer- und Anzahl Pakte-Byte.

    Die Paketnummer geht in "menschlicher Zählweise" von 1 bis 255, also max. 255 Pakete á ca. 1.490 Byte = 379.950 Bytes für einen per tpm2.net verschickten Frame, das wären immerhin z.B. 126.650 RGB-LEDs.

    Die Gesamtzahl der Pakete wird auch mitgeschickt, zur Kontrolle, bzw. automatischen Aufteilung im Empfänger.

    Die Aufteilung der Pakete legt i.A. der Empfänger fest - so dass es sinnvoll ist, das kann je nach Fall unterschiedlich sein. Es sollten jedoch alle Pakete die selbe Größe haben, da es sonst deutlich aufwändiger wird, diese im Empfänger wieder zusammenzusetzen

    Z.B. man hat eine RGB-Matrix mit 32x32 = 1.024 Pixel / 3.072 Bytes - das kann man dann z.B. in 3 Pakete á 1.024 Bytes aufteilen...

    Baut man eine größere Matrix aus Modulen auf, kann es z.B. auch sinnvoll sein, Module mit 16x16 = 256 RGB-Pixel zu bauen, und dann pro Modul ein Paket mit 768 Bytes zu schicken.

    Die Sende-SW muss dann die Pakete so zusammenstellen, wie der Empfänger das haben will - meist hat man dort ja sowieso eine Möglichkeit, das einzustellen bzw. die Pixel zu patchen (in Glediator z.B. ab Version 1.1 über ein GUI, In Jinx! ebenfalls).

    Eine zeitliche Nummerierung ist nicht vorgesehen, es wird davon ausgegangen, dass tpm2.net nur in kleinen Netzen ohne Router o.ä. verwendet wird, die Pakete also in der Reihenfolge ankommen, in der sie losgeschickt wurden.
    It's only light - but we like it!

    Da es sich in letzter Zeit häuft: Ich beantworte keine PNs mit Fragen, die sich auch im Forum beantworten lassen!
    Insbesondere solche von Mitgliedern mit 0 Beiträgen, die dann meist auch noch Sachen fragen, die bereits im entsprechenden Thread beantwortet wurden.
    Ich bin keine private Bastler-Hotline, technische Tipps etc. sollen möglichst vielen Lesern im Forum helfen!

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Pesi“ ()

    Offizielle TPM2 Specs

    Es wird ja schon eine ganze Weile mit den Protokollen TPM2 / TPM2.Net gearbeitet und schon einiges an Hardware unterstützt TPM2.

    Was bisher leider noch gefehlt hat ist eine offizielle Protokoll-Spezifikation.

    Wenn es um die reine Nutzdatenübertragung geht benötigt man die eigentlich auch nicht. Da TPM2 aber u.a. dafür konzipiert wurde mehr leisten zu können bedarf es da schon noch einiges an Spezifikation. Diese haben einige Mitglieder hier im Forum (u.a. Pesi und meine Wenigkeit) lange "ausgeknobelt".

    Anbei nun die daraus resultierten finalen SPECs des TPM2-Protokolls mit der Bitte sich an diese Specs zu halten wenn man Software / Hardware entwickelt die sich "TPM2-kompatibel" nennt.

    @Pesi: Kannst Du das PDF oder den Link auf diesen Beitrag oder auch den Beitrag selbst in den Eigentlichen TPM2-Thread kopieren / verschieben. Der ist "closed" sodass ich hier was neues aufmachen musste.

    Wer sich aus den Specs eine .h-Datei für ein Projekt machen möchte dem hänge ich hier mal meine Version an, das spart sicher einiges an Zeit beim definieren der ganzen Konstanten.

    Quellcode

    1. //Port for TMM2.Net communication
    2. #define TPM2_NET_PORT 50200
    3. //Header Bytes
    4. #define TPM2_SER_BLOCK_START_BYTE 0xC9 // 'NEW BLOCK BYTE' for TPM2.Serial
    5. #define TPM2_NET_BLOCK_START_BYTE 0x9C // 'NEW BLOCK BYTE' for TPM2.Net
    6. #define TPM2_BLOCK_TYPE_DATA 0xDA // Block is a 'DATA BLOCK'
    7. #define TPM2_BLOCK_TYPE_CMD 0xC0 // Block is a 'COMMAND BLOCK'
    8. #define TPM2_BLOCK_TYPE_ACK 0xAC // Block is an 'ANSWER without DATA' (Acknowledge)
    9. #define TPM2_BLOCK_TYPE_ACK_DATA 0xAD // Block is an 'ANSWER containing DATA'
    10. #define TPM2_BLOCK_END_BYTE 0x36 // Last Byte of a TMP2 Block
    11. //Header & footer size
    12. #define TPM2_SER_HEADER_SIZE 0x04 // Header size of a TMP.Serial packet
    13. #define TPM2_NET_HEADER_SIZE 0x06 // Header size of a TMP.Serial packet
    14. #define TPM2_FOOTER_SIZE 0x01 // The packet ends with just one byte (TPM2_BLOCK_END_BYTE)
    15. //Positions within a TPM2 Block
    16. #define TPM2_BLOCK_START_BYTE_P 0x00 // 'NEW BLOCK BYTE' is allways on 1st position
    17. #define TPM2_BLOCK_TYPE_P 0x01 // Block type allways on 2nd position
    18. #define TPM2_FRAME_SIZE_HIGH_P 0x02 // Frame size allways on 3rd and 4th position
    19. #define TPM2_FRAME_SIZE_LOW_P 0x03
    20. #define TPM2_NET_PACKET_NUM_P 0x04 // A TPM2.Net block has packet number on 5th position
    21. #define TPM2_NET_PACKET_TOTAL_PACK_NUM_P 0x05 // The total nummber of packets to be transfered for one frame
    22. #define TPM2_SER_DATA_P 0x04 // Data starts at 5th position
    23. #define TPM2_NET_DATA_P 0x06 // Data starts at 7th position
    24. #define TPM2_SER_CMD_CONTROL_P 0x04 // If block is a 'COMMAND BLOCK' than 5th position contains the 'COMMAND CONTROL BYTE' (described below)
    25. #define TPM2_NET_CMD_CONTROL_P 0x06 // If block is a 'COMMAND BLOCK' than 7th position contains the 'COMMAND CONTROL BYTE' (described below)
    26. #define TPM2_SER_CMD_TYPE_P 0x05 // If block is a 'COMMAND BLOCK' than the actual command type is on 6th position
    27. #define TPM2_NET_CMD_TYPE_P 0x07 // If block is a 'COMMAND BLOCK' than the actual command type is on 8th position
    28. #define TPM2_SER_SP_CMD_P 0x06 // If command type is 'SPECIAL COMMAND' than the actual special command is on 7th position
    29. #define TPM2_NET_SP_CMD_P 0x08 // If command type is 'SPECIAL COMMAND' than the actual special command is on 9th position
    30. #define TPM2_SER_CMD_DATA_P 0x06 // If block is a 'COMMAND BLOCK' than command data starts at 7th position
    31. #define TPM2_NET_CMD_DATA_P 0x08 // If block is a 'COMMAND BLOCK' than command data starts at 9th position
    32. #define TPM2_SER_SP_CMD_DATA_P 0x07 // If block is a 'COMMAND BLOCK' and command type is 'SPECIAL COMMAND' than command data starts at 8th position
    33. #define TPM2_NET_SP_CMD_DATA_P 0x09 // If block is a 'COMMAND BLOCK' and command type is 'SPECIAL COMMAND' than command data starts at 10th position
    34. //Command type definitions
    35. #define TPM2_CMD_BLOCK_CONFIG 0x00 // configuration data as one block
    36. #define TPM2_CMD_STORE_CONFIG 0x01 // save actual configuration
    37. #define TPM2_CMD_LOAD_CONFIG 0x02 // load a specific configuration
    38. #define TPM2_CMD_INIT_SD_TRANSFER 0x03 // prepare SD card for receiving a file
    39. #define TPM2_CMD_PLAY_SD 0x04 // play data from SD card
    40. #define TPM2_CMD_STOP_SD 0x05 // stop playing data from SD card
    41. #define TPM2_CMD_RW_SD 0x06 // read/write one frame from/to SD card
    42. #define TPM2_CMD_BRIGHTNESS 0x0A // master brightness
    43. #define TPM2_CMD_GAMMA 0x0B // gamma correction
    44. #define TPM2_CMD_SPEED 0x0C // master speed
    45. #define TPM2_CMD_PROGRAMM 0x0D // program number
    46. #define TPM2_CMD_START_COLOR 0x0E // start color
    47. #define TPM2_CMD_TIMEOUT 0x0F // time out value
    48. #define TPM2_CMD_NUM_PIXEL 0x10 // number of pixels
    49. #define TPM2_CMD_REPETITIONS 0x11 // number of repetitions
    50. #define TPM2_CMD_START_ADRESS 0x12 // start address
    51. #define TPM2_CMD_PING 0x20 // 'Ping' --> Device has to replay with an ACK
    52. #define TPM2_CMD_SPECIAL_CMD 0xFF // SPECIAL COMMAND --> In this case user can define any own command on position 'TPM2_SP_CMD_P'
    53. //Reply definitions
    54. #define TPM2_ACK_OK 0x00 // Command was acknowledged and data received fine
    55. #define TPM2_ACK_CORRUPT_DATA 0x01 // Command was acknowledged but data was bad
    56. #define TPM2_ACK_UNNOKWN_COMMAD 0x02 // Command unknown
    57. #define TPM2_ACK_FRAME_ERROR 0x03 // Frame Error
    58. #define TPM2_ACK_FRAME_OVER_SIZE 0x04 // Frame too big for device's buffer
    59. //Bit positions within the 'COMMAND CONTROL BYTE'
    60. #define TPM2_CMD_DIR_BIT 0x07 // The MSB of the 'CONTROL BYTE' determines whether the command is a READ command or a WRITE command
    61. #define TPM2_CMD_DIR_READ 0x00 // For a READ command it is 0
    62. #define TPM2_CMD_DIR_WRITE 0x01 // For a WRITE command it is 1
    63. #define TPM2_CMD_ACK_BIT 0x06 // The MSB-1 of the 'CONTROL BYTE' determines whether the command expects an answer or not


    Die Formatierung ist natürlich (mal wieder) vollkommen "anders" wenn man sie hier im Post als Code einfügt ...

    LG,

    Pepe

    TPM2_Specs_V1.0_2013_ger.pdf