Skip to content

[rs485_frame] a generic, YAML-declarative engine for DLE/STX/ETX byte-stuffed framed serial buses#6719

Open
b3nj1 wants to merge 1 commit into
esphome:nextfrom
b3nj1:rs485_frame
Open

[rs485_frame] a generic, YAML-declarative engine for DLE/STX/ETX byte-stuffed framed serial buses#6719
b3nj1 wants to merge 1 commit into
esphome:nextfrom
b3nj1:rs485_frame

Conversation

@b3nj1
Copy link
Copy Markdown

@b3nj1 b3nj1 commented Jun 1, 2026

Description

Pull request in esphome with YAML changes (if applicable):

Documentation for a new esphome component added in above PR:
Adds a new rs485_frame component: a generic, YAML-declarative engine for DLE/STX/ETX byte-stuffed
framed serial buses
(the framing family used by Hayward AquaLogic/ProLogic, Jandy AquaLink RS, and
other RS485 controllers). It grew out of ESPHome's existing serial tooling, the Serial Proxy in
particular, and complements that work by adding a layer that today usually lives in custom C++:
reassembling byte-stuffed frames, validating a configurable CRC, and scheduling half-duplex transmits
gated on a received frame, all declaratively from YAML.

Checklist

  • I am merging into next because this is new documentation that has a matching pull-request in esphome as linked above.
    or

  • I am merging into current because this is a fix, change and/or adjustment in the current documentation and is not for a new component or feature.

  • Link added in /src/content/docs/components/index.mdx when creating new documents for new components or cookbook.

New Component Images

If you are adding a new component to ESPHome, you can automatically generate a standardized black and white component name image for the documentation.

To generate a component image:

  1. Comment on this pull request with the following command, replacing component_name with your component name in lower_case format with underscores (e.g., bme280, sht3x, dallas_temp):

    @esphomebot generate image component_name
    
  2. The ESPHome bot will respond with a downloadable ZIP file containing the SVG image.

  3. Extract the SVG file and place it in the /public/images/ folder of this repository.

  4. Use the image in your component's index table entry in /src/content/docs/components/index.mdx.

Example: For a component called "DHT22 Temperature Sensor", use:

@esphomebot generate image dht22

Note: All images used in ImgTable components must be placed in /public/images/ as the component resolves them to absolute paths.

@netlify
Copy link
Copy Markdown

netlify Bot commented Jun 1, 2026

Deploy Preview for esphome ready!

Name Link
🔨 Latest commit b5957ff
🔍 Latest deploy log https://app.netlify.com/projects/esphome/deploys/6a1e173bbf791a00089a1de7
😎 Deploy Preview https://deploy-preview-6719--esphome.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@b3nj1
Copy link
Copy Markdown
Author

b3nj1 commented Jun 1, 2026

@esphomebot generate image rs485_frame

Adds a new `rs485_frame` component: a generic, YAML-declarative engine for **DLE/STX/ETX
byte-stuffed framed serial buses** (the framing family used by Hayward AquaLogic/ProLogic,
Jandy AquaLink RS, and many other RS485 controllers). The idea grew out of ESPHome's existing
serial tooling — the Serial Proxy in particular sparked it — and aims to complement that work
by adding a piece that today usually lives in custom C++: reassembling byte-stuffed frames,
validating a configurable CRC variant, and scheduling half-duplex transmits gated on a received
frame, all declaratively from YAML.

The component provides:

- **RX framing engine** — DLE byte-stuffed frame reassembly with escape unwrapping, an
  intra-frame timeout that recovers from mid-frame cable pulls/noise, and `max_frame_length`
  bounding. All scratch buffers are pre-reserved in `setup()`; the receive path performs no
  heap allocation.
- **Configurable CRC** — `none` / `sum8` / `sum16` / `xor8` / `crc16_modbus`, each selectable
  as header-inclusive or payload-only, with RX able to accept either coverage.
- **Half-duplex TX scheduling** — three gate modes (`frame_trigger`, `idle_gap`,
  `fixed_delay`), a pre-reserved allocation-free TX ring buffer (`replace_latest` / `fifo`),
  and optional `idle_command` keepalives.
- **`on_frame:` triggers** with multi-prefix `frame_type` matching, so all RX payload
  decoding is done in user YAML/lambdas (no per-protocol C++).
- **`command_format:`** — a data-driven command encoder (preamble / command_size /
  endianness / repeat / postamble) that expresses every supported protocol's command layout
  as YAML data, plus a templatable `rs485_frame.send_frame` action for arbitrary frames.
- **Platforms:** `button` (raw-frame or `command:` form), `number` (lambda encoder),
  `sensor` / `text_sensor` (hub diagnostics: frames received, CRC failures, commands
  sent/dropped, queue depth, keepalive interval, last frame type).
- **Optional `sniffer_stats:`** diagnostic (compiled out entirely unless configured via
  `USE_RS485_FRAME_SNIFFER_STATS`) that buckets RX frames by type and logs cadence and
  unique-payload histograms — useful for reverse-engineering an unknown bus.
- **Optional `discovery:`** mode (compiled out entirely unless configured via
  `USE_RS485_FRAME_DISCOVERY`) that turns the hub into a passive analyzer for a completely
  unknown bus: it captures raw bytes, segments them by idle gap, and reports the candidate
  framing bytes, escape scheme, and CRC variant, gated by a configurable framing-confidence
  threshold. An optional `baud_sweep` cycles the UART through candidate baud rates and data-bit
  widths (runtime UART reconfiguration on ESP-IDF and ESP8266) and locks onto the best-scoring
  setting, then prints a ready-to-paste `uart:`/`framing:` block. Discovery and `sniffer_stats:`
  are mutually exclusive (discovery bypasses the validated-frame path).

There are **no built-in device profiles**. The component is a single generic engine
configured explicitly (framing bytes, required `crc:`, `command_format:`, `tx.gate`), which
keeps all vendor specifics out of the codebase. Ready-to-flash configurations for specific
controllers (e.g. Hayward AquaLogic) live in a separate community
[examples repository](https://github.com/b3nj1/rs485_frame-examples) rather than in core.

The discovery and sniffer features are what let a user go from "unknown bus" to a working
config without prior protocol knowledge. For a worked end-to-end example, see this
[annotated Hayward AquaLogic capture](https://github.com/b3nj1/rs485_frame-examples/blob/main/captures/hayward-aqualogic-20260529.md):
it walks a real bus from `discovery:` + baud sweep (auto-detecting 19200 8-data-bit framing and
the `sum16` CRC), through the `sniffer_stats:` frame-type table on an idle bus and under live
remote commands, to decoded frames — every CRC verified by hand.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant