An open API service indexing awesome lists of open source software.

https://github.com/marklynch/pool-controller-code

ESP32 Code for pool controller for Connect 10
https://github.com/marklynch/pool-controller-code

esp32 home-assistant home-automation swimming-pool

Last synced: about 1 month ago
JSON representation

ESP32 Code for pool controller for Connect 10

Awesome Lists containing this project

README

          

# Pool Controller

[![Build](https://github.com/marklynch/pool-controller-code/actions/workflows/build.yml/badge.svg)](https://github.com/marklynch/pool-controller-code/actions/workflows/build.yml
)
[![Release](https://img.shields.io/github/v/release/marklynch/pool-controller-code)](https://github.com/marklynch/pool-controller-code/releases)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![ESP-IDF](https://img.shields.io/badge/ESP--IDF-v5.5+-red)](https://github.com/espressif/esp-idf)
[![Platform](https://img.shields.io/badge/platform-ESP32--C6-blue)](https://core-electronics.com.au/esp32-c6-mini-development-board-wifi6-bluetooth5.html)

Code to listen on and control a Connect 10 pool controller. I created it as a learning project and happy to collaborate with people who find it useful.
This has been created by listening to the communications on the control bus, and decoding the instructions by trial and error.

## Core components
1. [Controller code (this repo)](https://github.com/marklynch/pool-controller-code)
2. [Circuit and PCB design](https://github.com/marklynch/pool-controller-pcb)
3. ESP32-C6 - It's been designed around the [Waveshare ESP32-C6 Mini Development Board ](https://core-electronics.com.au/esp32-c6-mini-development-board-wifi6-bluetooth5.html)
4. [Case for Pool Controller](https://github.com/marklynch/pool-controller-case)

**Note** this is **not an official product** and does not come with support or any warranty. Note it is NOT connected to or supported by Fluidra.

## Current Status

Tested as working:
- Lights work fully — state, colour, zone name, multicolor capability ✅
- Pool/Spa mode works ✅
- Temperature set points for pool and spa work ✅
- Heater on/off works ✅
- Channel switching working - toggle On/Auto/Off ✅
- Valves reading and switching working ✅
- Reading of timers ✅
- Reading of ORP/PH settings ✅
- Reading of water temperature ✅
- Reading config for channels, lights and heater ✅
- Reading of config/state for Internet Gateway ✅
- Reading of touchscreen and Internet Gateway firmware versions ✅
- Auto-requests missing timer and light config when Internet Gateway is absent ✅

## Getting Started

The device has three connectors:

- **2 × RJ12 sockets** — for the pool control bus. Use a standard flat RJ12 cable to connect either socket to your Connect 10 system; this also powers the device. The two sockets are wired in parallel, so the second one can be used to daisy-chain another device (e.g. another controller, gateway, or accessory) on the same bus.
- **1 × USB-C socket** — for manually flashing firmware and serial monitoring from a computer. It is **not** required for normal operation.

To bring the device online for the first time:

1. Plug a flat RJ12 cable from the Connect 10 into either RJ12 socket on the device. This both connects it to the pool bus and powers it. (Optional: run a second cable from the other RJ12 socket to daisy-chain the next device on the bus.)
2. Wait for the LED to turn **purple**, which indicates the device is in provisioning mode and ready for WiFi setup (see [Initial Wifi Provisioning](#initial-wifi-provisioning) below).

## Initial Wifi Provisioning

1. When the LED is **purple**, the device is in provisioning mode.
2. On your phone, connect to the WiFi network named **`POOL_AABBCC`** (e.g. `POOL_A1B2C3`) — the `AABBCC` suffix is unique to each device. The password is **`poolsetup`**.
3. In your phone's browser navigate to **http://192.168.4.1** and choose your WiFi network and enter the password.
4. The device will save the credentials and restart. The LED will turn white then green once connected.

Once on your network the device is accessible at **`http://poolcontrol-AABBCC.local`** — using the same `AABBCC` suffix as the AP you provisioned through (e.g. `http://poolcontrol-A1B2C3.local`).

**Note:** If the wrong password is entered the device will retry for about 30 seconds then return to provisioning mode.

**Note:** To re-provision, erase the flash ("Erase Flash Memory from device" in your IDE) to clear the saved credentials.

## Visual Feedback (LED Status):

### Persistent States (Solid Colors)
* **Blue** - Startup (brief, during boot)
* **Purple** - Unconfigured (no WiFi credentials, provisioning mode active)
* **White** - WiFi connected, waiting for MQTT connection
* **Green** - Fully operational (WiFi + MQTT connected) ✓
* **Orange** - MQTT disconnected (WiFi ok, MQTT issue)

### Activity Indicators (Brief Flashes)
* **Cyan flash** - RJ12 data received (RX)
* **Magenta flash** - RJ12 data transmitted (TX)

### Boot Flow Examples

**First Boot (No WiFi):**
1. Blue (startup)
2. Purple (unconfigured - connect to AP)
3. Connect to AP → Configure WiFi → Device restarts

**Normal Boot (WiFi Configured):**
1. Blue (startup)
2. White (WiFi connected)
3. Green (MQTT connected) ✓

**MQTT Connection Issue:**
1. Blue (startup)
2. White (WiFi connected)
3. Orange (MQTT failed to connect)

## TCP Debug Connection (Port 7373)

The device exposes a raw TCP server on port 7373 that streams all bus traffic as hex and forwards any bytes you send back onto the bus. It also mirrors the device's log output, so you can monitor activity without a USB cable.

Each device gets a unique mDNS hostname derived from the last 3 bytes of its MAC address:
`poolcontrol-AABBCC.local` — where `AABBCC` matches the suffix of the provisioning AP name (`POOL_AABBCC`).

For example, if you provisioned via the `POOL_A1B2C3` network, the device will be accessible at `poolcontrol-A1B2C3.local`.

### Mac / Linux

Use `nc` (netcat), which is installed by default:

```bash
nc poolcontrol-A1B2C3.local 7373
```

Example session:
```
Connected to pool control bus bridge.
UART bytes will be shown here in hex.
Bytes you send will be forwarded to the bus.

00
02 00 50 FF FF 80 00 FD 0F DC 19 0E 01 28 03
00
```

To send a raw command to the bus, type the bytes as a hex string and press Enter:
```
02 00 F0 00 50 80 00 39 0F 0E E7 01 00 00 03
```

### Windows

**Option 1 — PuTTY (recommended)**

1. Download [PuTTY](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html) if you don't have it.
2. Set **Connection type** to **Raw**.
3. Enter `poolcontrol-A1B2C3.local` as the host and `7373` as the port.
4. Click **Open**.

**Option 2 — Telnet**

If the Telnet client is enabled (Control Panel → Programs → Turn Windows features on/off → Telnet Client):

```cmd
telnet poolcontrol-A1B2C3.local 7373
```

**Option 3 — Windows Subsystem for Linux (WSL)**

If WSL is installed, use `nc` exactly as on Mac/Linux:

```bash
nc poolcontrol-A1B2C3.local 7373
```

## Testing Message Decoding

You can test individual messages against the decoder using the HTTP API endpoint:

```bash
curl -X POST http://poolcontrol-A1B2C3.local/api/test_decode \
-d "02 00 50 FF FF 80 00 38 0F 17 D0 01 02 1A 03"
```

**Response:**
```json
{
"success": true,
"decoded": true,
"length": 15,
"hex": "02 00 50 FF FF 80 00 38 0F 17 D0 01 02 1A 03",
"message": "Check ESP logs for decode details"
}
```

- `decoded: true` - Pattern matched and message was decoded
- `decoded: false` - Unknown message type

**To see full decode details**, monitor the ESP logs:
```bash
idf.py monitor
```

You'll see output like:
```
I (12345) MSG_DECODER: [Controller -> Broadcast] Lighting zone 1 state - On
```

This allows you to quickly test message patterns and verify decoder behavior without needing to send messages to the actual bus.

## General architecture

```mermaid
flowchart TD

Pool[fa:fa-life-ring Pool Connect 10]

subgraph ESP32-C6[fa:fa-microchip ESP32-C6 Pool Controller]
subgraph Transport[Transport Layer]
Bus[Bus Interface
UART 9600 baud]
TCP[TCP Bridge
Port 7373]
RegReq[Register Requester
Auto-polls when GW absent]
end

subgraph Protocol[Protocol Layer]
Decoder[Message Decoder
Register Dispatch Table]
end

subgraph State[State Management]
PoolState[Pool State
Mutex Protected]
end

subgraph Network[Network Layer]
WiFi[WiFi Provisioning
SoftAP + mDNS]
end

subgraph Application[Application Layer]
WebAPI[Web Handlers
HTTP Endpoints]

subgraph MQTTSub[MQTT Subsystem]
MQTTClient[MQTT Client]
MQTTPub[MQTT Publish]
MQTTDisc[MQTT Discovery]
MQTTCmd[MQTT Commands]
end
end

subgraph Status[Status Indication]
LED[LED Helper
WS2812 RGB]
end

Bus <--> TCP
Bus --> Decoder
Decoder --> PoolState
Decoder -.->|notify on new zone| RegReq
RegReq --> PoolState
RegReq -->|CMD 0x39 requests| Bus
PoolState --> MQTTPub
PoolState --> WebAPI
MQTTCmd --> PoolState
WiFi -.-> WebAPI
WiFi -.-> MQTTClient
MQTTClient --> MQTTPub
MQTTClient --> MQTTDisc
MQTTClient --> MQTTCmd
MQTTPub --> MQTTSub
MQTTDisc --> MQTTSub
MQTTCmd --> MQTTSub
PoolState -.-> LED
WiFi -.-> LED
end

Clients[Network Clients
nc, telnet, custom]
Browser[Web Browser]
HA[Home Assistant]

Pool <-->|RJ12 Serial Bus| Bus
TCP <-->|TCP/IP Port 7373| Clients
WebAPI <-->|HTTP Port 80
poolcontrol-AABBCC.local| Browser
MQTTSub <-->|MQTT over WiFi| HA
```

The system consists of an ESP32 C6 module that can be daisy chained into an existing connect 10 system via a RJ12 connection.

It sets up a WiFi AP called `POOL_AABBCC` (where `AABBCC` is the last 3 bytes of the device's MAC address). Connecting to that AP and navigating to `192.168.4.1` opens the provisioning page. Once configured and on your network, the device is accessible at `poolcontrol-AABBCC.local` using the same suffix.

It uses MQTT to connect and publish information and receive information from Home Assistant.

## Building and Flashing

This project uses ESP-IDF v5.5+. See `CLAUDE.md` for build commands and architecture details.

```bash
idf.py build # Build the project
idf.py flash monitor # Flash to device and monitor output
```

## Releases

Releases are produced by a GitHub Actions workflow (`.github/workflows/build.yml`) that fires on any tag matching `v*`. To cut a release:

```bash
git tag -a v1.0.0 -m "Release v1.0.0"
git push origin v1.0.0
```

The workflow will:

1. Build the firmware with `PROJECT_VER` set to the tag name (embedded into the binary and visible via the device's `/status` page).
2. Create a draft GitHub Release with auto-generated notes.
3. Attach two assets:
- `pool-controller-update-v1.0.0.bin` — app-only binary, for the existing `/update` OTA flow.
- `pool-controller-full-v1.0.0.bin` — merged bootloader + partition table + otadata + app, for first-time flashing via `esptool.py --chip esp32c6 write_flash 0x0 pool-controller-full-v1.0.0.bin`.
4. Publish the draft.

The published release appears at `https://github.com/marklynch/pool-controller-code/releases/tag/v1.0.0`.

To re-test the workflow without cutting a real release, run it manually from the Actions tab (Build & Release → Run workflow). Manual runs build the firmware and upload it as a workflow artifact but do not create a GitHub Release.

## Documentation

### [PROTOCOL.md](PROTOCOL.md) — Bus Protocol Reference

Documents the proprietary serial protocol used by the Connect 10, reverse-engineered by sniffing bus traffic. Covers:

- **Message framing** — `START (0x02) | SRC | DST | CTRL | CMD | DATA | CHECKSUM | END (0x03)`
- **Device addresses** — Touch screen (`0x0050`), controller (`0x006F`), chlorinator (`0x0090`), internet gateway (`0x00F0`)
- **30+ decoded message types** — temperatures, channel states, lighting zones (state, colour, name, multicolor capability), chlorinator pH/ORP, controller clock, firmware versions, gateway network status, and more
- **Register system** — A unified register/slot dispatch mechanism used for channel names, types, lighting colors, and labels
- **Control commands** — How to toggle channels, set temperature setpoints, control lighting zones, switch pool/spa mode, and control the heater (all by impersonating the internet gateway address `0x00F0`)
- **Checksum algorithm** and message validation rules

### [OTA_UPDATE.md](OTA_UPDATE.md) — Over-The-Air Firmware Updates

Describes the web-based OTA update system. Covers:

- **How to update** — Build the `.bin`, navigate to `http:///update`, upload via the web form
- **Dual-partition layout** — Updates alternate between `ota_0` and `ota_1`, with automatic rollback if the new firmware fails to boot
- **Safety** — Image validation before write, boot confirmation required by new firmware, rollback after 3 failed boots
- **Version information** — Version string generated from `git describe` (e.g. `v1.0.0-5-g870d65b`)
- **Security notes** — No authentication on `/update` currently; see the doc for recommended production hardening