Yet another Chicken Coop project under ESPHOME

Hi to All,

i want to share a simple ChickenCoop project. Based on an ESP32, a servo motor, a lead battery and a little solar panel i have a fully autonomous solution to open/close the chicken coop door, and monitor under Home Assistant the correct execution, the battery SOC, and obviously override the actions anytime by opening/closing the door remotely through Home Assistant. I am very satisfied and it took some time for debugging, that’s why i am happy to share!

If it’s of any interest, i can provide more details on the hardware installation but it was all very straightforward.

The principle is a simple DC servomotor moving up and down a sliding door. In my case, through a little pulley that i 3d-printed to allow the motor not to slide/consume any current when the door is open. Two reed switches sense when the door is open or closed, giving me 100% certain feedback on the door position. An I2C light sensor complements the system in order to run with or without WiFi connection…

esphome: 
  name: chickencoop
  friendly_name: ChickenCoopDoor
  on_boot:
    priority: 800
    then:
      - logger.log: "The Chicken coop door has been rebooted!"
      - text_sensor.template.publish:
          id: Door_State
          state: 'REBOOTED'      
      - if:
          condition:
      # check for door state 
            binary_sensor.is_on: Top_Switch
          then:
            - text_sensor.template.publish:
                id: Door_State
                state: 'OPEN'
          else:
            if:
              condition:
      # check for door state
                binary_sensor.is_on: Bottom_Switch
              then:
                - text_sensor.template.publish:
                    id: Door_State
                    state: 'CLOSED'




esp32:
  board: esp32dev
  framework:
    type: arduino

esp32_ble:
  enable_on_boot: false


# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

ota:
  password: "XXXXXXXXXXXXXXXXXXXXXXXXX"

wifi:
 # power_save_mode: none
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Chickencoop Fallback Hotspot"
    password: "XXXXXXXXXXXXXXXXXXXXXXXXXXX"

captive_portal:



substitutions:
  max_chicken_out_time: 30.0min
  max_move_time: 60s
  light_threshold: "7.0"
  battery_threshold: "12.0"
  safety_delay: 800ms


# manual actuators for home assistant - human opening and closing of chicken coop door
button:
  - platform: template
    name: 'Open Door'
    id: Open_Door
    # Optional variables:
    icon: "mdi:door-sliding-open"
    on_press:
      then:
        if: 
          condition: 
            binary_sensor.is_on: Bottom_Switch
          then:  
            - logger.log: "Open Door action starts because bottom switch is on"
            - number.set:
                id: servo_control
                value: -100
            - delay: ${max_move_time}
            - number.set:
               id: servo_control
               value: 0
          else:
          - logger.log: Open Door request, the bottom switch is off therefore the operation is aborted." 

  - platform: template
    name: 'Close Door'
    id: Close_Door
    # Optional variables:
    icon: "mdi:door-sliding-lock"
    on_press:
      then:      
        if:
          condition:
            binary_sensor.is_on: Top_Switch
          then:
          - logger.log: "Now we finally close the door..."
          - number.set:
             id: servo_control
             value: 100
          - delay: ${max_move_time}
          - number.set:
              id: servo_control
              value: 0
          else:
          - logger.log: "Close Door request, the top switch is off therefore the operation is aborted." 

  - platform: template
    name: 'Reset & Open Door'
    id: Reset_Door
    # Optional variables:
    icon: "mdi:door-sliding-lock"
    on_press:      
      - logger.log: "We Open the door irrespectively of the top or bottom switch..."
      - number.set:
         id: servo_control
         value: -100
      - delay: ${max_move_time}
      - number.set:
          id: servo_control
          value: 0
 
  
  - platform: template
    name: 'Stop Door'
    id: Stop_Door

    # Optional variables:
    icon: "mdi:door-sliding-lock"
    on_press:
      - logger.log: "Stop Door request"
      - number.set:
            id: servo_control
            value: 0

#switch:
 # - platform: gpio
  #  name: "Open Door"
  #  pin: 5
  #- platform: gpio
  #  name: "Close Door"
  #  pin: 4

# these are the open/close endswitches to detect the door physical position.
# the servo will run for xx seconds, up to detecting the hall sensors.
binary_sensor:
  - platform: gpio
    name: "Top switch"
    id: Top_Switch
    icon: "mdi:garage-open"
    pin:
# pin number is validated
      number: 32
      mode:
        input: true
        pullup: true
      inverted: true
    filters:
      - delayed_on_off: 800ms
    on_press:
      then:
        - delay: ${safety_delay}
        - button.press: Stop_Door
        - text_sensor.template.publish:
            id: Door_State
            state: 'OPEN'
    on_release:
        - text_sensor.template.publish:
            id: Door_State
            state: 'LEFT_OPEN_STATE'

  - platform: gpio
    name: "Bottom switch"
    id: Bottom_Switch
    icon: "mdi:garage-lock"
    pin:
