<!-- Source: https://docs.captron.com/de/trms/seh/modbus-interface -->

# Modbus Interface

## Übersicht

Dieses Dokument beschreibt die Modbus-TCP/IP-Schnittstelle zur Steuerung der LED-Streifen und SMC-Taster von SEHx01/SEH11-Geräten. Die Modbus-Schnittstelle arbeitet parallel zur bestehenden MQTT-Steuerung. Beide Schnittstellen können gleichzeitig verwendet werden.

## Physikalische Schnittstelle

<table>
  <thead>
    <tr>
      <th>Parameter</th>
      <th>Wert</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Protokoll</td>
      <td>Modbus</td>
    </tr>
    <tr>
      <td>Schnittstelle</td>
      <td>TCP/IP</td>
    </tr>
    <tr>
      <td>Geräte-ID</td>
      <td>1 (konfigurierbar)</td>
    </tr>
  </tbody>
</table>

## Register-Übersicht

### Holding Registers (Function 03/06/16)

Der Controller unterstützt 5 LED-Streifen (LED_STRIP_1 bis LED_STRIP_5). Jeder Streifen kann mehrere Segmente enthalten. Pro Streifen sind bis zu 8 Segmente konfigurierbar. Jedes Segment unterstützt bis zu 3 Farben.

#### Globale Register

<table>
  <thead>
    <tr>
      <th>Register</th>
      <th>Beschreibung</th>
      <th>Datentyp</th>
      <th>Bereich</th>
      <th>R/W</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>Modbus Device ID</td>
      <td>UINT16</td>
      <td>1-247</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>2</td>
      <td>Apply/Trigger</td>
      <td>UINT16</td>
      <td>0/1</td>
      <td>W</td>
    </tr>
    <tr>
      <td>3</td>
      <td>Reserved</td>
      <td>-</td>
      <td>-</td>
      <td>-</td>
    </tr>
    <tr>
      <td>4</td>
      <td>Reserved</td>
      <td>-</td>
      <td>-</td>
      <td>-</td>
    </tr>
  </tbody>
</table>

**Apply/Trigger (Register 2):** Nachdem alle gewünschten Streifen-/Segment-Register beschrieben wurden, muss Register 2 auf `1` gesetzt werden, um die Änderungen zu übernehmen. Das Register wird nach der Anwendung der Änderungen automatisch wieder auf `0` zurückgesetzt.

---

#### Streifen-Register

Jeder Streifen belegt einen Block von 100 Registern. Die Basisadresse für Streifen N (N = 1..5) berechnet sich wie folgt:

**Basisadresse = 100 \* N**

<table>
  <thead>
    <tr>
      <th>Offset</th>
      <th>Beschreibung</th>
      <th>Datentyp</th>
      <th>Bereich</th>
      <th>R/W</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>Active</td>
      <td>UINT16</td>
      <td>0/1</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>1</td>
      <td>Segment Count</td>
      <td>UINT16</td>
      <td>0-8</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>2-9</td>
      <td>Reserved</td>
      <td>-</td>
      <td>-</td>
      <td>-</td>
    </tr>
  </tbody>
</table>

#### Segment-Register

Innerhalb eines Streifen-Blocks belegt jedes Segment 11 Register. Die Basisadresse für Segment M (M = 0..7) innerhalb von Streifen N lautet:

**Segment-Basisadresse = (100 _ N) + 10 + (M _ 11)**

<table>
  <thead>
    <tr>
      <th>Offset</th>
      <th>Beschreibung</th>
      <th>Datentyp</th>
      <th>Bereich</th>
      <th>R/W</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>StartLED</td>
      <td>UINT16</td>
      <td>0-311</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>1</td>
      <td>StopLED</td>
      <td>UINT16</td>
      <td>0-311</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>2</td>
      <td>Effect</td>
      <td>UINT16</td>
      <td>siehe Effekte</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>3</td>
      <td>Speed</td>
      <td>UINT16</td>
      <td>1-246</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>4</td>
      <td>Color 1 - Red</td>
      <td>UINT16</td>
      <td>0-255</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>5</td>
      <td>Color 1 - Green</td>
      <td>UINT16</td>
      <td>0-255</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>6</td>
      <td>Color 1 - Blue</td>
      <td>UINT16</td>
      <td>0-255</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>7</td>
      <td>Color 2 - Red</td>
      <td>UINT16</td>
      <td>0-255</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>8</td>
      <td>Color 2 - Green</td>
      <td>UINT16</td>
      <td>0-255</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>9</td>
      <td>Color 2 - Blue</td>
      <td>UINT16</td>
      <td>0-255</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>10</td>
      <td>Color 3 - Red</td>
      <td>UINT16</td>
      <td>0-255</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>11</td>
      <td>Color 3 - Green</td>
      <td>UINT16</td>
      <td>0-255</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>12</td>
      <td>Color 3 - Blue</td>
      <td>UINT16</td>
      <td>0-255</td>
      <td>R/W</td>
    </tr>
  </tbody>
</table>

---

### SMC-Taster-Steuerung (Holding Registers)

Zusätzlich zu den LED-Streifen können auch die angeschlossenen SMC-Taster über Modbus gesteuert werden. Dies entspricht der bestehenden MQTT-Steuerung (Topic `/Set/Data/Smc`). Beide Wege erzeugen intern denselben SMC-Befehl und können parallel verwendet werden.

