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

# Modbus Interface

## Overview

This document describes the Modbus TCP/IP interface for controlling the LED strips and SMC buttons of SEHx01/SEH11 devices. The Modbus interface operates in parallel with the existing MQTT control. Both interfaces can be used simultaneously.

## Physical Interface

<table>
  <thead>
    <tr>
      <th>Parameter</th>
      <th>Value</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Protocol</td>
      <td>Modbus</td>
    </tr>
    <tr>
      <td>Interface</td>
      <td>TCP/IP</td>
    </tr>
    <tr>
      <td>Device ID</td>
      <td>1 (configurable)</td>
    </tr>
  </tbody>
</table>

## Register Overview

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

The controller supports 5 LED strips (LED_STRIP_1 through LED_STRIP_5). Each strip can contain multiple segments. Up to 8 segments per strip are configurable. Each segment supports up to 3 colors.

#### Global Registers

<table>
  <thead>
    <tr>
      <th>Register</th>
      <th>Description</th>
      <th>Data Type</th>
      <th>Range</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):** After writing all desired strip/segment registers, register 2 must be set to `1` to apply the changes. The register is automatically reset to `0` after the changes are applied.

---

#### Strip Registers

Each strip occupies a block of 100 registers. The base address for strip N (N = 1..5) is calculated as:

**Base Address = 100 \* N**

<table>
  <thead>
    <tr>
      <th>Offset</th>
      <th>Description</th>
      <th>Data Type</th>
      <th>Range</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 Registers

Within a strip block, each segment occupies 11 registers. The base address for segment M (M = 0..7) within strip N is:

**Segment Base Address = (100 _ N) + 10 + (M _ 11)**

<table>
  <thead>
    <tr>
      <th>Offset</th>
      <th>Description</th>
      <th>Data Type</th>
      <th>Range</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>see Effects</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 Button Control (Holding Registers)

In addition to the LED strips, the connected SMC buttons can also be controlled via Modbus. This corresponds to the existing MQTT control (topic `/Set/Data/Smc`). Both paths internally generate the same SMC command and can be used in parallel.

The SMC block occupies a dedicated register range starting at base address **1000** and has its **own Apply trigger**, independent of the LED Apply (register 2).

<table>
  <thead>
    <tr>
      <th>Register</th>
      <th>Description</th>
      <th>Data Type</th>
      <th>Range</th>
      <th>R/W</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1000</td>
      <td>SMC Address</td>
      <td>UINT16</td>
      <td>see below</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>1001</td>
      <td>SMC CommandType</td>
      <td>UINT16</td>
      <td>0-2 (see below)</td>
      <td>R/W</td>
    </tr>
    <tr>
      <td>1002</td>
      <td>SMC Offset</td>
      <td>UINT16</td>
      <td>0-5 (see below)</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>see Ring Effects</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>see Special Commands</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>see below</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>see below</td>
      <td>R/W</td>
    </tr>
  </tbody>
</table>

Following the MQTT protocol, `CommandType` (register 1001) and `Offset` (register 1002) are kept as two separate fields. Together they select which payload registers are evaluated on Apply. The hub polling channels occupy a separate block (registers 1100-1228), described further below.

**SMC Address (Register 1000):** Target address of the command.

<table>
  <thead>
    <tr>
      <th>Value</th>
      <th>Meaning</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>HUB (SEH itself)</td>
    </tr>
    <tr>
      <td>0xFFFF</td>
      <td>ALL_SENSORS (broadcast to all buttons)</td>
    </tr>
    <tr>
      <td>other</td>
      <td>Specific button address (device address)</td>
    </tr>
  </tbody>
</table>

**SMC CommandType (Register 1001):** Mirrors the `CommandType` field of the MQTT protocol.

<table>
  <thead>
    <tr>
      <th>Value</th>
      <th>CommandType</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>- (idle, nothing sent)</td>
    </tr>
    <tr>
      <td>1</td>
      <td>SET_PARAMETER</td>
    </tr>
    <tr>
      <td>2</td>
      <td>SET_STATUS</td>
    </tr>
  </tbody>
</table>

_The SMC protocol additionally defines GET_STATUS, SET_UPDATE, GET_INTERNAL_STATUS and SET_INTERNAL_STATUS. These are not yet mapped and can be added later._

**SMC Offset (Register 1002):** Mirrors the `Offset` field of the MQTT protocol.

<table>
  <thead>
    <tr>
      <th>Value</th>
      <th>Offset</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>(none / empty)</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>

**Valid combinations:** On Apply, `CommandType` and `Offset` together determine which payload registers are evaluated.

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

**Button Mode (Register 1003):**

