home-assistant-config icon indicating copy to clipboard operation
home-assistant-config copied to clipboard

Hot tub water quality

Open jcallaghan opened this issue 4 years ago • 40 comments

Objective

My hot tub requires pH, alkalinity and free chlorine levels to be maintained. The levels of these need to be tested daily using dip test strips and chemicals added to maintain appropriate water quality. I want to add sensors to help me monitor the quality of the water in my hot tub. As part of this exercise, I also want to learn about water quality and the chemicals I am adding to my hot tub.

So my plan is to try and monitor the following:

  • [x] Temperature
  • [x] TDS
  • [x] pH
  • [ ] ORP (free chlorine)
  • [ ] Alkalinity
  • [ ] Fill with freshwater solenoid (tap switch)
  • [x] ESPHome configuration
  • [ ] Home Assistant configuration and automation
  • [x] Enclosure and sensor cable management

Ingredients

Hot tub specifics

My hot tub is a Lay-z Spa Hawaii Hydrojet Pro.

Type Measurement
pH 7.4-7.6
Total Alkalinity 80-120ppm
Free Chlorine 2-4ppm
Volume 795L

Other considerations

jcallaghan avatar Apr 24 '20 14:04 jcallaghan

To get the water temperature I'm using a Dallas temperature probe with the native ESPHome integration. This is an example of using this in the ESPHome config.

dallas:
  - pin: GPIO21

sensor:
  - platform: dallas
    address: 0x770316457608FF28
    name: "${system_name}_temperature"

jcallaghan avatar Apr 24 '20 14:04 jcallaghan

I'm using a single ESP32 for all of these sensors.

esphome:
  name: ${system_name}
  platform: ESP32
  board: pico32

jcallaghan avatar Apr 24 '20 14:04 jcallaghan

The TDS sensor is of the same kind available from Seed Studio. They have a great blog article about the sensor. Amongst the sample sketch, there is a calculation which converts the value returned from the sensor (v) to the TDS ppm value.

  Voltage = sensorValue*5/1024.0;
  tdsValue=(133.42*Voltage*Voltage*Voltage - 255.86*Voltage*Voltage + 857.39*Voltage)*0.5;

I converted this using a lambda filter in ESPHome.

filters:
      - lambda: return (133.42*x*x*x - 255.86*x*x + 857.39*x)*0.5;

To integrate this with ESPHome I am using the ADC integration and a filter to allow me to capture a moving average while also capturing data regularly. This prevents fluctuations in the recorded data.

sensor:
  - platform: adc
    pin: GPIO34
    name: "${system_name}_tds"
    update_interval: 10s
    unit_of_measurement: "ppm"
    icon: "mdi:water-percent"
    filters:
      - lambda: return (133.42*x*x*x - 255.86*x*x + 857.39*x)*0.5;
      - sliding_window_moving_average:
          window_size: 15
          send_every: 15

jcallaghan avatar Apr 24 '20 14:04 jcallaghan

Here are the test results I captured as soon as I got it working with some repeatable tests (milk and my tap water). I plan to get a TDS pen tester to validate these tests.

TDS water quality test results

  • 0.5m lead from the breakout board
    • Milk = 400ppm
    • Tap water = 40ppm
  • 5m lead from the breakout board
    • Milk = 405ppm
    • Water = 360ppm
    • Hot tub water (few weeks old and chemicals) = 400ppm

jcallaghan avatar Apr 24 '20 15:04 jcallaghan

Hands-on for the first time with the TDS sensor. This arrived 23rd April 2020 and I had it up and running in ESPHome within 30 minutes. I was testing it with some milk and fresh tap water. I figured I could easily repeat these tests if I needed to.

Really useful information about the sensor can be found on the DF Robot site.

image

jcallaghan avatar Apr 24 '20 15:04 jcallaghan

Last weekend I added too much foam remover to the hot tub. To avoid poor water quality and remove the weird white bits that also formed in the water I added some fresh water to the hot tub. The intention was to leave the hose on for 30 minutes or so and let the bad water overfill except I forgot I was running water into the hot tub and I woke a 2am in a panic the hose was still running. I promptly got up, disarmed the house, went downstairs, unlocked the patio and went out and turned the tap off. I then reversed all this and went back to bed. Whilst struggling to sleep I decided I wasn't going to be doing this again. Two things that will help me.

