Craftiarenko
Published © MIT

Feeder for chinchillas or other small pets

I’ve taken on a fun and practical challenge—creating an automatic feeder for chinchillas.

IntermediateFull instructions provided5 hours136
Feeder for chinchillas or other small pets

Things used in this project

Hardware components

KW4-3Z-3 Micro Limit Switch SPDT NO NC 3
×2
ESP32-C3 OLED 0.42
×1
N20 Micro DC Gear Motor 6V 50 RPM
×1
Button 6x6x11
×2
HW-517 Mosfet board
×1
MT3608
×1
Battery case 18650x1
×1
TP4056
×1
Battery 18650
×1
Resistor 10k ohm
Resistor 10k ohm
×2
Prototype PCB
×1

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Soldering iron (generic)
Soldering iron (generic)
Hot glue gun (generic)
Hot glue gun (generic)

Story

Read more

Custom parts and enclosures

STL files

3D models

Schematics

Schematic

Code

pet-feeder-v3.yaml

YAML
ESPHome Configuration
esphome:
  name: pet-feeder-v3
  friendly_name: pet-feeder-v3

esp32:
  board: esp32-c3-devkitm-1
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "your-encryption-key"

ota:
  - platform: esphome
    password: "your-ota-password"

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Pet-Feeder-V3 Fallback Hotspot"
    password: "your-ap-password"

captive_portal:
    
i2c:
  sda: 5
  scl: 6
  frequency: 300kHz
  scan: false

font:
  - file: "gfonts://Roboto"
    id: dfont
    size: 12

display:
  - platform: ssd1306_i2c
    id: oled_42
    model: "SSD1306 72x40"
    address: 0x3C
    lambda: |-
       it.printf(0, 0, id(dfont), "%.2fV %.0f%%", id(v_bat).state, id(bat_percent).state);
       if(id(time_synced).state) {
        it.strftime(0, 14, id(dfont), "Now: %H:%M", id(sntp_time).now());
        it.strftime(0, 28, id(dfont), "Next: %H:%M", id(feeding_date).state_as_esptime());
       } else {
        it.printf(0, 14, id(dfont), "Time");
        it.printf(0, 28, id(dfont), "sync...", id(feeding_date).state_as_esptime());
       }

time:
  - platform: sntp
    id: sntp_time
    timezone: Europe/Kyiv
    servers:
     - 0.pool.ntp.org
     - 1.pool.ntp.org
     - 2.pool.ntp.org
    on_time_sync: 
      then:
        - component.update: feeding_date
        - switch.turn_on: time_synced

deep_sleep:
  id: deep_sleep_obj
  wakeup_pin_mode: KEEP_AWAKE
  wakeup_pin:
    number: 3
    allow_other_uses: true 

globals:
  - id: next_feeding_timestamp
    type: long int
    restore_value: yes
    initial_value: '1681182000'

datetime:
  - platform: template
    id: feeding_date
    type: datetime
    name: "Next feeding date"
    lambda: |-
      char str[17];
      strftime(str, sizeof(str), "%Y-%m-%d %H:%M", localtime(&id(next_feeding_timestamp)));
      ESPTime esp_time;
      ESPTime::strptime(str, esp_time);
      return esp_time;
    set_action:
      lambda: |-
        x.recalc_timestamp_local();
        // ESP_LOGD("TIME", "%d",x.timestamp);
        id(next_feeding_timestamp) = x.timestamp;
        id(feeding_date).update();

  - platform: template
    id: time_diff
    type: time
    name: "Time between feedings"
    optimistic: yes
    initial_value: "12:00"
    restore_value: true

switch:
  - platform: template
    id: sleep_switch
    optimistic: true
    restore_mode: ALWAYS_OFF
    name: "Sleep"
    turn_on_action:
      if:
        condition:
          - binary_sensor.is_off: wake_binary_sensor
        then:
          - lambda: !lambda |-
              long int next = id(next_feeding_timestamp) - id(sntp_time).now().timestamp;
              if(next < 0){
                  auto diff = id(time_diff).state_as_esptime();
                  long int seconds = diff.hour * 3600 + diff.minute * 60 + diff.second;
                  id(next_feeding_timestamp) = id(next_feeding_timestamp) + seconds;
                  id(feeding_date).update();
                  next = id(next_feeding_timestamp) - id(sntp_time).now().timestamp; 
              }

              id(oled_42).turn_off();
              id(deep_sleep_obj).set_sleep_duration(next * 1000);
              id(deep_sleep_obj).begin_sleep(false);

  - platform: template
    id: step_switch
    optimistic: true
    restore_mode: RESTORE_DEFAULT_OFF 
    name: "Half step"

  - platform: template
    id: double_switch
    optimistic: true
    restore_mode: ALWAYS_OFF 

  - platform: template
    id: time_synced
    optimistic: true
    restore_mode: ALWAYS_OFF
    on_turn_on: 
      then:
        if:
          condition:
            - binary_sensor.is_off: wake_binary_sensor
          then:
            if:
              condition:
                - lambda: "return id(sntp_time).now().timestamp >= id(next_feeding_timestamp);"
              then:
                - button.press: feed_button
              else:
                - switch.turn_on: sleep_switch

  - platform: gpio
    id: motor_switch
    name: "Motor switch"
    pin:
      number: 7
    on_turn_on :
      - delay: 3s
      - if:
          condition:
            - switch.is_on: motor_switch
          then:
            - switch.turn_off: motor_switch
            # TODO: show error

  - platform: gpio
    id: board_led
    name: "Board led"
    pin:
      inverted: true
      number: 8

