Cat feeder and water fountain ESPhome project

I want to share my cat feeder and water fountain.

Before, I had a pretty similar set up but I had to move to ESP because ZigBee wasn´t so reliable and several times the feeder didn’t work, but now that is all in the past.

So, the setup is an ESP8266 to control a 24VDC (can be 12VDC) motor and a 5VDC water pump (almost all the water cat fountain are 5VDC) and a PIR motion sensor to start the pump when is need it.

The part list is the following:

  • 220VAC to 24VDC converter

  • 220VAC to 5VDC converter

  • Two Push buttons

  • ESP8266 NodeMCU

  • 2 Ralay Module

  • Generic PIR sensor

  • 24 VDC (about 15 rpm)

  • 5VDC water fountain

  • Cereal Dispenser

  • CNC Motor Jaw Shaft Coupler 5mm To 8mm Flexible Coupling (size depend on the shaft of the motor and the cereal dispenser)

  • Some DuPont cables

  • Some extra cables

  • Some patience…

Most of the code is on ESPHome, so is no need to be connected all the time to HA. I’m planning to add a clock module to the ESP8266 so in case of blackout it still has the time…

The ESPHome program has the following controls on HA use:

  • First meal Time
  • Second meal Time.
  • Time duration of the feed motor (let say 15 sec each meal)
  • Water time after motion is clear (let say 10 sec.)
  • Feed Button
  • Water Buton

Hope you enjoy it!!

ps. Some parts of the code is in spanish… just google the words so it make sense…

The code:

esphome:
  name: cat-feeder
  friendly_name: cat feeder

esp8266:
  board: nodemcuv2

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "Vnvo3Jd6ufsdffsfdshgtfhtrhrth7AN8O8="

ota:
  password: "5d1hdfhfgghfg7813c436a73a158"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Cat-Feeder Fallback Hotspot"
    password: !secret wifi_password


captive_portal:

switch:

  - platform: gpio
    pin: D1
    id: relay_agua
    inverted: true
    restore_mode: ALWAYS_OFF
 
  - platform: template
    id: ha_switch_agua
    name: "HA switch agua"
    optimistic: true
    on_turn_on:
      - logger.log: "############################# Power ON #############################"
      - switch.turn_on: relay_agua
    on_turn_off:
      - logger.log: "############################# Power OFF #############################"
      - switch.turn_off: relay_agua

  - platform: template
    id: water_on_delay
    optimistic: true
    on_turn_on:
      - switch.toggle: ha_switch_agua
      - delay: 240 sec
      - if:
          condition:
            # Same syntax for is_off
            binary_sensor.is_off: pir_sensor
          then:
            - switch.turn_off: ha_switch_agua
            - switch.turn_off: water_on_delay

  - platform: gpio
    name: "relay"
    pin: D7
    id: relay
    inverted: true
    restore_mode: ALWAYS_OFF

  # The following can be omitted
  - platform: restart
    name: restart

  - platform: template
    name: "HA Feed Button"
    id: ha_feed_button
    optimistic: true
    on_turn_on:
      - logger.log: "############################# Power ON #############################"
      - switch.turn_on: relay
      - switch.turn_on: water_on_delay
    on_turn_off:
      - logger.log: "############################# Power OFF #############################"
      - switch.turn_off: relay
      - text_sensor.template.publish:
            id: ha_hora_encendido
            state: !lambda |-
                char str[17];
                time_t currTime = id(homeassistant_time).now().timestamp;
                strftime(str, sizeof(str), "%m-%d-%Y %H:%M", localtime(&currTime));
                return  { str };

#                strftime(str, sizeof(str), "%Y-%m-%d %H:%M:%S", localtime(&currTime));
                
      


number:
  - platform: template
    name: "Timepo agua despues de sin movimiento"
    id: water_time_after_clear
    optimistic: true
    min_value: 0
    max_value: 60
    step: 5
    mode: box
    initial_value: 10
    restore_value: true

    
  - platform: template
    id: duration
    name: "duration"
    optimistic: true
    min_value: 1
    max_value: 25
    step: 1
    mode: box
    initial_value: 12
    restore_value: true

  - platform: template
    name: "Hora 1"
    id: hora_1
    optimistic: true
    initial_value: 7
    min_value: 0
    max_value: 24
    step: 1
    mode: box

  - platform: template
    name: "Hora 2"
    id: hora_2
    optimistic: true
    initial_value: 19
    min_value: 0
    max_value: 24
    step: 1
    mode: box

  - platform: template
    name: "Minuto 1"
    id: minuto_1
    optimistic: true
    initial_value: 30
    min_value: 0
    max_value: 60
    step: 1
    mode: box

  - platform: template
    name: "Minuto 2"
    id: minuto_2
    optimistic: true
    initial_value: 30
    min_value: 0
    max_value: 60
    step: 1
    mode: box

