Frischwasser Tankinhalt und Entnahme mit einem ESP32

Jeder kennt es, in der Regel taugt die Anzeige für den Tankinhalt für Frischwasser nicht die Bohne. Zu ungenau, oft Fehlfunktionen. Sensoren mit Ultraschall oder Messung des Drucks sind teuer und je nach Tank auch nicht viel besser. Lange habe ich mich mit der Frage beschäftigt, wie ich das Problem löse. Natürlich ist die Schätzung für viele Camper die beste Messung, aber ich mag es genau und außerdem smart.

Solltest Du Fragen haben oder Unterstützung brauchen, nutze bitte das Kontaktformular, um uns anzuschreiben.

Ich habe eine ganze Reihe von Durchflusssensoren ausprobiert. Viele davon funktionieren gut im Trockentest (Zuhause an einem Schlauch und einem Eimer mit Wasser). Eingebaut zeigen sich aber einige Probleme. z.B. macht der Sensor keinen Sinn hinter der Druckpumpe (anders bei Tauchpumpen), denn dann steht der Sensor mit seinen Anschlüssen immer unter Druck. Das funktioniert zwar, birgt aber eine zusätzliche Gefahr für Undichtigkeiten.

Also ab dafür und vor die Druckpumpe. Das scheint erstmal wunderbar, allerdings hatte ich bei vielen der Durchflusssesnoren das Problem, das die Druckpumpe anfing zu stottern. Die Menge an Wasser die durch den Sensor ging war nicht ausreichend um das Ansaug- zu Druckverhalten richtig hin zu bekommen, auch aufbohren der Anschlüsse brachte keine wesentliche Verbesserung. Dann hielt ich eines Tages einen Sensor in der Hand von den ich erst dachte, das er zu klein sei. Allerdings ist er ordentlich im Durchfluss da es keinen Kreisel- sondern eine turbinenatrigen Aufbau hat. Dazu kommt, dass der Sensor sogar genauer ist als die anderen.

Das Schöne an diesen Sensoren ist, dass sie über einen HALL Sensor verfügen und somit hervorragend an einem ESP32 betrieben werden können. Kurzerhand habe ich mir also einen ESP32-C3 geschnappt, das Ganze in ESPHome in mehreren Anläufen zu einem brauchbaren Wasserverbrauchssensor zusammengebaut.

Der ESP32 hat noch ein kleines Gehäuse mit einem 12v zu 5v Step-Down bekommen und wurde dann in die Leitung von Frischwassertank zur Druckpumpe eingebaut. Dadurch, dass kaum Druck auf dem Stück ist, muss ich mich auch nicht um Undichtigkeiten sorgen.

Der Sensor kann in ein bestehendes WiFi Netz und somit in Homeassistant übernommen werden, oder einzeln als AP und per Handy angesprochen werden. Den aktuellen Wert und wie der Verbrauch aussieht, bekommt man natürlich nur brauchbar angezeigt, wenn man sich das Ganze dann auf ein Dashboard bringt.

Die benötigten Teile bekommt man soweit alle auf Amazon, hier eine Liste der Teile:
Wasserdurchflusssensor -> https://www.amazon.de/dp/B0CZRZ3CB1
Lilie Anschlüsse -> https://www.amazon.de/dp/B07QS55GTM
ESP32-C3 Super Mini -> https://amzn.eu/d/0bo8eZLd
Gehäuse -> https://www.amazon.de/dp/B09PTJ492D

Der entsprechende ESPHome Code (jeder Sensor muss mit Wasserentnahmen (Messbecher) eingemessen werden):

###
# Version 20260606-1400

esphome:
  name: esp-water-sensor
  friendly_name: "ESP Water Sensor"
  name_add_mac_suffix: false
  platformio_options:
    board_build.flash_mode: dio
  on_boot:
    then:
      - light.turn_off:
          id: led_status
  #        effect: none
  #        brightness: 40%
  #        red: 100%
  #        green: 100%
  #        blue: 0%
      - component.update: template_refill_total
      - component.update: template_total_consumed
      - component.update: template_tank_current
      - component.update: template_last_taken
      - component.update: template_count_taken


