EMB1A: Controlling a FAN with PWM

This scenario presents how to handle the rotation speed of the fan. It is done using a PWM signal, as presented in this tutorial. The fan pumps the air into the yellow pressure chamber with a pressure sensor inside. Because it is pretty hard to monitor rotation speed with the camera, this scenario should be run along with scenario following scenario EMB1B: Reading environmental data with a Bosch integrated sensor that will indirectly measure rotation speed using pressure changes.


A good understanding of the PWM signal and duty cycle is necessary. We also use built-in timers to control the ESP32 chip's PWM hardware channels. In this case, we do not use an external library; instead, we use built-in tools in the Arduino framework for ESP32, so no additional libraries will be included in the project.

Suggested Readings and Knowledge Resources

Hands-on Lab Scenario

Task to be implemented

Implement a program that will spin the fan using a PWM signal.

Execute your experiment once, e.g. in the setup() function, but not infinitely, in the loop(). We appreciate letting you keep your fan off when finishing your experiments. They are very noisy, and our servers love silence!


Assuming you will use 8-bit PWM resolution, the minimum value is 0, and the max (full speed) is 255.


To use PWM in ESP32, it is best to use built-in ledc* functions LEDC documentation on the ESP32 manufacturer's page. The “leds” use timers and have channels attached to the timer. We will use one channel numbered 0. The channel controls the PWM duty cycle. Do not be misled by the “leds” functions: they generate a PWM signal, and it is meaningless whether you control an LED, bulb, or fan (as here).

Step 1

Define some parameters, including channel numbers, PWM resolution (here 8-bit) and PWM frequency (1000Hz):

#define FAN_PIN 35
#define FAN_PWM_Ch 0
#define FAN_PWM_FREQ 1000

GPIO pins controlling the fan is 35.

Step 2

Initialise fan PWM channel and make it to stop (duty cycle 0):

    ledcSetup(FAN_PWM_Ch, FAN_PWM_FREQ, FAN_PWM_RESOLUTION); //Initialise channel
    ledcAttachPin(FAN_PIN, FAN_PWM_Ch);                      //Bind it to the PWM
    ledcWrite(FAN_PWM_Ch,0);                                 //Write to CHANNEL, not to PIN!

Step 3

To control the fan rotation speed (via PWM), use ledcWrite(FAN_PWM_Ch, duty_cycle_value);.

Note you write to channel, not to the pin!
A common source code mistake causes the fan not to operate correctly.

A ledcWrite with a duty_cycle_value equal to 0 causes the fan to stop.
The maximum duty_cycle_value is determined by the FAN_PWM_RESOLUTION, which is 8-bit in our case, so maximum is 255. In that case, the fan operates at full speed.

There is a number of handy functions in the ledc library, including (among others):
  • analogWrite(pin,value); to keep compatibility with genuine Arduino,
  • ledcFade(pin, start_dutycycle, end_dutycycle, fade_time_ms); that you can use instead of the loop to slow down rotation gently,
  • ledcDetach(pin); to detach a pin from the channel (and thus release the PWM channel),
  • ledcWriteNote(pin, note, octave); where note is one of the values: NOTE_C, NOTE_Cs, … (and so on, music notes, up to NOTE_B) - it is useful when PWM controls a speaker, to generate a perfect musical note, but we do not use speakers here, sorry.

Result validation

You can observe rotation change by measuring air pressure in the chamber only. To distinguish whether the fan is stopped or rotating, you can observe it via the video stream.


The fan rotates itself. Why?: It is connected via the MOS FET transistor that tends to saturate when the GPIO (35) is not controlling it. So, to ensure the fan is stopped, bind a PWM channel to it and force the duty cycle set to 0.

The fan is not rotating?: Besides possible configuration and programming errors, it is essential to understand that setting duty cycle, e.g. to 1, won't start spinning: a minimum value (a threshold) causes rotation. Moreover, the relation between rotation speed, air pressure and duty cycle controlling the fan is not linear. You may also be a victim of the common coding mistake of using a GPIO number instead of the channel number (first parameter) in the ledcWrite function: use the PWM channel number!
What is the maximum number of channels?: the MCU we use here is ESP32-S3, so it has 8 PWM channels. You can use timers and software implementation of the PWM signal if you need more, but that is a bit tricky and may not be as precise as hardware PWM implementation.
What is the maximum bit resolution for PWM?: In this particular MCU, it is between 1 and 14 bits.
What PWM frequency should I use?: there is no straightforward answer to this question, but setting too low a frequency will cause the inability to control the fan: 1000Hz (1kHz) seems reasonable and has been tested with this configuration.

Project information

This Intellectual Output was implemented under the Erasmus+ KA2.
Project IOT-OPEN.EU Reloaded – Education-based strengthening of the European universities, companies and labour force in the global IoT market.
Project number: 2022-1-PL01-KA220-HED-000085090.

Erasmus+ Disclaimer
This project has been funded with support from the European Commission.
This publication reflects the views of only the author, and the Commission cannot be held responsible for any use that may be made of the information contained therein.

Copyright Notice
This content was created by the IOT-OPEN.EU Reloaded consortium, 2022,2024.
The content is Copyrighted and distributed under CC BY-NC Creative Commons Licence, free for Non-Commercial use.

en/iot-open/practical/hardware/sut/esp32/emb1a_1.txt · Last modified: 2024/04/09 12:55 by pczekalski
CC Attribution-Share Alike 4.0 International
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0