PWM pulse width is the exact duration, usually measured in microseconds, that a digital signal stays HIGH during a single repeating cycle. In a real circuit, altering the pulse width changes the average power delivered to a load by varying the ON time relative to the OFF time, effectively simulating a lower analog voltage without using a wasteful linear regulator. What people commonly confuse it with is "duty cycle" (which is a percentage ratio) or "frequency" (which is how often the cycles repeat). While duty cycle and frequency describe the waveform's shape and rate, pulse width is the absolute, hard measurement of time the voltage is actually applied.

Bench Rule of Thumb: If you are dimming an LED, the microcontroller only cares about the ratio (duty cycle). If you are commanding an RC servo or an Electronic Speed Controller (ESC), the hardware strictly cares about the absolute time (pulse width). Mixing these two concepts up is the number one reason embedded projects fail on the workbench.

The Core Concept: Absolute Time vs. Ratio

To understand why pulse width matters, we have to separate the time the signal is HIGH from the total time of the cycle. The total time of one cycle is the period, which is the inverse of frequency ($T = 1/f$).

Duty cycle is simply the pulse width divided by the period, expressed as a percentage. If your period is 20 milliseconds (ms) and your pulse width is 2 ms, your duty cycle is 10%. But here is the critical distinction: if you change the frequency to 100 Hz (a 10 ms period) and keep the pulse width at 2 ms, your duty cycle jumps to 20%. The absolute pulse width remained exactly the same, but the ratio changed entirely.

For thermal or optical loads like heating elements or LEDs, the physical process averages out the energy, so the 20% duty cycle will indeed deliver more average power. But for digital decoding loads like servo motors, the internal microcontroller is literally timing the HIGH state with a hardware clock. It doesn't care about your duty cycle; it only measures that 2 ms pulse width.

Worked Numeric Example: Sizing the Pulse

Let us look at a standard 50 Hz RC servo signal, which is the most common place you will need to calculate exact PWM pulse width manually.

  • Target Frequency: 50 Hz
  • Period: $1 / 50 = 0.02$ seconds, or 20,000 microseconds (µs)
  • Servo Center Position (90°): Requires a 1,500 µs pulse width

To achieve this on a microcontroller using a duty cycle API, we calculate the ratio: $1500 / 20000 = 0.075$, or 7.5%. If your microcontroller uses an 8-bit PWM resolution (0-255), you would write a value of $255 \times 0.075 \approx 19$.

However, an 8-bit resolution gives you a step size of $20,000 / 256 = 78$ µs per step. A standard servo expects 1,000 µs to 2,000 µs for its full 180-degree sweep. With 78 µs steps, you only get about 12 distinct positions across the entire physical rotation of the motor. This is why precise pulse width control requires higher resolution hardware timers.

Where You Meet PWM Pulse Width in Practice

You will encounter absolute pulse width requirements in several specific embedded scenarios:

  1. RC Servos and Actuators: Standard hobby servos (like the SG90 or MG996R) expect a 50 Hz signal where a 1,000 µs pulse width means 0°, 1,500 µs means 90°, and 2,000 µs means 180°.
  2. Brushless Motor ESCs: Electronic Speed Controllers use the exact same 1,000-2,000 µs pulse width protocol to determine throttle position, often requiring a specific 1,000 µs "arming" pulse width before they will spin the motor.
  3. DShot Protocols: Modern drone ESCs use digital protocols like DShot300 or DShot600, where the pulse width is shrunk down to roughly 3-6 µs to encode binary 1s and 0s, entirely replacing analog timing.
  4. Buck/Boost Converters: In switched-mode power supplies, the pulse width dictates exactly how long the inductor charges. Here, pulse width modulation is used dynamically in a feedback loop to maintain a stable output voltage under varying loads.

Bench Scenario: The ESP32 Servo Jitter Mystery

Let us walk through a classic workbench failure that perfectly illustrates what happens when you confuse duty cycle with pulse width.