esp32:
  board: lolin_c3_mini
  variant: esp32c3
  cpu_frequency: 160MHZ
  framework:
    type: esp-idf
    version: recommended
    sdkconfig_options:
      CONFIG_BT_ENABLED: "n"
      CONFIG_BT_NIMBLE_ENABLED: "n"


globals:
  - id: int_wifireconnect
    type: int
    restore_value: no
    initial_value: '-1'

  - id: float_tank_volume
    type: float
    restore_value: yes
    initial_value: "0"

  - id: float_tank_refilled
    type: float
    restore_value: yes
    initial_value: "0"

  - id: float_total_consumed
    type: float
    restore_value: yes
    initial_value: "0"

  - id: float_last_taken
    type: float
    restore_value: yes
    initial_value: "0"

  - id: float_count_taken
    type: float
    restore_value: yes
    initial_value: '0'

  - id: int_is_water_flow_status
    type: int
    initial_value: "0"


logger:
  level: WARN
  baud_rate: 0


api:
  encryption:
    key: "<NEW-API-KEY>"


ota:
  - platform: esphome
    password: !secret ota_password


wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  min_auth_mode: WPA2
  fast_connect: true
#  output_power: 20dB
  enable_btm: true
  enable_rrm: false
  on_connect:
        - lambda: id(int_wifireconnect)+=1;
  ap:
    ssid: "ESP-WATER-SENSOR"
    password: !secret ap_fallback_password


captive_portal:


#web_server:
#  port: 80
#  include_internal: true 
#  version: 3
#  auth:
#    username: !secret web_server_username
#    password: !secret web_server_password


time:
  - platform: homeassistant
    id: homeassistant_time
    timezone: Europe/Berlin


sensor:
  - platform: pulse_counter
  #  #YF-S201C
    pin:
      number: 4
      mode:
        input: true
        pullup: true
    name: "Durchfluss"
    id: water_pulses
    internal: false
    update_interval: 1s
    unit_of_measurement: "L/min"
    accuracy_decimals: 3
    filters:
      # pulse_counter liefert Pulse/min → × (1L / 300 Pulse) = L/min
      - multiply: 0.003386       # = 1/300 Pulse pro Liter (YF-S201C: 300 P/L)
      - sliding_window_moving_average:
          window_size: 3
          send_every: 1
      - filter_out: nan
    total:
      unit_of_measurement: "L"
      name: "Entnahme"
      id: total_count_zwischenverbrauch
      icon: mdi:water-pump
      accuracy_decimals: 3
      filters:
        - multiply: 0.003386
    count_mode:
      rising_edge: INCREMENT
      falling_edge: DISABLE

  - platform: template
    name: "Aufgefüllt"
    id: template_refill_total
    icon: mdi:water
    unit_of_measurement: "L"
    accuracy_decimals: 3
    update_interval: never
    lambda: |-
      return id(float_tank_refilled);

  - platform: template
    name: "Gesamte Entnahme"
    id: template_total_consumed
    icon: mdi:water-pump
    unit_of_measurement: "L"
    accuracy_decimals: 3
    update_interval: never
    lambda: |-
      return id(float_total_consumed);

  - platform: template
    name: "Tankinhalt"
    id: template_tank_current
    icon: mdi:waves
    unit_of_measurement: "L"
    accuracy_decimals: 3
    update_interval: never
    lambda: |-
      return id(float_tank_volume);

  - platform: template
    name: "Letzte Entnahme"
    id: template_last_taken
    icon: mdi:water-pump
    unit_of_measurement: "L"
    accuracy_decimals: 3
    update_interval: never
    lambda: |-
      return id(float_last_taken);

  - platform: template
    name: "Entnahmen"
    id: template_count_taken
    icon: mdi:numeric-1-box-multiple-outline
    unit_of_measurement: "x"
    accuracy_decimals: 0
    update_interval: never
    lambda: |-
      return (int) id(float_count_taken);

  - platform: internal_temperature
    id: esp_internal_temperatur
    name: "ESP Temperatur"

  - platform: uptime
    name: "ESP Uptime"
    id: esp_uptime_sensor
    update_interval: 60s

  - platform: template
    id: esp_wifi_reconnect
    name: "ESP Wifi reconnect"
    accuracy_decimals: 0
    icon: mdi:wifi-sync
    unit_of_measurement: "x"
    entity_category: "diagnostic"
    update_interval: 60s
    lambda: |-
      return (int) id(int_wifireconnect);

  - platform: wifi_signal
    name: "WiFi Signal dB"
    id: wifi_signal_db
    icon: mdi:wifi
    update_interval: 60s
    entity_category: "diagnostic"
    disabled_by_default: false
    internal: true

  - platform: copy
    source_id: wifi_signal_db
    name: "WiFi Signal Percent"
    icon: mdi:wifi
    filters:
      - lambda: return min(max(2 * (x + 100.0), 0.0), 100.0);
    unit_of_measurement: "%"
    entity_category: "diagnostic"
    device_class: ""
    disabled_by_default: false


