Motor speed controller - what am I missing?

Does “Pot Pump PWM” show up in the HA front end?

Yes. It does showed up in the HA front end. I can use this to adjust the pwm of the IRLZ44N mosfet using platform: ledc. I am not sure why the sensor.waterflowrate is not updated in the HA front end.(each time after automation triggered in esphome).

I have also created a helper input_number.waterflowrate in HA/automations&scenes/helper.
In HA, configuration.yaml

  - platform: template
    sensors:
      waterflowrate:
        value_template: '{{ states.input_number.waterflowrate.state | int }}'
        unit_of_measurement: "%"

In esphome,

  - platform: gpio                              # Physical manual watering switch
    pin:
      number: 23
      mode: INPUT_PULLUP
      inverted: True
    filters:
      - delayed_on: 10ms
    name: Manual watering
    id: manual_plant_watering
    device_class: running
    on_press:
      then: 
        - output.turn_on: pot_pump_pwm
        - output.ledc.set_frequency:
            id: pot_pump_pwm
            frequency: "3Hz"        # My motor is only worked correctly at 3Hz when put in a scale of a 100% input_number. 
        - output.set_level:
            id: pot_pump_pwm
            level: !lambda 'return (id(waterflowrate).state/100);' 
      on_release:
        then:
        - output.turn_off: pot_pump_pwm

How do you have waterflowrate defined in ESPHome?

Maybe I’ve missed it but I can’t see it above.

Sorry for missing out this part. Am I done it correctly?

#HA sensor taking data value from input_number, 
   - platform: homeassistant
     name: "Water flow rate %"
     entity_id: sensor.waterflowrate
     id: waterflowrate
     unit_of_measurement: "%"

When you look in HA front end, are you seeing “Water flow rate %” when looking at the integration?

Hi Ash,
In HA front end,inside the integration I only see fan.waterflowrate, I can adjust the switch and PWM in that “Controls” . I guess I have missed to define the Water flow rate % in Esphome. Can you please guide me in this part. :sweat_smile: I am just started as a beginner in esphome.
Thanks in advance.

Please can you post your ESPHome config in its entirety, in one block?

Hi Ash,
My esphome config is a mess now. Yesterday just got my esp32-devkit-v1 burn (it was real hot on touching, suspected from shorted or overloaded onboard regulator after adding a physical button). Below is the full config, many which were copied from your code to test it. Please kindly give me some guide to improve it.
I have not yet doing the code in HA config.yaml so this is only the esphome config.
TIA.

esphome:
  name: motor

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:

i2c:
  sda: GPIO21
  scl: GPIO22
  
sun:
  latitude: !secret latitude
  longitude: !secret longitude
  on_sunrise:
    - then:
      - binary_sensor.template.publish:
          id: pot_sun_is_up
          state: ON
  on_sunset:
    - then:
      - binary_sensor.template.publish:
          id: pot_sun_is_up
          state: OFF
          
ota:
  password: "eece41701fcccaad99cba87a8fbde705"

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

font:
  - file: "fonts/Arial.ttf"
    id: arial
    size: 12 
    
captive_portal:

display:
  - platform: ssd1306_i2c
    model: "SSD1306 128x64"
  #   model: "SSD1306 128x32"
    id: pot_display
    rotation: 180
  #    reset_pin: D0
    address: 0x3C
    pages:
      - id: pot_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), "Motion: %s", id(motion_sensor).state ? "Detected" : "Not detected");
            if (id(water_motor_running).state) {
              it.printf(0, 36, id(arial), "Motor: Running  %i%%", id(flow_rate_percent));
            } else {
              it.printf(0, 36, id(arial), "Motor: Stopped");
            }
      - id: watering
        lambda: |-
            it.printf(0, 00, id(arial), "Feeds for today: %i", id(calculated_water_per_day_variable));
            it.printf(0, 12, id(arial), "Remain water: %i", id(water_left_today_variable));
            it.printf(0, 24, id(arial), "Last water: %s", id(last_watering).state.c_str());
            it.printf(0, 36, id(arial), "Amount: %.0fcm", id(tank_water_height).state);
            it.printf(0, 48, id(arial), "Water in tank: %.0fcm", id(tank_water_height).state);

