PWM and Analog I/O Deep Dive
PWM Isn't Real Analog Output
analogWrite() doesn't produce a variable voltage — it rapidly switches the pin fully HIGH and fully LOW at a fixed frequency (~490Hz or ~980Hz depending on the pin, on Uno/Nano), and the duty cycle (percentage of time spent HIGH) is what you're actually controlling with the 0-255 value. LEDs and motors "average out" this rapid switching into something that looks/feels analog because of thermal/mechanical inertia — an oscilloscope would show you a square wave, not a smooth voltage ramp.
Which Pins Support It
Only pins marked with ~ on the board silkscreen (Uno: 3, 5, 6, 9, 10, 11) support analogWrite() — calling it on a non-PWM pin either does nothing or behaves like digitalWrite() depending on the core, don't rely on that behavior.
PWM Frequency Matters for Motors
The default ~490Hz PWM frequency is audible — you'll hear a faint whine from motors/piezo speakers driven at this rate. For silent motor control, you either need a motor driver IC with its own higher-frequency PWM generation, or you reconfigure the ATmega's timer registers directly to raise the frequency above the audible range (~20kHz+) — this is more advanced and affects delay()/millis() timing if you touch Timer0, so use Timer1/Timer2 registers instead.
Analog Input Resolution
analogRead() gives you 10-bit resolution (0-1023) on classic AVR boards — that's about 4.9mV per step at 5V reference. Compare this to ESP32's 12-bit ADC (0-4095) for finer resolution if precision actually matters for your project (e.g. reading a precision thermistor).
ADC Reference Voltage
By default the ADC reference is the board's supply voltage (5V or 3.3V depending on board). You can switch to the internal 1.1V reference with analogReference(INTERNAL) for higher resolution when measuring small voltage ranges — useful for precision sensor work, but remember every analogRead() after that call is now scaled against 1.1V, not 5V, so update your conversion math.
Reading a Potentiometer Into a PWM Output
void loop() { int raw = analogRead(A0); // 0-1023 int pwmValue = map(raw, 0, 1023, 0, 255); // scale to PWM range analogWrite(9, pwmValue); }This single pattern — analog in, mapped, PWM out — underlies a huge fraction of real Arduino projects: dimmers, motor speed controls, fan controllers.
Related Guides
- I2C vs SPI vs UART: How to Choose and Use Serial Communication Protocols
- I2C Wiring and Protocol Guide for Arduino, ESP32, and Raspberry Pi
- How to Build a Class-D Audio Amplifier: TPA3116, Power Supply, and Speaker Matching
- How to Build a LiPo Battery Charger with the TP4056: Circuits, Safety, and BMS Integration
- How to Salvage Electronic Components from E-Waste: Desoldering, Testing, and Reuse
- How to Program Addressable LED Strips: WS2812B Patterns, Effects, and Power Design
- How to Use the ESP32-CAM: Video Streaming, Motion Detection, and Time-Lapse
- How to Do Reflow Soldering with a Hot Plate: SMD Assembly for Makers