Arduino ADC and analogRead() Explained: Read Analog Sensors Accurately
Most Arduino sensors, such as potentiometers, light-dependent resistors, analog temperature sensors and microphone modules, output a voltage that changes continuously. The analogRead() function converts that voltage into a number that a sketch can store, compare and display. This tutorial explains how the ADC behind analogRead() works, how to convert the result back to volts, how the reference voltage changes the result and how fast the converter can sample.
Two complete examples follow the theory. The first reads a potentiometer and prints the raw value and the voltage to the Serial Monitor, and the second uses the same reading to set the brightness of an LED. The figures in this guide describe the ATmega328P used on the Arduino UNO and Nano unless a section names another board.
This guide belongs to the Arduino Core Tutorials series. Readers who are new to Arduino input and output can first read the Arduino UNO pinout guide, the digitalWrite() and digitalRead() tutorial and the PWM and analogWrite() tutorial, because the second example combines analogRead() with PWM output. Both examples print their results through Arduino UART serial communication, which has its own tutorial.
Table of contents
- Arduino ADC and analogRead() Explained: Read Analog Sensors Accurately
- How the Arduino ADC Works
- Arduino ADC Reference Voltage
- Arduino analogRead() Function
- Arduino ADC Speed and Sampling Rate
- Arduino ADC Prescaler Settings
- Arduino Analog Input Wiring
- Arduino analogRead() Potentiometer Example
- Arduino LED Brightness Control with analogRead()
- Arduino ADC Accuracy and Noise Tips
- Arduino Analog Input Pinout Table
- Conclusion
How the Arduino ADC Works
The ADC, or analog-to-digital converter, is a peripheral inside the microcontroller that measures a voltage and returns it as a whole number. An analog signal can take any value in a range such as 0 V to 5 V, while the processor can only work with integers. The ADC divides the measurable range into equal steps and reports the step that contains the input voltage.
Why the Arduino Needs an ADC
Many common sensors report their measurement as a voltage, and the ADC makes that voltage available to the sketch.
- A temperature sensor with an analog output produces a voltage that changes with temperature.
- A light-dependent resistor (LDR) changes its resistance with light, and a voltage divider turns that change into a voltage.
- A potentiometer outputs a voltage that depends on the position of its wiper.
The ADC turns each of these voltages into a number that the sketch can use in calculations, show on a display or use to control an output such as an LED.
How Voltage Becomes a Number
The ADC compares the voltage on the analog pin with the reference voltage (VREF) and returns a value between 0 and 1023 on a 10-bit converter such as the one in the Arduino UNO. With a 5 V reference, an input of 0 V gives 0, an input of 2.5 V gives 512 and an input at or above 5 V gives 1023.
10-Bit Resolution on the Arduino UNO
The Arduino UNO uses the ATmega328P microcontroller, which contains a 10-bit ADC and six analog input pins named A0 to A5. A 10-bit converter has 2^10 = 1024 possible results, so it divides the reference voltage into 1024 steps. The size of one step is the smallest change in input voltage that the converter can represent.
Step size = VREF / 1024 = 5 V / 1024 = 4.88 mV
The real accuracy is lower than the step size. The ATmega328P datasheet specifies an absolute accuracy of about ±2 steps under its test conditions, which is roughly ±10 mV with a 5 V reference, so the last digit or two of a reading should be treated as noise.
ADC Conversion Formula
The datasheet defines the result of a conversion with the following formula, where Vin is the voltage on the pin.
ADC = \frac{V_{in} \times 1024}{V_{REF}}
The highest possible result is 1023, which the datasheet describes as the reference voltage minus one step, so any input at or above that level reads 1023. The table shows the result for a 5 V reference.
| Input voltage | Formula result | analogRead() value |
|---|---|---|
| 0 V | 0 | 0 |
| 1.25 V | 256 | 256 |
| 2.5 V | 512 | 512 |
| 3.75 V | 768 | 768 |
| 5 V | 1024 | 1023 |
In practice a pin held at exactly 2.5 V may read 511 or 512 because of noise and the accuracy limit described above. Many tutorials, including the official Arduino examples, divide by 1023.0 instead of 1024.0 when they convert a reading to volts. The two versions differ by at most one step, which is about 0.1 %, and the 1023 version makes a full-scale reading print as exactly 5.00 V. This guide uses the datasheet formula with 1024 so that the voltage conversion matches the step size.
Arduino ADC Reference Voltage
The reference voltage (VREF) is the input level at which the ADC reaches its full-scale value, so it sets both the measurable range and the size of each step. A well-chosen reference improves the resolution for small signals, and an unstable one moves every reading.
Default, Internal and External References
By default the ADC uses the board supply as its reference, which is 5 V on the UNO, Nano and Mega and 3.3 V on boards such as the Due and the MKR series. The AVR-based boards offer three reference options through the analogReference() function.
| Option | Reference voltage | Notes |
|---|---|---|
| DEFAULT | Board supply, 5 V or 3.3 V | Follows the supply, so a USB or regulator drift changes every reading. |
| INTERNAL | 1.1 V on ATmega328P boards such as the UNO and Nano, 2.56 V on the ATmega32U4 used by the Leonardo | Not available on the Mega, which uses INTERNAL1V1 and INTERNAL2V56 instead. |
| EXTERNAL | Voltage applied to the AREF pin | Needs a stable, clean reference source. |
The internal reference does not depend on the supply voltage, but its exact value differs from chip to chip, and the ATmega328P datasheet allows anything from 1.0 V to 1.2 V around the typical 1.1 V. Boards outside the AVR family use other constants, for example the MKR series uses names that start with AR_, and the Due supports only its default 3.3 V reference, so the board’s analogReference() documentation should be checked before the function is used on those boards.
Change the Reference with analogReference()
The analogReference() function selects the reference that the ADC uses for the readings that follow.
analogReference(type);
analogReference(INTERNAL); // 1.1 V reference on the UNO and Nano
analogReference(DEFAULT); // board supply voltage
analogReference(EXTERNAL); // voltage applied to the AREF pin
When the external reference is used, analogReference(EXTERNAL) must run in setup() before the first analogRead(). If a voltage is applied to AREF while the chip still uses its internal reference, the two sources are connected together and the microcontroller can be damaged. The Arduino documentation allows 0 V to 5 V on AREF, while the ATmega328P datasheet specifies a minimum of 2.0 V and a maximum equal to the supply voltage, so the external reference should stay inside that narrower range.
The Arduino documentation also notes that the first few readings after a change of reference can be inaccurate, so a sketch should discard a few readings after it calls analogReference().
How the Reference Voltage Changes Resolution
A lower reference gives smaller steps, which helps when a sensor never produces more than a small voltage.
| Reference | Step size (VREF / 1024) | Input range |
|---|---|---|
| 5 V | 4.88 mV | 0 V to 5 V |
| 3.3 V | 3.22 mV | 0 V to 3.3 V |
| 1.1 V | 1.07 mV | 0 V to 1.1 V |
Any input at or above the reference reads 1023. The analog pin itself can still be driven up to the supply voltage when the 1.1 V reference is selected, but every reading above 1.1 V is clipped at 1023, so a signal that rises above that level needs a voltage divider before it reaches the pin.
Arduino analogRead() Function
The analogRead() function measures the voltage on one analog pin and returns the converted value as an integer. It is the function used in almost every sketch that works with a sensor or a variable input.
Syntax and Parameters
The function takes one parameter, which is the analog pin to read, and returns an int.
int value = analogRead(pin);
The pin can be written as A0, A1 and so on, or as the channel number 0, 1 and so on. The following line reads pin A0 and stores the result in sensorValue.
int sensorValue = analogRead(A0); // 0 to 1023 on a 10-bit board
Return Values and Voltage Conversion
On boards with a 10-bit ADC, such as the UNO, Nano, Mega and Leonardo, the function returns a value from 0 to 1023. The Due and the MKR series have a 12-bit ADC, but their default read resolution stays at 10 bits for compatibility with AVR boards, so the result reaches 4095 only after analogReadResolution(12) has been called in setup().
The voltage on the pin follows from the returned value, the reference voltage and the number of bits n of the active resolution.
V_{in} = \frac{\text{analogRead value} \times V_{REF}}{2^{n}}
On an UNO with the default 5 V reference the conversion looks like this.
float voltage = sensorValue * 5.0 / 1024.0; // volts
The constants must be written as floating-point numbers such as 5.0 and 1024.0. With integer constants the division is performed in integer arithmetic, which discards the fractional part and reduces the result to a whole number of volts. On a 12-bit board with a 3.3 V reference, the same line uses 3.3 and 4096.0.
analogRead() Timing
A single analogRead() call on the UNO takes about 104 microseconds with the default settings, which allows roughly 10,000 readings per second. The next two sections explain where that figure comes from and how to change it.
Arduino ADC Speed and Sampling Rate
On the ATmega328P a standard conversion takes 13 ADC clock cycles, and the default ADC clock of 125 kHz makes one analogRead() last about 104 microseconds. The speed matters for signal monitoring and for any sketch that samples an input at a fixed interval.
Default ADC Clock and Conversion Time
On 16 MHz boards the Arduino core sets the ADC prescaler to 128, so the ADC clock is the 16 MHz system clock divided by 128. The ATmega328P datasheet asks for an ADC clock between 50 kHz and 200 kHz when the full 10-bit resolution is needed, and 125 kHz sits inside that range.
ADC clock = 16 MHz / 128 = 125 kHz
Conversion time = 13 / 125 kHz = 104 µs
Sampling Rate
The maximum sampling rate is the inverse of the conversion time.
Sampling rate = 1 / 104 µs = about 9,600 samples per second
The Arduino documentation quotes a maximum of about 10,000 readings per second for the UNO. In a sketch that prints every value, the serial output usually limits the loop rate far more than the ADC does. A sketch that must sample at a fixed interval can use the timing methods in the FreeRTOS vTaskDelay and vTaskDelayUntil tutorial.
Arduino ADC Prescaler Settings
The ADC prescaler divides the 16 MHz system clock to produce the ADC clock, and a smaller prescaler gives faster conversions at the cost of accuracy. The ATmega328P accepts the prescaler values 2, 4, 8, 16, 32, 64 and 128, and the table lists the four that matter for a 16 MHz board.
| Prescaler | ADPS2:0 bits | ADC clock | Conversion time | Samples per second | Accuracy |
|---|---|---|---|---|---|
| 128 (default) | 111 | 125 kHz | 104 µs | About 9,600 | Inside the 50 to 200 kHz range |
| 64 | 110 | 250 kHz | 52 µs | About 19,200 | Above 200 kHz, resolution drops |
| 32 | 101 | 500 kHz | 26 µs | About 38,500 | Above 200 kHz, resolution drops further |
| 16 | 100 | 1 MHz | 13 µs | About 76,900 | Well above 200 kHz, clearly reduced |
No power-of-two prescaler gives exactly 200 kHz on a 16 MHz board, so the practical choice is between 125 kHz and 250 kHz. Above 200 kHz the datasheet no longer guarantees 10-bit resolution, which means the lower bits of each reading become unreliable. The datasheet also states that a lower resolution is acceptable in return for a higher sample rate, so applications that need only 8 significant bits can run the ADC faster.
The prescaler is set in the ADCSRA register. The following sketch is for ATmega328P boards such as the UNO and Nano, and it measures the time of one reading so that the effect of the prescaler is visible. The micros() function has a resolution of 4 µs on a 16 MHz board, so the printed time is approximate.
// Set the ADC prescaler to 64 on an ATmega328P board (UNO, Nano).
// ADPS2:ADPS0 = 110 gives an ADC clock of 16 MHz / 64 = 250 kHz.
void setup() {
Serial.begin(9600);
ADCSRA = (ADCSRA & 0xF8) | 0x06; // clear the three ADPS bits, then write 110
}
void loop() {
unsigned long start = micros();
int value = analogRead(A0); // one conversion
unsigned long elapsed = micros() - start; // time of the conversion in µs
Serial.print("Value: ");
Serial.print(value);
Serial.print(" Conversion time: ");
Serial.print(elapsed);
Serial.println(" us");
delay(200);
}Writing 0x07 instead of 0x06 restores the default prescaler of 128.
Arduino Analog Input Wiring
Any device that outputs a voltage between 0 V and the reference voltage can be connected to an Arduino analog input. Typical examples are potentiometers, LDR dividers, sound sensors, flex sensors and analog temperature sensors. The GP2Y0A41SK0F distance sensor tutorial and the IR sensor Arduino tutorial show two modules that use an analog output.
Connect a Sensor to an Analog Pin
The connection follows the same three steps for every analog sensor.
- Connect the VCC pin of the sensor to the 5V pin on a 5 V board or to the 3.3V pin on a 3.3 V board.
- Connect the GND pin of the sensor to a GND pin on the Arduino.
- Connect the output pin of the sensor to an analog input such as A0.
After the wiring is complete, analogRead() returns a number that represents the output voltage of the sensor.
Safe Input Voltage Range
A pin gives a valid reading when its voltage lies between 0 V and the reference voltage, and the pin must never be driven above the supply voltage. The ATmega328P datasheet lists an absolute maximum of the supply voltage plus 0.5 V on any pin, so a 5 V board tolerates at most 5.5 V, and sensors with a higher output level need a voltage divider. The analog pins of 3.3 V boards such as the Due and the MKR series are not 5 V tolerant.
Source Impedance
The datasheet states that the ADC is optimized for signal sources with an output impedance of about 10 kΩ or less, because the converter charges a small internal sample-and-hold capacitor during each conversion. A 10 kΩ potentiometer meets this condition. A divider built from large resistors can produce unstable readings and may need an op-amp buffer or a small capacitor across the lower resistor.
Arduino analogRead() Potentiometer Example
The first example reads a potentiometer on A0, converts the reading to volts and prints both values to the Serial Monitor.
Potentiometer Wiring
Connect the middle pin of the potentiometer, which is the wiper, to A0. Connect one outer pin to 5V and the other outer pin to GND.
Potentiometer Code
const uint8_t sensorPin = A0; // potentiometer wiper
const float vRef = 5.0; // reference voltage in volts (DEFAULT on a 5 V board)
void setup() {
Serial.begin(9600);
while (!Serial && millis() < 3000) {} // wait for USB serial on Leonardo-type boards
Serial.println("Arduino ADC Reading Example");
}
void loop() {
int sensorValue = analogRead(sensorPin); // 0 to 1023
float voltage = sensorValue * vRef / 1024.0; // Vin = ADC x VREF / 1024
Serial.print("Raw ADC Value: ");
Serial.print(sensorValue);
Serial.print(" Voltage: ");
Serial.print(voltage, 2); // two decimal places
Serial.println(" V");
// A reading at either end of the range means the input is at the ADC limit.
if (sensorValue == 0 || sensorValue == 1023) {
Serial.println("Note: input is at the limit of the ADC range.");
}
delay(100);
}The analogRead() call returns the raw value, and the next line scales it to volts with the datasheet formula. The Serial.print() calls send both numbers to the Serial Monitor at 9600 baud. The limit check prints a note when the reading reaches 0 or 1023, which also happens when the potentiometer is turned fully to one end, so the note can be removed once the wiring has been confirmed.
Serial Monitor Output
Open the Serial Monitor at 9600 baud and turn the potentiometer from one end to the other. The raw value moves between 0 and 1023 while the voltage moves between 0 V and about 5 V.
To show the reading on a display instead of the Serial Monitor, the I2C LCD1602 Arduino tutorial and the SSD1306 Arduino OLED tutorial explain the wiring and the code.
Arduino LED Brightness Control with analogRead()
The second example reads the same potentiometer and uses the value to set the brightness of an LED with analogWrite(). The combination of the two functions is the basis of many analog control projects.
LED Wiring
Keep the potentiometer connected to A0 as in the first example. Connect the anode of the LED through a 220 Ω resistor to pin D9 and connect the cathode to GND. Pin 9 supports PWM on the UNO, Nano and Mega.
With a 5 V output and a typical red LED, the 220 Ω resistor limits the current to roughly 10 mA to 15 mA. That value stays below the 20 mA per pin that Arduino recommends for the UNO, and the ATmega328P datasheet gives 40 mA as the absolute maximum.
LED Code
const uint8_t potPin = A0; // potentiometer on A0
const uint8_t ledPin = 9; // LED on pin 9 (PWM-capable on the UNO, Nano and Mega)
void setup() {
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
while (!Serial && millis() < 3000) {} // wait for USB serial on Leonardo-type boards
Serial.println("LED Brightness Control");
}
void loop() {
int potValue = analogRead(potPin); // 0 to 1023
int ledValue = map(potValue, 0, 1023, 0, 255); // scale to the 8-bit PWM range
analogWrite(ledPin, ledValue); // set the LED brightness
Serial.print("Potentiometer: ");
Serial.print(potValue);
Serial.print(" LED Brightness (PWM): ");
Serial.println(ledValue);
delay(100);
}The potentiometer works as a variable voltage divider that produces 0 V to 5 V at the wiper, and analogRead() converts that voltage to a value between 0 and 1023. The map() function rescales the value to the 0 to 255 range that analogWrite() accepts, and analogWrite() sets the duty cycle of the PWM signal on pin 9. On the UNO, pin 9 is driven by Timer1 at about 490 Hz, and the PWM and analogWrite() tutorial explains the timer frequencies and duty cycle in detail.
LED Output
Turning the potentiometer from one end to the other takes the LED from off to full brightness. The Serial Monitor shows the raw reading together with the PWM value that corresponds to it.

