Summary

Somfy Digital Network (SDN) is a half-duplex RS485 binary protocol for controlling Somfy motorized window-covering products (shades, blinds, drapery). A MASTER controller sends commands to one SLAVE, a group, or broadcast. Devices are addressed by 3-byte NodeID and 4-bit NodeType. This spec covers the MASTER-side message set documented in the SDN protocol manual.

Transport

protocols:
  - serial
serial:
  baud_rate: 4800
  data_bits: 8
  parity: odd
  stop_bits: 1
  flow_control: none  # UNRESOLVED: flow control not stated explicitly in source
auth:
  type: none  # inferred: no auth procedure in source

Traits

- queryable   # inferred from GET_* query commands
- levelable   # inferred from CTRL_MOVETO with position percentage (0-100)

Actions

- id: get_node_addr
  label: Get Node Address
  kind: query
  command: "40"   # MSG=40h GET_NODE_ADDR; DATA length 0
  params: []

- id: set_group_addr
  label: Set Group Address
  kind: action
  command: "51"   # MSG=51h SET_GROUP_ADDR; DATA length 4 (GroupIndex[8], GroupID[24])
  params:
    - name: group_index
      type: integer
      description: Entry in group table (0-15)
    - name: group_id
      type: string
      description: 24-bit group address (3 bytes, LSBF)

- id: get_group_addr
  label: Get Group Address
  kind: query
  command: "41"   # MSG=41h GET_GROUP_ADDR; DATA length 1 (GroupIndex[8])
  params:
    - name: group_index
      type: integer
      description: Entry in group table (0-15)

- id: get_node_app_version
  label: Get Firmware Revision
  kind: query
  command: "74"   # MSG=74h GET_NODE_APP_VERSION; DATA length 0
  params: []

- id: set_node_label
  label: Set Node Label
  kind: action
  command: "55"   # MSG=55h SET_NODE_LABEL; DATA length 16 (string, pad with spaces)
  params:
    - name: label
      type: string
      description: 16-char ASCII label (pad with spaces if shorter)

- id: get_node_label
  label: Get Node Label
  kind: query
  command: "45"   # MSG=45h GET_NODE_LABEL; DATA length 0
  params: []

- id: set_local_ui
  label: Set Local UI Lock
  kind: action
  command: "17"   # MSG=17h SET_LOCAL_UI; DATA length 3 (Function[8], UI_Index[8], Priority[8])
  params:
    - name: function
      type: integer
      description: 00h=Enable/Unlock, 01h=Disable/Lock
    - name: ui_index
      type: integer
      description: 00h=All, 01h=DCT, 02h=Local stimuli, 03h=Local Radio, 04h=Touch Motion, 05h=LEDs
    - name: priority
      type: integer
      description: Priority 00h-FFh (higher = more privileged)

- id: get_local_ui
  label: Get Local UI Status
  kind: query
  command: "27"   # MSG=27h GET_LOCAL_UI; DATA length 1 (UI_Index[8])
  params:
    - name: ui_index
      type: integer
      description: UI index 01h-UI_MAX (see set_local_ui)

- id: set_motor_ip
  label: Set Intermediate Position
  kind: action
  command: "15"   # MSG=15h SET_MOTOR_IP; DATA length 4 (Function[8], IP_Index[8], Value[16])
  params:
    - name: function
      type: integer
      description: 00h=Delete, 01h=Set IP at current pos, 03h=Set IP at specified %, 04h=Divide full range
    - name: ip_index
      type: integer
      description: Intermediate position index (1-16); ignored when function=04h
    - name: value
      type: integer
      description: 16-bit value (position % when function=03h; IP count when function=04h)

- id: get_motor_ip
  label: Get Intermediate Position
  kind: query
  command: "25"   # MSG=25h GET_MOTOR_IP; DATA length 1 (IP_Index[8])
  params:
    - name: ip_index
      type: integer
      description: Intermediate position index (1-16)

- id: set_motor_rolling_speed
  label: Set Motor Rolling Speed
  kind: action
  command: "13"   # MSG=13h SET_MOTOR_ROLLING_SPEED; DATA length 3 (UP_Speed[8], DOWN_Speed[8], Slow_Speed[8])
  params:
    - name: up_speed
      type: integer
      description: Speed during UP movement in rpm (range per motor datasheet)
    - name: down_speed
      type: integer
      description: Speed during DOWN movement in rpm (range per motor datasheet)
    - name: slow_speed
      type: integer
      description: Speed for adjustment movements in rpm (range per motor datasheet)