One, an automation to notify me if the temperature drops in the hot tub which is an indication although I plan to make this a little more sophisticated than it currently is and two a solenoid to turn the water tap on and off.

alias: 'Hot Tub - Water temperature falling'

trigger:

  # trigger when the water temperature falls below 35c
  - platform: numeric_state
    entity_id: sensor.esph_hot_tub_water_temperature
    below: '35'

condition:

  # check the automation has not run within the last 60 minutes
  - condition: template
    value_template: "{{ (as_timestamp(states.sensor.date_time.last_changed) - (as_timestamp(state_attr('automation.hot_tub_water_temperature_falling','last_triggered')))) > 3600 }}"

action:

  - service: notify.html5_notification
    data_template:
      title: "Hot Tub water temperature"
      message: "The hot tub water is at {{ states('sensor.esph_hot_tub_water_temperature') }} and falling."

  - service: notify.ios_james_iphone
    data_template:
      title: "Hot Tub water temperature"
      message: "The hot tub water is at {{ states('sensor.esph_hot_tub_water_temperature') }} and falling."

jcallaghan avatar Apr 24 '20 19:04 jcallaghan

Since extending the sensor on ~5m cable I'm getting different results from the tests I ran last night. This could be power or a signal issue. I'm going to doing some more testing to ensure the sensor is calibrated correctly.

jcallaghan avatar Apr 24 '20 21:04 jcallaghan

To validate the TDS sensor I attached to an ESP32 board and installed in my hot tub I brought a handheld TDS test pen. I will capture a sample of tests and compare them against my ESP readings.

Pen TDS Readings

Sample Result 1
Tap water 58 ppm
Milk (fridge temp) 1362 ppm
Milk (room temp)
Hot tub at 40c 480 ppm

Hot tub

Sample ESP32 TDS Sensor Test Pen TDS
26 April 405ppm 480 ppm

jcallaghan avatar Apr 26 '20 12:04 jcallaghan

The pH sensor finally arrived today. Guess I should hook it up and see how well it works.

image

image

jcallaghan avatar May 01 '20 17:05 jcallaghan

This evening I hooked up the pH sensor up to the ESP32 board I am using to track my hot tub water quality through ESPHome. The sensor was providing me with data but not with results I expected based on my test samples. The voltage from the sensor was always maxing out with a low voltage 1.1V. With a little more reading I discovered ESP32 boards have an attenuation property which defaults to 1.1V (0db). When I switched this to 3.9V (11db) I was able to get results that I expected.

I used the following two guides to help me with the conversion from volts to pH and including temperature in the pH calculation.

I also found some libraries that were useful to understand how the sensor works.

  • https://www.electroniclinic.com/wp-content/uploads/2019/08/AnalogPHMeter.zip
  • https://www.atlas-scientific.com/_files/arduino_pH_meter.zip
  • https://www.atlas-scientific.com/_files/code/ino_files/atlas_gravity.zip

According to the Instructables guide, the sensor has an accuracy of +/- 0.2%. Therefore the sensor will operate within this accuracy in the temperature range of 7 - 46°C. As the hot tub heats the water to 40°C no temperature compensation or offset is required

Test samples

I filled espresso cups with samples to test the pH levels with liquids I could easily repeat again if needed.

  1. Malt vinegar
  2. Fresh semi-skimmed milk
  3. Cold tap water
  4. Bleach

Tests

  1. Calibration and test samples
  2. Test samples alongside TDS sensor
  3. Testing in hot-tub water

Observations

Sample Expected Test 1 Test 2 Test 3
Vinegar 2 2.09 pH (0.58V) 2.09 ph (0.59V) N/A
Milk 6 6.82 pH (1.96V) 6.65 pH (1.90V) N/A
Water 7 7.74 pH (2.21V) 7.19 (2.06V) 7.58 pH (2.16 V)
Bleach 13 13.65 pH (3.90V) 13.65 pH (3.90V) N/A

