AnalogWrite Library for ESP32-ESP32S2 Arduino core

A request to add the new AnalogWrite library for ESP32-ESP32S2 Arduino core.
Here's the link to the GitHub repo...

GitHub - Dlloydev/ESP32-ESP32S2-AnalogWrite: ESP32 PWM, SERVO, TONE and NOTE. Smart GPIO pin management and advanced control features.


So as not to confuse with another library with similar name, here's the ArduBadge so you can install the correct library or check out the documentation.


You can post feedback here if you like ... I'll try to answer any questions.

  • I fully tested this on a SP32Dev Board and couldn't find any issues.
  • Currently awaiting delivery of an ESP32-S2 Saola-1M for testing.
  • Plan of testing and updating this to work with the ESP32-S3 when available.

.... unless the Arduino core for the ESP32 eventually offers analogWrite as part of the core :slightly_smiling_face:

Something is currently broken with PWM on the ESP32 S2.

Thanks @cattledog! Yes, I saw that link about the PWM problem with the servo. I think he's using the MPWM peripheral, but didn't look too closely. There's also the Delta Sigma and LEDc peripherals ... the Arduino core provides functions that give analog write / PWM capabilities for these. I think the reason for not yet offering analogWrite functions with the core is the complexities of utilizing the best resource management strategy to avoid conflicts with other code.