- id: get_motor_rolling_speed
  label: Get Motor Rolling Speed
  kind: query
  command: "23"   # MSG=23h GET_MOTOR_ROLLING_SPEED; DATA length 0
  params: []

- id: set_network_lock
  label: Set Network Lock
  kind: action
  command: "16"   # MSG=16h SET_NETWORK_LOCK; DATA length 2 (Function[8], Priority[8])
  params:
    - name: function
      type: integer
      description: 00h=Unlock, 01h=Lock at current position, 03h=Save lock on power cycle, 04h=Do not save lock
    - name: priority
      type: integer
      description: Priority 00h-FFh (higher = more privileged); ignored for save/no-save functions

- id: get_network_lock
  label: Get Network Lock Status
  kind: query
  command: "26"   # MSG=26h GET_NETWORK_LOCK; DATA length 0
  params: []

- id: ctrl_moveto
  label: Move To Position
  kind: action
  command: "03"   # MSG=03h CTRL_MOVETO; DATA length 4 (Function[8], Position[16], Reserved[8])
  params:
    - name: function
      type: integer
      description: 00h=DOWN limit, 01h=UP limit, 02h=Intermediate Position, 04h=Position in % of full travel
    - name: position
      type: integer
      description: 16-bit value; IP index (0-15) when function=02h, percentage (0-100) when function=04h; ignored otherwise

- id: ctrl_stop
  label: Stop
  kind: action
  command: "02"   # MSG=02h CTRL_STOP; DATA length 1 (Reserved[8])
  params: []

- id: get_motor_position
  label: Get Motor Position
  kind: query
  command: "0C"   # MSG=0Ch GET_MOTOR_POSITION; DATA length 0
  params: []

- id: get_motor_status
  label: Get Motor Status
  kind: query
  command: "0E"   # MSG=0Eh GET_MOTOR_STATUS; DATA length 0
  params: []

Feedbacks

- id: post_node_addr
  label: Post Node Address
  type: response
  message_id: "60"   # MSG=60h POST_NODE_ADDR; DATA length 0 (address is in frame header)
  fields: []

- id: post_group_addr
  label: Post Group Address
  type: response
  message_id: "61"   # MSG=61h POST_GROUP_ADDR; DATA length 4
  fields:
    - name: group_index
      type: integer
      description: Entry in group table (0-15)
    - name: group_id
      type: string
      description: 24-bit group address (3 bytes, LSBF)

- id: post_node_app_version
  label: Post Firmware Revision
  type: response
  message_id: "75"   # MSG=75h POST_NODE_APP_VERSION; DATA length 6
  fields:
    - name: app_reference
      type: string
      description: 24-bit firmware part number
    - name: app_index_letter
      type: string
      description: 8-bit ASCII firmware major revision (41h-5Ah)
    - name: app_index_number
      type: integer
      description: 8-bit firmware revision
    - name: reserved
      type: integer
      description: 8-bit reserved field

- id: post_node_label
  label: Post Node Label
  type: response
  message_id: "65"   # MSG=65h POST_NODE_LABEL; DATA length 16
  fields:
    - name: label
      type: string
      description: 16-char ASCII label

- id: post_local_ui
  label: Post Local UI Status
  type: response
  message_id: "37"   # MSG=37h POST_LOCAL_UI; DATA length 5
  fields:
    - name: status
      type: enum
      values: [enabled, locked]
      description: 00h=Enabled/Unlocked, 01h=Disabled/Locked
    - name: source_addr
      type: string
      description: 24-bit NodeID of device that sent the lock command
    - name: priority
      type: integer
      description: Lock priority (00h-FFh)

- id: post_motor_ip
  label: Post Intermediate Position
  type: response
  message_id: "35"   # MSG=35h POST_MOTOR_IP; DATA length 4
  fields:
    - name: ip_index
      type: integer
      description: IP index (1-16)
    - name: reserved
      type: integer
      description: 16-bit reserved
    - name: ip_position_percentage
      type: integer
      description: IP position 0-100 percent (FFh if IP not set)