# pin number is validated  
      number: 25
      inverted: true
      mode:
        input: true
        pullup: true
    filters:
      - delayed_on_off: 800ms
    on_press:
      then:
        - delay: ${safety_delay}
        - button.press: Stop_Door
        - text_sensor.template.publish:
            id: Door_State
            state: 'CLOSED'
    on_release:
      - text_sensor.template.publish:
          id: Door_State
          state: 'LEFT_CLOSED_STATE'

  - platform: analog_threshold
    name: "Low Light Trigger"
    id: low_light_trigger
    sensor_id: chickencoop_illuminance
    threshold: ${light_threshold}
    filters:
      - invert:
      - delayed_on: 0s
      - delayed_off: 0s
    on_press:
      then:
        - logger.log: "Close Door request because it's low light, exactly as it should be! but to allow chicken in, wait ${max_chicken_out_time}!"
        - delay: ${max_chicken_out_time}
        - logger.log: ${max_chicken_out_time} delay executed, now starting the closing procedure
        - button.press: Close_Door
        - logger.log: "closing procedure ended"

    on_release:
      then:
        - logger.log: "Open Door because it's light, exactly as it should be! No delay is required"
        - button.press: Open_Door
        - logger.log: "Opening procedure ended"

  - platform: analog_threshold
    name: "Low Battery Action"
    id: low_battery_action
    sensor_id: battery_voltage
    threshold: ${battery_threshold}
    filters:
      - invert:
      - delayed_on: 0s
      - delayed_off: 0s
    on_press:
      then:
        - logger.log: "Battery voltage is getting dangerously low, so let's keep the chicken FREE... "
        - button.press: Open_Door
        - logger.log: "Chickens are free, let's keep going and hope that the sun comes back..."
    on_release:
      then:
        - logger.log: "Battery is getting charged, no other action but better to let you know..."
        - logger.log: "Battery monitoring ended"




# This is required to initialize the i2c bus for the BH1750 light sensor
i2c:
# pin number is validated
  sda: 21
  scl: 22
  scan: true
  id: bus_a

# This is the sensor to detect the light level, governing the local automatic open/close without any home assistant intervention.
sensor:
  - platform: bh1750
    name: "ChickenCoop Illuminance"
    id: chickencoop_illuminance
    address: 0x23
    update_interval: 30s
    filters:
    - sliding_window_moving_average:
       window_size: 10
       send_every: 10
    - lambda: return x + 0.0001;


# this is an accessory sensor to detect the SoC of the battery. Below XX volts, the door will open and notifications will be sent to home assistant
  - platform: adc
# pin number is validated
    pin: 36
    name: "Battery Voltage"
    icon: "mdi:battery-charging-medium"
    id: battery_voltage
    update_interval: 65s
    filters:
    - sliding_window_moving_average:
       window_size: 10
       send_every: 10
    - lambda: return x * 15.2623;
    
    # the factor is 4730.0/330.0 * 1.0720 * 13.34/ 13.43, from theoretical resistors value plus measurements with tester;

# Some temperature measurement...
  - platform: internal_temperature
    name: "Internal Temperature"

# Door state
text_sensor:
  - platform: template
    name: Door State
    id: Door_State
# Optional variables:
    icon: "mdi:state-machine"
    on_value:
      then:
        - lambda: |-
            ESP_LOGD("main", "The chicken door state changed to %s", x.c_str());

# this is the servo opening and closing the sliding door through a simple wire on pulley.
servo:
    # [...] servo config
    id: my_servo
    output: pwm_output

output:
  - platform: ledc
    id: pwm_output
# pin number is validated
    pin: 27
    frequency: 50 Hz

number:
  - platform: template
    icon: "mdi:engine"
    name: Servo Control
    id: servo_control
    min_value: -100
    initial_value: 0
    max_value: 100
    step: 1
    optimistic: true
    set_action:
      then:
       - servo.write:
           id: my_servo
           level: !lambda 'return x / 100.0;'
  

time:
  - platform: sntp
    on_time:
      # Safety to release chickens ANYWAY
      - seconds: 0
        minutes: 0
        hours: 9
        then:
          - button.press: Open_Door
          - logger.log: "Open Door because it's time: something failed in the Light level sensing logic!"
      # Safety to close indoor chickens ANYWAY
      - seconds: 0
        minutes: 0
        hours: 23
        then:
          - button.press: Close_Door
          - logger.log: "Close Door because it's time: something failed in the Light level sensing logic!"
      # Every 5 minutes
      - seconds: 0
        minutes: /10
        then:     
          - if:
              condition:
      # check for door state 
                binary_sensor.is_on: Top_Switch
              then:
                - text_sensor.template.publish:
                    id: Door_State
                    state: 'OPEN'
              else:
                if:
                  condition:
      # check for door state
                    binary_sensor.is_on: Bottom_Switch
                  then:
                    - text_sensor.template.publish:
                        id: Door_State
                        state: 'CLOSED'
                  else:
                    - text_sensor.template.publish:
                        id: Door_State
                        state: 'UNKNOWN'


1 Like

Would love to see build photos, BOM, video in action!

Thanks for sharing!

If it works, cool. Its a little baffling why you chose to overcomplicate the door like this and not just use a cover. The whole on_boot section is completely unnecessary, you’ve got a whole automation in on_boot when simply adding (publish_initial_state: true) to your binary sensors would have done the same thing.

Personally, I wouldnt use a light sensor to shut the door because it isnt factoring in unforseen low light levels like the sky going black from an incoming storm or something falling on it, a leaf or whatever and then it appears to be night time. You do have some lighting fail-safes based on time that open or close the door without even doing condition checks to see if its open and needs closed at the set time.

Using the sun platform and using above/below horizon with offsets would be more accurate and wont get tricked into thinking its dark like the light sensor. Use the light sensor as a redundancy in the event wifi or api is disconnected and the sun plarform cant update.

All the “notes to self” in the million log events are a little creepy but, to each his own.

I wasnt trying to put your project down incase it wasnt clear. Im just suggesting things to clean up the config and cut the fat off

Dunno about your definition of creepy, but the word conjures up “sex offender” in my vocab, perhaps it is the criminal lawyer in me.

The log comment are great for testing and also are quite good code commentary.