PWM on ESP32 not working?

Hi guys.
I have some problems getting PWM working on my new ESP32..
In my test sketch below I try to use a potentiometer to control the speed of a small fan connected to the board, and I can see the potentiometer readings vary between 0-4095, but my duty cycle only switches between 0 and 255, which I can't seem to figure out why..
But the real problem is that the ESP32 doesn't output a pwm signal on the set pwm_pin. I expect the signal to be readable with a multimeter ranging from 0-3.3V on that pin, or is that incorrect?
Or maybe someone more experienced can see why the signal isn't output?

Code:

#include <Wire.h>

#define pwm_pin 21 // PWM output pin 19 for fan speed control

#define level_selector 34 // Analog pin input 

int max_cycle = 255; // Setting maximum pwm value for fan_speed (should be integers)
int reading;
int map_level;
int pwm_channel = 0;
int pwm_freq = 25000;
int pwm_res = 8;

void setup() {

  // Setup pwm frequency on channel 0
  ledcSetup(pwm_channel, pwm_freq, pwm_res);  // Channel 0, 35 kHz frequency, 8-bit resolution
  ledcAttachPin(pwm_pin, pwm_channel);

  Serial.begin(115200); // For ESP32

  pinMode(pwm_pin, OUTPUT);
  pinMode(level_selector, INPUT_PULLUP); 

  delay(50);

  // Setting fan speed to 0
  ledcWrite(pwm_channel, 0);  // Set duty cycle to 0%

}

void speedcontrol(int level) {
  int new_cycle = (level/4095)*max_cycle;
  // Setting fan speed 
  ledcWrite(pwm_channel, new_cycle);  // Set duty cycle 
  Serial.print(F("     Duty Cycle: "));
  Serial.println(new_cycle);
}

void loop() {
  
  reading = analogRead(level_selector);
  map_level = map(reading, 0, 4095, 0, 9); // Range normally from 0-4095
  Serial.print(F("Reading: "));
  Serial.print(reading);
  Serial.print(F("     Mapping: "));
  Serial.print(map_level);
  speedcontrol(reading);
  delay(10);

}

Output in Serial Monitor:

10:07:57.470 -> Reading: 2058     Mapping: 4     Duty Cycle: 0
10:07:57.505 -> Reading: 2059     Mapping: 4     Duty Cycle: 0
10:07:57.505 -> Reading: 2058     Mapping: 4     Duty Cycle: 0
10:07:57.505 -> Reading: 2061     Mapping: 4     Duty Cycle: 0

While ints are 32 bits on an ESP32, they are still ints.

Try int new_cycle = (level/4095.0)*max_cycle;

Thank you, that solved the duty cycle part of problem. What exactly did I do wrong before though? I thought by dividing level with 4095 got me a decimal value, or percentage, that when multiplied by max_cycle would give me a decimal value between 0-255, and declaring new_cycle as an integer would remove all decimals only?

Dividing integers gives integers, it's easy as that.

Oh wait, I see. I declared level as an integer which would make level/4095 still be an integer with value 0 or 1 :slight_smile:

Yeah I realized. Thank you :slight_smile:
Any ideas regarding the pwm output?

It has an 8 bit range, so 0 gives steady 0V and 255 gives steady 3.3V

My problem however is that 255 still gives 0V on pwm_pin.. I have no idea why. Tried both pin 19 and 21 for pwm but still 0V output on 255 duty cycle.

A pin could be damaged, you could measure the wrong pins, who knows?

I can assure you, PWM works perfectly on an ESP32.

I have the exact same problem on 3 new NodeMCU ESP-32s though. I'm thinking I must have something wrong with my code? Or could I be using a pin that doesn't support pwm output? Or something else?

All OUTPUT capable pins support PWM.

The pinMode statement may be a problem. The ESP32 creates the PWM pulses by a special HW, and with ledcAttach you connect this HW to the correspondig pin. I think the statement

  pinMode(pwm_pin, OUTPUT);

connects the pin to the standard output ports again and disconnects it from the ledc hardware.

That solved it :slight_smile: Thank you so much!
Just to make sure I don't do anything stupid when trying to control the speed now, can I do this or will that be a problem too?

ledcWrite(pwm_channel, new_cycle * 0.85);

User @dlloyd made this library: GitHub - Dlloydev/ESP32-ESP32S2-AnalogWrite: ESP32 PWM, Servo, Easing and Tone. Smart GPIO pin management and advanced control features.

I don't think it will be a problem. But why using floats? The duty value is an integer, and because you set the resolution to 8 Bits, it is an interger between 0 and 255. So you could map your pot reading to directly give an integer between 0 and 255 ( or even less, if you don't want it to reach 255 ).

In another program, where I also had this issue, I calculate the percentage for duty cycle using some conditions and the result is a percentage of the total. But I'll give it a go and see :slight_smile: Thanks for all help!

This is an article that discusses the ESP32 PWM in detail:

This explains and demonstrates, how to implement PWM on a DC motor and LED using the ESP WROOM-32 module. The behavior of the PWM signal is determined by duty cycle, resolution, and frequency.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.