- id: post_motor_rolling_speed
  label: Post Motor Rolling Speed
  type: response
  message_id: "33"   # MSG=33h POST_MOTOR_ROLLING_SPEED; DATA length 3
  fields:
    - name: up_speed
      type: integer
      description: Speed during UP movement (rpm)
    - name: down_speed
      type: integer
      description: Speed during DOWN movement (rpm)
    - name: slow_speed
      type: integer
      description: Speed for adjustments (rpm)

- id: post_network_lock
  label: Post Network Lock Status
  type: response
  message_id: "36"   # MSG=36h POST_NETWORK_LOCK; DATA length 6
  fields:
    - name: status
      type: enum
      values: [unlocked, locked]
      description: 00h=Unlocked, 01h=Locked
    - name: source_addr
      type: string
      description: 24-bit NodeID of device that sent the lock command
    - name: priority
      type: integer
      description: Lock priority (00h-FFh)
    - name: saved
      type: enum
      values: [not_saved, saved]
      description: 00h=Not restored on power cycle, 01h=Restored on power cycle

- id: post_motor_position
  label: Post Motor Position
  type: response
  message_id: "0D"   # MSG=0Dh POST_MOTOR_POSITION; DATA length 5
  fields:
    - name: position_pulse
      type: integer
      description: 16-bit pulse count (range UP_LIMIT to DOWN_LIMIT)
    - name: position_percentage
      type: integer
      description: Position as percentage (0-100)
    - name: reserved
      type: integer
      description: 8-bit reserved
    - name: ip
      type: integer
      description: Matching IP index (01h-IP_MAX); FFh if position does not match any IP

- id: post_motor_status
  label: Post Motor Status
  type: response
  message_id: "0F"   # MSG=0Fh POST_MOTOR_STATUS; DATA length 4
  fields:
    - name: status
      type: enum
      values: [stopped, running, blocked, locked]
      description: 00h=Stopped, 01h=Running, 02h=Blocked (thermal/obstacle), 03h=Locked (NETWORK_LOCK)
    - name: direction
      type: enum
      values: [down, up, unknown]
      description: 00h=Going DOWN, 01h=Going UP, FFh=Unknown
    - name: source
      type: enum
      values: [internal, network, local_ui]
      description: 00h=Internal trigger, 01h=Network message, 02h=Local UI
    - name: cause
      type: integer
      description: 00h=Target reached, 01h=Explicit command, 02h=Wink, 20h=Obstacle, 21h=Over-current, 22h=Thermal, 30h=Run time exceeded, 32h=Timeout, FFh=Reset/PowerUp

- id: ack
  label: Acknowledgement
  type: response
  message_id: "7F"   # MSG=7Fh ACK; DATA length 0
  fields: []

- id: nack
  label: Negative Acknowledgement
  type: response
  message_id: "6F"   # MSG=6Fh NACK; DATA length 1
  fields:
    - name: error_code
      type: integer
      description: 01h=Data out of range, 10h=Unknown message, 11h=Message length error, FFh=Busy

Variables

# UNRESOLVED: settable parameters are exposed through SET_* actions; no separate variable model in source

Events

# UNRESOLVED: source documents one unsolicited behavior - some devices can send their address when a local pushbutton is pressed (POST_NODE_ADDR) - but no general event model is defined
- id: unsolicited_node_addr
  label: Unsolicited Node Address Announcement
  description: Some devices can send POST_NODE_ADDR (60h) without a MASTER request when the user presses a local pushbutton on the device.
  message_id: "60"

Macros

# UNRESOLVED: no multi-step macros described in source

Safety

confirmation_required_for: []
interlocks:
  - id: network_lock
    description: A device under NETWORK_LOCK (Lock function 01h) will reject CTRL_XXX movements and SET_MOTOR_LIMITS/SET_TILT_LIMITS unless the requesting command carries equal or higher priority. NACK(NODE_IS_LOCKED) is returned otherwise.
  - id: local_ui_lock
    description: Local UI components (DCT, LEDs, Bluetooth, Touch Motion, local stimuli) can be individually disabled via SET_LOCAL_UI with a priority level; further lock changes require equal or higher priority or NACK(LOW_PRIORITY) is returned.
  - id: motor_thermal_overcurrent_protection
    description: Motor self-reports Blocked status (02h) on thermal protection, over-current, or obstacle detection. These are device-internal interlocks (cause codes 20h obstacle, 21h over-current, 22h thermal) reported via POST_MOTOR_STATUS.