button:
  - platform: restart
    name: "ESP Restart"
    entity_category: config
    disabled_by_default: false

  - platform: template
    name: "Reset Data"
    id: button_reset_data
    disabled_by_default: false
    entity_category: config
    on_press:
      - lambda: |-
            id(float_tank_refilled) = 0;
            id(float_total_consumed) = 0;
            id(float_tank_volume) = 0;
            id(float_last_taken) = 0;
            id(float_count_taken) = 0;
            global_preferences->sync();
      - component.update: template_refill_total
      - component.update: template_total_consumed
      - component.update: template_tank_current
      - component.update: template_last_taken
      - component.update: template_count_taken

  - platform: template
    name: "Nachfüllen"
    id: button_refill
    entity_category: config
    on_press:
      - lambda: |-
          id(float_tank_refilled) = id(number_refill_amount).state;
          id(float_tank_volume) += id(number_refill_amount).state;
      - number.set:
          id: number_refill_amount
          value: 0
      - component.update: template_refill_total
      - component.update: template_tank_current


number:
  - platform: template
    name: "Nachfüllmenge (L)"
    id: number_refill_amount
    entity_category: config
    icon: mdi:waves
    optimistic: true
    restore_value: true
    min_value: -125
    max_value: 125
    step: 0.001
    unit_of_measurement: "L"


text_sensor:
  - platform: version
    hide_timestamp: true
    hide_hash: true
    name: "ESP Version"
    disabled_by_default: false

  - platform: wifi_info
    ip_address:
       name: "IP Address"
    mac_address:
      name: "Mac Address"


light:
  - platform: esp32_rmt_led_strip
    name: "Status LED"
    id: led_status
    pin: GPIO10
    num_leds: 1
    rgb_order: GRB
    chipset: ws2812
    default_transition_length: 250ms
    effects:
      - pulse:
          name: Pulse
          transition_length: 100ms
          update_interval: 250ms


interval:
  - interval: 250ms
    then:
      - if:
          condition:
            lambda: 'return id(water_pulses).state <= 0.01 && id(int_is_water_flow_status) == 1;'
          then:
            - lambda: |-
                id(int_is_water_flow_status) = 0;
                id(float_tank_volume) -= id(total_count_zwischenverbrauch).state;
                id(float_total_consumed) += id(total_count_zwischenverbrauch).state;
                id(float_last_taken) = id(total_count_zwischenverbrauch).state;
                id(float_count_taken) += 1;

            - component.update: template_refill_total
            - component.update: template_total_consumed
            - component.update: template_tank_current
            - component.update: template_last_taken
            - component.update: template_count_taken

            - pulse_counter.set_total_pulses:
                id: water_pulses
                value: 0
            - light.control:
                id: led_status
                effect: none
                brightness: 50%
                red: 0%
                green: 100%
                blue: 0%
      - if:
          condition:
            lambda: 'return id(water_pulses).state >= 0.02&& id(int_is_water_flow_status) == 0;'
          then:
            - lambda: |-
                id(int_is_water_flow_status) = 1;
            - light.control:
                id: led_status
                effect: none
                brightness: 50%
                red: 100%
                green: 0%
                blue: 0%

Solltest Du Fragen haben oder Unterstützung brauchen, nutze bitte das Kontaktformular, um uns anzuschreiben.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre, wie deine Kommentardaten verarbeitet werden.