I'm using the LEDc peripheral and have discovered something that's quite simple to check and see if any pin has been assigned to GPIO. (the MCU_SEL mux register). Apparently, after power-up, the GPIO pins are assigned to function 1 (0), but after any code works with a pin ... even just setting it to input, this mux register switches to function 3 (GPIO). So now I can monitor if a pin is available for use and avoid taking over pins used by other code. With LEDc, I don't think there's any purpose (or if it's even possible) to use a timer/channel without dedicating it to a pin.

I couldn't find any code that puts this multiplexor back to startup status ... took forever to work out something to control this, but ended up being quite simple.

1 Like

I have a Feather S2 from Unexpected Maker

I have not gotten very far into using it, and mostly used it as an opportunity to experiment with Circuit Python. The native USB support makes that possible. Both UM and Adafruit recommend using Circuit Python, and advise that the Arduino core is not fully developed and to not use it.

The Arduino S2 core is not available from the board manager, and the procedure for installing it from the development branch is complicated. I also understand that Espressif has slowed work on the S2 core, and was working on the S3. I'm not clear about the long term role that the S2 has in the product line.

I have never used the Espressif IDF directly, but I guess that is another option.

My advice would be to focus your library work on the ESP32 and leave the S2 and S3 alone until there is Arduino core support available through the boards manager. People who can currently use the Arduino core on those devices are likely experienced enough to use the ledc peripheral.

Good luck with your efforts.

1 Like

The analogWrite library is now fully tested on the ESP32-S2 Saola-1M (library has been updated to version 1.1.0). I used this link to install the S2 support and it worked fine. I have both cores installed on my PC and just rename the folders navigate between both cores which was quite handy during development.

For ESP-S2 boards, I found that the issue #5050 with the ledc function will set the frequency low by a factor of 80. So the temporary workaround is to enter a value 80x high.

The analogWrite, analogWriteFrequency and analogWriteResolution functions provided are quite simple to use ... no requirement as to when, where or in what order they're used. That is, frequency, resolution and duty cycle can be changed on the fly.

Pin resource and timer management is automatic and transparent. It detects if a timer resource or pin is used by external code to avoid conflicts. A PWM status function is provided.

1 Like

Thanks for this very good library.

The Arduino S2 core is not available from the board manager, and the procedure for installing it from the development branch is complicated.

I used this link to install the S2 support and it worked fine

It's much easier to install S2 and C3 core with esp32 core v1.0.6+ now.

HOWTO Install esp32 core for ESP32-S2 (Saola, AI-Thinker ESP-12K) and ESP32-C3 boards

1 Like

As of April 16th 2021 , the esp32-s2/c3 board support has been included in master branch of esp32 core.

This is likely a big step forward towards getting the core through the boards manager.

I followed this procedure to install the current master development branch which supports the s2/s3 boards.

I can now see the UM Feather S2 as a board choice.

It's getting easier. Add this link to the preferences file in Arduino15 and the boards manager will install v2.0.0 alpha1

I don't know when they added the feature of installing the development master through the boards manager but it makes things simple


Both UM and Adafruit recommend using Circuit Python, and advise that the Arduino core is not fully developed and to not use it.

I can now see the UM Feather S2 as a board choice.

The S2 now is OK to use, especially with esp32 core v1.0.6+. The SSL, Blynk, LittleFS and SPIFFS are all working now. You don't need to use only Python anymore.

Try these libraries which supports those features on ESP32-S2

  1. WebSockets2_Generic
  2. Blynk_WM
  3. Blynk_Async_WM
  4. ESP_WiFiManager
  5. ESPAsync_WiFiManager

and many more to come.


The Arduino reference for analogWrite() describes the PWM wave characteristics for various hardware architecture. The general operational characteristic is 8-bit duty cycle control where the output will be always off for value 0 and always on for value 255. With the various devices and timer modes, sometimes a bit correction is required to achieve full off or on. The ESP8266 follows this mode of operation, but with the different timer architecture on the ESP32 devices, the LEDc PWM operates in a different manner, where duty value 0 is always off, but duty value 255 will give an output that's 255/256 duty cycle (not fully on). This happens for any setting for bit resolution.

As of today's new version 1.2.0, this condition is detected and corrected, where the hardware timer is programmed with 2resolution, which drives the output signal fully on. The pin status function will return 2resolution-1 which is the duty cycle value entered.

New version 1.2.1

  • When using ESP32S2 devices, this version offers a temporary fix for: ESP32-S2 PWM for a Servo pulse issue #5050 but with limited frequency range. Tested range is 8-bit: 4Hz to 2.5kHz, 13-bit 0.2Hz to 120Hz.

  • There is now normal LEDc PWM for 1-7 bit resolution (ideal for high frequency applications) and the max duty value is set to 2resolution for 8-16-bit resolution for 0-100% PWM (ideal for complete control of common anode LED devices (and other devices).

AnalogWrite with phase control!


Now analogWrite can assign a pin and contol PWM duty value, frequency, resolution and phase all from one function. This function now returns the PWM frequency reported from the ledc framework.


float analogWrite(int8_t pin, int32_t value, float frequency, uint8_t resolution, uint32_t phase);
float analogWrite(int8_t pin, int32_t value, float frequency, uint8_t resolution);
float analogWrite(int8_t pin, int32_t value, float frequency);
float analogWrite(int8_t pin, int32_t value);

3-phase PWM Example:

Details: 3-pins (4, 5 and 12), 10-bit PWM split into 3 equal ON-periods of 341. Frequency is 100Hz. Signal on pin 5 is phase shifted by 341 steps, signal on pin 12 is shifted by 682 steps.

The control range for duty value and phase is the same (0-1023) for 10-bit resolution.

  analogWrite(4, 341, 100, 10, 0);
  analogWrite(5, 341, 100, 10, 341);
  analogWrite(12, 341, 100, 10, 682);

1 Like

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

pwm.write() 3.0.2

  • Compatible with latest ESP32 Arduino core
  • Code refactored, now in a Class
  • The main function syntax is pwm.write()
  • Added ESP32-C3 support and example

Wokwi phase shift examples ... ESP32 ... ESP32-S2 ... ESP32-C3

1 Like

I've been working on a major update to this library (version 4.0.0) which will have some significant new capabilities using the LEDC PWM controller of the ESP32/S2/S3/C3. For the ESP32, all 16 PWM channels will be available.

The new version will exploit the capabilities of the LEDC controller to add PWM synchronization. Its not perfect, but multiple PWM signals can be synchronized to within 1μs. I think this pushes the LEDC capabilities much closer to that of the MCPWM controller and it has the capacity to synchronize more signals than MCPWM.

The user will now have the capability of manually attaching (assigning) a pin to any PWM channel.

ESP32 PWM Synchronization:

  • New capability to pause/resume the timers to allow PWM startup synchronization.
  • Up to 8 PWM signal pairs are inherently synchronized. Channels 0-1, 2-3, 4-5, 6-7, 8-9, 10-11, 12-13, 14-15.
  • Each channel pair uses a different timer and/or speed mode. I've found that there's a consistent delay of about 11μs between each signal pair. This can be essentially calibrated out by using the hpoint (phase) value to delay the PWM signal start. The result is the alignment of each signal pair (timer to timer synchronization).

Here's the logic trace of 8 PWM signals (synchronized) at 25kHz frequency and 10-bit resolution:


Zooming in, the timer to timer synchronization (after phase delay calibration) was only100ns:


Zooming in to max, here we can see perfect synchronization between a signal pair because they use the same timer:


I should have the new release ready within the next week. Still need to update the documentation and add examples (here's a start) ...

Board PWM Pins PWM, Duty and Phase Channels Frequency and Resolution Channels
ESP32 2, 4, 5, 12-19, 21-23, 27, 32, 33 16 8
ESP32‑S2 1- 14, 21, 33-42, 45 8 4
ESP32‑C3 0- 9, 18, 19 6 3

PWM Channel Configuration

Frequency and resolution values are shared by each channel pair. For example, channels 0 and 1 share the same frequency and resolution values. When any channel gets configured, the next lower or higher channel gets updated with the same frequency and resolution values as appropriate.

PWM Channel Speed Mode Timer Frequency Resolution Duty Phase
0 0 0 1 1 1 1
1 0 0 1 1 2 2
2 0 1 2 2 3 3
3 0 1 2 2 4 4
4 0 2 3 3 5 5
5 0 2 3 3 6 6
6 0 3 4 4 7 7
7 0 3 4 4 8 8
8 1 0 5 5 9 9
9 1 0 5 5 10 10
10 1 1 6 6 11 11
11 1 1 6 6 12 12
12 1 2 7 7 13 13
13 1 2 7 7 14 14
14 1 3 8 8 15 15
15 1 3 8 8 16 16


pwmWrite 4.0.0

  • For the ESP32, all 16 PWM channels are now available
  • The user can now manually attach (assign) a pin to any PWM channel.
  • New PWM synchronization pushes the LEDC capabilities closer to that of the MCPWM controller and it can synchronize more signals than MCPWM.


ESP32 PWM and SERVO Library 4.1.0

Some 3 phase simulation examples for the ESP32, ESP32-S2 and ESP32-C3.

Includes use of pwm.pause and pwm.resume for signal synchronization and also shows method of adding deadtime.