Aquarium water quality TDS sensor

Thanks for this helpful reply :slight_smile:

I’ve already :

Could you please tell me which pH meter you have ?
Can I use the 3.3v of the ESP to connect the pH meter ? If not do you have any link to buy voltage divider ?

I will buy both of Multiplexer, pH meter and voltage divider

Thanks

2 Likes

Thanks for the detailed instruction! I built it but I’m struggling that my PH sensor measure constant 1V. I recabled, changed the USB cable and the power supply, tried with 5V with 3.3V converter, with 3.3V, still the same. 1V. Anyway the TDS/EC working fine and get the correct value. I created a config without the multiplexer and in this case the PH sensor measuring the correct values so I’m totally lost.

Hey,
How do you stop biofilm from ruining the results?
Cheers

I want to do a TDS sensor for my organic hydroponic system and I am wondering how you are liking your setup??? Would you do anything differently??

I am Wondering if a more expensive ($200) TDS sensor from Atlas is worth it or if a cheaper one is just fine

so i followed your calculations;


since I don’t have a temperature sensor connected to my wemos, i’ve swapped any temperature bits with “25”. While testing the sensor by dumping it in a bottle of lemonade-syrup, i’ve reached a ppm of 15. I then put the sensor in my hydro bucket where it started around 3.39 without adding any nutrients. The readings have been swinging a little bit, rising ever so slightly. Then today I put in fresh water and i’m reading 2.44 ppm. I’m guessing I’m going wrong somewhere in the formula calculation.
My code is below, mind giving it a look and telling me where i’m being stupid?

sensor:
  - platform: adc
    pin: A0
    name: "tds raw voltage"
    id: tds_raw_v
    update_interval: 5s
    unit_of_measurement: "V"
    icon: "mdi:water-percent"
    internal: true

# Temperature Compensated Voltage  
  - platform: template
    name: "TDS TCV"
    id: tds_tcv
    unit_of_measurement: 'v'
    accuracy_decimals: 3
    lambda: 'return ((id(tds_raw_v).state) / (1 + (0.02 * (25) - 25.0)));'
    update_interval: 5s
    internal: true
  
# Temperature Compensated TDS
  - platform: template
    name: "TDS"
    id: hydro_tds
    icon: "hass:water-opacity"
    unit_of_measurement: 'PPM'
    accuracy_decimals: 2    
    update_interval: 5s
    lambda: return ((133.42*(id(tds_tcv).state)*(id(tds_tcv).state)*(id(tds_tcv).state) - 255.86*(id(tds_tcv).state)*(id(tds_tcv).state) + 857.39*(id(tds_tcv).state))*0.5) * -1;
    
  - platform: template
    name: "EC Reading"
    id: ec
    unit_of_measurement: 'EC'
    accuracy_decimals: 2
    update_interval: 5s
    lambda: return (((id(hydro_tds).state) * 2) / 1000);

I know i should up the update_interval because it has no reason to be at 5 seconds, but this was mostly for testing purposes. i’ve already put them back to 120seconds to give the esp a lil break now and again.

Sorry for my late response, I’m not visiting this forum so often…

Finally I had to get rid of TDS meter, because even if was powered by esp every half hour, pH meter readings were constantly distorted. I removed multiplexer and TDS meter and now pH readings looks much constant. There are still big differences between max and min readings, but after using median it looks good and credibly:

I’m using simplest pH probe and board/module from AliExpress:
pH_probe
pH_module

It’s connected like here:

but without lcd and using 3.3V diverter based on 2k2 and 3k3 ohm resistors to be more stable.

After setting this up, pH V readings on A0 need to be calibrated with voltmeter and ph buffers:

filters:
      - multiply: 3.1
      # Measured voltage / 3.1 -> Actual pH (buffer solution)
      - calibrate_linear:
          - 2.244 -> 9.18
          - 2.487 -> 6.86
          - 2.783 -> 4.00

I think your formula is incorrect. After substituting 25 for the temperature value it should look like:

