Motor speed controller - what am I missing?

I have an ESP32 using GPIO19 into an L298N PWM controller.

Config is as below:

output:
  - platform: ledc
    pin: 19
    id: fish_feed_motor

script:
  - id: run_motor
    then:
      - output.set_level:
          id: fish_feed_motor
          level: 10%
      - output.ledc.set_frequency:
          id: fish_feed_motor
          frequency: 500Hz
      - output.turn_on: fish_feed_motor
      - binary_sensor.template.publish:
          id: feed_motor_running
          state: ON

When I turn the ouput on, the motor runs.

However, changing level and frequency values, in the config above, makes no difference to motor speed.

Am I missing something obvious here?

Looking at the docs on that motor controller, it seems that you need at least two GPIO signals to vary speed, but your code seems to use only one.

Thereā€™s only three pins:

Forward, reverse and speed control.

Which docs have you seen?

This is it being used with Arduino.

I saw this one:

And I see that it would appear that with your device only one signal would be needed for variable-speed one-direction. So Iā€™m stumped. Sorry.

So, I assume that GPIO19 is connected to Enable A and you havenā€™t connected anything to Input 1 or Input 2? My understanding was that with the L298N module if you neither set Input 1 or Input 2 to HIGH then the motor should not go anywhere. So that might be something to further investigate.

I have just recently built a pool chlorinator pump with a L298N connected to an ESP32, and can share my configuration for comparison:

output:
  - platform: ledc  # or esp8266_pwm if on ESP8266
    pin: GPIO25
    id: motor_speed_pin
  - platform: gpio
    pin: GPIO26
    id: motor_direction_1_pin
  - platform: gpio
    pin: GPIO27
    id: motor_direction_2_pin

switch:
  - platform: template
    name: "Motor"
    id: motor
    icon: "mdi:chemical-weapon"
    turn_on_action:
      # Clockwise direction:
      - output.turn_off: motor_direction_1_pin
      - output.turn_on: motor_direction_2_pin
      - delay: 500ms
      - output.set_level:
          id: motor_speed_pin
          level: 90%
      - switch.template.publish:
          id: motor
          state: ON
    turn_off_action:
      - output.turn_off: motor_direction_1_pin
      - output.turn_off: motor_direction_2_pin
      - output.turn_off: motor_speed_pin
      - switch.template.publish:
          id: motor
          state: OFF

In my case, I noticed that I cannot go below about 70% with my particular motor under load, otherwise it wouldnā€™t start at all. I havenā€™t actually changed the frequency at all.

1 Like

Thanks for the reply.

IN1 is high and the motor runs. However, I have no control over the speed. It always runs 100%, regardless of what I set frequency or level at.

Is the frequency something you would adjust on a motor? I thought it was a fixed value base on the motor specs. Have you tried use an adjustable pwm output to change the values on the fly? Speed fan should allow you to ramp the motor up/down via a slider.

1 Like

Frequency should be set to whatever the L298N recommends. Its docs will probably say something to the effect of ā€œfrequency doesnā€™t matter, ideally set it between here and here.ā€
Thatā€™s because varying the frequency is not what controls the motor speed.

What matters is the PWM ratio, a.k.a. the duty-cycle of the PWM; how much time the pulse is on versus off.
Your motor may in fact be slowing down when you vary the PWM duty cycle, but if there isnā€™t any load on it you may not notice the change.

Thank you, @glyndon, Iā€™ll take a look at that in the morning.

Thereā€™s an L293 arriving tomorrow. Iā€™ll see how I get on with that.

No, not necessarily. Iā€™m just trying to get a response of some kind at the moment.

Iā€™ve just hooked up a scope to GPIO19 and the output just goes to 3.3V when I ask the motor to run. No sign of PWM!

Iā€™ve now changed over to using the fan: component and have full control of the speed all the way down to 20% of max.

Thank you, @Mikefila, for pointing me in that direction.

The issue I have now is the automating of speed control.

I have the motor (ā€œfanā€) configured as below:

output:
  - platform: ledc
    pin: 19
    id: fish_feed_motor

fan:
  - platform: speed
    output: fish_feed_motor
    name: "Fish feed motor"
    speed_count: 10
    id: feed_motor

My run script is:

script:
  - id: run_motor
    then:
      - fan.turn_on: 
          id: feed_motor
          speed: 10

I need to be able to control the speed from the front end using an input_number and have tried this:

      - fan.turn_on: 
          id: feed_motor
          speed: !lambda 'return id(feed_rate);'

but it does not change the speed. Speed is always 100%.

Hard coding the speed makes no difference either, however, using the front-end fan component slider does.

Additionally, on reboot of the ESP32, the fan speed returns to 100%.

Any ideas?

Solved!

Whist the Speed Fan option was working to a degree, it was a bit of a fudge.

Going back to my original post, using output.turn_on was the issue. This sets the output to ā€˜onā€™, ignoring any PWM setting and the motor runs at 100%.

Just using output.set_level runs the output at the required PWM rate and output.turn_off stops it.

1 Like

I run a bunch of PC fans with the speed fan component and itā€™s the same deal, turn_on will bring the fan to 100%. You have to call fan.set_percentage, then you can supply a value from 0-100.

Yes, I realise that now. I need the motor to always start at the same speed, hence Fan being a bit of a fudge for me.

Thank you for your help. I appreciate it.

1 Like

At the request of @mofonics, hereā€™s the config that works for me.

Iā€™m using an L293D to drive one motor.

Define the pins:

output:
  - platform: ledc
    pin: 19
    id: fish_feed_motor_pwm
  
  - platform: 
    pin: 18
    id: enable_motor

Start the motor: (This is part of a longer script. Iā€™ve just included the relevant parts. The !lambda can be replaced by a float).

script:
  - id: run_motor
    then:
      - output.set_level:
          id: fish_feed_motor_pwm
          level: !lambda 'return id(feed_rate);'

Stop the motor: (I use another script here).

script:
  - id: stop_motor
    then:
      - output.turn_off: fish_feed_motor_pwm

Pretty simple now that I know what Iā€™m doing!

1 Like

Thank you, @ashscott!

hi there,
I am currently doing coding in esphome for my automatic plant watering system using esp32. I am wondering how do you setup the code for your motor feed_rate? Can you please share the full codes? Thanks.

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
        

1 Like

Thanks Ash.
I am using IRLZ44N to control the PWM of a 3-6V mini peristaltic water pump. (which is only worked fine only at 3Hz). Using the logic 5v supply. I was able to change the speed of the motor via HA interface. But there is a problemā€¦when the automation of output.turn_on: pot_pump_pwm was triggered, it does not reflect in the pot_pump_pwm in HA interface. How do I mitigate this problem.
Can you also share your HA config yaml here.
Thanks so much.

output:                           #pump motor PWM pin output to IRLZ44N Gate pin
  - platform: ledc
    pin: 32
    frequency: "3Hz"
    id: pot_pump_pwm

fan:
  - platform: speed
    output: pot_pump_pwm
    name: "Pot Pump PWM"