Description
Board
ESP32-S3
Device Description
ESP32-S3 module on custom board. Only one GPIO (GPIO10) is used for PWM output via RMT.
Hardware Configuration
Only GPIO10 is used as output to measure PWM via oscilloscope. No other components attached.
Version
latest stable Release (if not listed below)
IDE Name
Arduino IDE
Operating System
Windows 11
Flash frequency
80 MHz
PSRAM enabled
no
Upload speed
921600
Description
When sweeping PWM frequency and Ton dynamically using RMT on ESP32-S3, the output is not smooth. There's a noticeable glitch or "bang" at each frequency step. The same logic works perfectly and seamlessly on Arduino Uno using Timer1 hardware PWM with Phase Correct mode.
Expected: clean frequency and Ton sweep with no break in output
Observed: visible discontinuity or glitch at each step due to teardown/reinit of RMT channel
Sketch
Below code i used in Arduino UNO worked flawlessly.
const int pwmPin = 9; // OC1A = Pin 9 on Arduino Uno
const float freqStart = 28000.0; // Hz
const float freqEnd = 24000.0; // Hz
const float freqStep = -10.0; // Hz
const float tonStart = 8.0; // µs
const float tonEnd = 21.0; // µs
const int delayMs = 5; // Delay per step
const int offDelayMs = 3000; // 5 sec OFF after sweep
void setup() {
pinMode(pwmPin, OUTPUT);
delay(2000);
// Timer1: Phase Correct PWM, TOP = ICR1
TCCR1A = _BV(COM1A1); // Non-inverting PWM on OC1A
TCCR1B = _BV(WGM13) | _BV(CS10); // Mode 8: Phase-correct, no prescaler
Serial.begin(115200);
}
void loop() {
float freq = freqStart;
float ton = tonStart;
float freqRange = freqStart - freqEnd;
float tonRange = tonEnd - tonStart;
int steps = freqRange / -freqStep;
for (int i = 0; i <= steps; i++) {
freq = freqStart + i * freqStep;
ton = tonStart + (tonRange * i / steps);
float period_us = 1000000.0 / freq;
uint16_t top = (16000000UL / (2 * freq)); // ICR1 value
uint16_t duty = (ton / period_us) * top; // OCR1A value
ICR1 = top;
OCR1A = duty;
Serial.print("Freq: ");
Serial.print(freq, 1);
Serial.print(" Hz | Ton: ");
Serial.print(ton, 1);
Serial.print(" us | TOP: ");
Serial.print(top);
Serial.print(" | OCR1A: ");
Serial.println(duty);
delay(delayMs);
}
// Turn off PWM
OCR1A = 0;
Serial.println("PWM OFF for 5 seconds");
delay(offDelayMs);
}
Below code i used in ESP32-S3 but the wave is banging.
#include <Arduino.h>
#include <driver/rmt_tx.h>
#include <Ticker.h>
// --- Pin Setup ---
#define PWM_PIN 10 // Pick a reliable RMT-capable GPIO
// --- Sweep Parameters ---
const float freqStart = 28000.0;
const float freqEnd = 24000.0;
const float freqStep = -10.0;
const float tonStart = 8.0;
const float tonEnd = 21.0;
const int delayMs = 5;
const int offDelayMs = 5000;
rmt_channel_handle_t rmt_chan = nullptr;
rmt_encoder_handle_t rmt_encoder = nullptr;
void setupRMT(float freq, float ton_us) {
if (rmt_chan) {
rmt_disable(rmt_chan);
rmt_del_channel(rmt_chan);
rmt_chan = nullptr;
}
uint32_t period_us = 1000000.0 / freq;
uint32_t toff_us = period_us - ton_us;
rmt_tx_channel_config_t tx_config = {};
tx_config.clk_src = RMT_CLK_SRC_DEFAULT;
tx_config.gpio_num = (gpio_num_t)PWM_PIN;
tx_config.mem_block_symbols = 64;
tx_config.resolution_hz = 1000000; // 1 µs resolution
tx_config.trans_queue_depth = 1;
tx_config.flags.invert_out = false;
tx_config.flags.with_dma = false;
ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_config, &rmt_chan));
rmt_copy_encoder_config_t enc_config = {};
ESP_ERROR_CHECK(rmt_new_copy_encoder(&enc_config, &rmt_encoder));
ESP_ERROR_CHECK(rmt_enable(rmt_chan));
rmt_symbol_word_t item;
item.level0 = 1;
item.duration0 = (uint16_t)ton_us;
item.level1 = 0;
item.duration1 = (uint16_t)toff_us;
rmt_transmit_config_t config = {};
config.loop_count = 1000;
ESP_ERROR_CHECK(rmt_transmit(rmt_chan, rmt_encoder, &item, sizeof(item), &config));
}
void stopPWM() {
if (rmt_chan) {
rmt_disable(rmt_chan);
gpio_reset_pin((gpio_num_t)PWM_PIN);
gpio_set_direction((gpio_num_t)PWM_PIN, GPIO_MODE_OUTPUT);
gpio_set_level((gpio_num_t)PWM_PIN, 0);
rmt_del_channel(rmt_chan);
rmt_chan = nullptr;
}
}
void setup() {
Serial.begin(115200);
delay(2000);
Serial.println("Starting PWM Sweep...");
}
void loop() {
float freq = freqStart;
float ton = tonStart;
float freqRange = freqStart - freqEnd;
float tonRange = tonEnd - tonStart;
int steps = freqRange / -freqStep;
for (int i = 0; i <= steps; i++) {
freq = freqStart + i * freqStep;
ton = tonStart + (tonRange * i / steps);
setupRMT(freq, ton);
float period_us = 1000000.0 / freq;
Serial.printf("Freq: %.1f Hz | Ton: %.1f us | Period: %.1f us\n", freq, ton, period_us);
delay(delayMs);
}
stopPWM();
Serial.println("PWM OFF for 5 seconds");
delay(offDelayMs);
}
Debug Message
No crash or backtrace. The issue is in waveform stability and timing.
Other Steps to Reproduce
Output is connected to oscilloscope
Observe the waveform during frequency sweep
Clear gaps and interruptions are visible in ESP32-S3 output
Arduino Uno waveform is smooth and uninterrupted
I have checked existing issues, online documentation and the Troubleshooting Guide
- I confirm I have checked existing issues, online documentation and Troubleshooting guide.