As mentioned in the README.md
, the OPNpool integrates the functionality of a traditional Pool Controller into the modern smart home. It keeps tabs on the status of the connected controller, pool pump and chlorinator. This provides not only a more convenient solution than physically interacting with the pool equipment, but the ability to create automations that runs the pump for a duration depending on the temperature.
Features:
- Visualizes the status of the thermostats, pump, chlorinator, circuits, and schedules.
- Allows you adjust the thermostats and toggle circuits
- No physical connection to your LAN
- Supports over-the-air updates
- Easily one-time provisioning from an Android phone
- Integrates with MQTT and Home Assistant
- Accessible as a webapp
- Protected with IP68 waterproof case and connectors
- Does not require a power adapter
- Open source!
This device was tested with the Pentair SunTouch controller with firmware 2.080, connected to an IntelliFlo pump and IntelliChlor salt water chlorinator.
This open source and hardware project is intended to comply with the October 2016 exemption to the Digital Millennium Copyright Act allowing "good-faith" testing," in a controlled environment designed to avoid any harm to individuals or to the public.
If you only want to take this project for a quick spin, refer to the README.md
instead. The remainder of this document will walk you through the full installation.
We will build a printed circuit board (PCB) with an ESP32 module, RS-485 adapter and DC/DC converter.
If you're in Silicon Valley, give me a ping to avoid long lead times. I have extra (partly) assenmbled units and will provide them at cost. If there is enough interest, I can start a project on Tindie or Crowd Supply to get some volume pricing.
A buck converter provides 5 Volts to the battery connector on the LOLIN D32 daughterboard. Using the battery input, helps prevent problems when it is also powered through the USB connector.
The data path is between the RS-485 connector and the ESP32 on the LOLIN D32 daughterboard. There is an optional terminator resistor to prevent reflections on the bus. The JTAG header is for debugging as detailed in the Debugging chapter of the design document.
The schematic fits easily on a two layer PCB. Note the cut out for the RF antenna.
Name | Description | Sugggested mfr/part# |
---|---|---|
PBC r2 | Printed circuit board | OSHPark |
Enclosure | 158x90x60mm clear plastic project enclosure, IP65 | white label |
LOLIN D32 | Wemos LOLIN D32, based on ESP-WROOM-32 4MB | Wemos LOLIN-D32 |
RS485_CONN | Plug+socket, male+female, 5-pin, 16mm aviation, IP68 | SD 16 |
MAX3485 | Maxim MAX3485CSA, RS-485/UART interface IC 3.3V, 8-SOIC | Analog-Devices MAX3490ECSA |
DC1 | DC/DC Converter R-78E5.0-0.5, 7-28V to 5V, 0.5A, 3-SIP | RECOM-Power R-78E5.0-0.5 |
D1 | Schottky Diode, 1N5818, DO-41 | ON-Semiconductor 1N5818RLG |
LED1 | LED, Green Clear 571nm, 1206 | Lite-On LTST-C150KGKT |
LED2 | LED, Amber Clear 602nm, 1206 | Lite-On LTST-C150AKT |
C1, C2 | Capacitor, 10 µF, 25 V, multi-layer ceramic, 0805 | KEMET C0805C106K3PACTU |
C3 | Capacitor, 0.1 µF, 6.3 V, multi-layer ceramic, 0805 | KEMET C0805C104M3RACTU |
R1, R2 | Resistor, 68 Ω, 1/8 W, 0805 | YAGEO RC0805FR-0768RL |
R3 | Not stuffed, resistor, 120 Ω, 1/4 W, 0805 | KAO SG73S2ATTD121J |
RS485-TERM | Fixed terminal block, 4-pin, screwless, 5 mm pitch | Phoenix-Contact 1862291 |
SW1 | Tactile Switch, 6x6mm, through hole | TE-Connectivity 1825910-4 |
PCB Screws | Machine screw, #6-32 x x 3/16", panhead | Keystone-Electronics 9306 |
CONN Screws | Machine screw, M2-0.4 x 16 mm, cheese head | Essentra 50M020040D016 |
CONN Nuts | Hex nut, M2-0.4, nylon | Essentra 04M020040HN |
At the core this project is an ESP32 module and a 3.3 Volt RS-485 adapter. You can breadboard this using:
- Any ESP32 module that has an USB connector and
GPIO#25
toGPIO#27
available (such as the Wemos LOLIN D32). - Any "Max485 Module TTL". To make it 3.3 Volt compliant, change the chip to a MAX3485CSA+. While you're at it, you may as well remove the 10 kΩ pullup resistors (
R1
toR4
). - A piece of Cat5 ethernet cable to connect to the pool controller.
The open software is hosted on GitHub.
We proudly acklowledge the work of reverse engineering pioneers Joshua Bloch, Michael Russe, and George Saw. (Drop me a line if if I forgot you.)
Clone the repository and its submodules to a local directory. The --recursive
flag automatically initializes and updates the submodules in the repository. Start with a fresh clone:
git clone --recursive https://github.com/cvonk/OPNpool.git
or using ssh
git clone --recursive git@github.com:cvonk/OPNpool.git
From within Microsoft Visual Code (VScode), add the Microsoft's C/C++ extension. Then add the Espressif IDF extension ≥4.4. ESP-IDF will automatically start its configuration. Answer according to the table below
Question | Choice |
---|---|
Mode | Advanced |
ESP-IDF path | C:/espressif |
Tools | C:/espressif/bin |
Download | yes |
As usual, the bootloader
image does some minimum initializations. If it finds a valid ota
image, it passes control over to that image. If not, it starts the factory
image.
- The
factory
image takes care of provisioning Wi-Fi and MQTT credentials with the help of a phone app. These credentials are stored in thenvs
partition. It then downloads theota
image, and restarts the device. - We refer to the
ota
image as theinterface
, as it provides the core of the functionality of the OPNpool device.
To host your own interface
image, you will need to place it on your LAN or on the Web. If you're fine using my logiciel du jour, you can skip this section.
To use HTTPS, you will need to add the server's public certificate to
interface/components/ota_update_task/CMakelists.txt
, and uncomment some lines inota_update_task.c
withserver_cert_pem
.
From VScode:
- Change to the
OPNpool/interface
folder, usingFile > Open
. - Connect your ESP32 module, and once more select the serial port using
ctrl-e p
. - Edit the SDK configuration (
ctrl-e g
) and scroll down to OPNpool and specify your "Firmware upgrade url endpoint" (e.g. http://host.domain/path/interface.bin). - Start the build cycle using
ctrl-e b
. - Upload
OPNpool/interface/build/interface.bin
to your site.
We will build the factory
image and provision it using an Android phone app.
If you have an iPhone, or you have problems running the Android app, you can extend
esp_prov.py
to includemqtt_url
similar to what is shown here. Sorry, I don't have the iOS development environment.
In the last step of provisioning, this factory
image will download the interface
image from an external site.
From VScode:
- Change to the
OPNpool/factory
folder, usingFile > Open
. - Connect your ESP32 module, and select the serial port using
ctrl-e p
. - Erase the NVRAM in flash using
ctrl-e r
. - If you built and host your own
interface
image, you need to specify the path by editing the SDK configuration (ctrl-e g
) and scroll down to OPNpool and specify your "Firmware upgrade url endpoint" (e.g. http://host.domain/path/interface.bin). - Start the build-upload-monitor cycle using
ctrl-e d
.
Using an Android phone:
- Install and run the OPNpool app from the Play Store.
- Using the overflow menu, select "Provision device".
- Click on the "Provision" button and grant it access 1.
- Click on the name of the OPNpool device one it is detected (
POOL*
). - Select the Wi-Fi SSID to connect to and give it the password.
- If you don't have a MQTT broker press
Skip
. Otherwise, specify the broker URL in the formatmqtt://username:passwd@host.domain:1883
. - Wait a few minutes for the provisioning to complete.
The device will appear on your network segment as opnpool.local
. You can access its web interface through http://pool.local
. If MQTT is configured, it will publish MQTT messages. If you also use the Home Assistant, entities will appear after a few minutes with .opnpool
in their name.
⚠️ THIS PROJECT IS OFFERED AS IS. IF YOU USE IT YOU ASSUME ALL RISKS. NO WARRENTIES. At the very least, turn off the power while you work on your pool equipment. Be careful, THERE IS ALWAYS A RISK OF BREAKING YOUR POOL EQUIPMENT.
Understanding the above warning .. the RS-485 header can be found on the back of the control board. There are probably already wires connected that go to the devices such as pump and chlorinator.
To minimize electromagnetic interference, use a twisted pairs from e.g. CAT-5 cable to connect the A
/B
pair to the RS-485 adapter as shown in the table below.
Controller | PCB | idle state |
---|---|---|
-DATA (green) |
A |
negative |
+DATA (yellow) |
B |
positive |
In VSCode, the serial monitor will show decoded messages such as:
{
"state":{"system":{"tod":{"time":"14:01","date":"2022-04-05"},"firmware":"v0.000"},"temps":{"air":69,"solar":80},
"thermos":{"pool":{"temp":68,"src":"None","heating":false},"spa":{"temp":69,"src":"None","heating":false}},
"scheds":{"pool":{"start":"08:00","stop":"10:00"}},
"modes":{"service":false,"UNKOWN_01":false,"tempInc":false,"freezeProt":false,"timeout":false},
"circuits":{"active":{"spa":false,"aux1":false,"aux2":false,"aux3":false,"ft1":false,"pool":true,"ft2":false,"ft3":false,"ft4":false},"delay":{"spa":false,"aux1":false,"aux2":false,"aux3":false,"ft1":false,"pool":false,"ft2":false,"ft3":false,"ft4":false}}}}
}
The web UI at http://opnpool.lcoal/
, will show the pool state and allow you to change the thermostat and circuits.
If you are using Home Assistant, the *.opnpool*
entities will update automatically. The hassio
directory has some YAML code to use with the Lovelace dashboard.
If you go that route, also remember to install modcard
, button-card
, bar-card
, simple-thermostat
, template-entity-row
and mini-graph-card
available through the Home Assistant Community Store (HACS).
When the path to the MQTT broker is provisioned, OPNpool will publish its state to topics
topic | values |
---|---|
homeassistant/switch/opnpool/pool_circuit/state |
(ON |OFF ) |
homeassistant/switch/opnpool/spa_circuit/state |
(ON |OFF ) |
homeassistant/switch/opnpool/aux1_circuit/state |
(ON |OFF ) |
homeassistant/switch/opnpool/aux2_circuit/state |
(ON |OFF ) |
homeassistant/switch/opnpool/aux3_circuit/state |
(ON |OFF ) |
homeassistant/switch/opnpool/ft1_circuit/state |
(ON |OFF ) |
homeassistant/switch/opnpool/ft2_circuit/state |
(ON |OFF ) |
homeassistant/switch/opnpool/ft3_circuit/state |
(ON |OFF ) |
homeassistant/switch/opnpool/ft4_circuit/state |
(ON |OFF ) |
homeassistant/climate/opnpool/pool_heater/available |
(online |offline ) |
homeassistant/climate/opnpool/pool_heater/state |
see below |
homeassistant/climate/opnpool/spa_heater/available |
(online |offline ) |
homeassistant/climate/opnpool/spa_heater/state |
see below |
homeassistant/sensor/opnpool/pool_sched/state |
hh:mm - hh:mm |
homeassistant/sensor/opnpool/spa_sched/state |
hh:mm - hh:mm |
homeassistant/sensor/opnpool/aux1_sched/state |
hh:mm - hh:mm |
homeassistant/sensor/opnpool/aux2_sched/state |
hh:mm - hh:mm |
homeassistant/sensor/opnpool/air_temp/state |
integer |
homeassistant/sensor/opnpool/water_temp/state |
integer |
homeassistant/sensor/opnpool/system_time/state |
integer |
homeassistant/sensor/opnpool/ctrl_version/state |
integer |
homeassistant/sensor/opnpool/if_version/state |
integer |
homeassistant/sensor/opnpool/pump_mode/state |
(FILTER |MAN |BKWASH | EP1 |EP2 |EP3 |EP4 ) |
homeassistant/sensor/opnpool/pump_status/state |
integer |
homeassistant/sensor/opnpool/pump_power/state |
integer |
homeassistant/sensor/opnpool/pump_gpm/state |
integer |
homeassistant/sensor/opnpool/pump_speed/state |
integer |
homeassistant/sensor/opnpool/pump_error/state |
integer |
homeassistant/sensor/opnpool/chlor_name/state |
string |
homeassistant/sensor/opnpool/chlor_pct/state |
integer |
homeassistant/sensor/opnpool/chlor_salt/state |
integer |
homeassistant/sensor/opnpool/chlor_status/state |
(OK |LOW_FLOW |LOW_SALT | HIGH_SALT |COLD |CLEAN_CELL | OTHER ) |
homeassistant/binary_sensor/opnpool/pump_running/state |
(ON |OFF ) |
homeassistant/binary_sensor/opnpool/mode_service/state |
(ON |OFF ) |
homeassistant/binary_sensor/opnpool/mode_temp_inc/state |
(ON |OFF ) |
homeassistant/binary_sensor/opnpool/mode_freeze_prot/state |
(ON |OFF ) |
homeassistant/binary_sensor/opnpool/mode_timeout/state |
(ON |OFF ) |
The thermostat state is a JSON string, for example
{
"mode": "heat", // static
"heatsrc": "None", // None/Heater/SolarPref/Solar
"target_temp": 70,
"current_temp": 67,
"action": "off" // off/heating/idle
}
When the path to the MQTT broker is provisioned, OPNpool will subscribe to the topics listed below
topic | accepts |
---|---|
homeassistant/switch/opnpool/pool_circuit/config |
(ON |OFF ) |
homeassistant/switch/opnpool/spa_circuit/config |
(ON |OFF ) |
homeassistant/switch/opnpool/aux1_circuit/config |
(ON |OFF ) |
homeassistant/switch/opnpool/aux2_circuit/config |
(ON |OFF ) |
homeassistant/switch/opnpool/aux3_circuit/config |
(ON |OFF ) |
homeassistant/switch/opnpool/ft1_circuit/config |
(ON |OFF ) |
homeassistant/switch/opnpool/ft2_circuit/config |
(ON |OFF ) |
homeassistant/switch/opnpool/ft3_circuit/config |
(ON |OFF ) |
homeassistant/switch/opnpool/ft4_circuit/config |
(ON |OFF ) |
homeassistant/climate/opnpool/pool_heater/set_temp |
integer |
homeassistant/climate/opnpool/pool_heater/set_heatsrc |
(None |Heater |SolarPref |Solar ) |
To host the Web UI yourself, copy the files from webui
to your web server. Then reflect this change in the file interface/main/httpd/httpd_root.c
.
The design documentation for this project is available at
It includes the chapters
- Introduction
- RS-485 bus
- Hardware
- Tools
- Debugging
- Protocol
- Deploying
- Interface
- Web UI
- Home Automation
Footnotes
-
Precise location permission is needed to find and connect to the OPNpool device using Bluetooth LE. ↩