Der SMC-Block belegt einen eigenen Registerbereich ab Basisadresse **1000** und besitzt einen **eigenen Apply-Trigger**, unabhängig vom LED-Apply (Register 2).

<table>
  <thead>
    <tr>
      <th>Register</th>
      <th>Beschreibung</th>
      <th>Datentyp</th>
      <th>Bereich</th>
      <th>R/W</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1000</td>
      <td>SMC Address</td>
      <td>UINT16</td>
      <td>siehe unten</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>1001</td>
      <td>SMC CommandType</td>
      <td>UINT16</td>
      <td>0-2 (siehe unten)</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>1002</td>
      <td>SMC Offset</td>
      <td>UINT16</td>
      <td>0-5 (siehe unten)</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>1003</td>
      <td>Button Mode</td>
      <td>UINT16</td>
      <td>0-2</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>1004</td>
      <td>Quantity</td>
      <td>UINT16</td>
      <td>0-65535</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>1005</td>
      <td>Color</td>
      <td>UINT16</td>
      <td>0-7</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>1006</td>
      <td>Ring Effect</td>
      <td>UINT16</td>
      <td>siehe Ring-Effekt</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>1007</td>
      <td>Text char 0</td>
      <td>UINT16</td>
      <td>0-255 (ASCII)</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>1008</td>
      <td>Text char 1</td>
      <td>UINT16</td>
      <td>0-255 (ASCII)</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>1009</td>
      <td>Text char 2</td>
      <td>UINT16</td>
      <td>0-255 (ASCII)</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>1010</td>
      <td>Text char 3</td>
      <td>UINT16</td>
      <td>0-255 (ASCII)</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>1011</td>
      <td>Special Command</td>
      <td>UINT16</td>
      <td>siehe Spezialbefehl</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>1012</td>
      <td>Button Rotation</td>
      <td>UINT16</td>
      <td>0-1</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>1013</td>
      <td>Assign Address</td>
      <td>UINT16</td>
      <td>siehe unten</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>1014-1019</td>
      <td>Reserved</td>
      <td>-</td>
      <td>-</td>
      <td>-</td>
    </tr>
    <tr>
      <td>1020</td>
      <td>SMC Apply/Trigger</td>
      <td>UINT16</td>
      <td>0/1</td>
      <td>W</td>
    </tr>
    <tr>
      <td>1100-1228</td>
      <td>Hub Polling Channels</td>
      <td>UINT16</td>
      <td>siehe unten</td>
      <td>R/W</td>
    </tr>
  </tbody>
</table>

Analog zum MQTT-Protokoll werden `CommandType` (Register 1001) und `Offset` (Register 1002) als zwei getrennte Felder geführt. Gemeinsam legen sie fest, welche Payload-Register beim Apply ausgewertet werden. Die Hub Polling Channels belegen einen eigenen Block (Register 1100-1228), der weiter unten beschrieben wird.

**SMC Address (Register 1000):** Zieladresse des Befehls.

<table>
  <thead>
    <tr>
      <th>Wert</th>
      <th>Bedeutung</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>HUB (SEH selbst)</td>
    </tr>
    <tr>
      <td>0xFFFF</td>
      <td>ALL_SENSORS (Broadcast an alle Taster)</td>
    </tr>
    <tr>
      <td>andere</td>
      <td>Spezifische Taster-Adresse (Geräteadresse)</td>
    </tr>
  </tbody>
</table>

**SMC CommandType (Register 1001):** Spiegelt das Feld `CommandType` des MQTT-Protokolls.

<table>
  <thead>
    <tr>
      <th>Wert</th>
      <th>CommandType</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>- (inaktiv, nichts gesendet)</td>
    </tr>
    <tr>
      <td>1</td>
      <td>SET_PARAMETER</td>
    </tr>
    <tr>
      <td>2</td>
      <td>SET_STATUS</td>
    </tr>
  </tbody>
</table>

_Das SMC-Protokoll definiert zusätzlich GET_STATUS, SET_UPDATE, GET_INTERNAL_STATUS und SET_INTERNAL_STATUS. Diese sind noch nicht abgebildet und können später ergänzt werden._

**SMC Offset (Register 1002):** Spiegelt das Feld `Offset` des MQTT-Protokolls.

<table>
  <thead>
    <tr>
      <th>Wert</th>
      <th>Offset</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>(keine / leer)</td>
    </tr>
    <tr>
      <td>1</td>
      <td>PRE_BUTTON_MODE</td>
    </tr>
    <tr>
      <td>2</td>
      <td>POST_BUTTON_MODE</td>
    </tr>
    <tr>
      <td>3</td>
      <td>BUTTON_ROTATE</td>
    </tr>
    <tr>
      <td>4</td>
      <td>SPECIAL_COMMANDS</td>
    </tr>
    <tr>
      <td>5</td>
      <td>ADDRESS_TO_BE_ASSIGNED</td>
    </tr>
  </tbody>
</table>

**Gültige Kombinationen:** Beim Apply bestimmen `CommandType` und `Offset` gemeinsam, welche Payload-Register ausgewertet werden.