<table>
  <thead>
    <tr>
      <th>Value</th>
      <th>Meaning</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>Value</th>
      <th>Meaning</th>
      <th>Value</th>
      <th>Meaning</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>Value</th>
      <th>Meaning</th>
      <th>Value</th>
      <th>Meaning</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):** Only effective when CommandType = SET_STATUS and Offset = SPECIAL_COMMANDS.

<table>
  <thead>
    <tr>
      <th>Value</th>
      <th>Meaning</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):** Up to 4 ASCII characters for the button display, one character per register (register 1007 = first character). The character is stored in the low byte; the high byte is ignored. Unused characters (value `0`) are internally padded with `@`. Only relevant for ring effects with text display.

**Button Rotation (Register 1012):** Only effective when CommandType = SET_PARAMETER and Offset = BUTTON_ROTATE. Sets the display/button orientation.

<table>
  <thead>
    <tr>
      <th>Value</th>
      <th>Meaning</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):** Only effective when CommandType = SET_STATUS and Offset = ADDRESS_TO_BE_ASSIGNED. Assigns a device address to the addressed button(s).

<table>
  <thead>
    <tr>
      <th>Value</th>
      <th>Meaning</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>NEXT (auto-assign next free address)</td>
    </tr>
    <tr>
      <td>other</td>
      <td>Explicit device address to assign</td>
    </tr>
  </tbody>
</table>

**Hub Polling Channels (Register 1100-1228):** Only effective when CommandType = SET_PARAMETER, Offset = (none) and Address = HUB (0). Defines which button addresses the HUB (SEH) actively polls. Up to **128** addresses are supported.

<table>
  <thead>
    <tr>
      <th>Register</th>
      <th>Description</th>
      <th>Data Type</th>
      <th>Range</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>button address</td>
    </tr>
    <tr>
      <td>1102</td>
      <td>Polling Channel 1</td>
      <td>UINT16</td>
      <td>button address</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>button address</td>
    </tr>
  </tbody>
</table>

Polling channel K is located at register `1101 + K` (K = 0..127). Register 1100 holds the number of valid channels. As a single Modbus request is limited to ~123 registers, a long channel list must be written in multiple Function-16 requests before setting the Apply trigger (register 1020).

The maximum number of polling addresses that can actually be used is product-dependent and may be lower than the 128 registers reserved here.

**SMC Apply/Trigger (Register 1020):** After writing the SMC registers, register 1020 must be set to `1` to send the command to the buttons. The register is automatically reset to `0`. The SMC Apply is independent of the LED Apply (register 2).

---

### Input Registers (Function 04) - Status/Read-Only

<table>
  <thead>
    <tr>
      <th>Register</th>
      <th>Description</th>
      <th>Data Type</th>
      <th>Range</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>

**Error Codes:**

<table>
  <thead>
    <tr>
      <th>Code</th>
      <th>Meaning</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>No error</td>
    </tr>
    <tr>
      <td>1</td>
      <td>No network</td>
    </tr>
    <tr>
      <td>2</td>
      <td>No MQTT connection</td>
    </tr>
    <tr>
      <td>3</td>
      <td>No configuration</td>
    </tr>
    <tr>
      <td>4</td>
      <td>Normal operation</td>
    </tr>
  </tbody>
</table>

---

### SMC Button Status (Input Registers)

The states of the connected SMC buttons can be read back via input registers. This corresponds to the button feedback published over MQTT on `/Pub/Data/Smc`. The SMC status range starts at input register address **100**.

Because Modbus has no server-initiated push, status changes must be detected by polling (see also the Notes section). To avoid reading the full status block cyclically, the **SMC Event Counter** is provided.

#### SMC Event Counter

<table>
  <thead>
    <tr>
      <th>Register</th>
      <th>Description</th>
      <th>Data Type</th>
      <th>Range</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>100</td>
      <td>SMC Event Counter</td>
      <td>UINT16</td>
      <td>0-65535</td>
    </tr>
  </tbody>
</table>

The counter is incremented on every SMC button status change and wraps around at 65535. A client can poll this single register cheaply and only re-read the button status blocks when the value has changed. Registers 101-109 are reserved.

#### Button Status Registers

The number of mapped buttons equals `MAX_SMC_COUNT`. This value depends on the build target (e.g. 5 for standard variants, up to 128 for certain devices). Each button occupies a block of 4 registers. The base address for button K (K = 0 .. MAX_SMC_COUNT-1) is calculated as:

**Button Base Address = 110 + (K \* 4)**

<table>
  <thead>
    <tr>
      <th>Offset</th>
      <th>Description</th>
      <th>Data Type</th>
      <th>Range</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>see below</td>
    </tr>
    <tr>
      <td>3</td>
      <td>Reserved</td>
      <td>-</td>
      <td>-</td>
    </tr>
  </tbody>
</table>

A Button Address of `0` indicates an unused/empty slot.

**Press State (Offset 2):**

