pH metre DIY via ESPHome

Comme je vois pas mal de personnes intéressées par la mesure du pH et que je n’ai pas trouver de solutions probantes sur les divers forum, je vous livre ma solution et les divers points pour faire évoluer le projet. Je ne suis pas expert dans le domaine et ai des connaissance très limitées en électronique. Toutes expériences ou renseignements complémentaire sont les bien venus.

Pour ma part il s’agit d’une mesure en continue pour un aquarium d’eau de mer. Je précise ce point car ma calibration se fait entre pH 7 et 9. Il faudra adapter cette solution à votre usage et vos solutions de calibration.

Le principe :

J’utilise ESPHome pour faire remonter les valeurs de lecteur en mV et je gère la calibration depuis HA. J’envisageais au début de tout faire sur l’ESP mais cela implique de régénérer le code à chaque fois. Idem pour la formule de conversion, pour le moment à deux points mais qui pourrait se faire à pH 4, 7 et 10 simultanément.

Le matériel de base:

  • une carte pH metre acheté sur AE ref. PH-4502C


    Le connecteur possède 6 broches.
    To : Température pour un sonde externe au besoin (voir compensation ci-dessous)
    Do : sortie logique : si la valeur du pH est supérieure ou inférieure à un seuil réglé par le potentiomètre l’état est signalé par une LED. (port que je n’utilise pas)
    Po : La sortie analogique du PH
    G : Masse
    G : Masse (les deux sont à relier pour une meilleur lecture)
    V+ : 5V
    Ils existe des cartes plus évoluée avec isolement de la carte pour éviter les faux signaux si il y a des fuites de courant dans l’eau (du moins c’est que j’en ai compris). Chez moi le bac est relié à la terre ce qui doit limiter les problèmes.

  • Une sonde pH :
    Pour ma part une vielle sonde du commerce d’aquariophile de marque JBL. Le choix de cette dernière à son importance en fonction du milieu à mesurer et du temp d’immersion. Je ne vais pas faire de cours ici, mais préférez une double jonction à gel si vous la laisser constamment dans l’eau. Cette dernière s’usera plus rapidement dans des eaux à faible salinité de type eau du robinet qu’en eau de mer car la concentration en ions KCl qui la compose générera moins de pression osmotique et donc moins de perte. C’est aussi pour cela qu’il faut étalonner la sonde de manière régulière. Il me semble que les pH mètre les plus perfectionnés testent la sonde lors de la calibration pour ajuster cette dérive. Pour le moment je n’ai pas observé de problème de dérive dans le temps sur des solutions tampons.

  • Un ESP : dans mon cas un wemos d1 mini avec un carte ads115 pour l’acquisition analogique car le port A0 de la carte ne donne pas de mesure stable. Du coup la sortie analogique de la carte pH passe par l’entrée de la ads115 qui converti en I2C… si je dis pas de conneries. Pour le montage il faut se référer à la doc de la carte car le montage change en fonction de l’adressage de la carte. Là encore il faut il me semble qu’il faille relier les entrées analogiques non utilisées à la masse pour une meilleur lecture.

  • Une sonde de température : J’ai une sonde dallas extérieure qui est reliée directement à l’ESP avec la petite résistance qui va bien. Il me semble que la carte compense la température avec la une sonde qui lui est propre. Cette sonde mesure la température de la carte et non de l’échantillon et qui est possible de lui en adjoindre un externe via le connecteur To. Chez moi la sonde me sert à remonter la température de l’eau et non pas à compenser la valeur. Je crois que la compensation de température est superflue pour mon utilisation (cf les tableau ci joint). Si quelqu’un trouve un moyen de le faire je suis toute ouie à l’option pour l’intégrer dans mon projet.


    (http://www.crisoninstruments.com/site/pdfs_fr/01/crison_fr_1_1.pdf)

La mise en place:

SHEMA MONTAGE A FAIRE

Réglage de l’offset : Ph 7 est la moyenne des valeurs. Pour forcer la valeur il faut faire un cour-circuit au niveau de la connexion BNC à l’aide d’un trombone par exemple et mesurer la tension à l’aide d’un multimètre au niveau de la pin Po. Il faut ajuster la valeur à 2,5 V. On peut aussi faire varier la valeur de façon à ce que l’amplitude de mesures réelles soit équivalent à la moyenne de ce que l’on veut mesurer ( je sais pas ci c’est clair).

Le code ESPHome:

  name: phmeter2
  platform: ESP8266
  board: d1_mini

wifi:
  ssid: !secret wifissid
  password: !secret wifipassword
    
captive_portal:

# Enable logging
logger:

# Enable Home Assistant API
api:

ota:

i2c:
  sda: D2
  scl: D1
  scan: True

ads1115:
  - address: 0x48
  
dallas:
  - pin: D0
    update_interval: 10s
sensor:
  - platform: dallas
    address: 0xCA3C01B55648B728
    name: "Reef Temperature"
    filters:
      - median:
          window_size: 6
          send_every: 6
          send_first_at: 1
    unit_of_measurement: "°C"
    icon: "mdi:temperature-celsius"
    accuracy_decimals: 1
  - platform: ads1115
    multiplexer: 'A0_GND'
    gain: 6.144
    name: "Reef pH ads"
    id: ph_ads
    update_interval: 10s
    filters:
      - median:
          window_size: 6
          send_every: 6
          send_first_at: 1
    unit_of_measurement: "mV"
    icon: "mdi:water"
    accuracy_decimals: 3

On retrouve le port I2C et l’adresse de l’ads1115.
Le sensor de ma sonde dallas et de l’ads1115.
Le gain est de 6.144 car c’est avec lui que j’ai le plus d’aptitude et donc de précision de mesure. Je relève la mesure toutes les 10 secondes et envoie la valeur moyenne au bout 6 mesures soit toutes les minutes.

Remontée des valeurs dans MQTT


Comme expliqué plus haut je fais remonter la valeur en mV la conversion se fait dans HA.

Le package HA

Pour simplifier la gestion de mes projets je travaille avec un package pour chacun d’entre eux.
Les automatisations pour les notifications sont en cours de développement et ne devraient pas intéresser grand monde. Je mets le tout au cas où pour donner des idées ou avoir vos corrections éventuelles.

###################################################################################
#                                  device_tracker:                                #
###################################################################################
device_tracker:
  - platform: ping
    interval_seconds: 30
    hosts:
      ph_meter: 192.168.1.26
####################################################################################
##                               PH meter                          #
####################################################################################
input_boolean:
################################## 
  calib_ph:
    name: "Calib mode"

input_select:
################################## 
  c_calib_ph4:
    name: Calibration pH4
    icon: mdi:adjust
    options:
      - "4.00"
      - "4.01"
  c_calib_ph7:
    name: Calibration pH7
    icon: mdi:adjust
    options:
      - "6.86 "
      - "7.00"
      - "7.01"
  c_calib_ph9_10:
    name: Calibration pH9-10
    icon: mdi:adjust
    options:
      - "9.00"
      - "9.18"
      - "9.21"
      - "10.00"
      - "10.01"

input_number:
  v_calib_mv4:
    name: Calibration mV à 4 
    icon: mdi:gauge
 #   initial: 4.504
    min: 3.500
    max: 5.000
    step: 0.001
    mode: box
  v_calib_mv7:
    name: Calibration mV à 7 
    icon: mdi:gauge
 #   initial: 4.013
    min: 3.500
    max: 5.000
    step: 0.001
    mode: box
  v_calib_mv9:
    name: Calibration mV à 9
    icon: mdi:gauge
  #  initial: 3.703
    min: 3.500
    max: 5.000
    step: 0.001
    mode: box

sensor: 
  - platform: template
    sensors:
      reef_ph:   
        friendly_name: "Valeur PH"      
        value_template: '{{ (states.input_select.c_calib_ph9_10.state | float) - ( (((states.input_number.v_calib_mv9.state | float)-(states.sensor.reef_ph_ads.state | float))*((states.input_select.c_calib_ph7.state | float)-(states.input_select.c_calib_ph9_10.state | float))) / ((states.input_number.v_calib_mv7.state | float)-(states.input_number.v_calib_mv9.state | float)) ) | round(2) }}'
#      xc  (states.sensor.reef_ph_ads.state | float)
#          (states.input_select.c_calib_ph4.state | float)
#      ya  (states.input_select.c_calib_ph7.state | float)
#      yb  (states.input_select.c_calib_ph9_10.state | float)
#          (states.input_number.v_calib_mv4.state | float)
#      xa  (states.input_number.v_calib_mv7.state | float)
#      xb  (states.input_number.v_calib_mv9.state | float)
#########################################################ph_meter
# ph_meter ONLINE status sensors
      ph_meter:
        value_template: '{% if is_state("device_tracker.ph_meter", "home") %}Online{% else %}offline{% endif %}'
        friendly_name: 'État ph_meter'
        icon_template: >-
          {% if is_state('device_tracker.ph_meter', 'home') %}
            mdi:power-plug
          {% else %}
            mdi:power-plug-off
          {% endif %}

automation: 
# Notify offline     
  - alias: Notify ph_meter offline 
    trigger:
      platform: state
      entity_id: device_tracker.ph_meter
      to: 'not_home'
    action:
      service: notify.iosdevice
      data:
        title: "ph_meter offline"
        message: "le pH mètre est hors ligne !!! Mesures impossibles !!! "
        data:
          push:
            thread-id: "activité_reef-group"

  - alias: 'Notif Reef Temp'
    initial_state: 'on'
    trigger:
      - platform: time_pattern
        hours: "/1"
        seconds: "03"
    condition: 
      condition: and
      conditions:
        - condition: template
          value_template: >
            {% if ( states('sensor.time') > "08:00" ) and ( states('sensor.time') < "20:00" )%}
              True
            {% else %}
              False
            {% endif %}
        - condition: or
          conditions: 
            - condition: template
              value_template: "{{states('sensor.reef_temperature_2')| int > 26 | int}}"
            - condition: template
              value_template: "{{states('sensor.reef_temperature_2')| int < 23 | int}}"
    action:
      service: notify.iosdevice
      data_template:
        title: "Reef : Alerte Température"
        message: "La température d'eau est à {{states('sensor.reef_temperature_2')}} alors qu'elle devrait être comprise entre 23 et 26°C"
        data:
        push:
          thread-id: "activité_reef-group"
  • Je commence avec un device tracker qui me permet de me faire une alerte en cas de déconnexion

  • Un input_boolean qui permet d’accéder aux fonctions de calibration sur la carte Lovelace.

  • Ensuite, des inputs_select pour un faire le choix en fonction des solutions de calibration que je serai amené à utiliser sans avoir à modifier le code. Les PH de celle-ci varient en fonction de fabricants. Pour le moment la formule de calibration ne se base que sur deux valeurs (neutre et basique) mais pourrait se faire sur trois au besoin. Il faudrait par contre revoir la formule pour faire une régression linéaire à trois inconnues.

  • Les input_number sont là pour rentrer les valeurs en mV lors de la calibration pour chaque solution tampon directement depuis l’interface Lovelace.

  • les sensor :
    reef_ph convertit la valeur mV en PH en fonction du pH des 2 solutions de calibration choisies et de la valeur de la mesure réalisée pour chacune d’elle.
    ph_meter présente l’état connecté ou non du pH-mètre.

Intégration Lovelace

Le graphique : Capture d’écran 2020-10-11 à 17.21.12
Le pic bas de valeurs est dû à la vérification de la propreté de la sonde. Dommage que c’est bien plus beau d’ordinaire. Le graph présente 48h de valeurs et je suis en train de plancher à une échelle dynamique. Il me reste aussi a trouvé une icône pour le pH si quelqu’un a une piste je suis preneur.
Le code du graph:

entities:
  - entity: sensor.reef_ph
    index: 0
  - aggregate_func: min
    color: 'rgba(0,0,0,.25)'
    entity: sensor.nigh_time
    label: false
    show_line: false
    show_points: false
    show_state: false
    smoothing: false
    y_axis: secondary
font_size: 75
hours_to_show: 48
line_color: green
line_width: 2
points_per_hour: 4
show:
  extrema: true
  fill: true
  graph: line
  icon: true
  icon_adaptive_color: true
  labels: false
  legend: false
  name: true
  name_adaptive_color: true
  points: hover
  state: true
type: 'custom:mini-graph-card'

La Carte et Calibration

La carte est conditionnelle et affiche les inputs qu’en mode calibration pour éviter les fausses manipulations.

Le code de la carte

cards:
  - card:
      entities:
        - entity: sensor.reef_ph_ads
        - entity: sensor.reef_ph
        - entity: sensor.reef_temperature_2
        - entity: input_boolean.calib_ph
      show_header_toggle: false
      title: Reef pH  / Temp.
      type: entities
    conditions:
      - entity: input_boolean.calib_ph
        state: 'off'
    title: Reef pH  / Temp.
    type: conditional
  - card:
      entities:
        - entity: sensor.reef_ph_ads
        - entity: sensor.reef_ph
        - entity: sensor.reef_temperature_2
        - entity: input_boolean.calib_ph
        - entity: input_select.c_calib_ph4
        - entity: input_number.v_calib_mv4
        - entity: input_select.c_calib_ph7
        - entity: input_number.v_calib_mv7
        - entity: input_select.c_calib_ph9_10
        - entity: input_number.v_calib_mv9
      show_header_toggle: false
      title: Reef pH  / Temp. Calib mode
      type: entities
    conditions:
      - entity: input_boolean.calib_ph
        state: 'on'
    type: conditional
type: vertical-stack

Au final …

Voilà ! Bravo au courageux qui auront pris le temps de lire le post jusqu’au bout. Ça m’a pris une partie de l’après-midi rien que pour la rédaction du post en gérant les gamins à côté ;-). Raison pour laquelle j’ai du mal à partager de tels projets.

Je vous ai tout mis, car j’ai pas mal galéré dans mes débuts sur HA et ai du faire pas mal de recherche pour trouver l’inspiration pour réaliser des projets aboutis. J’espère que cela donnera envie aux nouveaux utilisateurs. J’ai un projet similaire en cours avec mesure de l’ORP et un gros projet à base de pompe doseuse (note à moi-même : trouver un WE entier pour la rédaction du post) que j’espère pouvoir vous partager.

Désolé pour les fautes de frappes, de grammaires ou d’orthographes…

Je compte sur vous pour les retours. Tout est bon à prendre. Avis aux amateurs pour compléter le projet, voir corriger des erreurs éventuelles.

4 J'aime

Merci du partage ! :+1:

Au top un bon début.

Yes merci pour le tuto

Tip top :slight_smile: :+1: :+1: :+1: :+1:

Beau boulot, je verrai pour l’adapter certainement pour l’été prochain pour la piscine…Merci du partage

Trés beau projet et bien expliqué merci