globals:

  - id: last_feed_time_text
    type: std::string
    restore_value: no
  
  - id: flow_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: no
    initial_value: '0.2'
  
  - id: flow_rate_percent   # feed_rate converted to percentage dor display
    type: int
    restore_value: no
    initial_value: '20'
    
  - id: water_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: no
    initial_value: '0'

  - id: calculated_water_per_day_variable # From HA sensor.calculated_feeds_per_day
    type: int
    restore_value: no
    initial_value: '12'
    
  - id: amount_per_dose        # From HA sensor.feed_amount_per_dose
    type: int
    restore_value: no
    initial_value: '20'
    
  - id: post_feed_overrun_variable    # From HA input_number.post_feed_overrun_HA
    type: int
    restore_value: no
    initial_value: '5'
  
  - id: pre_dose_height                # What was the weight before feeding?
    type: int
    restore_value: no
    initial_value: '0'
    
  - id: post_dose_target_weight        # What is the target weight after feeding finished?
    type: int
    restore_value: no
    initial_value: '0'

  - id: last_water_height_variable               # What was the weight of the last feed?
    type: int
    restore_value: no
    initial_value: '0'

  - id: post_watering_tank_height_variable               # Weight after last feed?
    type: int
    restore_value: no
    initial_value: '0'
    
  - id: disable_reset_time
    type: int
    restore_value: no
    initial_value: '5'
    
time:
  - platform: homeassistant
    id: homeassistant_time
    
sensor:
#DHT11
  - platform: dht
    pin: 5
    model: DHT22
    temperature:
      name: "Pot Temp"
    humidity:
      name: "Pot Humidity"
    update_interval: 2s
    
# HC-SR04
  - platform: ultrasonic
    trigger_pin: 18
    echo_pin: 19
    name: "Pot water tank height"
    id: tank_water_height
    update_interval: 3s
    filters:
    # Replace the 0.42 by the height of your container. From the sensor to the bottom.
    # I multiplied by 100 in order to get CM since the sensor works in meters
    - lambda: return (0.12-x)*100;
    unit_of_measurement: "cm"
    on_value: 
      then:
        - if:                                   # Hopper weight is within limits
           condition:
              sensor.in_range:
                id: tank_water_height
                above: 2.0
                below: 10.0
           then:
              - binary_sensor.template.publish:
                  id: tank_is_at_low_level
                  state: OFF
              - binary_sensor.template.publish:
                  id: tank_is_full
                  state: OFF
              - binary_sensor.template.publish:
                  id: tank_is_empty
                  state: OFF
              - binary_sensor.template.publish:
                  id: tank_is_ok
                  state: ON
        -  if:                                   # Hopper is bordering on empty
            condition:
              sensor.in_range:
                id: tank_water_height
                below: 2.0
            then:
              - binary_sensor.template.publish:
                  id: tank_is_empty
                  state: ON
              - binary_sensor.template.publish:
                  id: tank_is_at_low_level
                  state: OFF
              - binary_sensor.template.publish:
                  id: tank_is_full
                  state: OFF
              - binary_sensor.template.publish:
                  id: tank_is_ok
                  state: OFF
        -  if:                                   # Hopper is low level
            condition:
              sensor.in_range:
                id: tank_water_height
                above: 2.0
                below: 5.0
            then:
              - binary_sensor.template.publish:
                  id: tank_is_empty
                  state: OFF
              - binary_sensor.template.publish:
                  id: tank_is_at_low_level
                  state: ON
              - binary_sensor.template.publish:
                  id: tank_is_full
                  state: OFF
              - binary_sensor.template.publish:
                  id: tank_is_ok
                  state: OFF
    on_value_range:                              # Hopper is high level
    - above: 10.0
      then:
        - binary_sensor.template.publish:
            id: tank_is_empty
            state: OFF
        - binary_sensor.template.publish:
            id: tank_is_at_low_level
            state: OFF
        - binary_sensor.template.publish:
            id: tank_is_full
            state: ON
        - binary_sensor.template.publish:
            id: tank_is_ok
            state: OFF
            
  - platform: homeassistant                      
    name: "Amount per dose"
    entity_id: sensor.water_amount_per_dose #defined in homeassistant
    id: water_amount_per_dose
    on_value:
      then:
        - globals.set:
            id: amount_per_dose
            value: !lambda 'return x;'

  - platform: homeassistant                      
    name: "Calculated water per day"
    entity_id: sensor.calculated_water_per_day #defined in homeassistant
    id: feeds_per_day_from_ha
    on_value:
      then:
        - globals.set:
            id: calculated_water_per_day_variable
            value: !lambda 'return x;'
            
