Esphome + multiple L298N motor drivers - Limited ledc/pwm channels

Would anyone be willing to educate me on what I am doing wrong here or suggest a better alternative?

I have over extended my ESP32-S3’s (N8R2) ledc capability by wiring 5 motor driver boards (L298N - controlling 10 motors). I have read and proven that an S3 only has 8 available channels or 8 independent cases where I can use “ledc” which means I can use without issue, 4 motors with said code below. Anything more, than I get multiple motors running at once:

output:
  # Pump 1
  - platform: ledc
    id: pump_1_forward
    pin: 6

  - platform: ledc
    id: pump_1_reverse
    pin: 7

  # Pump 2
  - platform: ledc
    id: pump_2_forward
    pin: 15

  - platform: ledc
    id: pump_2_reverse
    pin: 16

  # Pump 3
  - platform: ledc
    id: pump_3_forward
    pin: 13

  - platform: ledc
    id: pump_3_reverse
    pin: 14

  # Pump 4
  - platform: ledc
    id: pump_4_forward
    pin: 12

  - platform: ledc
    id: pump_4_reverse
    pin: 11

fan:
  - platform: hbridge
    id: pump_1
    name: "Pump 1"
    pin_a: pump_1_forward
    pin_b: pump_1_reverse
    decay_mode: SLOW
    speed_count: 100
    restore_mode: ALWAYS_OFF

  - platform: hbridge
    id: pump_2
    name: "Pump 2"
    pin_a: pump_2_forward
    pin_b: pump_2_reverse
    decay_mode: SLOW
    speed_count: 100
    restore_mode: ALWAYS_OFF

  - platform: hbridge
    id: pump_3
    name: "Pump 3"
    pin_a: pump_3_forward
    pin_b: pump_3_reverse
    decay_mode: SLOW
    speed_count: 100
    restore_mode: ALWAYS_OFF

  - platform: hbridge
    id: pump_4
    name: "Pump 4"
    pin_a: pump_4_forward
    pin_b: pump_4_reverse
    decay_mode: SLOW
    speed_count: 100
    restore_mode: ALWAYS_OFF 

So obviously to most people I’m sure, the standard ESP32 is available with 16 channels which would let me run 8 motors independently and apparently I could use something like a PCA9685 16 Channel 12-bit PWM which would also give an additional 16 channels via i2c?

By this point I started getting AI involved as I found limited to no examples of other people making this mistake and thus possible ways around this. The general consensus of AI was that I could in theory just use two ledc per motor board by wiring them to the ENA/ENB pins but the code it offers just doesn’t work and I’m not that surprised:

output:
## Enable Pins for PWM/Ledc
  # Board 1 ENA
  - platform: ledc
    id: board0_pwm
    pin: 10
    channel: 0
    frequency: 2000 Hz

  # Board 1 ENB
  - platform: ledc
    id: board1_pwm
    pin: 9
    channel: 1
    frequency: 2000 Hz

  # Board 2 ENA
  - platform: ledc
    id: board2_pwm
    pin: 40
    channel: 2
    frequency: 2000 Hz

  # Board 2 ENB
  - platform: ledc
    id: board3_pwm
    pin: 39
    channel: 3
    frequency: 2000 Hz

##GPIO IN Pins For Motor Direction
  # Pump 1
  - platform: gpio
    id: pump1_forward
    pin: 6
  - platform: gpio
    id: pump1_reverse
    pin: 7

  # Pump 2
  - platform: gpio
    id: pump2_forward
    pin: 15
  - platform: gpio
    id: pump2_reverse
    pin: 16

  # Pump 3
  - platform: gpio
    id: pump3_forward
    pin: 13
  - platform: gpio
    id: pump3_reverse
    pin: 14

  # Pump 4
  - platform: gpio
    id: pump4_forward
    pin: 12
  - platform: gpio
    id: pump4_reverse
    pin: 11

fan:
  # Board 1 Motor 1
  - platform: hbridge
    id: motor1
    pin_a: pump1_forward
    pin_b: pump1_reverse
    enable_pin: board0_pwm

  # Board 1 Motor 2
  - platform: hbridge
    id: motor2
    pin_a: pump2_forward
    pin_b: pump2_reverse
    enable_pin: board1_pwm

  # Board 2 Motor 3
  - platform: hbridge
    id: motor3
    pin_a: pump3_forward
    pin_b: pump3_reverse
    enable_pin: board2_pwm

  # Board 2 Motor 4
  - platform: hbridge
    id: motor4
    pin_a: pump4_forward
    pin_b: pump4_reverse
    enable_pin: board3_pwm

This code snippet fails due to the pin_a/pin_b being “platorm: GPIO” - I think

ERROR: “ID ‘pump4_forward’ of type gpio::GPIOBinaryOutput doesn’t inherit from output::FloatOutput. Please double check your ID is pointing to the correct value.”

Seems to me I just need to add more channels via an additional i2c board or using a standard ESP32 but I was curious to see if anyone had anything to say on the topic.

I’m surprised that you got that much from AI.
It’s correct, L298N is supposed to be driven by PWM on ENA and static direction pins.
So normal Esp32 should be able to drive 16 motors.