((id(tds_raw_v).state) / (1 + (0.02 * (25 - 25.0))))

but I’m not sure if you can simple use 25…

I have a CQRobot CQRADS1115 TDS sensor. Trying to get it integrated into Home Assistant. Will this code work for that? I also have dallas temp sensors already configured and working for temp comp.

### TDS 01
# Raw TDS Reading
  - platform: ads1115
    multiplexer: 'A0_GND'
    gain: 6.144
    name: "TDS 01 Raw"
    ads1115_id: ads1
    id: tds01_raw
    update_interval: 10s
    unit_of_measurement: "v"
    accuracy_decimals: 3
    internal: true

# Temperature Compensated Voltage
  - platform: template
    name: "TDS 01 TCV"
    id: temp01_comp_v
    unit_of_measurement: 'v'
    accuracy_decimals: 3
    lambda: 'return ((id(tds01_raw).state) / (1 + (0.02 * ((id(probe01).state) - 25.0))));'
    update_interval: 10s
    internal: true

# Temperature Compensated TDS
  - platform: template
    name: "55G TDS 01"
    id: tds01_55g
    icon: "hass:water-opacity"
    unit_of_measurement: 'PPM'
    accuracy_decimals: 0    
    lambda: return (133.42*(id(temp01_comp_v).state)*(id(temp01_comp_v).state)*(id(temp01_comp_v).state) - 255.86*(id(temp01_comp_v).state)*(id(temp01_comp_v).state) + 857.39*(id(temp01_comp_v).state))*0.5;

# Temperature In °C
  - platform: dallas
    address: 0x6f01205fd42aa428
    name: "Probe 01"
    id: probe01
    unit_of_measurement: "°C"
    internal: true

# Temperature In °F
  - platform: template
    name: "55G TEMP 01"
    id: temp01_55g
    lambda: |-
      return id(probe01).state * (9.0/5.0) + 32.0;
    unit_of_measurement: "°F"
2 Likes

Yes that should do it

I made a youtube tutorial (in Spanish but with english subs) about how use TDS sensor and integrate with HA:

4 Likes

Would it still be connected like this? (On A0 instead of A1, and Im using a nodemcu)…Can’t get it to read the sensor.

Will this work with the ADS1115 as well?

Yes, I’m using “board: nodemcu-32s”, and dual sensors on both “A0_GND” & “A1_GND”

Almost certainly yes, although you may have to adjust the gain, you can check the RAW voltage with a multimeter

I know this is an older thread, however I am trying to do something similar here to monitor the TDS in an RODI filter. With that I will not be using a temperature probe and understand the significance of requiring temperature compensation.

I am new to ESPHome so am unsure how to essentially set the temperature compensation to 25.0 as a pre-defined value rather than one calculated by a probe.

I also have 3 TDS sensors and noticed above someone mentioned they have 4, so would love for someone to shed some light.

This is what I have so far but it wont compile because I am obviously using the wrong syntax/code

dallas:
  - pin: GPIO23
  
sensor:
  - platform: dallas
    address: 0xb7000006c34eb728
    name: "Aquarium Temperature"
    id: aquarium_temperature
    icon: "mdi:water-percent"
    unit_of_measurement: "°C"
    device_class: "temperature"
    state_class: "measurement"
    state: 25
    filters:
      - offset: -2.0
      
  - platform: adc
    pin: GPIO0
    name: "Tap TDS Raw Voltage"
    id: tapTDS_raw
    update_interval: 120s
    unit_of_measurement: "V"
    icon: "mdi:water-percent"
    internal: true

# Temperature Compensated Voltage  
  - platform: template
    name: "tapTDS TCV"
    id: taptds_tcv
    unit_of_measurement: 'v'
    accuracy_decimals: 3
    lambda: 'return ((id(tapTDS_raw).state) / (1 + (0.02 * ((id(aquarium_temperature).state) - 25.0))));'
    update_interval: 120s
    internal: true
  