<table>
  <thead>
    <tr>
      <th>CommandType</th>
      <th>Offset</th>
      <th>Adresse</th>
      <th>Payload-Register</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>SET_PARAMETER</td>
      <td>PRE_BUTTON_MODE</td>
      <td>beliebig</td>
      <td>Button Mode, Quantity, Color, Ring Effect, Text (1003-1010)</td>
    </tr>
    <tr>
      <td>SET_PARAMETER</td>
      <td>POST_BUTTON_MODE</td>
      <td>beliebig</td>
      <td>Button Mode, Quantity, Color, Ring Effect, Text (1003-1010)</td>
    </tr>
    <tr>
      <td>SET_PARAMETER</td>
      <td>BUTTON_ROTATE</td>
      <td>beliebig</td>
      <td>Button Rotation (1012)</td>
    </tr>
    <tr>
      <td>SET_PARAMETER</td>
      <td>(keine)</td>
      <td>HUB (0)</td>
      <td>Hub Polling Channels (1100-1228)</td>
    </tr>
    <tr>
      <td>SET_STATUS</td>
      <td>SPECIAL_COMMANDS</td>
      <td>beliebig</td>
      <td>Special Command (1011)</td>
    </tr>
    <tr>
      <td>SET_STATUS</td>
      <td>ADDRESS_TO_BE_ASSIGNED</td>
      <td>beliebig</td>
      <td>Assign Address (1013)</td>
    </tr>
  </tbody>
</table>

**Button Mode (Register 1003):**

<table>
  <thead>
    <tr>
      <th>Wert</th>
      <th>Bedeutung</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>DISABLED</td>
    </tr>
    <tr>
      <td>1</td>
      <td>ENABLED</td>
    </tr>
    <tr>
      <td>2</td>
      <td>LONGPRESS_ENABLED</td>
    </tr>
  </tbody>
</table>

**Color (Register 1005):**

<table>
  <thead>
    <tr>
      <th>Wert</th>
      <th>Bedeutung</th>
      <th>Wert</th>
      <th>Bedeutung</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>COLOFF</td>
      <td>4</td>
      <td>COLYELLOW</td>
    </tr>
    <tr>
      <td>1</td>
      <td>COLRED</td>
      <td>5</td>
      <td>COLMAGENTA</td>
    </tr>
    <tr>
      <td>2</td>
      <td>COLGREEN</td>
      <td>6</td>
      <td>COLCYAN</td>
    </tr>
    <tr>
      <td>3</td>
      <td>COLBLUE</td>
      <td>7</td>
      <td>COLWHITE</td>
    </tr>
  </tbody>
</table>

**Ring Effect (Register 1006):**

<table>
  <thead>
    <tr>
      <th>Wert</th>
      <th>Bedeutung</th>
      <th>Wert</th>
      <th>Bedeutung</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>SOLID_RING</td>
      <td>94</td>
      <td>ANIMATED_ARROW_UP</td>
    </tr>
    <tr>
      <td>1</td>
      <td>FLASH_RING</td>
      <td>95</td>
      <td>ANIMATED_ARROW_DOWN</td>
    </tr>
    <tr>
      <td>2</td>
      <td>CONFIRM</td>
      <td>96</td>
      <td>ANIMATED_ARROW_LEFT</td>
    </tr>
    <tr>
      <td>84</td>
      <td>POINT_ANIMATED_CLOCK</td>
      <td>97</td>
      <td>ANIMATED_ARROW_RIGHT</td>
    </tr>
    <tr>
      <td>85</td>
      <td>CIRCLE_ANIMATED_CLOCK</td>
      <td>102</td>
      <td>SOLID_ARROW_UP_LEFT</td>
    </tr>
    <tr>
      <td>86</td>
      <td>SOLID_ARROW_UP</td>
      <td>103</td>
      <td>SOLID_ARROW_UP_RIGHT</td>
    </tr>
    <tr>
      <td>87</td>
      <td>SOLID_ARROW_DOWN</td>
      <td>104</td>
      <td>SOLID_ARROW_DOWN_LEFT</td>
    </tr>
    <tr>
      <td>88</td>
      <td>SOLID_ARROW_LEFT</td>
      <td>105</td>
      <td>SOLID_ARROW_DOWN_RIGHT</td>
    </tr>
    <tr>
      <td>89</td>
      <td>SOLID_ARROW_RIGHT</td>
      <td>106</td>
      <td>FLASH_ARROW_UP_LEFT</td>
    </tr>
    <tr>
      <td>90</td>
      <td>FLASH_ARROW_UP</td>
      <td>107</td>
      <td>FLASH_ARROW_UP_RIGHT</td>
    </tr>
    <tr>
      <td>91</td>
      <td>FLASH_ARROW_DOWN</td>
      <td>108</td>
      <td>FLASH_ARROW_DOWN_LEFT</td>
    </tr>
    <tr>
      <td>92</td>
      <td>FLASH_ARROW_LEFT</td>
      <td>109</td>
      <td>FLASH_ARROW_DOWN_RIGHT</td>
    </tr>
    <tr>
      <td>93</td>
      <td>FLASH_ARROW_RIGHT</td>
      <td></td>
      <td></td>
    </tr>
  </tbody>
</table>

**Special Command (Register 1011):** Nur wirksam, wenn CommandType = SET_STATUS und Offset = SPECIAL_COMMANDS.