That’s the problem with hbridge fan component. It doesn’t have the same logic, it’s using pwm on dir pins.
But do you really need that fan component?

Interesting because the h-bridge seems to run fine with pwm using just the IN pins. Are there any drawbacks to this? Just for curiosity

I do not need the fan component, just the ability to adjust the motor speed. What would you suggest otherwise?

I don’t think so. Except that you need 2 pwm pins…

You could use template number for speed and two switches for direction.

output:
  - platform: ledc
    id: motor_pwm
    pin: GPIO9
    frequency: 20000 Hz  
    resolution: 8bit

number:
  - platform: template
    name: "Motor Speed"
    id: motor_speed
    min_value: 0
    max_value: 100
    step: 1
    unit_of_measurement: "%"
    set_action:
      - output.set_level:
          id: motor_pwm
          level: !lambda "return x / 100.0;"

Thank you for your suggestions, very much appreciated. I have given this a try. Although it didn’t seem to like resolution:

output:
  # Pump 1 PWM
  - platform: ledc
    id: pump_1_pwm
    pin: 10
    frequency: 20000 Hz

  # Pump 2 PWM
  - platform: ledc
    id: pump_2_pwm
    pin: 9
    frequency: 20000 Hz

  ## Pump IN Pins
  # Pump 1
  - platform: gpio
    id: pump_1_forward
    pin: 6

  - platform: gpio
    id: pump_1_reverse
    pin: 7

  # Pump 2
  - platform: gpio
    id: pump_2_forward
    pin: 15

  - platform: gpio
    id: pump_2_reverse
    pin: 16

switch:
  - platform: template
    name: "Pump 1 Forward"
    id: pump_1_direction_forward_switch
    turn_on_action:
      - output.turn_on: pump_1_forward
      - output.turn_off: pump_1_reverse  # Forward
    turn_off_action:
      - output.turn_off: pump_1_forward
      - output.turn_off: pump_1_reverse   # OFF

  - platform: template
    name: "Pump 1 Reverse"
    id: pump_1_direction_reverse_switch
    turn_on_action:
      - output.turn_on: pump_1_reverse
      - output.turn_off: pump_1_forward  # Reverse
    turn_off_action:
      - output.turn_off: pump_1_forward
      - output.turn_off: pump_1_reverse   # OFF
    
  - platform: template
    name: "Pump 2 Forward"
    id: pump_2_direction_forward_switch
    turn_on_action:
      - output.turn_on: pump_2_forward
      - output.turn_off: pump_2_reverse  # Forward
    turn_off_action:
      - output.turn_off: pump_2_forward
      - output.turn_off: pump_2_reverse   # OFF

  - platform: template
    name: "Pump 2 Reverse"
    id: pump_2_direction_reverse_switch
    turn_on_action:
      - output.turn_on: pump_2_reverse
      - output.turn_off: pump_2_forward  # Reverse
    turn_off_action:
      - output.turn_off: pump_2_forward
      - output.turn_off: pump_2_reverse   # OFF

# Home Assistant sliders for forward/reverse
number:
  # Pump 1
  - platform: template
    name: "Pump 1 Speed"
    id: Pump_1_speed
    min_value: 0
    max_value: 100
    step: 0.1
    unit_of_measurement: "%"
    set_action:
      - output.set_level:
          id: pump_1_pwm
          level: !lambda "return x / 100.0;"

  # Pump 2
  - platform: template
    name: "Pump 2 Speed"
    id: Pump_2_speed
    min_value: 0
    max_value: 100
    step: 0.1
    unit_of_measurement: "%"
    set_action:
      - output.set_level:
          id: pump_2_pwm
          level: !lambda "return x / 100.0;"

I haven’t tested it yet using all 8 channels but it would appear to work based on this one example. However, I have two issues with this method:

  • Both the switch and the number (%) cards in home assistant do not retain the state you set. I.E. set the % to 90% but the card will always display 0
  • The speed at which a motor runs seems to run slower using the number % than the fan component. I.E. set both the number % card and the fan component card in home assistant to 50% and the speed of the motor configured to the fan component is faster. I think at 50% the number % card motor doesn’t even run

In this regard, I prefer how you interact with the fan component. Is there a way to adjust/resolve the above two points?

Add
optimistic: true
to the template number.

Instead for direction pins you can simply use gpio switch component, no need for separate template switch and output. If you want, you can also use interlocking, it would likely make switches easier to use.

Pwm duty cycle is NOT linear with motor speed. Fan component uses some conversion to get closer.
You can do something similar on the level lambda.

1 Like
switch:
  - platform: gpio
    name: "Pump 1 Forward"
    id: pump_1_direction_forward_switch
    pin: GPIO6
    interlock:
      - pump_1_direction_reverse_switch
    restore_mode: OFF

  - platform: gpio
    name: "Pump 1 Reverse"
    id: pump_1_direction_reverse_switch
    pin: GPIO7
    interlock:
      - pump_1_direction_forward_switch
    restore_mode: OFF
1 Like

Awesome! Thank you. I was going to save looking into what you meant by interlocking for tomorrow but now I know

I corrected missing not from duty cycle explanation on prev post…