binary_sensor:
##################################### agua############################
  - platform: gpio
    pin: D0
    name: "PIR Sensor"
    id: pir_sensor
    device_class: motion
    on_press:
      - if:
          condition:
           and:
            - lambda: 'return id(homeassistant_time).now().hour >= 7;'
            - lambda: 'return id(homeassistant_time).now().hour <= 23;'
          then:
            - switch.turn_on: ha_switch_agua
    on_release:
      then:
        - delay:  !lambda "return ((id(water_time_after_clear).state * 1000));"
        - if:
            condition:
              # Same syntax for is_off
              binary_sensor.is_off: pir_sensor
            then:
              - switch.turn_off: ha_switch_agua


  - platform: gpio
    id: button_water
    pin: 
      number: D2
      inverted: true
    filters:
      - delayed_on: 50ms
      - delayed_off: 50ms
    on_press:
      then:
        - logger.log: "############################# Power on/off PRESS #############################"
        - switch.toggle: ha_switch_agua
        - delay: 240 sec
        - if:
            condition:
              # Same syntax for is_off
              binary_sensor.is_off: pir_sensor
            then:
              - switch.turn_off: ha_switch_agua

########################### fin agua############## inicio comida ########################
  - platform: gpio
    id: power_on_off
    pin: 
      number: D3
      inverted: true
    filters:
      - delayed_on: 400ms
      - delayed_off: 50ms
    on_press:
      then:
        - logger.log: "############################# Power on/off PRESS #############################"
        - delay: 0.0 sec
        - switch.turn_on: ha_feed_button
    on_release:
        then:
          - switch.turn_off: ha_feed_button

interval:

  - interval: 60sec
    then:
      - lambda: |-
          float h1 = id(hora_1).state;
          float m1 = id(minuto_1).state;
          float hah = id(homeassistant_time).now().hour;
          float ham = id(homeassistant_time).now().minute;
          ESP_LOGD("main", "hora_1: %0.0f; minuto_1: %0.0f xxxxxxx HA_hora: %0.0f; HA_minuto: %0.0f; ",h1,m1,hah,ham);

      - if:
          
          condition:
            or:
              - and:
                - lambda: 'return id(hora_1).state == id(homeassistant_time).now().hour ;'
                - lambda: 'return id(minuto_1).state == id(homeassistant_time).now().minute;'
              - and:
                - lambda: 'return id(hora_2).state == id(homeassistant_time).now().hour;'
                - lambda: 'return id(minuto_2).state == id(homeassistant_time).now().minute;'       
          then:
            - lambda: ESP_LOGD("main", "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ");
            - lambda: ESP_LOGD("main", "Inicio");
            - switch.turn_on: ha_feed_button
            - delay: !lambda "return ((id(duration).state * 1000));"
            - lambda: ESP_LOGD("main", "Fin");
            - switch.turn_off: ha_feed_button

##################################################################################################

##################################################################################################

##################################################################################################

time:

  - platform: homeassistant
    id: homeassistant_time
 

# Text sensors with general information.
text_sensor:
  # Expose ESPHome version as sensor.
  - platform: version
    name: Version
  # Expose WiFi information as sensors.
  - platform: wifi_info
    ip_address:
      name: IP
    bssid:
      name: BSSID

  # human readable update text sensor from sensor:uptime
  - platform: template
    name: Uptime Human Readable
    id: uptime_human
    icon: mdi:clock-start

  - platform: template
    name: "Hora Encendido"
    id: ha_hora_encendido



sensor:
  - platform: wifi_signal
    id: wifi_signal_db
    name: wifi signal
    update_interval: 60s

  - platform: copy # Reports the WiFi signal strength in %
    source_id: wifi_signal_db
    name: "WiFi Signal Percent"
    filters:
      - lambda: return min(max(2 * (x + 100.0), 0.0), 100.0);
    unit_of_measurement: "Signal %"
    entity_category: "diagnostic"

  # human readable uptime sensor output to the text sensor above
  - platform: uptime
    name: Uptime in Days
    id: uptime_sensor_days
    update_interval: 60s
    on_raw_value:
      then:
        - text_sensor.template.publish:
            id: uptime_human
            state: !lambda |-
              int seconds = round(id(uptime_sensor_days).raw_state);
              int days = seconds / (24 * 3600);
              seconds = seconds % (24 * 3600);
              int hours = seconds / 3600;
              seconds = seconds % 3600;
              int minutes = seconds /  60;
              seconds = seconds % 60;
              return (
                (days ? String(days) + "d " : "") +
                (hours ? String(hours) + "h " : "") +
                (minutes ? String(minutes) + "m " : "") +
                (String(seconds) + "s")
              ).c_str();
   

Some Pictures:


IMG_0882
IMG_0884
IMG_0885

5 Likes