#HA sensor taking data value from input_number, 
  # - platform: homeassistant
  #   name: "Water flow rate %"
  #   entity_id: sensor.waterflowrate
  #   id: waterflowrate
  #   unit_of_measurement: "%"
    
  - platform: homeassistant           # Set motor feed rate and intial value for motor           
    name: "Motor flow rate"
    entity_id: input_number.pot_motor_flow_rate   #defined in homeassistant
    id: motor_flow_rate
    on_value:
      then:
        - globals.set:
            id: flow_rate
            value: !lambda 'return x;'
        - globals.set:
            id: flow_rate_percent
            value: !lambda 'return x * 100;'
        - output.set_level:
            id: pot_pump_pwm
            level: !lambda 'return id(flow_rate);'
    
#Capacitive soil moisture sensor
#Dry 3.25 - taken from in the air, probably should do it directly in dry soil
#Wet 1.75 - taken from in a glass of water, probably should do it directly in saturated soil
  - platform: adc
    pin: GPIO33
    id: moisture
    filters:
      - lambda: |-
          if (x > 3.11) {                                     // if over 3.25 volts, we are dry
            return 0;
          } else if (x < 1.68) {                              // if under 1.75 volts, we are fully saturated
            return 100;
          } else {
            return (3.11-x) / (3.25-1.68) * 100.0;            // use a linear fit for any values in between
          }
    name: "Pot Moisture"
    unit_of_measurement: "% Moist"
    icon: "mdi:water-percent"
    update_interval: 2s
    attenuation: 11db
    on_value_range: 
      - below: 65.0
        then:
        - output.turn_on: pot_pump_pwm
        - output.ledc.set_frequency:
            id: pot_pump_pwm
            frequency: "3Hz"  
        - output.set_level:
            id: pot_pump_pwm
            level: !lambda 'return (id(motor_flow_rate).state/100);'     #return sensor value devided by 100, to make in percentage
      - above: 80.0
        then:
        - output.turn_off: pot_pump_pwm
        

  - platform: template                            # Feeds left to HA
    name: "water left today"
    id: water_left_today        
        
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 watering"
    id: last_watering
  # - platform: template
  #   name: "On board time"
  #   id: on_board_time_text
    
binary_sensor: 

  - platform: status                            # Availabilty for HA
    name: "Pot status"
    id: system_status  

  - platform: gpio                              # Motion sensor
    pin: 25
    name: "Pot PIR Sensor"
    id: motion_sensor
    device_class: motion
    on_press:
      then:
      - light.turn_on: 
          id: light_status
          brightness: 100%
          red: 100%
          green: 0%
          blue: 0%
    on_release:
      then:
      - light.turn_off:
          id: light_status
          
  - platform: gpio                              # Physical manual watering switch
    pin:
      number: 23
      mode: INPUT_PULLUP
      inverted: True
    filters:
      - delayed_on: 10ms
    name: Manual watering
    id: manual_plant_watering
    device_class: running
    on_press:
      then: 
        - script.execute: dispense_food
        - binary_sensor.template.publish:
            id: watering_is_disabled
            state: OFF
    #   - output.turn_on: pot_pump_pwm
    #   - output.ledc.set_frequency:
    #       id: pot_pump_pwm
    #       frequency: "3Hz"
    #   - output.set_level:
    #       id: pot_pump_pwm
    #       level: !lambda 'return (id(waterflowrate).state/100);' 
    # on_release:
    #   then:
    #   - output.turn_off: pot_pump_pwm
    

  - platform: gpio                              # Physical advance display page button
    pin: 
      number: 39
      # mode: INPUT_PULLUP
      inverted: True
    name: "Next page"
    id: next_page
    on_press:
      then:
        - display.page.show_next: pot_display

  - platform: gpio                               # Physical feed disable switch
    pin: 
      number: 35
      # mode: INPUT_PULLUP
    name: "Disable Fish Feed"
    id: disable_watering
    filters:
      - invert:    
    on_press:
      then:
        - if:
            condition:
              binary_sensor.is_on: watering_is_disabled
            then:
              - binary_sensor.template.publish:
                  id: watering_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: watering_is_disabled
                  state: ON



  - platform: template                           # Sun is up to HA
    name: "Pot sun is up"
    id: pot_sun_is_up

  - 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: "Tank is full"
    id: tank_is_full

  - platform: template                           # Hopper is low alert to HA from HX711 definition
    name: "Tank is at low level"
    id: tank_is_at_low_level

  - platform: template                           # Hopper is empty alert to HA from HX711 definition
    name: "Tank is empty"
    id: tank_is_empty

  - platform: template                           # Hopper is OK alert to HA from HX711 definition
    name: "Tank is OK"
    id: tank_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: "Pot is over watering"
    id: pot_is_over_watering
    
  - platform: template                           # Hopper is over feeding alert to HA from HX711 definition
    name: "Water motor running"
    id: water_motor_running

  - platform: template
    name: "Home Assistant connected"
    id: home_assistant_api_connected
      
  - platform: template
    name: "WiFi Connected"
    id: wifi_connected
    
  - platform: template
    name: "Watering is disabled"                   # Feeding is disabled
    id: watering_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: watering_is_disabled
            state: OFF
    on_release:
      then:
        - homeassistant.service:
            service: input_boolean.turn_off
            data:
              entity_id: input_boolean.disable_fish_feeder
    