# UNRESOLVED: no power-on sequencing or voltage/current requirements stated in refined source

Notes

Frame structure — every message is wrapped in the SDN frame: MSG | ACK/LEN | NODE_TYPE | SOURCE@(3 bytes) | DEST@(3 bytes) | DATA(0-21 bytes) | CHECKSUM(2 bytes). Minimum frame length is 11 bytes, maximum 32. The command: field on each action is the MSG opcode byte only — the implementer must construct the full frame around it.

Bit inversion — to maintain backward compatibility with earlier protocol versions, all data bits must be inverted before transmission. Transmitting byte 58h means putting NOT(58h) = A7h on the bus. This applies to every byte in the frame including MSG and CHECKSUM.

Checksum — computed as the sum of the complement of every byte in the frame (bytes 1 through n-2), occupying the final 2 bytes. Basic error detection only; no correction.

LSBF — source/destination addresses are transmitted least-significant-byte first. A NodeID printed 05:04:03 on the device label appears in the frame as bytes 03 04 05.

Addressing modes — Point-to-Point uses DEST@ = target NodeID. Group uses SOURCE@ = GroupID and DEST@ = 00 00 00. Broadcast uses DEST@ = FF FF FF. NodeType filtering further restricts which devices act on a frame.

Timings — Treq ≥ 10 ms before a MASTER may transmit. Tc ≤ 1 ms between consecutive characters of a frame. Tfree = 3 ms typical for end-of-frame detection (no sync byte). Trep (slave reply delay) is randomized between 5 ms and 255 ms.

Acknowledgements — ACK is only sent when the requesting MASTER sets the ACK bit (bit 7 of byte 2 in the frame header). For SET_* messages, ACK is sent after parameters are saved; for CTRL_* messages, ACK is sent when execution starts (not when finished). GET_* messages get no ACK because the POST_* response is itself the acknowledgement.

Collision avoidance — broadcast and group commands should NOT request ACK or feedback, since multiple slaves may reply simultaneously on the RS485 bus and corrupt each other's transmissions.

Physical layer — source specifies RS485 (not RS-232). 4800 baud, 8 data bits, odd parity, 1 stop bit.

Provenance

source_domains:
  - service.somfy.com
source_urls:
  - https://service.somfy.com/downloads/bui_v4/sdn-integration-guide--preliminary.pdf
retrieved_at: 2026-06-02T06:13:23.596Z
last_checked_at: 2026-06-02T22:14:49.105Z

Verification Summary

verdict: verified
checked_at: 2026-06-02T22:14:49.105Z
matched_actions: 18
action_count: 18
confidence: medium
summary: "All 18 spec actions traced to source (dip-safe re-verify). (12 unresolved item(s) noted in Known Gaps.)"

Known Gaps

- "physical wiring polarity, terminator resistance, and electrical specs not stated in refined source"
- "firmware version compatibility ranges not stated"
- "flow control not stated explicitly in source"
- "settable parameters are exposed through SET_* actions; no separate variable model in source"
- "source documents one unsolicited behavior - some devices can send their address when a local pushbutton is pressed (POST_NODE_ADDR) - but no general event model is defined"
- "no multi-step macros described in source"
- "no power-on sequencing or voltage/current requirements stated in refined source"
- "termination resistor value, bus length limits, recommended cable type, idle-state polarity not stated in refined source"
- "SET_MOTOR_LIMITS and SET_TILT_LIMITS messages are referenced in the NETWORK_LOCK section but not documented in the refined source — these are MASTER actions that exist but their opcodes/DATA structures are not in the excerpt provided"
- "CTRL_MOVE message referenced in motor status Cause 32h (Timeout exceeded \"when using CTRL_MOVE\") is not documented in the refined source — opcode not given"
- "CTRL_NETWORK_LOCK referenced in lock section as an additional control message but not documented in the refined source"
- "WINK behaviour (Cause 02h in POST_MOTOR_STATUS) suggests a Wink control command exists but it is not documented in the refined source"

From the AI4AV catalog (https://ai4av.net) · ODbL-1.0