# Temperature Compensated TDS
  - platform: template
    name: "Tap TDS"
    id: tapTDS
    icon: "hass:water-opacity"
    unit_of_measurement: 'PPM'
    accuracy_decimals: 0    
    update_interval: 120s
    lambda: return (133.42*(id(taptds_tcv).state)*(id(taptds_tcv).state)*(id(taptds_tcv).state) - 255.86*(id(taptds_tcv).state)*(id(taptds_tcv).state) + 857.39*(id(taptds_tcv).state))*0.5;

  - platform: adc
    pin: GPIO4
    name: "Pre-DI TDS Raw Voltage"
    id: prediTDS_raw
    update_interval: 120s
    unit_of_measurement: "V"
    icon: "mdi:water-percent"
    internal: true

# Temperature Compensated Voltage  
  - platform: template
    name: "prediTDS TCV"
    id: preditds_tcv
    unit_of_measurement: 'v'
    accuracy_decimals: 3
    lambda: 'return ((id(prediTDS_raw).state) / (1 + (0.02 * ((id(aquarium_temperature).state) - 25.0))));'
    update_interval: 120s
    internal: true
  
# Temperature Compensated TDS
  - platform: template
    name: "Pre-DI TDS"
    id: prediTDS
    icon: "hass:water-opacity"
    unit_of_measurement: 'PPM'
    accuracy_decimals: 0    
    update_interval: 120s
    lambda: return (133.42*(id(preditds_tcv).state)*(id(preditds_tcv).state)*(id(preditds_tcv).state) - 255.86*(id(preditds_tcv).state)*(id(preditds_tcv).state) + 857.39*(id(preditds_tcv).state))*0.5;

  - platform: adc
    pin: GPIO13
    name: "Output TDS Raw Voltage"
    id: outTDS_raw
    update_interval: 120s
    unit_of_measurement: "V"
    icon: "mdi:water-percent"
    internal: true

# Temperature Compensated Voltage  
  - platform: template
    name: "outTDS TCV"
    id: outtds_tcv
    unit_of_measurement: 'v'
    accuracy_decimals: 3
    lambda: 'return ((id(outTDS_raw).state) / (1 + (0.02 * ((id(aquarium_temperature).state) - 25.0))));'
    update_interval: 120s
    internal: true
  
# Temperature Compensated TDS
  - platform: template
    name: "Output TDS"
    id: outTDS
    icon: "hass:water-opacity"
    unit_of_measurement: 'PPM'
    accuracy_decimals: 0    
    update_interval: 120s
    lambda: return (133.42*(id(outtds_tcv).state)*(id(outtds_tcv).state)*(id(outtds_tcv).state) - 255.86*(id(outtds_tcv).state)*(id(outtds_tcv).state) + 857.39*(id(outtds_tcv).state))*0.5;

Hi I have an issue with the TDS using ESP32 and ADS1115 and the code used above. I get an error on compiling that there is no state member meaning it fails to call the ADS channel raw value. I just don’t get that. Any help appreciated.
sensor:

  • platform: dallas_temp
    #address:
    name: “Mixing tank water temperature”
    id: mixing_tank_temperature
  • platform: ads1115
    ads1115_id: ec_raw_v
    update_interval: 120s
    unit_of_measurement: V
    accuracy_decimals: 3
    multiplexer: ‘A0_GND’
    gain: 6.144
    name: “ADS1115_EC Channel A0-GND”

Temperature Compensated Voltage

  • platform: template
    name: “EC TCV”
    id: ec_tcv
    unit_of_measurement: ‘v’
    accuracy_decimals: 3
    lambda: ‘return ((id(ec_raw_v).state) / (1 + (0.02 * ((id(mixing_tank_temperature).state) - 25.0))));’
    update_interval: 120s
    internal: true

Hi.

Why did you use ADS1115 instead of ESP ADC? I’m trying to read ESP ADC input voltage but it jumps randomly from 0,300V TO 0,450V. The ADC input voltage is stable (0,438V) according to my multimeter and osciloscope.

Is this ESP32 ADC issue as suggested on link bellow?

Thank you