I suspect the subtle differences between Test 1 and Test 2 were due to the milk ageing. I also noticed the sensor takes a few minutes to return to a neutral level when added to an acidic or alkaline substance

Hot tub

Sample ESP32 pH Sensor Test Pen pH
2 May 6.7 pH 6.7 pH

pH Sample Charts

image

image

jcallaghan avatar May 01 '20 22:05 jcallaghan

Some interesting reading on pH sensors. Age/lifetime of the sensor and how the sensor is affected by temperatures.

jcallaghan avatar May 02 '20 15:05 jcallaghan

Time to squeeze all the the sensor boards into an enclosure. I want the unit to live beside the hot tub. This is mainly driven by the range of leads and not wanting to run them over a long lead.

This is a pre-production version. This summer I plan to build a wooden frame around my hot tub. I will house the sensor boards amongst this frame and that will be my production board.

image

image

image

image

image

image

jcallaghan avatar May 02 '20 22:05 jcallaghan

pH and temperature values are spot on. I have a pH tester arriving today but I don't think there will be any discrepancies with the results I'm getting. What I'm not confident with is the TDS value. My TDS test pen is reporting a value that is 200 points higher than my TDS sensor. I have a feeling this is related to the attenuation or the calculation from volts to TDS. I will carry on reviewing this.

pH test pen confirms my sensor is calibrated and reporting correctly. Both the pen and sensor had identical readings.

jcallaghan avatar May 03 '20 09:05 jcallaghan

I found a really some helpful articles on pool chemistry which pointed me towards the need for an ORP sensor. This will allow me to measure my chlorine levels.

  • https://sensorex.com/pool-chemistry/
  • https://blog.intheswim.com/total-dissolved-solids-in-swimming-pools/

jcallaghan avatar May 03 '20 10:05 jcallaghan

This DF Robot blog post about creating an automatic water sensor calls out a few points for me to consider. Sensors should not be in the water 24/7 as it affects their life.

A random thought came as writing this. What if the pH sensor was in a tube and could be lowered in the hot-tub for a regular sample.

Also when using multiple sensors an analog signal isolator should be used to avoid sensors from affecting other sensors readings.

jcallaghan avatar May 03 '20 10:05 jcallaghan

Alkalinity

There isn't a sensor for alkalinity which made me think, could I build a sensor to read the colors from a test strip.

jcallaghan avatar May 03 '20 10:05 jcallaghan

This is all really great stuff. I'm getting a new hot tub is a few weeks. (I have an inflatable one at the moment).

I'd be really interested to replicate what you've done.

Is it still working sucessfully for you? any learnings ?

B-Hartley avatar Feb 01 '21 15:02 B-Hartley

Just found this as I just started getting into ESP stuff to integrate with my HA. What an amazing write up, I may look into giving this a go myself!

monsieurlatte avatar Feb 22 '21 19:02 monsieurlatte

https://twitter.com/pvizeli/status/1392532069427863560

https://ufire.co/shop/

Twitter
“I found some nice hardware on @ufire_co. My order comes next Friday, time to start adding upstream support for @esphome_ and make the sensor data easily available on @home_assistant ❤️”

jcallaghan avatar May 12 '21 17:05 jcallaghan

Looks interesting. Will be following with interest.

B-Hartley avatar May 13 '21 07:05 B-Hartley

I found this super useful as I wanted to use esp home to program a sensor for TDS and Im more familiar with raw arduino code. I found that you were struggling with accurate values from your TDS. I believe it may be related to the boards analog port measuring between 0 and 1 volt at the chip level is what is outputted so you have to relate that voltage to a converted value of 3.3 volts at least for a wemos D1 mini thats what I had to do.... heres the rteference on the esp homesite Note This component prints the voltage as seen by the chip pin. On the ESP8266, this is always 0.0V to 1.0V Some development boards like the Wemos D1 mini include external voltage divider circuitry to scale down a 3.3V input signal to the chip-internal 1.0V. If your board has this circuitry, add a multiply filter to get correct values:

sensor:
  - platform: adc
    # ...
    filters:
      - multiply: 3.3

so my sensor looks like this and it now matches my handheld tds meter very very close

