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
| Parameter | Value |
|---|---|
| Protocol | Modbus |
| Interface | TCP/IP |
| Device ID | 1 (configurable) |
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
| Register | Description | Data Type | Range | R/W |
|---|---|---|---|---|
| 0 | Modbus Device ID | UINT16 | 1-247 | R/W |
| 2 | Apply/Trigger | UINT16 | 0/1 | W |
| 3 | Reserved | - | - | - |
| 4 | Reserved | - | - | - |
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
| Offset | Description | Data Type | Range | R/W |
|---|---|---|---|---|
| 0 | Active | UINT16 | 0/1 | R/W |
| 1 | Segment Count | UINT16 | 0-8 | R/W |
| 2-9 | Reserved | - | - | - |
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)
| Offset | Description | Data Type | Range | R/W |
|---|---|---|---|---|
| 0 | StartLED | UINT16 | 0-311 | R/W |
| 1 | StopLED | UINT16 | 0-311 | R/W |
| 2 | Effect | UINT16 | see Effects | R/W |
| 3 | Speed | UINT16 | 1-246 | R/W |
| 4 | Color 1 - Red | UINT16 | 0-255 | R/W |
| 5 | Color 1 - Green | UINT16 | 0-255 | R/W |
| 6 | Color 1 - Blue | UINT16 | 0-255 | R/W |
| 7 | Color 2 - Red | UINT16 | 0-255 | R/W |
| 8 | Color 2 - Green | UINT16 | 0-255 | R/W |
| 9 | Color 2 - Blue | UINT16 | 0-255 | R/W |
| 10 | Color 3 - Red | UINT16 | 0-255 | R/W |
| 11 | Color 3 - Green | UINT16 | 0-255 | R/W |
| 12 | Color 3 - Blue | UINT16 | 0-255 | R/W |
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).
| Register | Description | Data Type | Range | R/W |
|---|---|---|---|---|
| 1000 | SMC Address | UINT16 | see below | R/W |
| 1001 | SMC CommandType | UINT16 | 0-2 (see below) | R/W |
| 1002 | SMC Offset | UINT16 | 0-5 (see below) | R/W |
| 1003 | Button Mode | UINT16 | 0-2 | R/W |
| 1004 | Quantity | UINT16 | 0-65535 | R/W |
| 1005 | Color | UINT16 | 0-7 | R/W |
| 1006 | Ring Effect | UINT16 | see Ring Effects | R/W |
| 1007 | Text char 0 | UINT16 | 0-255 (ASCII) | R/W |
| 1008 | Text char 1 | UINT16 | 0-255 (ASCII) | R/W |
| 1009 | Text char 2 | UINT16 | 0-255 (ASCII) | R/W |
| 1010 | Text char 3 | UINT16 | 0-255 (ASCII) | R/W |
| 1011 | Special Command | UINT16 | see Special Commands | R/W |
| 1012 | Button Rotation | UINT16 | 0-1 | R/W |
| 1013 | Assign Address | UINT16 | see below | R/W |
| 1014-1019 | Reserved | - | - | - |
| 1020 | SMC Apply/Trigger | UINT16 | 0/1 | W |
| 1100-1228 | Hub Polling Channels | UINT16 | see below | R/W |
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.
| Value | Meaning |
|---|---|
| 0 | HUB (SEH itself) |
| 0xFFFF | ALL_SENSORS (broadcast to all buttons) |
| other | Specific button address (device address) |
SMC CommandType (Register 1001): Mirrors the CommandType field of the MQTT protocol.
| Value | CommandType |
|---|---|
| 0 | - (idle, nothing sent) |
| 1 | SET_PARAMETER |
| 2 | SET_STATUS |
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.
| Value | Offset |
|---|---|
| 0 | (none / empty) |
| 1 | PRE_BUTTON_MODE |
| 2 | POST_BUTTON_MODE |
| 3 | BUTTON_ROTATE |
| 4 | SPECIAL_COMMANDS |
| 5 | ADDRESS_TO_BE_ASSIGNED |
Valid combinations: On Apply, CommandType and Offset together determine which payload registers are evaluated.
| CommandType | Offset | Address | Payload registers |
|---|---|---|---|
| SET_PARAMETER | PRE_BUTTON_MODE | any | Button Mode, Quantity, Color, Ring Effect, Text (1003-1010) |
| SET_PARAMETER | POST_BUTTON_MODE | any | Button Mode, Quantity, Color, Ring Effect, Text (1003-1010) |
| SET_PARAMETER | BUTTON_ROTATE | any | Button Rotation (1012) |
| SET_PARAMETER | (none) | HUB (0) | Hub Polling Channels (1100-1228) |
| SET_STATUS | SPECIAL_COMMANDS | any | Special Command (1011) |
| SET_STATUS | ADDRESS_TO_BE_ASSIGNED | any | Assign Address (1013) |
Button Mode (Register 1003):
| Value | Meaning |
|---|---|
| 0 | DISABLED |
| 1 | ENABLED |
| 2 | LONGPRESS_ENABLED |
Color (Register 1005):
| Value | Meaning | Value | Meaning |
|---|---|---|---|
| 0 | COLOFF | 4 | COLYELLOW |
| 1 | COLRED | 5 | COLMAGENTA |
| 2 | COLGREEN | 6 | COLCYAN |
| 3 | COLBLUE | 7 | COLWHITE |
Ring Effect (Register 1006):
| Value | Meaning | Value | Meaning |
|---|---|---|---|
| 0 | SOLID_RING | 94 | ANIMATED_ARROW_UP |
| 1 | FLASH_RING | 95 | ANIMATED_ARROW_DOWN |
| 2 | CONFIRM | 96 | ANIMATED_ARROW_LEFT |
| 84 | POINT_ANIMATED_CLOCK | 97 | ANIMATED_ARROW_RIGHT |
| 85 | CIRCLE_ANIMATED_CLOCK | 102 | SOLID_ARROW_UP_LEFT |
| 86 | SOLID_ARROW_UP | 103 | SOLID_ARROW_UP_RIGHT |
| 87 | SOLID_ARROW_DOWN | 104 | SOLID_ARROW_DOWN_LEFT |
| 88 | SOLID_ARROW_LEFT | 105 | SOLID_ARROW_DOWN_RIGHT |
| 89 | SOLID_ARROW_RIGHT | 106 | FLASH_ARROW_UP_LEFT |
| 90 | FLASH_ARROW_UP | 107 | FLASH_ARROW_UP_RIGHT |
| 91 | FLASH_ARROW_DOWN | 108 | FLASH_ARROW_DOWN_LEFT |
| 92 | FLASH_ARROW_LEFT | 109 | FLASH_ARROW_DOWN_RIGHT |
| 93 | FLASH_ARROW_RIGHT |
Special Command (Register 1011): Only effective when CommandType = SET_STATUS and Offset = SPECIAL_COMMANDS.
| Value | Meaning |
|---|---|
| 0 | PRE_PRESSED_STATE |
| 1 | POST_PRESSED_STATE |
| 2 | REBOOT |
| 4 | SHOW_CURRENT_ADDRESS |
| 5 | SELF_TRIGGER |
| 6 | LONG_PRESSED_STATE |
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.
| Value | Meaning |
|---|---|
| 0 | BUTTON_ROTATION_NORMAL |
| 1 | BUTTON_ROTATION_UPSIDEDOWN |
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).
| Value | Meaning |
|---|---|
| 0 | NEXT (auto-assign next free address) |
| other | Explicit device address to assign |
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.
| Register | Description | Data Type | Range |
|---|---|---|---|
| 1100 | Polling Channel Count | UINT16 | 0-128 |
| 1101 | Polling Channel 0 | UINT16 | button address |
| 1102 | Polling Channel 1 | UINT16 | button address |
| ... | ... | ... | ... |
| 1228 | Polling Channel 127 | UINT16 | button address |
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
| Register | Description | Data Type | Range |
|---|---|---|---|
| 0 | Firmware Version Major | UINT16 | 0-255 |
| 1 | Firmware Version Minor | UINT16 | 0-255 |
| 2 | Firmware Version Patch | UINT16 | 0-255 |
| 3 | Number of Configured Strips | UINT16 | 0-5 |
| 4 | Strip 1 - Length (LEDs) | UINT16 | 0-312 |
| 5 | Strip 2 - Length (LEDs) | UINT16 | 0-312 |
| 6 | Strip 3 - Length (LEDs) | UINT16 | 0-312 |
| 7 | Strip 4 - Length (LEDs) | UINT16 | 0-312 |
| 8 | Strip 5 - Length (LEDs) | UINT16 | 0-312 |
| 9 | Power Supply Limit (W×10) | UINT16 | 0-65535 |
| 10 | Total Active LEDs | UINT16 | 0-65535 |
| 11 | Error Code | UINT16 | 0-4 |
Error Codes:
| Code | Meaning |
|---|---|
| 0 | No error |
| 1 | No network |
| 2 | No MQTT connection |
| 3 | No configuration |
| 4 | Normal operation |
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
| Register | Description | Data Type | Range |
|---|---|---|---|
| 100 | SMC Event Counter | UINT16 | 0-65535 |
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)
| Offset | Description | Data Type | Range |
|---|---|---|---|
| 0 | Button Address | UINT16 | 0-65535 |
| 1 | Press Count | UINT16 | 0-65535 |
| 2 | Press State | UINT16 | see below |
| 3 | Reserved | - | - |
A Button Address of 0 indicates an unused/empty slot.
Press State (Offset 2):
| Value | Meaning |
|---|---|
| 0 | PRE_PRESSED_STATE |
| 1 | POST_PRESSED_STATE |
| 3 | LONGPRESS_STATE |
| Button | Address | Press Count | Press State |
|---|---|---|---|
| 0 | 110 | 111 | 112 |
| 1 | 114 | 115 | 116 |
| 2 | 118 | 119 | 120 |
| ... | ... | ... | ... |
| K | 110 + K×4 | 111 + K×4 | 112 + K×4 |
Available LED Effects
| Effect ID | Name | Description |
|---|---|---|
| 1 | Static | All LEDs lit at constant color |
| 2 | Blink | On/off blinking |
| 3 | Flash | Quick bright pulses |
| 4 | Leading Lines | Running lines forward |
| 5 | Leading Lines Reverse | Running lines backward |
| 6 | Multipicker | Cycling through multiple colors |
| 7 | Pick | Animation from center outward |
| 8 | Place | Animation from outside to center |
| 200 | Knight Rider | Bouncing bar animation |
Register Mapping - Example
Activate Strip 1, Segment 0 (green, static)
The following registers must be written (Function 16 - Write Multiple Registers):
| Register | Value | Description |
|---|---|---|
| 100 | 1 | Strip 1 Active = true |
| 101 | 1 | Strip 1 Segment Count = 1 |
| 110 | 0 | Segment 0: StartLED = 0 |
| 111 | 20 | Segment 0: StopLED = 20 |
| 112 | 1 | Segment 0: Effect = Static |
| 113 | 200 | Segment 0: Speed = 200 |
| 114 | 0 | Segment 0: Color1 R = 0 |
| 115 | 150 | Segment 0: Color1 G = 150 |
| 116 | 0 | Segment 0: Color1 B = 0 |
| 117 | 0 | Segment 0: Color2 R = 0 |
| 118 | 0 | Segment 0: Color2 G = 0 |
| 119 | 0 | Segment 0: Color2 B = 0 |
| 120 | 0 | Segment 0: Color3 R = 0 |
| 121 | 0 | Segment 0: Color3 G = 0 |
| 122 | 0 | Segment 0: Color3 B = 0 |
| 2 | 1 | Apply/Trigger |
This corresponds to the following MQTT message:
{
"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):
| Register | Value | Description |
|---|---|---|
| 1000 | 0xFFFF | Address = ALL_SENSORS (broadcast) |
| 1001 | 1 | CommandType = SET_PARAMETER |
| 1002 | 1 | Offset = PRE_BUTTON_MODE |
| 1003 | 1 | Button Mode = ENABLED |
| 1004 | 5 | Quantity = 5 |
| 1005 | 2 | Color = COLGREEN |
| 1006 | 1 | Ring Effect = FLASH_RING |
| 1020 | 1 | SMC Apply/Trigger |
This corresponds to the following MQTT message:
{
"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)
| Register | Value | Description |
|---|---|---|
| 1000 | 0xFFFF | Address = ALL_SENSORS (broadcast) |
| 1001 | 2 | CommandType = SET_STATUS |
| 1002 | 4 | Offset = SPECIAL_COMMANDS |
| 1011 | 0 | Special Command = PRE_PRESSED_STATE |
| 1020 | 1 | SMC Apply/Trigger |
This corresponds to the following MQTT message:
{
"Address": "ALL_SENSORS",
"CommandType": "SET_STATUS",
"Offset": "SPECIAL_COMMANDS",
"Payload": "PRE_PRESSED_STATE"
}
SMC Button: Set Button Rotation (all buttons, upside down)
| Register | Value | Description |
|---|---|---|
| 1000 | 0xFFFF | Address = ALL_SENSORS (broadcast) |
| 1001 | 1 | CommandType = SET_PARAMETER |
| 1002 | 3 | Offset = BUTTON_ROTATE |
| 1012 | 1 | Button Rotation = BUTTON_ROTATION_UPSIDEDOWN |
| 1020 | 1 | SMC Apply/Trigger |
This corresponds to the following MQTT message:
{
"Address": "ALL_SENSORS",
"CommandType": "SET_PARAMETER",
"Offset": "BUTTON_ROTATE",
"Payload": "BUTTON_ROTATION_UPSIDEDOWN"
}
SMC Button: Assign Address (all buttons, address 0x1000)
| Register | Value | Description |
|---|---|---|
| 1000 | 0xFFFF | Address = ALL_SENSORS (broadcast) |
| 1001 | 2 | CommandType = SET_STATUS |
| 1002 | 5 | Offset = ADDRESS_TO_BE_ASSIGNED |
| 1013 | 0x1000 | Assign Address = 0x1000 |
| 1020 | 1 | SMC Apply/Trigger |
This corresponds to the following MQTT message:
{
"Address": "ALL_SENSORS",
"CommandType": "SET_STATUS",
"Offset": "ADDRESS_TO_BE_ASSIGNED",
"Payload": "1000"
}
Set register 1013 to
0to assign the next free address automatically (MQTT payload"NEXT").
SMC HUB: Configure Polling Channels (poll buttons 0x1000-0x1003)
| Register | Value | Description |
|---|---|---|
| 1000 | 0 | Address = HUB |
| 1001 | 1 | CommandType = SET_PARAMETER |
| 1002 | 0 | Offset = (none) |
| 1100 | 4 | Polling Channel Count = 4 |
| 1101 | 0x1000 | Polling channel 0 |
| 1102 | 0x1001 | Polling channel 1 |
| 1103 | 0x1002 | Polling channel 2 |
| 1104 | 0x1003 | Polling channel 3 |
| 1020 | 1 | SMC Apply/Trigger |
This corresponds to the following MQTT message:
{
"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)
| Strip | Active | Seg Count | Seg 0 | Seg 1 | Seg 2 | Seg 3 |
|---|---|---|---|---|---|---|
| 1 | 100 | 101 | 110 | 121 | 132 | 143 |
| 2 | 200 | 201 | 210 | 221 | 232 | 243 |
| 3 | 300 | 301 | 310 | 321 | 332 | 343 |
| 4 | 400 | 401 | 410 | 421 | 432 | 443 |
| 5 | 500 | 501 | 510 | 521 | 532 | 543 |
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
1is 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:
0xFFFFaddresses all buttons (broadcast),0the 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
0marks 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.