light:                                             #led light    
  - platform: fastled_clockless
    chipset: WS2811
    id: light_status
    pin: GPIO3
    num_leds: 1
    rgb_order: grb
    name: "Pot LED"

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

  - platform: gpio
    pin: 23
    id: enable_motor
    
fan:
  - platform: speed
    output: pot_pump_pwm
    name: "Pot Pump PWM"

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: water_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(tank_water_height).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(tank_water_height).state < (id(post_dose_target_weight) + id(post_feed_overrun_variable));'
          then:
            - binary_sensor.template.publish:
                id: pot_is_over_watering
                state: ON
      - binary_sensor.template.publish:
          id: water_motor_running
          state: OFF 
      - binary_sensor.template.publish:
          id: manual_plant_watering
          state: OFF

      - text_sensor.template.publish:      # Publish feed time to HA when motor stops # "%Y-%m-%d %H:%M"
          id: last_watering
          state: !lambda |-
              char str[17];
              time_t currTime = id(homeassistant_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: pot_is_over_watering
          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_water_height_variable
          value: !lambda 'return id(pre_dose_height) - id(tank_water_height).state;'

      - globals.set:                                 # Store last feed weight
          id: post_watering_tank_height_variable
          value: !lambda 'return id(tank_water_height).state;'

      - lambda: 'id(water_left_today).publish_state(id(water_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: water_left_today_variable
          value: !lambda 'return id(water_left_today_variable) - 1;'   
          
        
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

I’m working in a RC Car with espHome, take a look please:

I really don’t understand at all because I use 2 switches of 4 at the same time connected to a L298N.
I have to use all 4 pins for forward, backward, right and left control. The other two pins on each side are for enabling the motors. There is no pin left for speed control.

This is the key to make it work perfectly. Could you tell me how to do it? Thanks in advance

Apologies for missing your reply.

Have you tidied it up yet or got it working?

1 Like

I’m not too sure what you are asking.

If you need to control additional motors you’ll need an additional LM298.

I need to control speed… the RC Car is very fast and sometimes uncontrollable. The important thing is not that it is connected to the L298N, it is important that are switches and each switch should be set to the same power when it is on. That should have same pwm on each switch to control it.
Here you have two examples of movement, 2 switch on and 2 switch off:

rc_car_forward:
  alias: RC Car forward
  sequence:
  - service: switch.turn_off
    data:
      entity_id: switch.input1
  - service: switch.turn_off
    data:
      entity_id: switch.input3
  - delay:
      milliseconds: 500
  - service: switch.turn_on
    data:
      entity_id: switch.input2
  - service: switch.turn_on
    data:
      entity_id: switch.input4

rc_car_right_rotation:
  alias: RC Car right rotation
  sequence:
  - service: switch.turn_off
    data:
      entity_id: switch.input1
  - service: switch.turn_off
    data:
      entity_id: switch.input4
  - delay:
      milliseconds: 500
  - service: switch.turn_on
    data: 
      entity_id: switch.input3
  - service: switch.turn_on
    data:
      entity_id: switch.input2
  mode: single

Just got my L298N and ongoing testing the esphome codes. I am now using L298N H-bridge platform and power-up the esp32 using the L298N 5V output. I guess I have figured out the “speed platform for the fan” thingy: I cant use the fan.set_level'. Instead, I can actually use output.set_level` to control the PWM for the ENABLE-A. This is so far so good for me…but still in progress of testing. Thanks for your help.

@ashscott Do you have a post describing the hardware design of your fish feeder?

Do you mean the feeder of the control system?

Yea, I was looking at your yaml and curious what the hardware implementation looks like

It’s based around the Profifeeder, with my own load cell mount and ESP32 control system.

Thank you! I solved as a light component and pwm output I also I read this later.

would you show us how to wire the esp32/8266 to the l293d shield

This, I think, is what you need.