sensor:
  - platform: adc
    pin: A0
    unit_of_measurement: 'PPM'
    name: "Gary TDS"
    update_interval: 10s
    icon: "mdi:water-percent"
    filters:
      - multiply: 3.3
      - lambda: return (133.42*x*x*x - 255.86*x*x + 857.39*x)*0.5;
      - sliding_window_moving_average:
          window_size: 15
          send_every: 15

bscuderi13 avatar Jan 13 '22 01:01 bscuderi13

This looks awesome, and I'm curious if it means no more need for test strips or anything. How's it been working?

Do you have a full build manual, rather than having to piece it together from each of the different updates?

Gamerayers avatar Jan 31 '22 17:01 Gamerayers

Interesting project!

How did you integrate the ORP sensor?

SawadeeKC avatar Feb 22 '22 08:02 SawadeeKC

    filters:
      - lambda: return (133.42*x*x*x - 255.86*x*x + 857.39*x)*0.5;

Doesn't the TDS reading require temperature compensation?

Coolie1101 avatar Mar 17 '22 01:03 Coolie1101

```yaml
    filters:
      - lambda: return (133.42*x*x*x - 255.86*x*x + 857.39*x)*0.5;

Doesn't the TDS reading require temperature compensation?

Yes and here’s how I solved that dilema if it helps. In a nutshell I take a raw reading of analog for tds then temp compensate that reading before plugging into the formula as per documentation on tds sensors.

# Raw TDS Reading
 - platform: adc
   pin: A0
   unit_of_measurement: 'V'
   name: "Gary Aquarium Raw Voltage"
   id: gary_raw
   update_interval: 60s
   icon: "mdi:eye"
   accuracy_decimals: 3
   filters:
     - multiply: 3.0
         
# Temperature Compensated Voltage
 - platform: template
   name: "Gary Aquarium Compensated Voltage"
   icon: "mdi:eye"
   id: gary_comp
   unit_of_measurement: 'V'
   accuracy_decimals: 3
   lambda: 'return ((id(gary_raw).state) / (1 + (0.02 * ((id(gary_temp).state) - 25.0))));'
   update_interval: 60s    

# Temperature Compensated TDS
 - platform: template
   name: "Gary Aquarium TDS"
   icon: "mdi:water-percent"
   unit_of_measurement: 'PPM'
   accuracy_decimals: 0    
   lambda: return (133.42*(id(gary_comp).state)*(id(gary_comp).state)*(id(gary_comp).state) - 255.86*(id(gary_comp).state)*(id(gary_comp).state) + 857.39*(id(gary_comp).state))*0.5;

bscuderi13 avatar Mar 17 '22 03:03 bscuderi13

Amazing, are the readings comparable to a pen test?, when I use your formula my reading is more than 100ppm less, see below

The first reading is the result of (133.42*x*x*x - 255.86*x*x + 857.39*x)*0.5; without any compensation, which is closer to my TDS pen reading, and the following is the reading after applying your templates.

image

Came across the following which doesn't use any temperature compensation. https://www.seeedstudio.com/blog/2020/01/19/tds-in-water-what-is-tds-and-how-do-you-measure-tds-in-water/

Coolie1101 avatar Mar 17 '22 04:03 Coolie1101

Amazing, are the readings comparable to a pen test?, when I use your formula my reading is more than 100ppm less, see below

The first reading is the result of (133.42*x*x*x - 255.86*x*x + 857.39*x)*0.5; without any compensation, which is closer to my TDS pen reading, and the following is the reading after applying your templates.

image

Came across the following which doesn't use any temperature compensation. https://www.seeedstudio.com/blog/2020/01/19/tds-in-water-what-is-tds-and-how-do-you-measure-tds-in-water/

Hmmm it was closer for me using the pen one I had to compare. What are you using for the temperature? Celsius it should be if it isn’t already that as that could cause a big difference in the two.

bscuderi13 avatar Mar 17 '22 05:03 bscuderi13

What are you using for the temperature? Celsius it should be if it isn’t already that as that could cause a big difference in the two.

O, Fahrenheit, I'll switch it and check.

Coolie1101 avatar Mar 17 '22 05:03 Coolie1101