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.