I have the same problem, if the sensor is not in any liquid then the ESP shows me 0.1 volts at the input, although this should actually be 0 V. Otherwise everything works as expected. I would now suspect that the TDS value is not being measured quite correctly. I use it for my aquarium.

This is my code:

Does anyone have a solution for this?

substitutions:
  name: esphome-web-9e0310
  friendly_name: Aquarium

esphome:
  name: ${name}
  friendly_name: ${friendly_name}
  min_version: 2024.6.0
  name_add_mac_suffix: false
  project:
    name: esphome.web
    version: dev

esp32:
  board: esp32dev
  framework:
    type: arduino

one_wire:
  - platform: gpio
    pin:
        number: GPIO23
        mode:
          input: true
          pullup: true
sensor:
  - platform: dallas_temp
    address: 0x6d1d11d446eec628
    name: "Temperatur Aquarium"
    unit_of_measurement: "°C"
    id: temp
    icon: "mdi:thermometer-plus"
    device_class: "temperature"
    state_class: "measurement"
    accuracy_decimals: 2

  # https://esphome.io/components/sensor/adc.html
  - platform: adc
    pin: GPIO35
    id: ph
    name: "pH Sensor"
    attenuation: auto 
    update_interval: 1s
    unit_of_measurement: pH

  # https://esphome.io/components/sensor/index.html#sensor-filters
    filters:
      - median:
          window_size: 15
          send_every: 8
      # Measured voltage -> Actual pH (buffer solution)
      - calibrate_polynomial:
          degree: 2
          datapoints:
            
            #- 3.435 -> 7.4
            - 2.02 -> 4.0
            - 1.68 -> 7.0
            - 1.33 -> 10.0
            #- 0 -> 14
            #- 5 -> 0
      # Enable logging

  # Raw TDS Reading
  - platform: adc
    pin: GPIO34
    name: "TDS 01 Raw"
    attenuation: 6db # only for ESP32
    id: tds01_raw
    update_interval: 10s
    unit_of_measurement: "v"
    accuracy_decimals: 3
    internal: False
  
# Temperature Compensated Voltage
  - platform: template
    name: "TDS 01 TCV"
    id: temp01_comp_v
    unit_of_measurement: 'v'
    accuracy_decimals: 3
    lambda: 'return ((id(tds01_raw).state) / (1 + (0.02 * ((id(temp).state) - 25.0))));'
    update_interval: 10s
    internal: False

# Temperature Compensated TDS
  - platform: template
    name: "TDS-01"
    id: tds01_55g
    icon: "hass:water-opacity"
    unit_of_measurement: 'PPM'
    accuracy_decimals: 0    
    lambda: return (133.42*(id(temp01_comp_v).state)*(id(temp01_comp_v).state)*(id(temp01_comp_v).state) - 255.86*(id(temp01_comp_v).state)*(id(temp01_comp_v).state) + 857.39*(id(temp01_comp_v).state))*0.5;

  - platform: template
    name: "EC Reading"
    id: ec
    unit_of_measurement: 'EC'
    accuracy_decimals: 2
    update_interval: 5s
    lambda: return (((id(tds01_55g).state) * 2) / 1000);

logger:

# Enable Home Assistant API
api:

# Allow Over-The-Air updates
ota:
- platform: esphome

# Allow provisioning Wi-Fi via serial
improv_serial:

wifi:
  # Set up a wifi access point
  ap: {}

# In combination with the `ap` this allows the user
# to provision wifi credentials to the device via WiFi AP.
captive_portal:

dashboard_import:
  package_import_url: github://esphome/example-configs/esphome-web/esp32.yaml@main
  import_full_config: true

# Sets up Bluetooth LE (Only on ESP32) to allow the user
# to provision wifi credentials to the device.
esp32_improv:
  authorizer: none

# To have a "next url" for improv serial
web_server:



Hallo Damian, also hat es schlußendlich nicht zusammen funktioniert ? Also pH Messung und TDS Messung oder bist du doch noch zu einer Lösung gekommen ?

