Summary
Jandy AquaLink RS pool/spa controller talks RS485 to a bus of equipment (SWG, ePumps, heaters, lights, chem feeders, panels). This spec covers the Jandy DLE/STX/ETX framed protocol at 9600 8N1, including the bus-side device IDs and per-device command sets.
Transport
protocols:
- serial
serial:
baud_rate: 9600
data_bits: 8
parity: none
stop_bits: 1
flow_control: none
auth:
type: none # inferred: no auth procedure in source
Traits
- queryable # inferred from status query commands (0x02, 0x0D, 0x1F, 0x16, 0x07, 0x13)
- levelable # inferred from SWG percent, ePump RPM/Watts, heater, light commands
- routable # inferred from iAqualinkTouch page navigation + PDA menu commands
Actions
# === Core Jandy frame envelope ===
# All Jandy-protocol actions share the envelope:
# [DLE 0x10] [STX 0x02] [DEST] [CMD] [DATA...] [CHKSUM] [DLE 0x10] [ETX 0x03]
# CHKSUM = (sum of DEST..last DATA byte) & 0xFF
# A 0x10 appearing in DATA is escaped as 0x10 0x00 (skip extra 0x00 on receive).
# Each action below lists DEST, CMD, DATA, then the literal payload with a placeholder checksum.
# --- Core commands (CMD byte 0x00-0x09) ---
- id: probe
label: Probe (poll device)
kind: action
command: "10 02 {dest} 00 {chksum} 10 03"
params:
- name: dest
type: integer
description: Target device ID (0x00 = master; 0x08-0xF4 per device table)
notes: CMD_PROBE (0x00). Polling/probe message.
- id: send_ack
label: Send ACK response
kind: action
command: "10 02 00 01 80 00 {chksum} 10 03"
params: []
notes: CMD_ACK (0x01) with ACK_NORMAL (0x80), no command echoed. To acknowledge received packet to master (DEST=0x00).
- id: ack_screen_busy_scroll
label: ACK (screen busy, cache next)
kind: action
command: "10 02 00 01 81 00 {chksum} 10 03"
params: []
notes: CMD_ACK with ACK_SCREEN_BUSY_SCROLL (0x81).
- id: ack_screen_busy_block
label: ACK (screen busy, block)
kind: action
command: "10 02 00 01 83 00 {chksum} 10 03"
params: []
notes: CMD_ACK with ACK_SCREEN_BUSY_BLOCK (0x83).
- id: status
label: Status (display panels)
kind: action
command: "10 02 {dest} 02 {chksum} 10 03"
params:
- name: dest
type: integer
description: Panel device ID
notes: CMD_STATUS (0x02). Status info for display panels.
- id: message_short
label: Display message (16 bytes)
kind: action
command: "10 02 {dest} 03 {message16} {chksum} 10 03"
params:
- name: dest
type: integer
description: Target device ID
- name: message16
type: string
description: Up to 16 bytes of message payload
notes: CMD_MSG (0x03). Display 16-byte message.
- id: message_long
label: Display message (128 bytes)
kind: action
command: "10 02 {dest} 04 {message128} {chksum} 10 03"
params:
- name: dest
type: integer
description: Target device ID
- name: message128
type: string
description: Up to 128 bytes of message payload
notes: CMD_MSG_LONG (0x04).
- id: message_loop_start
label: Message loop start
kind: action
command: "10 02 00 08 {chksum} 10 03"
params: []
notes: CMD_MSG_LOOP_ST (0x08). Sent by device to mark start of message loop cycle.
# --- Aquapure SWG (device IDs 0x50-0x53) ---
- id: swg_set_percent
label: Set SWG chlorine generation percent
kind: action
command: "10 02 {swg_id} 11 {percent} {chksum} 10 03"
params:
- name: swg_id
type: integer
description: Aquapure device ID (0x50-0x53)
- name: percent
type: integer
description: "0-100 percent; >100 (e.g. 0x65=101) enters boost mode; 0xFF=all-on service mode"
notes: CMD_PERCENT (0x11). Example 75%: 10 02 50 11 4B 72 10 03.
- id: swg_query_ppm
label: Query SWG PPM and status
kind: action
command: "10 02 {swg_id} 02 {chksum} 10 03"
params:
- name: swg_id
type: integer
description: Aquapure device ID (0x50-0x53)
notes: CMD_STATUS (0x02) to SWG. Response: 10 02 00 16 PPM VAL STATUS chksum 10 03. PPM = PPM_VAL * 100. STATUS per SWG status byte table.
# --- ePump variable speed (device IDs 0x78-0x7B standard; 0xE0-0xE3 panel rev W+) ---
- id: epump_set_watts
label: Set ePump Watts
kind: action
command: "10 02 {pump_id} 45 00 {hi_watts} {lo_watts} {chksum} 10 03"
params:
- name: pump_id
type: integer
description: ePump device ID (0x78-0x7B or 0xE0-0xE3)
- name: hi_watts
type: integer
description: High byte of 16-bit watts
- name: lo_watts
type: integer
description: Low byte of 16-bit watts
notes: CMD_EPUMP_WATTS (0x45). Watts = hi*256 + lo. Example 1309W: 10 02 78 45 00 05 1D chksum 10 03.
- id: epump_set_rpm
label: Set ePump RPM
kind: action
command: "10 02 {pump_id} 44 00 {hi_rpm} {lo_rpm} {chksum} 10 03"
params:
- name: pump_id
type: integer
description: ePump device ID
- name: hi_rpm
type: integer
description: High byte of 16-bit RPM
- name: lo_rpm
type: integer
description: Low byte of 16-bit RPM
notes: CMD_EPUMP_RPM (0x44). RPM = hi*256 + lo.
- id: epump_status_request
label: Request ePump status
kind: action
command: "10 02 {pump_id} 1F {chksum} 10 03"
params:
- name: pump_id
type: integer
description: ePump device ID
notes: CMD_EPUMP_STATUS (0x1F). Bidirectional. Response structure: 10 02 00 1F Orig_CMD 0x00 Hi_W Lo_W Hi_RPM Lo_RPM ... chksum 10 03. Watts at bytes 7-8, RPM at bytes 9-10.
# --- JXi Heater (device IDs 0x68-0x6B) ---
- id: jxi_ping
label: JXi heater ping
kind: action
command: "10 02 {heater_id} 0C {chksum} 10 03"
params:
- name: heater_id
type: integer
description: JXi heater device ID (0x68-0x6B)
notes: CMD_JXI_PING (0x0C). Poll heater status.
- id: jxi_query_status
label: JXi heater status
kind: action
command: "10 02 {heater_id} 0D {chksum} 10 03"
params:
- name: heater_id
type: integer
description: JXi heater device ID
notes: CMD_JXI_STATUS (0x0D). Byte 6 == 0x10 indicates error condition. Example: 10 02 00 0D 00 00 00 1F 10 03.
# --- LX Heater (device IDs 0x38-0x3B) ---
- id: lx_ping
label: LX heater ping
kind: action
command: "10 02 {heater_id} 0C {chksum} 10 03"
params:
- name: heater_id
type: integer
description: LX heater device ID (0x38-0x3B)
notes: Same command as JXi ping, different device ID range.
- id: lx_query_status
label: LX heater status
kind: action
command: "10 02 {heater_id} 0D {chksum} 10 03"
params:
- name: heater_id
type: integer
description: LX heater device ID
notes: CMD_JXI_STATUS (0x0D) reused. Example: 10 02 00 0A 00 00 00 1F 10 03.
# --- iAqualinkTouch panels (device IDs 0x30-0x33) ---
- id: iaq_page_start
label: iAqualinkTouch page start
kind: action
command: "10 02 {panel_id} 23 {chksum} 10 03"
params:
- name: panel_id
type: integer
description: iAqualinkTouch device ID (0x30-0x33)
notes: CMD_IAQ_PAGE_START (0x23). Begin new menu page.
- id: iaq_page_button
label: iAqualinkTouch page button
kind: action
command: "10 02 {panel_id} 24 {button_data} {chksum} 10 03"
params:
- name: panel_id
type: integer
description: iAqualinkTouch device ID
- name: button_data
type: string
description: Button definition payload
notes: CMD_IAQ_PAGE_BUTTON (0x24).
- id: iaq_page_message
label: iAqualinkTouch page message
kind: action
command: "10 02 {panel_id} 25 {message_data} {chksum} 10 03"
params:
- name: panel_id
type: integer
description: iAqualinkTouch device ID
- name: message_data
type: string
description: Page text/content payload
notes: CMD_IAQ_PAGE_MSG (0x25).
- id: iaq_table_message
label: iAqualinkTouch table message
kind: action
command: "10 02 {panel_id} 26 {table_data} {chksum} 10 03"
params:
- name: panel_id
type: integer
description: iAqualinkTouch device ID
- name: table_data
type: string
description: Table data payload
notes: CMD_IAQ_TABLE_MSG (0x26).
- id: iaq_page_end
label: iAqualinkTouch page end
kind: action
command: "10 02 {panel_id} 28 {chksum} 10 03"
params:
- name: panel_id
type: integer
description: iAqualinkTouch device ID
notes: CMD_IAQ_PAGE_END (0x28). Page complete.
- id: iaq_startup
label: iAqualinkTouch startup
kind: action
command: "10 02 {panel_id} 29 {chksum} 10 03"
params:
- name: panel_id
type: integer
description: iAqualinkTouch device ID
notes: CMD_IAQ_STARTUP (0x29).
- id: iaq_poll
label: iAqualinkTouch poll (ready)
kind: action
command: "10 02 {panel_id} 30 {chksum} 10 03"
params:
- name: panel_id
type: integer
description: iAqualinkTouch device ID
notes: CMD_IAQ_POLL (0x30). Panel ready to receive.
- id: iaq_ctrl_ready
label: iAqualinkTouch control ready
kind: action
command: "10 02 {panel_id} 31 {chksum} 10 03"
params:
- name: panel_id
type: integer
description: iAqualinkTouch device ID
notes: CMD_IAQ_CTRL_READY (0x31). Ready for big control command.
- id: iaq_page_continue
label: iAqualinkTouch page continue
kind: action
command: "10 02 {panel_id} 40 {chksum} 10 03"
params:
- name: panel_id
type: integer
description: iAqualinkTouch device ID
notes: CMD_IAQ_PAGE_CONTINUE (0x40). More pages follow.
- id: iaq_msg_long
label: iAqualinkTouch long message (popup)
kind: action
command: "10 02 {panel_id} 2C {message} {chksum} 10 03"
params:
- name: panel_id
type: integer
description: iAqualinkTouch device ID
- name: message
type: string
description: Popup message payload
notes: CMD_IAQ_MSG_LONG (0x2C).
- id: iaq_main_status
label: iAqualinkTouch main status
kind: action
command: "10 02 {panel_id} 70 {status_data} {chksum} 10 03"
params:
- name: panel_id
type: integer
description: iAqualinkTouch device ID
- name: status_data
type: string
description: "100+ byte main status payload"
notes: CMD_IAQ_MAIN_STATUS (0x70). Large status, full system view.
- id: iaq_1touch_status
label: iAqualinkTouch OneTouch status
kind: action
command: "10 02 {panel_id} 71 {status_data} {chksum} 10 03"
params:
- name: panel_id
type: integer
description: iAqualinkTouch device ID
- name: status_data
type: string
description: OneTouch emulation status payload
notes: CMD_IAQ_1TOUCH_STATUS (0x71).
- id: iaq_aux_status
label: iAqualinkTouch auxiliary status
kind: action
command: "10 02 {panel_id} 72 {aux_data} {chksum} 10 03"
params:
- name: panel_id
type: integer
description: iAqualinkTouch device ID
- name: aux_data
type: string
description: "Large auxiliary equipment status (pumps, heaters, etc.)"
notes: CMD_IAQ_AUX_STATUS (0x72).
- id: iaq_cmd_ready
label: iAqualinkTouch command ready
kind: action
command: "10 02 {panel_id} 73 {chksum} 10 03"
params:
- name: panel_id
type: integer
description: iAqualinkTouch device ID
notes: CMD_IAQ_CMD_READY (0x73).
- id: iaq_title_message
label: iAqualinkTouch title message
kind: action
command: "10 02 {panel_id} 2D {title} {chksum} 10 03"
params:
- name: panel_id
type: integer
description: iAqualinkTouch device ID
- name: title
type: string
description: Product name/title payload
notes: CMD_IAQ_TITLE_MESSAGE (0x2D).
# --- PDA / AquaPalm (device IDs 0x60-0x63) ---
- id: pda_cmd_0x04
label: PDA command 0x04 (menu building)
kind: action
command: "10 02 {pda_id} 04 {data} {chksum} 10 03"
params:
- name: pda_id
type: integer
description: PDA device ID (0x60-0x63)
- name: data
type: string
description: Menu payload
notes: CMD_PDA_0x04. Purpose not fully documented.
- id: pda_cmd_0x05
label: PDA command 0x05
kind: action
command: "10 02 {pda_id} 05 {data} {chksum} 10 03"
params:
- name: pda_id
type: integer
description: PDA device ID
- name: data
type: string
description: Payload bytes
notes: CMD_PDA_0x05. Purpose unknown.
- id: pda_cmd_0x1b
label: PDA command 0x1B
kind: action
command: "10 02 {pda_id} 1B {data} {chksum} 10 03"
params:
- name: pda_id
type: integer
description: PDA device ID
- name: data
type: string
description: Payload bytes
notes: CMD_PDA_0x1B. Purpose unknown.
- id: pda_highlight
label: PDA highlight line
kind: action
command: "10 02 {pda_id} 08 {line} {chksum} 10 03"
params:
- name: pda_id
type: integer
description: PDA device ID
- name: line
type: integer
description: Line number to highlight
notes: CMD_PDA_HIGHLIGHT (0x08).
- id: pda_clear
label: PDA clear display
kind: action
command: "10 02 {pda_id} 09 {chksum} 10 03"
params:
- name: pda_id
type: integer
description: PDA device ID
notes: CMD_PDA_CLEAR (0x09).
- id: pda_shift_lines
label: PDA shift display lines
kind: action
command: "10 02 {pda_id} 0F {shift} {chksum} 10 03"
params:
- name: pda_id
type: integer
description: PDA device ID
- name: shift
type: integer
description: Line shift count
notes: CMD_PDA_SHIFTLINES (0x0F).
- id: pda_highlight_chars
label: PDA highlight characters
kind: action
command: "10 02 {pda_id} 10 {ch_start} {ch_count} {chksum} 10 03"
params:
- name: pda_id
type: integer
description: PDA device ID
- name: ch_start
type: integer
description: Starting character index
- name: ch_count
type: integer
description: Number of characters to highlight
notes: CMD_PDA_HIGHLIGHTCHARS (0x10).
# --- RS Serial Adapter (device IDs 0x48-0x49) ---
- id: rssa_device_status
label: RS Serial Adapter device status / error
kind: action
command: "10 02 {adapter_id} 13 {status_data} {chksum} 10 03"
params:
- name: adapter_id
type: integer
description: RS Serial Adapter device ID (0x48-0x49)
- name: status_data
type: string
description: Status or error payload
notes: RSSA_DEV_STATUS (0x13).
- id: rssa_device_ready
label: RS Serial Adapter device ready
kind: action
command: "10 02 {adapter_id} 07 {chksum} 10 03"
params:
- name: adapter_id
type: integer
description: RS Serial Adapter device ID
notes: RSSA_DEV_READY (0x07). Ready to receive command.
# --- Pentair protocol (separate framing, same bus) ---
- id: pentair_set_speed
label: Pentair pump set speed
kind: action
command: "FF 00 FF A5 {from} {pump_dest} 01 {len} {speed_data} {cksum_hi} {cksum_lo}"
params:
- name: from
type: integer
description: Source device ID (master = 0x10)
- name: pump_dest
type: integer
description: Pentair pump device ID (0x60-0x6F)
- name: len
type: integer
description: Data length byte
- name: speed_data
type: string
description: RPM or GPM speed payload
- name: cksum_hi
type: integer
description: 16-bit checksum high byte (sum of CMD..last DATA)
- name: cksum_lo
type: integer
description: 16-bit checksum low byte
notes: PEN_CMD_SPEED (0x01). Pentair framing differs from Jandy.
- id: pentair_remote_control
label: Pentair pump remote control
kind: action
command: "FF 00 FF A5 {from} {pump_dest} 04 {len} {data} {cksum_hi} {cksum_lo}"
params:
- name: from
type: integer
description: Source device ID
- name: pump_dest
type: integer
description: Pentair pump device ID
- name: len
type: integer
description: Data length
- name: data
type: string
description: Remote control payload
- name: cksum_hi
type: integer
description: Checksum high
- name: cksum_lo
type: integer
description: Checksum low
notes: PEN_CMD_REMOTECTL (0x04).
- id: pentair_set_power
label: Pentair pump set power
kind: action
command: "FF 00 FF A5 {from} {pump_dest} 06 {len} {power_data} {cksum_hi} {cksum_lo}"
params:
- name: from
type: integer
description: Source device ID
- name: pump_dest
type: integer
description: Pentair pump device ID
- name: len
type: integer
description: Data length
- name: power_data
type: string
description: "ON/OFF payload"
- name: cksum_hi
type: integer
description: Checksum high
- name: cksum_lo
type: integer
description: Checksum low
notes: PEN_CMD_POWER (0x06).
- id: pentair_status
label: Pentair pump status
kind: action
command: "FF 00 FF A5 {from} {pump_dest} 07 {len} {data} {cksum_hi} {cksum_lo}"
params:
- name: from
type: integer
description: Source device ID
- name: pump_dest
type: integer
description: Pentair pump device ID
- name: len
type: integer
description: Data length
- name: data
type: string
description: Status payload
- name: cksum_hi
type: integer
description: Checksum high
- name: cksum_lo
type: integer
description: Checksum low
notes: PEN_CMD_STATUS (0x07). Bidirectional.
Feedbacks
- id: swg_status
type: enum
values:
- on
- no_flow
- low_salt
- high_salt
- clean_cell
- turning_off
- high_current
- low_volts
- low_temp
- check_pcb
- gen_fault
- unknown
- off
description: SWG_STATUS_* byte returned in CMD_PPM response (0x00=ON, 0x01=NO_FLOW, 0x02=LOW_SALT, 0x04=HI_SALT, 0x08=CLEAN_CELL, 0x09=TURNING_OFF, 0x10=HIGH_CURRENT, 0x20=LOW_VOLTS, 0x40=LOW_TEMP, 0x80=CHECK_PCB, 0xFD=GENFAULT, 0xFE=UNKNOWN, 0xFF=OFF).
- id: swg_ppm
type: integer
description: PPM value reported by SWG. Actual PPM = returned PPM_VAL byte * 100.
- id: epump_watts
type: integer
description: ePump current watts. Decoded as (status_byte_8 * 256) + status_byte_7.
- id: epump_rpm
type: integer
description: ePump current RPM. Decoded as (status_byte_10 * 256) + status_byte_11.
- id: jxi_error
type: boolean
description: "True when JXi status byte 6 == 0x10 indicates error condition."
- id: ack_type
type: enum
values:
- normal
- screen_busy_scroll
- screen_busy_block
description: ACK response type (0x80 normal, 0x81 screen busy cache next, 0x83 screen busy block).
Variables
# Per-action parameters that take ranges:
- id: swg_percent
type: integer
range: "0-255"
description: "0-100 normal percent; >100 boost mode; 0xFF service all-on mode."
- id: epump_watts_value
type: integer
range: "0-65535"
description: 16-bit watts value, hi then lo byte.
- id: epump_rpm_value
type: integer
range: "0-65535"
description: 16-bit RPM value, hi then lo byte.
- id: frame_delay_ms
type: integer
description: Configurable inter-packet delay (milliseconds) to prevent bus collisions.
- id: ftdi_low_latency
type: boolean
description: Enable ASYNC_LOW_LATENCY ioctl for FTDI adapters.
Events
# Unsolicited notifications the device sends on the bus:
- id: msg_loop_start
trigger: "0x10 0x02 0x00 0x08 {chksum} 0x10 0x03"
description: CMD_MSG_LOOP_ST (0x08). Sent by panel when using 0x80 ACK variant to mark loop start.
- id: swg_ppm_update
trigger: "0x10 0x02 0x00 0x16 {ppm_val} {status} {chksum} 0x10 0x03"
description: CMD_PPM (0x16) sent by SWG with PPM and status. PPM_VAL is divided by 100 to get real PPM.
- id: epump_status_update
trigger: "0x10 0x02 0x00 0x1F {orig_cmd} 0x00 {hi_w} {lo_w} {hi_rpm} {lo_rpm} ... {chksum} 0x10 0x03"
description: CMD_EPUMP_STATUS (0x1F) sent by pump with current watts/RPM/pressure/temperature.
- id: jxi_status_update
trigger: "0x10 0x02 0x00 0x0D ... {chksum} 0x10 0x03"
description: CMD_JXI_STATUS (0x0D) sent by JXi heater. Byte 6 = 0x10 means error.
- id: onetouch_keypress
trigger: "0x10 0x02 0x00 {key_code} {details} {chksum} 0x10 0x03"
description: OneTouch keypress event. Key codes: UP=0x06, DOWN=0x05, SELECT=0x04, PAGE_UP/SELECT_1=0x03, BACK/SELECT_2=0x02, PAGE_DOWN/SELECT_3=0x01.
Macros
# UNRESOLVED: source describes packet-level patterns but no explicit multi-step macro sequences named as such.
Safety
confirmation_required_for: []
interlocks: []
# UNRESOLVED: source contains no explicit safety warnings, interlock procedures, or power-on sequencing requirements.
Notes
RS485 bus, multi-drop, 8N1 @ 9600 baud, no hardware/software flow control. Frame: DLE STX DEST CMD DATA... CHKSUM DLE ETX. Checksum = (sum of DEST..last DATA) & 0xFF. 0x10 in DATA escaped as 0x10 0x00 (parser skips extra 0x00). Known Jandy OneTouch long-message checksum bug: packets with packet[3]==0x04 && packet[4]==0x03 && packet[length-3]==0x0a have invalid checksums — accept with warning. AqualinkD v1.0 supports only one SWG despite protocol allowing 4 IDs (0x50-0x53). ePump extended IDs 0xE0-0xE3 require panel revision W+. iAqualinkTouch status packets (0x70, 0x71, 0x72) can exceed 128 bytes — buffer must handle up to 512. frame_delay ms minimum gap between bus writes. Pentair devices (0x60-0x6F, master 0x10) coexist on the same wire with their own FF 00 FF A5 framing and 16-bit checksum. Connection is half-duplex RS485; do not assume simultaneous TX/RX.
Provenance
source_domains:
- github.com
source_urls:
- https://github.com/aqualinkd/AqualinkD/blob/master/JANDY_RS485_PROTOCOL.md
- https://github.com/aqualinkd/AqualinkD/raw/refs/heads/master/JANDY_RS485_PROTOCOL.md
retrieved_at: 2026-06-02T02:18:33.684Z
last_checked_at: 2026-06-02T08:25:20.670Z
Verification Summary
verdict: verified
checked_at: 2026-06-02T08:25:20.670Z
matched_actions: 45
action_count: 45
confidence: medium
summary: "All 45 spec actions map one-to-one to command tokens present verbatim in the source; transport parameters match exactly; source catalogue is fully covered. (4 unresolved item(s) noted in Known Gaps.)"
Known Gaps
- "Pentair sub-protocol present on same bus but listed for reference only. Firmware version of controller not stated."
- "source describes packet-level patterns but no explicit multi-step macro sequences named as such."
- "source contains no explicit safety warnings, interlock procedures, or power-on sequencing requirements."
- "firmware version compatibility not stated. Source originates from reverse-engineering AqualinkD project, not an official Jandy protocol document."
From the AI4AV catalog (https://ai4av.net) · ODbL-1.0