<table>
  <thead>
    <tr>
      <th>Value</th>
      <th>Meaning</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>Button</th>
      <th>Address</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>

---

## Available LED Effects

<table>
  <thead>
    <tr>
      <th>Effect ID</th>
      <th>Name</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>Static</td>
      <td>All LEDs lit at constant color</td>
    </tr>
    <tr>
      <td>2</td>
      <td>Blink</td>
      <td>On/off blinking</td>
    </tr>
    <tr>
      <td>3</td>
      <td>Flash</td>
      <td>Quick bright pulses</td>
    </tr>
    <tr>
      <td>4</td>
      <td>Leading Lines</td>
      <td>Running lines forward</td>
    </tr>
    <tr>
      <td>5</td>
      <td>Leading Lines Reverse</td>
      <td>Running lines backward</td>
    </tr>
    <tr>
      <td>6</td>
      <td>Multipicker</td>
      <td>Cycling through multiple colors</td>
    </tr>
    <tr>
      <td>7</td>
      <td>Pick</td>
      <td>Animation from center outward</td>
    </tr>
    <tr>
      <td>8</td>
      <td>Place</td>
      <td>Animation from outside to center</td>
    </tr>
    <tr>
      <td>200</td>
      <td>Knight Rider</td>
      <td>Bouncing bar animation</td>
    </tr>
  </tbody>
</table>

## Register Mapping - Example

### Activate Strip 1, Segment 0 (green, static)

The following registers must be written (Function 16 - Write Multiple Registers):

<table>
  <thead>
    <tr>
      <th>Register</th>
      <th>Value</th>
      <th>Description</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>

This corresponds to the following MQTT message:

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

### SMC Button: Set PRE_BUTTON_MODE (all buttons, green, flashing ring)

The following registers must be written (Function 16 - Write Multiple Registers):

<table>
  <thead>
    <tr>
      <th>Register</th>
      <th>Value</th>
      <th>Description</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>

This corresponds to the following MQTT message:

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

### SMC Button: Trigger Special Command (PRE_PRESSED_STATE, all buttons)

<table>
  <thead>
    <tr>
      <th>Register</th>
      <th>Value</th>
      <th>Description</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>

This corresponds to the following MQTT message:

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

### SMC Button: Set Button Rotation (all buttons, upside down)

<table>
  <thead>
    <tr>
      <th>Register</th>
      <th>Value</th>
      <th>Description</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>

This corresponds to the following MQTT message:

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

### SMC Button: Assign Address (all buttons, address 0x1000)

<table>
  <thead>
    <tr>
      <th>Register</th>
      <th>Value</th>
      <th>Description</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>

This corresponds to the following MQTT message:

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

> Set register 1013 to `0` to assign the next free address automatically (MQTT payload `"NEXT"`).

### SMC HUB: Configure Polling Channels (poll buttons 0x1000-0x1003)

<table>
  <thead>
    <tr>
      <th>Register</th>
      <th>Value</th>
      <th>Description</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 = (none)</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>

This corresponds to the following MQTT message:

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

## Address Calculation - Quick Reference

```
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>Strip</th>
      <th>Active</th>
      <th>Seg Count</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>

## Notes

- **Parallel MQTT/Modbus Operation:** Both interfaces write to the same LED strips. The most recently received command (whether MQTT or Modbus) determines the current state.
- **Power Limit:** The power limit applies globally. Segments that would exceed the power limit will not be activated (identical behavior to MQTT).
- **StopLED >= StartLED:** If StopLED < StartLED, StopLED is automatically set to StartLED.
- **Colors:** The sum of R+G+B per color is limited to a maximum of 180 (per-LED current limiting).
- **Unused Colors:** Set unused color registers (Color 2, Color 3) to 0/0/0.
- **Apply Register:** Changes to strip/segment registers only take effect when `1` is written to register 2 (Apply). This allows atomic configuration of multiple strips/segments.
- **SMC Control:** The SMC block (from register 1000) has its own Apply trigger (register 1020), independent of the LED Apply (register 2). LED and SMC commands can thus be triggered separately.
- **SMC Parallel to MQTT:** Modbus and MQTT SMC commands internally generate the same SMC command and are placed into the same send queue. The most recently received command determines the button state.
- **SMC Addressing:** `0xFFFF` addresses all buttons (broadcast), `0` the HUB (SEH itself), any other value a specific button.
- **SMC Status:** The button status input registers (from address 110) reflect the most recently received button feedback. A Button Address of `0` marks an unused slot.
- **Status Change Notification:** Modbus has no server-initiated push; the server only answers requests. Status changes are detected by polling the SMC Event Counter (register 100) and re-reading the button blocks when it changed. For true push notifications use the MQTT channel (`/Pub/Data/Smc`), which runs in parallel.
