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'