Arduino ADC Accuracy and Noise Tips
Three habits give the largest improvement in ADC accuracy, which are a stable reference, short signal wiring and averaging. Noise, supply drift and an unsuitable reference setting are the usual causes of readings that do not match the real voltage.
Reduce Noise on Analog Inputs
Electrical noise from motors, relays and nearby switching circuits adds jitter to the readings. The following measures reduce it.
- Keep the analog signal wires short and route them away from motor, relay and other high-current wiring.
- Place a 0.1 µF capacitor between the analog input and GND close to the pin, because together with the source resistance it forms a low-pass filter that removes fast noise.
- Use shielded cable for sensors that sit far from the board.
- Avoid switching digital outputs on the pins A0 to A5 while a conversion is running, because the datasheet warns that such switching disturbs the result.
Choose a Stable Reference
The DEFAULT reference suits sensors whose output scales with the same supply voltage, such as a potentiometer, because the supply drift cancels out of the ratio. A USB supply can vary by a few tenths of a volt, and that variation moves every reading of a sensor with a fixed output level by the same proportion. The INTERNAL or EXTERNAL reference is the better choice when the sensor output does not depend on the supply or when the absolute voltage matters.
Average Several Readings
Averaging smooths random noise, and the function below returns the average of a chosen number of readings. It returns -1 when the number of samples is zero, so the caller can detect an invalid request.
const uint8_t sensorPin = A0;
// Returns the average of 'samples' readings, or -1 if 'samples' is zero.
int readAverage(uint8_t pin, uint8_t samples) {
if (samples == 0) {
return -1; // invalid request
}
long sum = 0;
for (uint8_t i = 0; i < samples; i++) {
sum += analogRead(pin); // 255 readings of 1023 still fit in a long
}
return (int)(sum / samples);
}
void setup() {
Serial.begin(9600);
}
void loop() {
int average = readAverage(sensorPin, 16); // 16 readings take about 1.7 ms
Serial.println(average);
delay(200);
}Averaging 16 readings reduces random noise by a factor of about 4, but it does not remove a constant offset or a drift of the supply voltage. The averaged values can be written to an SD card with the SD card Arduino SPI tutorial, and a program that samples in one FreeRTOS task can pass the results to another task with the FreeRTOS queues tutorial.
Arduino Analog Input Pinout Table
The table lists the analog inputs, ADC resolution and default reference voltage of the common Arduino boards.
| Board | Analog input pins | ADC resolution | Default reference | Maximum pin voltage |
|---|---|---|---|---|
| Arduino UNO | A0 to A5 (6 pins) | 10-bit | 5 V | 5 V supply |
| Arduino Nano | A0 to A7 (8 pins) | 10-bit | 5 V | 5 V supply |
| Arduino Mega | A0 to A15 (16 pins) | 10-bit | 5 V | 5 V supply |
| Arduino Leonardo | A0 to A11 (12 pins) | 10-bit | 5 V | 5 V supply |
| Arduino Due | A0 to A11 (12 pins) | 12-bit with analogReadResolution(12), 10-bit by default | 3.3 V | 3.3 V |
| Arduino MKR series | A0 to A6 (7 pins) | 12-bit with analogReadResolution(12), 10-bit by default | 3.3 V | 3.3 V |
On the UNO the pins A0 to A5 can also work as digital pins 14 to 19. The pins A6 and A7 of the Nano are analog-only and cannot be used as digital pins. On the Leonardo, the inputs A6 to A11 share their pins with the digital pins D4, D6, D8, D9, D10 and D12. The Arduino UNO pinout guide and the Arduino Nano pinout guide show the full pin layout of each board.
Conclusion
The ADC in the ATmega328P compares the pin voltage with the reference voltage and returns a number from 0 to 1023, and each analogRead() call takes about 104 microseconds with the default settings. Knowing the conversion formula, the step size and the reference options makes it possible to predict the value for any input voltage and to choose a reference that suits the sensor.
The two settings that matter most for accuracy are the reference voltage and the noise control. A reference that does not match the sensor range compresses or clips the readings, while noise from motors, relays and long wires adds jitter that averaging reduces and that a small capacitor at the pin reduces further.
The PWM and analogWrite() tutorial is the natural next step, because the LED example in this guide already combines the two functions and the PWM tutorial covers frequency and multi-channel control in full. Projects that send sensor data to a computer or another board can continue with the Arduino UART tutorial. A reading that has to trigger an action immediately, without waiting for the next pass through loop(), is better handled with Arduino external interrupts.
Download Project Here
Download the CubeMX project files and HAL source code — all tested on real hardware.
If you find this useful, you can support us with a Donation as well.
Browse More Arduino Core Tutorials
Arun is an embedded systems engineer with 10+ years of experience in STM32, ESP32, and AVR microcontrollers. He created ControllersTech to share practical tutorials on embedded software, HAL drivers, RTOS, and hardware design — grounded in real industrial automation experience.
Recommended Tools
Essential dev tools
Categories
Browse by platform