<table>
  <thead>
    <tr>
      <th>Wert</th>
      <th>Bedeutung</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>PRE_PRESSED_STATE</td>
    </tr>
    <tr>
      <td>1</td>
      <td>POST_PRESSED_STATE</td>
    </tr>
    <tr>
      <td>2</td>
      <td>REBOOT</td>
    </tr>
    <tr>
      <td>4</td>
      <td>SHOW_CURRENT_ADDRESS</td>
    </tr>
    <tr>
      <td>5</td>
      <td>SELF_TRIGGER</td>
    </tr>
    <tr>
      <td>6</td>
      <td>LONG_PRESSED_STATE</td>
    </tr>
  </tbody>
</table>

**Text (Register 1007-1010):** Bis zu 4 ASCII-Zeichen für die Taster-Anzeige, ein Zeichen pro Register (Register 1007 = erstes Zeichen). Das Zeichen wird im Low-Byte gespeichert; das High-Byte wird ignoriert. Unbenutzte Zeichen (Wert `0`) werden intern mit `@` aufgefüllt. Nur relevant für Ring-Effekte mit Textanzeige.

**Button Rotation (Register 1012):** Nur wirksam, wenn CommandType = SET_PARAMETER und Offset = BUTTON_ROTATE. Legt die Ausrichtung von Anzeige/Taster fest.

<table>
  <thead>
    <tr>
      <th>Wert</th>
      <th>Bedeutung</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>BUTTON_ROTATION_NORMAL</td>
    </tr>
    <tr>
      <td>1</td>
      <td>BUTTON_ROTATION_UPSIDEDOWN</td>
    </tr>
  </tbody>
</table>

**Assign Address (Register 1013):** Nur wirksam, wenn CommandType = SET_STATUS und Offset = ADDRESS_TO_BE_ASSIGNED. Weist dem/den adressierten Taster(n) eine Geräteadresse zu.

<table>
  <thead>
    <tr>
      <th>Wert</th>
      <th>Bedeutung</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>NEXT (automatische Zuweisung der nächsten freien Adresse)</td>
    </tr>
    <tr>
      <td>andere</td>
      <td>Explizite zuzuweisende Geräteadresse</td>
    </tr>
  </tbody>
</table>

**Hub Polling Channels (Register 1100-1228):** Nur wirksam, wenn CommandType = SET_PARAMETER, Offset = (keine) und Address = HUB (0). Legt fest, welche Taster-Adressen der HUB (SEH) aktiv abfragt. Es werden bis zu **128** Adressen unterstützt.

<table>
  <thead>
    <tr>
      <th>Register</th>
      <th>Beschreibung</th>
      <th>Datentyp</th>
      <th>Bereich</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1100</td>
      <td>Polling Channel Count</td>
      <td>UINT16</td>
      <td>0-128</td>
    </tr>
    <tr>
      <td>1101</td>
      <td>Polling Channel 0</td>
      <td>UINT16</td>
      <td>Taster-Adresse</td>
    </tr>
    <tr>
      <td>1102</td>
      <td>Polling Channel 1</td>
      <td>UINT16</td>
      <td>Taster-Adresse</td>
    </tr>
    <tr>
      <td>...</td>
      <td>...</td>
      <td>...</td>
      <td>...</td>
    </tr>
    <tr>
      <td>1228</td>
      <td>Polling Channel 127</td>
      <td>UINT16</td>
      <td>Taster-Adresse</td>
    </tr>
  </tbody>
</table>

Polling-Kanal K befindet sich in Register `1101 + K` (K = 0..127). Register 1100 enthält die Anzahl der gültigen Kanäle. Da eine einzelne Modbus-Anfrage auf ~123 Register begrenzt ist, muss eine lange Kanalliste in mehreren Function-16-Anfragen geschrieben werden, bevor der Apply-Trigger (Register 1020) gesetzt wird.

Die maximale Anzahl tatsächlich nutzbarer Polling-Adressen ist produktabhängig und kann geringer sein als die hier reservierten 128 Register.

**SMC Apply/Trigger (Register 1020):** Nachdem die SMC-Register beschrieben wurden, muss Register 1020 auf `1` gesetzt werden, um den Befehl an die Taster zu senden. Das Register wird automatisch auf `0` zurückgesetzt. Der SMC-Apply ist unabhängig vom LED-Apply (Register 2).

---

### Input Registers (Function 04) – Status/Nur-Lese

