Summary
This spec documents the Somfy Digital Network (SDN) bus protocol used by Somfy RS-485 motorized window-covering products. The source document (Somfy "SDN Integration Guide", DOC155888, Nov 2019) describes a half-duplex RS-485 master/slave protocol at 4800 baud, 8 data bits, odd parity, with master-driven SET/CTRL/GET messages and slave POST/ACK/NACK replies.
Transport
protocols:
- serial
serial:
baud_rate: 4800
data_bits: 8
parity: odd
stop_bits: 1
# UNRESOLVED: flow control not stated in source (assumed none for half-duplex RS-485)
flow_control: null
electrical: rs485
bit_order: lsb_first
bit_inversion: true # source §4.2: all data bits are inverted before transmission for backward compatibility
start_bit_level: 0
stop_bit_level: 1
character_coding: nrz
auth:
type: none # inferred: no auth procedure in source
The on-wire frame layout (source §5) is identical for every message:
| Byte 1 | Byte 2 | Byte 3 | Bytes 4-6 | Bytes 7-9 | Bytes 10..n-2 | Bytes n-1, n |
|---|---|---|---|---|---|---|
| MSG | ACK/LEN | NODE TYPE (src hi-nibble / dst lo-nibble) | SOURCE@ (LSBF) | DEST@ (LSBF) | DATA | CHECKSUM |
- Minimum frame size: 11 bytes (no DATA).
- Maximum frame size: 32 bytes (21 bytes of DATA).
ACK/LENbyte: B7 = ACK request, b6 = EXT (reserved, always 0), b5..b0 = LEN.CHECKSUM= sum of Bytes 1..n-2 (complement-add error check, no correction).- Bit inversion: every data byte is bitwise-NOTed before transmission (e.g. 58h → A7h).
Traits
# - queryable (GET_xxx / POST_xxx status requests present in source)
# Source does not document explicit power on/off, input routing, or level control,
# so other listed traits are not added.
Actions
# All commands are framed as: MSG(1B) ACK/LEN(1B) NodeType(1B) SOURCE@(3B,LSBF)
# DEST@(3B,LSBF) DATA(nB) CHECKSUM(2B). Only the MSG byte and DATA payload vary
# by command; the header layout is documented in the Transport section above.
#
# Command field below names the MSG byte and DATA-field template per source §6.
# Parameterized fields are written as {Name} in the DATA template and enumerated
# in `params:`.
#
# Kind: action for SET_xxx and CTRL_xxx; kind: query for GET_xxx.
# ── Device Management ────────────────────────────────────────────────────────
- id: get_node_addr
label: Get Node Address
kind: query
command: "MSG=40h DATA=<empty>"
description: >
Request a device (or all devices on the bus) to report its NodeID. Response
is POST_NODE_ADDR (60h) with the NodeID carried in the SOURCE@ header.
Note: with many devices on the bus, replies from all devices are not
guaranteed (source §6.1.1).
params: []
- id: set_group_addr
label: Set Group Address
kind: action
command: "MSG=51h DATA=GroupIndex(1B) GroupID(3B,LSBF)"
description: >
Write a GroupID into one of the 16 group table entries of a device.
params:
- name: GroupIndex
type: integer
min: 0
max: 15
description: Entry index in the device's group table.
- name: GroupID
type: integer
min: 0
max: 16777215
description: 24-bit group address, LSBF.
- id: get_group_addr
label: Get Group Address
kind: query
command: "MSG=41h DATA=GroupIndex(1B)"
description: >
Read a GroupID from a device's group table.
params:
- name: GroupIndex
type: integer
min: 0
max: 15
# ── Device Information ───────────────────────────────────────────────────────
- id: get_node_app_version
label: Get Firmware Version
kind: query
command: "MSG=74h DATA=<empty>"
description: >
Request firmware version. Response is POST_NODE_APP_VERSION (75h) carrying
App_Reference(3B), App_IndexLetter(1B ASCII 0x41..0x5A), App_IndexNumber(1B),
Reserved(1B).
params: []
- id: set_node_label
label: Set User Label
kind: action
command: "MSG=55h DATA=Label(16B,ASCII,space-padded)"
description: >
Write a 16-character user-defined text label. Always 16 bytes; pad with
space (0x20) if the string is shorter (source §6.2.2).
params:
- name: Label
type: string
max_length: 16
description: Always padded/truncated to 16 ASCII bytes.
- id: get_node_label
label: Get User Label
kind: query
command: "MSG=45h DATA=<empty>"
description: Response is POST_NODE_LABEL (65h) with 16 ASCII bytes.
params: []
# ── Device Configuration ─────────────────────────────────────────────────────
- id: set_local_ui
label: Set Local UI Lock State
kind: action
command: "MSG=17h DATA=Function(1B) UI_Index(1B) Priority(1B)"
description: >
Enable/Unlock (Function=00h) or Disable/Lock (Function=01h) a local user
interface element. UI_Index selects which element (00h=all, 01h=DCT input,
02h=local stimuli, 03h=local radio, 04h=touch motion, 05h=LEDs). Priority
governs whether a higher-priority controller can override the lock.
Items disabled are ignored until re-enabled. Source §6.3.1.
params:
- name: Function
type: enum
values: [00h, 01h]
description: 00h=Enable/Unlock, 01h=Disable/Lock.
- name: UI_Index
type: enum
values: [00h, 01h, 02h, 03h, 04h, 05h]
description: 00h=all local controls/feedbacks, 01h=DCT input, 02h=local stimuli, 03h=local radio, 04h=touch motion, 05h=LEDs.
- name: Priority
type: integer
min: 0
max: 255
description: Higher number = higher priority.
- id: get_local_ui
label: Get Local UI Lock State
kind: query
command: "MSG=27h DATA=UI_Index(1B)"
description: >
Read the current lock state of a UI element. Response is POST_LOCAL_UI
(37h) with Status(1B), Source_Addr(3B), Priority(1B). Source §6.3.1.
params:
- name: UI_Index
type: integer
min: 1
description: 0x01..UI_MAX (refer to UI list in SET_LOCAL_UI).
- id: set_motor_ip
label: Set Intermediate Position
kind: action
command: "MSG=15h DATA=Function(1B) IP_Index(1B) Value(2B)"
description: >
Manage intermediate positions (IPs). Function: 00h=delete IP, 01h=set IP at
current position, 03h=set IP at specified % (Value = 0..100), 04h=divide
full range into N equally-spaced IPs (Value = N, IP_Index ignored). IP_Index
range 1..16. Setting an IP outside the configured limits returns
NACK(DATA_ERROR). Source §6.3.2.
params:
- name: Function
type: enum
values: [00h, 01h, 03h, 04h]
- name: IP_Index
type: integer
min: 1
max: 16
- name: Value
type: integer
description: Depends on Function: 00h/01h=ignored, 03h=position % (0..100), 04h=IP count.
- id: get_motor_ip
label: Get Intermediate Position
kind: query
command: "MSG=25h DATA=IP_Index(1B)"
description: >
Read the percentage of one IP. Response is POST_MOTOR_IP (35h) with
IP_index(1B), Reserved(2B), IP_position_percentage(1B, FFh if not set).
params:
- name: IP_Index
type: integer
min: 1
max: 16
- id: set_motor_rolling_speed
label: Set Motor Speed (DC motors only)
kind: action
command: "MSG=13h DATA=UP_Speed(1B) DOWN_Speed(1B) Slow_Speed(1B)"
description: >
Set UP / DOWN / Slow speeds in rpm. Speed range and default depend on the
specific motor - refer to device technical datasheet (not in this source).
Source §6.3.3.
params:
- name: UP_Speed
type: integer
description: Speed during UP movement, rpm. Range per motor datasheet.
- name: DOWN_Speed
type: integer
description: Speed during DOWN movement, rpm. Range per motor datasheet.
- name: Slow_Speed
type: integer
description: Speed for adjustment movements, rpm. Range per motor datasheet.
- id: get_motor_rolling_speed
label: Get Motor Speed
kind: query
command: "MSG=23h DATA=<empty>"
description: >
Read current UP/DOWN/Slow speeds. Response is POST_MOTOR_ROLLING_SPEED
(33h).
params: []
- id: set_network_lock
label: Set Network Lock
kind: action
command: "MSG=16h DATA=Function(1B) Priority(1B)"
description: >
Lock or unlock the device from network commands. Function: 00h=Unlock,
01h=Lock, 03h=Save lock state across power cycle, 04h=Do not save.
When locked, only CTRL_xxx messages with Priority ≥ current lock are
accepted; others are rejected with NACK(NODE_IS_LOCKED). Default factory
state is "Do not save" (Function=04h). Source §6.3.4.
params:
- name: Function
type: enum
values: [00h, 01h, 03h, 04h]
- name: Priority
type: integer
min: 0
max: 255
description: Higher number = higher priority. Ignored for Function 03h/04h.
- id: get_network_lock
label: Get Network Lock
kind: query
command: "MSG=26h DATA=<empty>"
description: >
Read current network-lock state. Response is POST_NETWORK_LOCK (36h) with
Status(1B), Source_Addr(3B), Priority(1B), Saved(1B).
params: []
# ── Device Control ───────────────────────────────────────────────────────────
- id: ctrl_moveto
label: Move to Position
kind: action
command: "MSG=03h DATA=Function(1B) Position(2B) Reserved(1B)"
description: >
Drive motor to a position. Function: 00h=DOWN limit (Position ignored),
01h=UP limit (Position ignored), 02h=Intermediate Position (Position = IP
index 0..15), 04h=Position in % of full travel (Position = 0..100).
Source §6.4.1.
params:
- name: Function
type: enum
values: [00h, 01h, 02h, 04h]
- name: Position
type: integer
description: Meaning depends on Function: ignored for 00h/01h, IP index (0..15) for 02h, % (0..100) for 04h.
- id: ctrl_stop
label: Stop Motor
kind: action
command: "MSG=02h DATA=Reserved(1B)"
description: >
Immediately stop the motor without speed ramp-down. Source §6.4.2.
params: []
# ── Device Status ────────────────────────────────────────────────────────────
- id: get_motor_position
label: Get Motor Position
kind: query
command: "MSG=0Ch DATA=<empty>"
description: >
Read current position. Response is POST_MOTOR_POSITION (0Dh) with
Position_pulse(2B), Position_percentage(1B), Reserved(1B), IP(1B, FFh if
not at an IP). Position is sent even while motor is running.
params: []
- id: get_motor_status
label: Get Motor Status
kind: query
command: "MSG=0Eh DATA=<empty>"
description: >
Read motor state. Response is POST_MOTOR_STATUS (0Fh) with
Status(1B), Direction(1B), Source(1B), Cause(1B). Source §6.5.2.
params: []
Feedbacks
# Derived from POST_xxx response messages (slave → master). Each entry mirrors
# one POST payload so a controller can interpret any slave response it sees on
# the bus.
- id: post_node_addr
type: object
description: >
Reply to GET_NODE_ADDR (40h). Carries no DATA - the reporting NodeID is in
the frame's SOURCE@ header. Source §6.1.1.
- id: post_group_addr
type: object
description: >
Reply to GET_GROUP_ADDR (41h). DATA: GroupIndex(1B), GroupID(3B,LSBF).
Source §6.1.2.
- id: post_node_app_version
type: object
description: >
Reply to GET_NODE_APP_VERSION (74h). DATA: App_Reference(3B),
App_IndexLetter(1B ASCII 0x41..0x5A), App_IndexNumber(1B), Reserved(1B).
Source §6.2.1.
- id: post_node_label
type: object
description: >
Reply to GET_NODE_LABEL (45h). DATA: 16 ASCII bytes (space-padded).
Source §6.2.2.
- id: post_local_ui
type: object
description: >
Reply to GET_LOCAL_UI (27h). DATA: Status(1B), Source_Addr(3B), Priority(1B).
Status: 00h=Enabled/Unlocked, 01h=Disabled/Locked. When unlocked,
Source_Addr=000000h and Priority=00h. Source §6.3.1.
- id: post_motor_ip
type: object
description: >
Reply to GET_MOTOR_IP (25h). DATA: IP_index(1B), Reserved(2B),
IP_position_percentage(1B, 0..100, FFh if IP not set). Source §6.3.2.
- id: post_motor_rolling_speed
type: object
description: >
Reply to GET_MOTOR_ROLLING_SPEED (23h). DATA: UP_Speed(1B), DOWN_Speed(1B),
Slow_Speed(1B). Source §6.3.3.
- id: post_network_lock
type: object
description: >
Reply to GET_NETWORK_LOCK (26h). DATA: Status(1B), Source_Addr(3B),
Priority(1B), Saved(1B). Status: 00h=Unlocked, 01h=Locked. Saved: 00h=not
restored on power cycle, 01h=restored. When unlocked, Source_Addr=000000h,
Priority=00h. Source §6.3.4.
- id: post_motor_position
type: object
description: >
Reply to GET_MOTOR_POSITION (0Ch). DATA: Position_pulse(2B, range
UP_LIMIT..DOWN_LIMIT), Position_percentage(1B, 0..100), Reserved(1B),
IP(1B, 01h..IP_MAX, FFh if not at an IP). Source §6.5.1.
- id: post_motor_status
type: object
description: >
Reply to GET_MOTOR_STATUS (0Eh). DATA: Status(1B), Direction(1B), Source(1B),
Cause(1B). Source §6.5.2.
Status: 00h=Stopped, 01h=Running, 02h=Blocked (thermal/obstacle),
03h=Locked by another device (NETWORK_LOCK).
Direction: 00h=Going DOWN, 01h=Going UP (or last direction if stopped),
FFh=Unknown.
Source: 00h=Internal (limit/IP/over-current/obstacle/thermal), 01h=Network
message, 02h=Local UI.
Cause: 00h=Target reached, 01h=Explicit command, 02h=Wink, 20h=Obstacle
detection, 21h=Over-current protection, 22h=Thermal protection,
30h=Run time exceeded, 32h=Timeout exceeded (CTRL_MOVE > 2 min),
FFh=Reset/PowerUp.
Events
- id: ack
description: >
Acknowledgement (MSG=7Fh, 0 bytes DATA). Sent by a slave when the master
requested an ACK and the message was processed without error. Source §6.1.3.
- id: nack
description: >
Negative acknowledgement (MSG=6Fh, 1 byte ErrorCode). Sent by a slave when
the master requested an ACK but the message could not be processed.
Source §6.1.3.
error_codes:
- code: 01h
meaning: "Data out of range"
- code: 10h
meaning: "Unknown message (MSG identifier not recognized)"
- code: 11h
meaning: "Message length error (below minimum)"
- code: FFh
meaning: "Busy - cannot process message"
Macros
# No multi-step sequences are described in source.
# UNRESOLVED: power-on sequencing and interlock macros are not enumerated in
# the source protocol guide; relevant wiring/bus-topology guidance lives in
# DOC114316 (separate document).
Safety
confirmation_required_for: []
interlocks:
- name: network_lock
description: >
When SET_NETWORK_LOCK(01h) is active on a device, all CTRL_xxx
movements and limit-changing SETs are rejected with NACK(NODE_IS_LOCKED)
unless the incoming message carries a Priority ≥ the current lock
priority. Source §6.3.4.
# UNRESOLVED: physical safety warnings, installation interlocks, and motor
# thermal/obstacle protection behavior are referenced in the protocol (Cause
# codes 20h/21h/22h) but not detailed as safety procedures in this source.
Notes
- Source scope: This spec was generated from Somfy's generic "SDN Integration Guide" (DOC155888, Nov 2019). The document covers the RS-485 SDN bus used by multiple Somfy motorized product families, not the myLink specifically. The myLink is a WiFi-to-RF bridge product; whether it exposes the SDN RS-485 surface described here is not stated in this source and is left UNRESOLVED.
- Bit inversion (source §4.2): Every data byte is bitwise-NOTed on the wire for backward compatibility with early protocol versions. Implementers must invert again on receive (e.g. 58h on the application side ↔ A7h on the bus). The CHECKSUM is calculated over the pre-inversion (logical) bytes.
- Bit order: LSB first on the wire (source §4.2).
- Addressing modes (source §3.4, §5.4): Point-to-point (specific NodeID), Group (DEST@=000000h), Broadcast (DEST@=FFFFFFh), and NodeType filtering (DEST NodeType in byte 3). SOURCE@ and DEST@ are LSBF.
- Timing (source §4.3): Tc ≤ 1ms between consecutive characters in a frame; Tfree ≥ 3ms bus idle; Treq ≥ 10ms master wait before next request; Trep 5..255ms (randomized) slave reply delay. No sync byte — frames are delimited by bus inactivity.
- Collisions (source §3.7): RS-485 is collision-prone. Source recommends avoiding ACK/feedback in group or broadcast addressing; if ACKs are used, implement a retry strategy on NACK or ACK timeout.
- Data length caveat (source §5.5): The "DATA length" column is a minimum; devices may append extra bytes. Reserved fields must be transmitted (set to 00h or FFh per the source).
- NodeType table (source §3.2.2): Reserved NodeTypes in this document: 02h=Ø30 DC Serie RS485, 05h=RS485 RTS transmitter, 06h=Glydea RS485, 07h=Ø50 AC Serie RS485, 08h=Ø50 DC Serie RS485, 09h=Ø40 AC Serie RS485 (not yet available at publication).
- Companion documents: DOC114316 "SDN Bus Wiring Guide" (bus topology, cable length, wiring) and "SDN Frame Builder" PC tool (encode/decode helper) are referenced by the source but not reproduced here.
Provenance
source_domains:
- service.somfy.com
source_urls:
- https://service.somfy.com/downloads/bui_v4/sdn-integration-guide--preliminary.pdf
retrieved_at: 2026-04-29T08:47:12.406Z
last_checked_at: 2026-06-02T05:46:19.451Z
Verification Summary
verdict: verified
checked_at: 2026-06-02T05:46:19.451Z
matched_actions: 18
action_count: 18
confidence: medium
summary: "All 18 spec actions matched exactly to source; transport parameters (4800 baud, odd parity, bit inversion) verified verbatim; source coverage complete. (6 unresolved item(s) noted in Known Gaps.)"
Known Gaps
- 7Fh
- 6Fh
- "source is the generic Somfy SDN protocol guide, not a myLink-specific datasheet. The myLink is a WiFi-to-RF bridge product; this protocol does not directly describe the myLink's externally-exposed control surface. Coverage of the myLink as such is not stated in the source."
- "voltage, current, and power specifications not stated in source."
- "cable length, bus topology, and termination details are in a separate document (DOC114316 \"SDN Bus Wiring Guide\") and are not reproduced here."
- "flow control not stated in source (assumed none for half-duplex RS-485)"
- "power-on sequencing and interlock macros are not enumerated in"
- "physical safety warnings, installation interlocks, and motor"
From the AI4AV catalog (https://ai4av.net) · ODbL-1.0