In einem anderen Forum habe ich folgenden Hinweis, wie es funktionieren soll, gelesen:

An unserem Flusswasserqualitätsmonitor schalten wir zwischen den Messungen alle Sensoren aus. Wenn es Zeit ist, Daten zu erfassen, gehen wir wie folgt vor.

  1. Temperatursensor einschalten und Temperatur ablesen. Temperatursensor ausschalten.
  2. PH-Sensor einschalten und ablesen. PH-Sensor ausschalten.
  3. Schalten Sie den Sensor für gelösten Sauerstoff ein und lesen Sie den Wert ab. Schalten Sie den Sensor für gelösten Sauerstoff aus.
  4. Leitfähigkeitssensor einschalten und ablesen. Leitfähigkeitssensor ausschalten.
  5. Erstellen Sie Protokolldaten aus Abtastzeit und Sensordaten.
  6. Protokolldaten auf die SD-Karte schreiben.

Wir verwenden diese Reihenfolge, da der Temperatursensor am wenigsten störend und am wenigsten empfindlich gegenüber äußeren elektrischen Einflüssen und Störungen ist. Außerdem kann die Temperaturkorrektur für andere Sensoren erforderlich sein.
Der PH-Sensor ist am empfindlichsten gegenüber elektrischen Störungen und verursacht keine elektrischen Störungen.
Der Sensor für gelösten Sauerstoff ist der nächstempfindlichste.
Der Leitfähigkeitssensor sendet elektrische Impulse durch das Wasser und verursacht bei empfindlichen Sensoren großes Chaos. Wenn der Leitfähigkeitssensor zuletzt verwendet wird, können sich die anderen Sensoren zwischen den Proben erholen.
aus dem Forum Interference between PH sensor and TDS sensor - #3 by gilshultz - Sensors - Arduino Forum

Hallo Damian, also hat es schlußendlich nicht zusammen funktioniert ? Also pH Messung und TDS Messung oder bist du doch noch zu einer Lösung gekommen ?

In einem anderen Forum habe ich folgenden Hinweis, wie es funktionieren soll, gelesen:

An unserem Flusswasserqualitätsmonitor schalten wir zwischen den Messungen alle Sensoren aus. Wenn es Zeit ist, Daten zu erfassen, gehen wir wie folgt vor.

  1. Temperatursensor einschalten und Temperatur ablesen. Temperatursensor ausschalten.
  2. PH-Sensor einschalten und ablesen. PH-Sensor ausschalten.
  3. Schalten Sie den Sensor für gelösten Sauerstoff ein und lesen Sie den Wert ab. Schalten Sie den Sensor für gelösten Sauerstoff aus.
  4. Leitfähigkeitssensor einschalten und ablesen. Leitfähigkeitssensor ausschalten.
  5. Erstellen Sie Protokolldaten aus Abtastzeit und Sensordaten.
  6. Protokolldaten auf die SD-Karte schreiben.

Wir verwenden diese Reihenfolge, da der Temperatursensor am wenigsten störend und am wenigsten empfindlich gegenüber äußeren elektrischen Einflüssen und Störungen ist. Außerdem kann die Temperaturkorrektur für andere Sensoren erforderlich sein.
Der PH-Sensor ist am empfindlichsten gegenüber elektrischen Störungen und verursacht keine elektrischen Störungen.
Der Sensor für gelösten Sauerstoff ist der nächstempfindlichste.
Der Leitfähigkeitssensor sendet elektrische Impulse durch das Wasser und verursacht bei empfindlichen Sensoren großes Chaos. Wenn der Leitfähigkeitssensor zuletzt verwendet wird, können sich die anderen Sensoren zwischen den Proben erholen.

Hello,

finally I gave up for TDS meter since as mentioned:

even if was powered by esp every half hour, pH meter readings were constantly distorted

In thread about pH sensor someone mentioned:

A TDS will alter the PH values without having an isolator if in the same liquid system

I agree. Didn’t check if TDS measured by another ESP will not distort pH readings.