<table>
  <thead>
    <tr>
      <th>Register</th>
      <th>Beschreibung</th>
      <th>Datentyp</th>
      <th>Bereich</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>Firmware Version Major</td>
      <td>UINT16</td>
      <td>0-255</td>
    </tr>
    <tr>
      <td>1</td>
      <td>Firmware Version Minor</td>
      <td>UINT16</td>
      <td>0-255</td>
    </tr>
    <tr>
      <td>2</td>
      <td>Firmware Version Patch</td>
      <td>UINT16</td>
      <td>0-255</td>
    </tr>
    <tr>
      <td>3</td>
      <td>Number of Configured Strips</td>
      <td>UINT16</td>
      <td>0-5</td>
    </tr>
    <tr>
      <td>4</td>
      <td>Strip 1 - Length (LEDs)</td>
      <td>UINT16</td>
      <td>0-312</td>
    </tr>
    <tr>
      <td>5</td>
      <td>Strip 2 - Length (LEDs)</td>
      <td>UINT16</td>
      <td>0-312</td>
    </tr>
    <tr>
      <td>6</td>
      <td>Strip 3 - Length (LEDs)</td>
      <td>UINT16</td>
      <td>0-312</td>
    </tr>
    <tr>
      <td>7</td>
      <td>Strip 4 - Length (LEDs)</td>
      <td>UINT16</td>
      <td>0-312</td>
    </tr>
    <tr>
      <td>8</td>
      <td>Strip 5 - Length (LEDs)</td>
      <td>UINT16</td>
      <td>0-312</td>
    </tr>
    <tr>
      <td>9</td>
      <td>Power Supply Limit (W×10)</td>
      <td>UINT16</td>
      <td>0-65535</td>
    </tr>
    <tr>
      <td>10</td>
      <td>Total Active LEDs</td>
      <td>UINT16</td>
      <td>0-65535</td>
    </tr>
    <tr>
      <td>11</td>
      <td>Error Code</td>
      <td>UINT16</td>
      <td>0-4</td>
    </tr>
  </tbody>
</table>

**Fehlercodes:**

<table>
  <thead>
    <tr>
      <th>Code</th>
      <th>Bedeutung</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>Kein Fehler</td>
    </tr>
    <tr>
      <td>1</td>
      <td>Kein Netzwerk</td>
    </tr>
    <tr>
      <td>2</td>
      <td>Keine MQTT-Verbindung</td>
    </tr>
    <tr>
      <td>3</td>
      <td>Keine Konfiguration</td>
    </tr>
    <tr>
      <td>4</td>
      <td>Normaler Betrieb</td>
    </tr>
  </tbody>
</table>

---

### SMC-Taster-Status (Input Registers)

Die Zustände der angeschlossenen SMC-Taster können über Input-Register zurückgelesen werden. Dies entspricht der über MQTT auf `/Pub/Data/Smc` publizierten Taster-Rückmeldung. Der SMC-Statusbereich beginnt bei Input-Register-Adresse **100**.

Da Modbus keinen serverseitig initiierten Push kennt, müssen Statusänderungen durch Polling erkannt werden (siehe auch den Abschnitt Hinweise). Um nicht zyklisch den gesamten Statusblock lesen zu müssen, steht der **SMC Event Counter** zur Verfügung.

#### SMC Event Counter

<table>
  <thead>
    <tr>
      <th>Register</th>
      <th>Beschreibung</th>
      <th>Datentyp</th>
      <th>Bereich</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>100</td>
      <td>SMC Event Counter</td>
      <td>UINT16</td>
      <td>0-65535</td>
    </tr>
  </tbody>
</table>

Der Zähler wird bei jeder SMC-Taster-Statusänderung erhöht und läuft bei 65535 über. Ein Client kann dieses einzelne Register kostengünstig abfragen und die Taster-Statusblöcke nur dann erneut lesen, wenn sich der Wert geändert hat. Die Register 101-109 sind reserviert.

#### Taster-Status-Register

Die Anzahl der abgebildeten Taster entspricht `MAX_SMC_COUNT`. Dieser Wert hängt vom Build-Target ab (z. B. 5 bei Standardvarianten, bis zu 128 bei bestimmten Geräten). Jeder Taster belegt einen Block von 4 Registern. Die Basisadresse für Taster K (K = 0 .. MAX_SMC_COUNT-1) berechnet sich wie folgt:

**Taster-Basisadresse = 110 + (K \* 4)**

<table>
  <thead>
    <tr>
      <th>Offset</th>
      <th>Beschreibung</th>
      <th>Datentyp</th>
      <th>Bereich</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>Button Address</td>
      <td>UINT16</td>
      <td>0-65535</td>
    </tr>
    <tr>
      <td>1</td>
      <td>Press Count</td>
      <td>UINT16</td>
      <td>0-65535</td>
    </tr>
    <tr>
      <td>2</td>
      <td>Press State</td>
      <td>UINT16</td>
      <td>siehe unten</td>
    </tr>
    <tr>
      <td>3</td>
      <td>Reserved</td>
      <td>-</td>
      <td>-</td>
    </tr>
  </tbody>
</table>

Eine Button Address von `0` kennzeichnet einen unbenutzten/leeren Slot.

**Press State (Offset 2):**

<table>
  <thead>
    <tr>
      <th>Wert</th>
      <th>Bedeutung</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>PRE_PRESSED_STATE</td>
    </tr>
    <tr>
      <td>1</td>
      <td>POST_PRESSED_STATE</td>
    </tr>
    <tr>
      <td>3</td>
      <td>LONGPRESS_STATE</td>
    </tr>
  </tbody>
</table>

<table>
  <thead>
    <tr>
      <th>Taster</th>
      <th>Adresse</th>
      <th>Press Count</th>
      <th>Press State</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>110</td>
      <td>111</td>
      <td>112</td>
    </tr>
    <tr>
      <td>1</td>
      <td>114</td>
      <td>115</td>
      <td>116</td>
    </tr>
    <tr>
      <td>2</td>
      <td>118</td>
      <td>119</td>
      <td>120</td>
    </tr>
    <tr>
      <td>...</td>
      <td>...</td>
      <td>...</td>
      <td>...</td>
    </tr>
    <tr>
      <td>K</td>
      <td>110 + K×4</td>
      <td>111 + K×4</td>
      <td>112 + K×4</td>
    </tr>
  </tbody>