button:
  - platform: template
    id: feed_button
    on_press:
      then:
        - if:
            condition:
              - switch.is_on: time_synced
            then:
              - lambda: |-
                  auto diff = id(time_diff).state_as_esptime();
                  int seconds = diff.hour * 3600 + diff.minute * 60 + diff.second;
                  id(next_feeding_timestamp) = id(next_feeding_timestamp) + seconds;
                  id(feeding_date).update();
              - switch.turn_on: motor_switch

binary_sensor:
  - platform: gpio
    pin:
      number: 3
      mode:
        input: true
        pulldown: True
      allow_other_uses: true 
    id: wake_binary_sensor
    name: "Wake button"
    filters:
      - delayed_on: 10ms

  - platform: gpio
    pin:
      number: 4
      mode:
        input: true
        pulldown: True
    id: feed_binary_sensor
    name: "Feed button"
    filters:
      - delayed_on: 10ms
    on_click:
      then:
        - button.press: feed_button

  - platform: gpio
    pin:
      mode:
        input: true
        pulldown: True
      number: 10
    id: endstop
    name: "Endstop"
    on_press:
      - if:
          condition:
            switch.is_on: step_switch
          then:
            - switch.turn_off: motor_switch
            - switch.turn_on: sleep_switch
          else:
            if:
              condition:
                - switch.is_off: double_switch
              then:
                - switch.turn_on: double_switch
              else:
                - switch.turn_off: motor_switch
                - switch.turn_off: double_switch
                - switch.turn_on: sleep_switch

sensor:
  - platform: adc
    pin: 0
    name: "VBat"
    id: v_bat
    update_interval: 5s
    attenuation: 11db
    filters:
      - multiply: 2

  - platform: template
    name: "Battery %"
    id: bat_percent
    lambda: return id(v_bat).state;
    update_interval: 5s
    accuracy_decimals: 0
    unit_of_measurement: "%"
    icon: mdi:battery-medium
    filters:
      - calibrate_linear:
         method: exact
         datapoints:
          - 0.00 -> 0.0
          - 3.30 -> 1.0
          - 3.39 -> 10.0
          - 3.75 -> 50.0
          - 4.11 -> 90.0
          - 4.20 -> 100.0
      - lambda: |-
          if (x <= 100) {
            return x;
          } else {
            return 100;
          }
          if (x < 0) {
            return 0;
          }


  - platform: template
    name: "Wakeup Cause"
    id: wakeup_cause
    accuracy_decimals: 0
    lambda: return esp_sleep_get_wakeup_cause();

    # 0 - ESP_SLEEP_WAKEUP_UNDEFINED: In case of deep sleep, reset was not caused by exit from deep sleep
    # 1 - ESP_SLEEP_WAKEUP_ALL: Not a wakeup cause, used to disable all wakeup sources with esp_sleep_disable_wakeup_source
    # 2 - ESP_SLEEP_WAKEUP_EXT0: Wakeup caused by external signal using RTC_IO
    # 3 - ESP_SLEEP_WAKEUP_EXT1: Wakeup caused by external signal using RTC_CNTL
    # 4 - ESP_SLEEP_WAKEUP_TIMER: Wakeup caused by timer
    # 5 - ESP_SLEEP_WAKEUP_TOUCHPAD: Wakeup caused by touchpad
    # 6 - ESP_SLEEP_WAKEUP_ULP: Wakeup caused by ULP program
    # 7 - ESP_SLEEP_WAKEUP_GPIO: Wakeup caused by GPIO (light sleep only on ESP32, S2 and S3)
    # 8 - ESP_SLEEP_WAKEUP_UART: Wakeup caused by UART (light sleep only)
    # 9 - ESP_SLEEP_WAKEUP_WIFI: Wakeup caused by WIFI (light sleep only)
    # 10 - ESP_SLEEP_WAKEUP_COCPU: Wakeup caused by COCPU int
    # 11 - ESP_SLEEP_WAKEUP_COCPU_TRAP_TRIG: Wakeup caused by COCPU crash
    # 12 - ESP_SLEEP_WAKEUP_BT: Wakeup caused by BT (light sleep only)

Credits

Craftiarenko
6 projects • 13 followers
Making simple things difficult :)
Contact

Comments

Please log in or sign up to comment.