Setup: You wire an MG996R high-torque servo to an ESP32-WROOM-32 on GPIO 13. The servo is powered by a dedicated 5V 2A bench supply (with grounds tied together), and the signal wire is connected directly to the ESP32.

Numbers: You write a quick Arduino sketch using analogWrite(13, 127). Assuming an 8-bit scale, 127 is roughly 50% duty cycle. You expect the servo to move to a specific angle and hold.

Outcome: The servo violently twitches back and forth, emits a high-pitched whine, and draws 1.2A peak, causing the bench supply to occasionally hiccup. The ESP32 occasionally brownouts and resets.

What went wrong: The default PWM frequency for analogWrite on the ESP32 Arduino core is 5,000 Hz (5 kHz). At 5 kHz, the total period is only 200 µs. A 50% duty cycle results in a 100 µs pulse width. The servo's internal comparator is expecting a pulse between 1,000 µs and 2,000 µs. When it sees a 100 µs pulse, it interprets this as an extreme, out-of-bounds physical position and aggressively drives the motor to hit the internal mechanical hard stop, causing the massive current spike and jitter.

The Fix: You must abandon analogWrite and use the ESP32's native LEDC (LED Control) peripheral, which allows you to set both the exact frequency and the timer resolution.

// ESP32 LEDC Setup for Exact Servo Pulse Width
const int servoPin = 13;
const int ledcChannel = 0;
const int freq = 50; // 50 Hz = 20ms period
const int resolution = 16; // 16-bit = 65536 steps

void setup() {
  ledcSetup(ledcChannel, freq, resolution);
  ledcAttachPin(servoPin, ledcChannel);
  
  // Calculate exact duty for 1500us (center) pulse width
  // Duty = (PulseWidth / Period) * MaxDuty
  // Duty = (1500 / 20000) * 65535 = 4915
  ledcWrite(ledcChannel, 4915); 
}
Resolution vs. Microsecond Step Size at 50 Hz (20,000 µs Period)
Timer ResolutionTotal StepsStep Size (µs)Servo Position Accuracy
8-bit25678.1 µsPoor (~14 positions)
10-bit102419.5 µsFair (~51 positions)
12-bit40964.8 µsGood (~208 positions)
16-bit655360.3 µsExcellent (Sub-degree)

Frequently Asked Questions

Does wire length or capacitance affect my PWM pulse width?

Yes, significantly at high frequencies or long runs. Every wire has parasitic capacitance and inductance. If you are sending a 50 kHz PWM signal with a very short pulse width (e.g., 2 µs) over a long, unshielded ribbon cable, the RC time constant of the cable will round off the sharp square-wave edges. By the time the signal reaches the gate of your MOSFET, the voltage might not cross the logic HIGH threshold before the pulse ends, effectively shrinking your pulse width to zero. For long runs, use a dedicated gate driver IC close to the MOSFET or lower your PWM frequency.

Why does my multimeter read a different voltage when I change the pulse width?

If you are measuring a PWM pin with a standard digital multimeter (DMM) in DC voltage mode, the meter is acting as a low-pass filter, averaging the voltage over time. A 5V logic pin with a 50% duty cycle (where pulse width equals half the period) will read roughly 2.5V. If you increase the pulse width to 80% of the period, the meter will read 4.0V. To see the actual 0V and 5V rails and measure the exact pulse width in microseconds, you must use an oscilloscope or a logic analyzer.

Can I use the Arduino Servo.h library on the ESP32?

While the standard Arduino Servo library works on AVR boards by tying into hardware timers, using it on the ESP32 via the Arduino core can sometimes cause conflicts with WiFi and Bluetooth interrupts, leading to microsecond-level timing jitter. For the ESP32, it is always better to use the native Espressif LEDC API or the ESP32-specific ESP32Servo library, which properly allocates hardware timers without disrupting the RTOS wireless stacks.