</table>

---

## Verfügbare LED-Effekte

<table>
  <thead>
    <tr>
      <th>Effekt-ID</th>
      <th>Name</th>
      <th>Beschreibung</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>Static</td>
      <td>Alle LEDs leuchten in konstanter Farbe</td>
    </tr>
    <tr>
      <td>2</td>
      <td>Blink</td>
      <td>Ein-/Aus-Blinken</td>
    </tr>
    <tr>
      <td>3</td>
      <td>Flash</td>
      <td>Kurze, helle Blitze</td>
    </tr>
    <tr>
      <td>4</td>
      <td>Leading Lines</td>
      <td>Vorwärts laufende Linien</td>
    </tr>
    <tr>
      <td>5</td>
      <td>Leading Lines Reverse</td>
      <td>Rückwärts laufende Linien</td>
    </tr>
    <tr>
      <td>6</td>
      <td>Multipicker</td>
      <td>Durchlaufen mehrerer Farben</td>
    </tr>
    <tr>
      <td>7</td>
      <td>Pick</td>
      <td>Animation von der Mitte nach außen</td>
    </tr>
    <tr>
      <td>8</td>
      <td>Place</td>
      <td>Animation von außen zur Mitte</td>
    </tr>
    <tr>
      <td>200</td>
      <td>Knight Rider</td>
      <td>Hin- und herlaufende Balkenanimation</td>
    </tr>
  </tbody>
</table>

## Register-Zuordnung – Beispiel

### Streifen 1, Segment 0 aktivieren (green, static)

Folgende Register müssen beschrieben werden (Function 16 - Write Multiple Registers):

<table>
  <thead>
    <tr>
      <th>Register</th>
      <th>Wert</th>
      <th>Beschreibung</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>100</td>
      <td>1</td>
      <td>Strip 1 Active = true</td>
    </tr>
    <tr>
      <td>101</td>
      <td>1</td>
      <td>Strip 1 Segment Count = 1</td>
    </tr>
    <tr>
      <td>110</td>
      <td>0</td>
      <td>Segment 0: StartLED = 0</td>
    </tr>
    <tr>
      <td>111</td>
      <td>20</td>
      <td>Segment 0: StopLED = 20</td>
    </tr>
    <tr>
      <td>112</td>
      <td>1</td>
      <td>Segment 0: Effect = Static</td>
    </tr>
    <tr>
      <td>113</td>
      <td>200</td>
      <td>Segment 0: Speed = 200</td>
    </tr>
    <tr>
      <td>114</td>
      <td>0</td>
      <td>Segment 0: Color1 R = 0</td>
    </tr>
    <tr>
      <td>115</td>
      <td>150</td>
      <td>Segment 0: Color1 G = 150</td>
    </tr>
    <tr>
      <td>116</td>
      <td>0</td>
      <td>Segment 0: Color1 B = 0</td>
    </tr>
    <tr>
      <td>117</td>
      <td>0</td>
      <td>Segment 0: Color2 R = 0</td>
    </tr>
    <tr>
      <td>118</td>
      <td>0</td>
      <td>Segment 0: Color2 G = 0</td>
    </tr>
    <tr>
      <td>119</td>
      <td>0</td>
      <td>Segment 0: Color2 B = 0</td>
    </tr>
    <tr>
      <td>120</td>
      <td>0</td>
      <td>Segment 0: Color3 R = 0</td>
    </tr>
    <tr>
      <td>121</td>
      <td>0</td>
      <td>Segment 0: Color3 G = 0</td>
    </tr>
    <tr>
      <td>122</td>
      <td>0</td>
      <td>Segment 0: Color3 B = 0</td>
    </tr>
    <tr>
      <td>2</td>
      <td>1</td>
      <td>Apply/Trigger</td>
    </tr>
  </tbody>
</table>

Dies entspricht der folgenden MQTT-Nachricht:

```json
{
  "LED_STRIP_1": {
    "Active": true,
    "Segments": [
      {
        "StartLED": 0,
        "StopLED": 20,
        "Effect": 1,
        "Speed": 200,
        "Colors": [{ "R": 0, "G": 150, "B": 0 }]
      }
    ]
  }
}
```

### SMC-Taster: PRE_BUTTON_MODE setzen (alle Taster, green, blinkender Ring)

Folgende Register müssen beschrieben werden (Function 16 - Write Multiple Registers):

<table>
  <thead>
    <tr>
      <th>Register</th>
      <th>Wert</th>
      <th>Beschreibung</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1000</td>
      <td>0xFFFF</td>
      <td>Address = ALL_SENSORS (Broadcast)</td>
    </tr>
    <tr>
      <td>1001</td>
      <td>1</td>
      <td>CommandType = SET_PARAMETER</td>
    </tr>
    <tr>
      <td>1002</td>
      <td>1</td>
      <td>Offset = PRE_BUTTON_MODE</td>
    </tr>
    <tr>
      <td>1003</td>
      <td>1</td>
      <td>Button Mode = ENABLED</td>
    </tr>
    <tr>
      <td>1004</td>
      <td>5</td>
      <td>Quantity = 5</td>
    </tr>
    <tr>
      <td>1005</td>
      <td>2</td>
      <td>Color = COLGREEN</td>
    </tr>
    <tr>
      <td>1006</td>
      <td>1</td>
      <td>Ring Effect = FLASH_RING</td>
    </tr>
    <tr>
      <td>1020</td>
      <td>1</td>
      <td>SMC Apply/Trigger</td>
    </tr>
  </tbody>
