Hi.
For me, feed_rate was a calculation based on gms of feed to be dispensed over ātā time.
I couldnāt get the torque of the steeper sufficiently high so reverted to a dc motor.
FWIW, hereās the entire config:
esphome:
name: stepper-test
platform: ESP32
board: esp32dev
platformio_options:
board_build.f_cpu: 80000000L # Reduce CPU speed to match HX711 sample rate
# Notes
# 5000g maximum tared weight
# 500g minimum weight
# 2 gms per second feed rate
# on_boot:
# then:
# - ds1307.read_time:
# - if:
# condition:
# sun.is_above_horizon:
# then:
# - binary_sensor.template.publish:
# id: fish_feeder_sun_is_up
# state: ON
# else:
# - binary_sensor.template.publish:
# id: fish_feeder_sun_is_up
# state: OFF
# - lambda: 'id(feeds_left_today).publish_state(id(feeds_left_today_variable));' # Restore rmeaining feeds for HA front end
# - lambda: 'id(pre_dose_hopper_weight).publish_state(id(pre_dose_weight));' # Restore weight before feed
# - lambda: 'id(weight_of_last_feed).publish_state(id(last_feed_weight_variable));' # Restore last feed weight
# - lambda: 'id(post_dose_hopper_weight).publish_state(id(post_feed_hopper_weight_variable));' # Restore weight after feed
# # - text_sensor.template.publish: # Restore last feed time
# # id: last_feed
# # state: !lambda 'return id(last_feed_time_text);'
# - output.turn_off: enable_motor
# Enable Home Assistant API
api:
i2c:
sda: 21
scl: 22
spi: #for e-paper display
clk_pin: 04
mosi_pin: 32
sun:
latitude: !secret latitude
longitude: !secret longitude
on_sunrise:
- then:
- binary_sensor.template.publish:
id: fish_feeder_sun_is_up
state: ON
on_sunset:
- then:
- binary_sensor.template.publish:
id: fish_feeder_sun_is_up
state: OFF
logger:
status_led:
pin:
number: GPIO0
inverted: True
web_server:
port: 80
auth:
username: admin
password: !secret OTA_password
ota:
password: !secret OTA_password
wifi:
ssid: !secret
password: !secret
ap:
ssid: "stepper_test_hotspot"
password: !secret OTA_password
power_save_mode: none
captive_portal:
font:
- file: "fonts/Arial.ttf"
id: arial
size: 12
display:
- platform: ssd1306_i2c
model: SH1106 128x64
# model: "SSD1306 128x32"
id: feeder_display
# # reset_pin: D0
address: 0x3C
pages:
- id: feeder_status
lambda: |-
it.printf(0, 00, id(arial), "System Status: %s", id(system_status).state ? "Good" : "Error");
it.printf(0, 12, id(arial), "WiFi: %s", id(wifi_connected).state ? "Connected" : "Disconnected");
it.printf(0, 24, id(arial), "API: %s", id(home_assistant_api_connected).state ? "Connected" : "Disconnected");
it.printf(0, 48, id(arial), "Valve: %s", id(feeder_water_valve_relay).state ? "Open" : "Closed");
if (id(feed_motor_running).state) {
it.printf(0, 36, id(arial), "Motor: Running %i%%", id(feed_rate_percent));
} else {
it.printf(0, 36, id(arial), "Motor: Stopped");
}
- id: feeds
lambda: |-
it.printf(0, 00, id(arial), "Feeds for today: %i", id(calculated_feeds_per_day_variable));
it.printf(0, 12, id(arial), "Remaining feeds: %i", id(feeds_left_today_variable));
it.printf(0, 24, id(arial), "Last feed: %s", id(last_feed).state.c_str());
it.printf(0, 36, id(arial), "Amount: %.0fg", id(fish_food_hopper_weight).state);
it.printf(0, 48, id(arial), "Food in hopper: %.0fg", id(fish_food_hopper_weight).state);
- platform: waveshare_epaper
cs_pin: 14
dc_pin: 13
#busy_pin:
#reset_pin:
model: 4.20in
#full_update_every: 30
id: epaper_display
pages:
- id: feeder_status_epaper
lambda: |-
it.printf(0, 00, id(arial), "System Status: %s", id(system_status).state ? "Good" : "Error");
it.printf(0, 24, id(arial), "API: %s", id(home_assistant_api_connected).state ? "Connected" : "Disconnected");
it.printf(0, 48, id(arial), "Valve: %s", id(feeder_water_valve_relay).state ? "Open" : "Closed");
if (id(feed_motor_running).state) {
it.printf(0, 36, id(arial), "Motor: Running %i%%", id(feed_rate_percent));
} else {
it.printf(0, 36, id(arial), "Motor: Stopped");
}
- id: feeds_epaper
lambda: |-
it.printf(0, 00, id(arial), "Feeds for today: %i", id(calculated_feeds_per_day_variable));
it.printf(0, 12, id(arial), "Remaining feeds: %i", id(feeds_left_today_variable));
it.printf(0, 24, id(arial), "Last feed: %s", id(last_feed).state.c_str());
it.printf(0, 36, id(arial), "Amount: %.0fg", id(fish_food_hopper_weight).state);
it.printf(0, 48, id(arial), "Food in hopper: %.0fg", id(fish_food_hopper_weight).state);
globals:
- id: last_feed_time_text
type: std::string
restore_value: yes
- id: feed_rate # Rate to run feed motor. 0.2 equates to 1 rpm. From HA input_number.fish_feed_motor_feed_rate
type: float
restore_value: yes
initial_value: '0.2'
- id: feed_rate_percent # feed_rate converted to percentage dor display
type: int
restore_value: yes
initial_value: '20'
- id: feeds_left_today_variable # Used as counter for number of feeds made today. Set from calculated_feeds_per_day_variable
type: int # Decremented by dispense_food script completion
restore_value: yes
initial_value: '0'
- id: calculated_feeds_per_day_variable # From HA sensor.calculated_feeds_per_day
type: int
restore_value: yes
initial_value: '12'
- id: amount_per_dose # From HA sensor.feed_amount_per_dose
type: int
restore_value: yes
initial_value: '20'
- id: temperature_adjusted_amount_per_dose # From HA sensor.feed_amount_per_dose
type: float
restore_value: yes
initial_value: '20'
- id: post_feed_overrun_variable # From HA input_number.post_feed_overrun_HA
type: int
restore_value: yes
initial_value: '5'
- id: water_pre_dispense_time_variable # From HA: Time to open water valve before feeding commences
type: int
restore_value: yes
initial_value: '5'
- id: water_post_dispense_time_variable # From HA: Time to wait before closing water valve after feeding
type: int
restore_value: yes
initial_value: '5'
- id: pre_dose_weight # What was the weight before feeding?
type: int
restore_value: yes
initial_value: '0'
- id: post_dose_target_weight # What is the target weight after feeding finished?
type: int
restore_value: yes
initial_value: '0'
- id: last_feed_weight_variable # What was the weight of the last feed?
type: int
restore_value: yes
initial_value: '0'
- id: post_feed_hopper_weight_variable # Weight after last feed?
type: int
restore_value: yes
initial_value: '0'
- id: disable_reset_time
type: int
restore_value: yes
initial_value: '5'
time:
- platform: ds1307
update_interval: never
id: on_board_time
on_time:
- minutes: 00
hours: 04
then:
- globals.set: # At 04:00, set feeds today to calculated feeds today from HA
id: feeds_left_today_variable
value: id(calculated_feeds_per_day_variable)
- seconds: 00 # On the hour, every hour
minutes: 00
then:
- if:
condition:
and:
- binary_sensor.is_off: feeding_is_disabled # If Feeding is not disabled
- binary_sensor.is_on: fish_feeder_sun_is_up # And It is daytime
- lambda: 'return id(feeds_left_today_variable) > 0;' # And Still have feeds left for the day
then:
- script.execute: dispense_food # Feed the fish
- platform: homeassistant # Sync time with HA
id: homeassistant_time
on_time_sync:
then:
ds1307.write_time:
sensor:
- platform: hx711
name: "Fish food hopper"
id: fish_food_hopper_weight
dout_pin: 27
clk_pin: 26
filters:
- calibrate_linear:
- 56550 -> 0
- 260000 -> 5000
- sliding_window_moving_average:
window_size: 10
send_every: 3
update_interval: 1.0s
unit_of_measurement: gm
accuracy_decimals: 0
on_value:
then:
- if: # Hopper weight is within limits
condition:
sensor.in_range:
id: fish_food_hopper_weight
above: 250.0
below: 5000.0
then:
- binary_sensor.template.publish:
id: hopper_is_at_low_level
state: OFF
- binary_sensor.template.publish:
id: hopper_is_full
state: OFF
- binary_sensor.template.publish:
id: hopper_is_empty
state: OFF
- binary_sensor.template.publish:
id: hopper_is_ok
state: ON
- if: # Hopper is bordering on empty
condition:
sensor.in_range:
id: fish_food_hopper_weight
below: 100.0
then:
- binary_sensor.template.publish:
id: hopper_is_empty
state: ON
- binary_sensor.template.publish:
id: hopper_is_at_low_level
state: OFF
- binary_sensor.template.publish:
id: hopper_is_full
state: OFF
- binary_sensor.template.publish:
id: hopper_is_ok
state: OFF
- if: # Hopper is low level
condition:
sensor.in_range:
id: fish_food_hopper_weight
above: 100.0
below: 250.0
then:
- binary_sensor.template.publish:
id: hopper_is_empty
state: OFF
- binary_sensor.template.publish:
id: hopper_is_at_low_level
state: ON
- binary_sensor.template.publish:
id: hopper_is_full
state: OFF
- binary_sensor.template.publish:
id: hopper_is_ok
state: OFF
on_value_range: # Hopper is high level
- above: 5000.0
then:
- binary_sensor.template.publish:
id: hopper_is_empty
state: OFF
- binary_sensor.template.publish:
id: hopper_is_at_low_level
state: OFF
- binary_sensor.template.publish:
id: hopper_is_full
state: ON
- binary_sensor.template.publish:
id: hopper_is_ok
state: OFF
- platform: homeassistant
name: "Amount per dose"
entity_id: sensor.feed_amount_per_dose #defined in homeassistant
id: feed_amount_per_dose
on_value:
then:
- globals.set:
id: amount_per_dose
value: !lambda 'return x;'
- platform: homeassistant
name: "Calculated feeds per day"
entity_id: sensor.calculated_feeds_per_day #defined in homeassistant
id: feeds_per_day_from_ha
on_value:
then:
- globals.set:
id: calculated_feeds_per_day_variable
value: !lambda 'return x;'
- platform: homeassistant # Set motor feed rate and intial value for motor
name: "Motor feed rate"
entity_id: input_number.fish_feed_motor_feed_rate #defined in homeassistant
id: motor_feed_rate
on_value:
then:
- globals.set:
id: feed_rate
value: !lambda 'return x;'
- globals.set:
id: feed_rate_percent
value: !lambda 'return x * 100;'
- output.set_level:
id: fish_feed_motor_pwm
level: !lambda 'return id(feed_rate);'
- platform: homeassistant # Set water run time pre dispense
name: "Water pre dispense time"
entity_id: input_number.water_pre_dispense_time #defined in homeassistant
id: water_pre_dispense_time
on_value:
then:
- globals.set:
id: water_pre_dispense_time_variable
value: !lambda 'return x;'
- platform: homeassistant # Set water run time post dispense
name: "Water post dispense time"
entity_id: input_number.water_post_dispense_time_ha #defined in homeassistant
id: water_post_dispense_time
on_value:
then:
- globals.set:
id: water_post_dispense_time_variable
value: !lambda 'return x;'
- platform: homeassistant
name: "Disable reset timer" # time before disable is turned off
entity_id: input_number.reset_fish_feeder_disable_timer #defined in homeassistant
id: disable_reset_timer
on_value:
then:
- globals.set:
id: disable_reset_time
value: !lambda 'return x;'
- platform: homeassistant
name: "Pond water temperature for feeder"
id: feeder_pond_water_temperature
entity_id: sensor.pond_water_temperature
on_value:
then:
- if:
condition:
sensor.in_range:
id: feeder_pond_water_temperature
above: 7.0
below: 30.0
then:
- globals.set:
id: temperature_adjusted_amount_per_dose
value: !lambda 'return ((id(feeder_pond_water_temperature).state - 6.0289) / 0.0549);'
- lambda: 'id(temperature_adjusted_grams_per_day).publish_state(id(temperature_adjusted_amount_per_dose));'
- platform: template # Feeds left to HA
name: "Feeds left today"
id: feeds_left_today
- platform: template # Grams per day to HA adjusted for temperature
name: "Temperature adjusted grams per day"
id: temperature_adjusted_grams_per_day
- platform: template # Hopper weight after feeding to HA
name: "Hopper weight after feeding"
id: post_dose_hopper_weight
- platform: template # Hopper weight before feeding to HA
name: "Hopper weight before feeding"
id: pre_dose_hopper_weight
- platform: template # Weight of last feed to HA
name: "Weight of last feed"
id: weight_of_last_feed
- platform: wifi_signal # WiFi signal to HA
name: "Feeder WiFi Signal Signal"
update_interval: 60s
text_sensor: # Calculated sunrise/sunset times for feeding window
- platform: sun
name: Sun Next Sunrise
type: sunrise
- platform: sun
name: Sun Next Sunset
type: sunset
- platform: template
name: "Last feed"
id: last_feed
- platform: template
name: "On board time"
id: on_board_time_text
binary_sensor:
- platform: status # Availabilty for HA
name: "Fish feeder status"
id: system_status
- platform: gpio # Physical manual feed switch
pin:
number: 17
mode: INPUT_PULLUP
name: "Manual Fish Feed"
id: manual_fish_feed
filters:
- invert:
on_press:
then:
- script.execute: dispense_food
- binary_sensor.template.publish:
id: feeding_is_disabled
state: OFF
- platform: gpio # Physical advance display page button
pin:
number: 34
# mode: INPUT_PULLUP
name: "Next page"
id: next_page
on_press:
then:
- display.page.show_next: feeder_display
- display.page.show_next: epaper_display
- platform: gpio # Physical feed disable switch
pin:
number: 16
mode: INPUT_PULLUP
name: "Disable Fish Feed"
id: disable_fish_feed
filters:
- invert:
on_press:
then:
- if:
condition:
binary_sensor.is_on: feeding_is_disabled
then:
- binary_sensor.template.publish:
id: feeding_is_disabled
state: OFF
- homeassistant.service:
service: input_boolean.turn_off
data:
entity_id: input_boolean.disable_fish_feeder
else:
- homeassistant.service:
service: input_boolean.turn_on
data:
entity_id: input_boolean.disable_fish_feeder
- binary_sensor.template.publish:
id: feeding_is_disabled
state: ON
- platform: template # Sun is up to HA
name: "Fish feeder sun is up"
id: fish_feeder_sun_is_up
- platform: template # Hopper is full alert to HA from HX711 definition
name: "Hopper is full"
id: hopper_is_full
- platform: template # Hopper is low alert to HA from HX711 definition
name: "Hopper is at low level"
id: hopper_is_at_low_level
- platform: template # Hopper is empty alert to HA from HX711 definition
name: "Hopper is empty"
id: hopper_is_empty
- platform: template # Hopper is OK alert to HA from HX711 definition
name: "Hopper is OK"
id: hopper_is_ok
- platform: template # Hopper is blocked alert to HA from HX711 definition
name: "Discharge is blocked"
id: discharge_is_blocked
- platform: template # Hopper is over feeding alert to HA from HX711 definition
name: "Feeder is over feeding"
id: feeder_is_over_feeding
- platform: template # Hopper is over feeding alert to HA from HX711 definition
name: "Feed motor running"
id: feed_motor_running
- platform: template
name: "Home Assistant connected"
id: home_assistant_api_connected
- platform: template
name: "Feeding is disabled" # Feeding is disabled
id: feeding_is_disabled
on_press:
then:
- script.execute: stop_motor
- delay: !lambda 'return id(water_post_dispense_time_variable) * 1000;' # Continue to run water for post dispense flush period
- switch.turn_off: feeder_water_valve_relay
- homeassistant.service:
service: input_boolean.turn_on
data:
entity_id: input_boolean.disable_fish_feeder
- delay: !lambda 'return id(disable_reset_time) * 1000 * 60;' # Rest 'disabled' after x minutes
- binary_sensor.template.publish:
id: feeding_is_disabled
state: OFF
on_release:
then:
- homeassistant.service:
service: input_boolean.turn_off
data:
entity_id: input_boolean.disable_fish_feeder
- platform: homeassistant # Disable feeding from HA
name: "Disable fish feeding"
entity_id: input_boolean.disable_fish_feeder # Defined in homeassistant
id: disable_from_ha_fish_feeding
on_press:
then:
- if:
condition:
binary_sensor.is_on: feeding_is_disabled
then:
- binary_sensor.template.publish:
id: feeding_is_disabled
state: OFF
- homeassistant.service:
service: input_boolean.turn_off
data:
entity_id: input_boolean.disable_fish_feeder
else:
- homeassistant.service:
service: input_boolean.turn_on
data:
entity_id: input_boolean.disable_fish_feeder
- binary_sensor.template.publish:
id: feeding_is_disabled
state: ON
- platform: homeassistant # Manual feeding from HA
name: "Manual feed"
entity_id: input_boolean.manually_feed_fish #defined in homeassistant
id: manual_fish_feeding
on_press:
then:
- script.execute: dispense_food
- binary_sensor.template.publish:
id: feeding_is_disabled
state: OFF
# on_release:
# then:
# - script.execute: stop_motor
# - delay: !lambda 'return id(water_post_dispense_time_variable) * 1000;' # Continue to run water for post dispense flush period
# - switch.turn_off: feeder_water_valve_relay
- platform: homeassistant
entity_id: input_boolean.force_feeds_today_update
id: restore_feeds_today
on_press:
then:
- globals.set:
id: feeds_left_today_variable
value: id(calculated_feeds_per_day_variable)
- lambda: 'id(feeds_left_today).publish_state(id(calculated_feeds_per_day_variable));'
- platform: template
name: "WiFi Connected"
id: wifi_connected
switch: # Restart system
- platform: restart
name: "Restart fish feeder control"
id: restart_fish_feeder_control
- platform: gpio # Relay for pond water fill valve
pin: 25
name: "Feeder Water valve relay"
id: feeder_water_valve_relay
inverted: false
restore_mode: RESTORE_DEFAULT_OFF
output:
- platform: ledc
pin: 19
id: fish_feed_motor_pwm
- platform: gpio
pin: 18
id: enable_motor
script:
- id: run_motor
then:
# - output.set_level:
# id: fish_feed_motor_pwm
# level: !lambda 'return id(feed_rate);'
- output.turn_on: enable_motor
- binary_sensor.template.publish:
id: feed_motor_running
state: ON
- id: stop_motor
then:
- output.turn_off: enable_motor
- if: # When motor is stopped, check if hopper weight has reduced, if not, alert to HA
condition:
lambda: 'return id(fish_food_hopper_weight).state > id(post_dose_target_weight);'
then:
- binary_sensor.template.publish:
id: discharge_is_blocked
state: ON
- if: # When motor is stopped, check if hopper weight is less than post
condition: # feed calculated weight (+ overrun allowance), if not, alert to HA
lambda: 'return id(fish_food_hopper_weight).state < (id(post_dose_target_weight) + id(post_feed_overrun_variable));'
then:
- binary_sensor.template.publish:
id: feeder_is_over_feeding
state: ON
- binary_sensor.template.publish:
id: feed_motor_running
state: OFF
- binary_sensor.template.publish:
id: manual_fish_feeding
state: OFF
- text_sensor.template.publish: # Publish feed time to HA when motor stops # "%Y-%m-%d %H:%M"
id: last_feed
state: !lambda |-
char str[17];
time_t currTime = id(on_board_time).now().timestamp;
strftime(str, sizeof(str), "%d-%m %H:%M", localtime(&currTime));
return { str };
- id: dispense_food
then:
- binary_sensor.template.publish: # Reset blocked alert
id: discharge_is_blocked
state: OFF
- binary_sensor.template.publish: # Reset over feeding alert
id: feeder_is_over_feeding
state: OFF
- globals.set: # Store hopper weight before feed
id: pre_dose_weight
value: !lambda 'return id(fish_food_hopper_weight).state;'
- lambda: 'id(pre_dose_hopper_weight).publish_state(id(pre_dose_weight));' # Publish weight before feeding to HA
- globals.set: # Store calculated hopper weight after feed
id: post_dose_target_weight
value: !lambda 'return id(pre_dose_weight) - id(amount_per_dose);'
- switch.turn_on: feeder_water_valve_relay # Open water valve for time period before food is dispensed
- delay: !lambda 'return id(water_pre_dispense_time_variable) * 1000;'
- script.execute: run_motor # Run feed motor until hopper weight has dropped to calculated post feed weight
# - wait_until:
# condition:
# # or:
# - lambda: 'return id(fish_food_hopper_weight).state <= id(post_dose_target_weight);' # Target weight is dispensed
# # - lambda: 'return id(feed_motor).current_position >= id(feed_motor).target_position;' # Nothing is dispensed & limit of stepper reached
- delay: !lambda 'return id(water_pre_dispense_time_variable) * 1000;' #temporary for testing
- script.execute: stop_motor # Turn off motor
- lambda: 'id(post_dose_hopper_weight).publish_state(id(fish_food_hopper_weight).state);' # Publish weight after feeding to HA
- lambda: 'id(weight_of_last_feed).publish_state(id(pre_dose_weight) - id(fish_food_hopper_weight).state);' # Publish weight of last feed to HA
- globals.set: # Store last feed weight
id: last_feed_weight_variable
value: !lambda 'return id(pre_dose_weight) - id(fish_food_hopper_weight).state;'
- globals.set: # Store last feed weight
id: post_feed_hopper_weight_variable
value: !lambda 'return id(fish_food_hopper_weight).state;'
- lambda: 'id(feeds_left_today).publish_state(id(feeds_left_today_variable));' # Publish feeds left today
- delay: !lambda 'return id(water_post_dispense_time_variable) * 1000;' # Continue to run water for post dispense flush period
- switch.turn_off: feeder_water_valve_relay
- homeassistant.service:
service: input_boolean.turn_off
data:
entity_id: input_boolean.manually_feed_fish
- globals.set: # Decrement feeds today
id: feeds_left_today_variable
value: !lambda 'return id(feeds_left_today_variable) - 1;' # Decrement feeds left today variable
interval:
- interval: 10s
then:
- if:
condition:
wifi.connected:
then:
- binary_sensor.template.publish:
id: wifi_connected
state: ON
else:
- binary_sensor.template.publish:
id: wifi_connected
state: OFF
- if:
condition:
api.connected:
then:
- binary_sensor.template.publish:
id: home_assistant_api_connected
state: ON
else:
- binary_sensor.template.publish:
id: home_assistant_api_connected
state: OFF