</table>

Dies entspricht der folgenden MQTT-Nachricht:

```json
{
  "Address": "ALL_SENSORS",
  "CommandType": "SET_PARAMETER",
  "Offset": "PRE_BUTTON_MODE",
  "Payload": "ENABLED/5/COLGREEN/FLASH_RING/@@@@"
}
```

### SMC-Taster: Spezialbefehl auslösen (PRE_PRESSED_STATE, alle Taster)

<table>
  <thead>
    <tr>
      <th>Register</th>
      <th>Wert</th>
      <th>Beschreibung</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1000</td>
      <td>0xFFFF</td>
      <td>Address = ALL_SENSORS (Broadcast)</td>
    </tr>
    <tr>
      <td>1001</td>
      <td>2</td>
      <td>CommandType = SET_STATUS</td>
    </tr>
    <tr>
      <td>1002</td>
      <td>4</td>
      <td>Offset = SPECIAL_COMMANDS</td>
    </tr>
    <tr>
      <td>1011</td>
      <td>0</td>
      <td>Special Command = PRE_PRESSED_STATE</td>
    </tr>
    <tr>
      <td>1020</td>
      <td>1</td>
      <td>SMC Apply/Trigger</td>
    </tr>
  </tbody>
</table>

Dies entspricht der folgenden MQTT-Nachricht:

```json
{
  "Address": "ALL_SENSORS",
  "CommandType": "SET_STATUS",
  "Offset": "SPECIAL_COMMANDS",
  "Payload": "PRE_PRESSED_STATE"
}
```

### SMC-Taster: Taster-Rotation setzen (alle Taster, kopfüber)

<table>
  <thead>
    <tr>
      <th>Register</th>
      <th>Wert</th>
      <th>Beschreibung</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1000</td>
      <td>0xFFFF</td>
      <td>Address = ALL_SENSORS (Broadcast)</td>
    </tr>
    <tr>
      <td>1001</td>
      <td>1</td>
      <td>CommandType = SET_PARAMETER</td>
    </tr>
    <tr>
      <td>1002</td>
      <td>3</td>
      <td>Offset = BUTTON_ROTATE</td>
    </tr>
    <tr>
      <td>1012</td>
      <td>1</td>
      <td>Button Rotation = BUTTON_ROTATION_UPSIDEDOWN</td>
    </tr>
    <tr>
      <td>1020</td>
      <td>1</td>
      <td>SMC Apply/Trigger</td>
    </tr>
  </tbody>
</table>

Dies entspricht der folgenden MQTT-Nachricht:

```json
{
  "Address": "ALL_SENSORS",
  "CommandType": "SET_PARAMETER",
  "Offset": "BUTTON_ROTATE",
  "Payload": "BUTTON_ROTATION_UPSIDEDOWN"
}
```

### SMC-Taster: Adresse zuweisen (alle Taster, Adresse 0x1000)

<table>
  <thead>
    <tr>
      <th>Register</th>
      <th>Wert</th>
      <th>Beschreibung</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1000</td>
      <td>0xFFFF</td>
      <td>Address = ALL_SENSORS (Broadcast)</td>
    </tr>
    <tr>
      <td>1001</td>
      <td>2</td>
      <td>CommandType = SET_STATUS</td>
    </tr>
    <tr>
      <td>1002</td>
      <td>5</td>
      <td>Offset = ADDRESS_TO_BE_ASSIGNED</td>
    </tr>
    <tr>
      <td>1013</td>
      <td>0x1000</td>
      <td>Assign Address = 0x1000</td>
    </tr>
    <tr>
      <td>1020</td>
      <td>1</td>
      <td>SMC Apply/Trigger</td>
    </tr>
  </tbody>
</table>

Dies entspricht der folgenden MQTT-Nachricht:

```json
{
  "Address": "ALL_SENSORS",
  "CommandType": "SET_STATUS",
  "Offset": "ADDRESS_TO_BE_ASSIGNED",
  "Payload": "1000"
}
```

> Setzen Sie Register 1013 auf `0`, um automatisch die nächste freie Adresse zuzuweisen (MQTT-Payload `"NEXT"`).

### SMC-HUB: Polling-Kanäle konfigurieren (Taster 0x1000-0x1003 abfragen)

<table>
  <thead>
    <tr>
      <th>Register</th>
      <th>Wert</th>
      <th>Beschreibung</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1000</td>
      <td>0</td>
      <td>Address = HUB</td>
    </tr>
    <tr>
      <td>1001</td>
      <td>1</td>
      <td>CommandType = SET_PARAMETER</td>
    </tr>
    <tr>
      <td>1002</td>
      <td>0</td>
      <td>Offset = (keine)</td>
    </tr>
    <tr>
      <td>1100</td>
      <td>4</td>
      <td>Polling Channel Count = 4</td>
    </tr>
    <tr>
      <td>1101</td>
      <td>0x1000</td>
      <td>Polling channel 0</td>
    </tr>
    <tr>
      <td>1102</td>
      <td>0x1001</td>
      <td>Polling channel 1</td>
    </tr>
    <tr>
      <td>1103</td>
      <td>0x1002</td>
      <td>Polling channel 2</td>
    </tr>
    <tr>
      <td>1104</td>
      <td>0x1003</td>
      <td>Polling channel 3</td>
    </tr>
    <tr>
      <td>1020</td>
      <td>1</td>
      <td>SMC Apply/Trigger</td>
    </tr>
  </tbody>
</table>

Dies entspricht der folgenden MQTT-Nachricht:

```json
{
  "Address": "HUB",
  "CommandType": "SET_PARAMETER",
  "Offset": "",
  "Payload": "1000/1001/1002/1003"
}
```

## Adressberechnung – Kurzreferenz

```
Strip N Base Address:    100 * N          (N = 1..5)
Strip N Active:          100 * N + 0
Strip N Segment Count:   100 * N + 1
Segment M Base Address:  100 * N + 10 + (M * 11)   (M = 0..7)

SMC block (holding):     selector 1000-1002, payload 1003-1013, Apply = 1020
SMC hub polling (holding): count 1100, channel K = 1101 + K   (K = 0..127)
SMC event counter (input): 100
SMC status (input):      button K base = 110 + (K * 4)   (K = 0 .. MAX_SMC_COUNT-1)
```

<table>
  <thead>
    <tr>
      <th>Streifen</th>
      <th>Active</th>
      <th>Seg.-Anzahl</th>
      <th>Seg 0</th>
      <th>Seg 1</th>
      <th>Seg 2</th>
      <th>Seg 3</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>100</td>
      <td>101</td>
      <td>110</td>
      <td>121</td>
      <td>132</td>
      <td>143</td>
    </tr>
    <tr>
      <td>2</td>
      <td>200</td>
      <td>201</td>
      <td>210</td>
      <td>221</td>
      <td>232</td>
      <td>243</td>
    </tr>
    <tr>
      <td>3</td>
      <td>300</td>
      <td>301</td>
      <td>310</td>
      <td>321</td>
      <td>332</td>
      <td>343</td>
    </tr>
    <tr>
      <td>4</td>
      <td>400</td>
      <td>401</td>
      <td>410</td>
      <td>421</td>
      <td>432</td>
      <td>443</td>
    </tr>
    <tr>
      <td>5</td>
      <td>500</td>
      <td>501</td>
      <td>510</td>
      <td>521</td>
      <td>532</td>
      <td>543</td>
    </tr>
  </tbody>
</table>

## Hinweise

- **Paralleler MQTT-/Modbus-Betrieb:** Beide Schnittstellen schreiben auf dieselben LED-Streifen. Der zuletzt empfangene Befehl (ob MQTT oder Modbus) bestimmt den aktuellen Zustand.
- **Leistungsgrenze:** Die Leistungsgrenze gilt global. Segmente, die die Leistungsgrenze überschreiten würden, werden nicht aktiviert (identisches Verhalten wie bei MQTT).
- **StopLED >= StartLED:** Ist StopLED < StartLED, wird StopLED automatisch auf StartLED gesetzt.
- **Farben:** Die Summe aus R+G+B pro Farbe ist auf maximal 180 begrenzt (Strombegrenzung pro LED).
- **Ungenutzte Farben:** Setzen Sie ungenutzte Farbregister (Color 2, Color 3) auf 0/0/0.
- **Apply-Register:** Änderungen an Streifen-/Segment-Registern werden erst wirksam, wenn `1` in Register 2 (Apply) geschrieben wird. Dies ermöglicht eine atomare Konfiguration mehrerer Streifen/Segmente.
- **SMC-Steuerung:** Der SMC-Block (ab Register 1000) besitzt einen eigenen Apply-Trigger (Register 1020), unabhängig vom LED-Apply (Register 2). LED- und SMC-Befehle können somit getrennt ausgelöst werden.
- **SMC parallel zu MQTT:** Modbus- und MQTT-SMC-Befehle erzeugen intern denselben SMC-Befehl und werden in dieselbe Sendewarteschlange eingereiht. Der zuletzt empfangene Befehl bestimmt den Taster-Zustand.
- **SMC-Adressierung:** `0xFFFF` adressiert alle Taster (Broadcast), `0` den HUB (SEH selbst), jeder andere Wert einen bestimmten Taster.
- **SMC-Status:** Die Input-Register für den Taster-Status (ab Adresse 110) spiegeln die zuletzt empfangene Taster-Rückmeldung wider. Eine Button Address von `0` kennzeichnet einen unbenutzten Slot.
- **Benachrichtigung bei Statusänderung:** Modbus verfügt über keinen serverseitig initiierten Push; der Server beantwortet nur Anfragen. Statusänderungen werden durch Polling des SMC Event Counter (Register 100) erkannt; die Taster-Statusblöcke werden bei einer Änderung erneut gelesen. Für echte Push-Benachrichtigungen nutzen Sie den parallel laufenden MQTT-Kanal (`/Pub/Data/Smc`).
