This article shows how to read analog inputs with the ESP32 using Arduino IDE. Analog reading is useful to read values from variable resistors like potentiometers, or analog sensors.
Reading analog inputs with the ESP32 is as easy as using the analogRead(GPIO) function, that accepts as argument, the GPIO you want to read.
We also have other tutorials on how to use analog pins with ESP board:
- ESP8266 ADC – Read Analog Values with Arduino IDE, MicroPython and Lua
- ESP32 Analog Readings with MicroPython
Watch the Video
You can watch the video tutorial or keep reading this page for the written instructions.
Analog Inputs (ADC)
Reading an analog value with the ESP32 means you can measure varying voltage levels between 0 V and 3.3 V.
The voltage measured is then assigned to a value between 0 and 4095, in which 0 V corresponds to 0, and 3.3 V corresponds to 4095. Any voltage between 0 V and 3.3 V will be given the corresponding value in between.
ADC is Non-linear
Ideally, you would expect a linear behavior when using the ESP32 ADC pins. However, that doesn’t happen. What you’ll get is a behavior as shown in the following chart:
This behavior means that your ESP32 is not able to distinguish 3.3 V from 3.2 V. You’ll get the same value for both voltages: 4095.
The same happens for very low voltage values: for 0 V and 0.1 V you’ll get the same value: 0. You need to keep this in mind when using the ESP32 ADC pins.
There’s a discussion on GitHub about this subject.
analogRead() Function
Reading an analog input with the ESP32 using the Arduino IDE is as simple as using the analogRead() function. It accepts as argument, the GPIO you want to read:
analogRead(GPIO);
The ESP32 supports measurements in 18 different channels. Only 15 are available in the DEVKIT V1 DOIT board (version with 30 GPIOs).
Grab your ESP32 board pinout and locate the ADC pins. These are highlighted with a red border in the figure below.
Learn more about the ESP32 GPIOs: ESP32 Pinout Reference.
These analog input pins have 12-bit resolution. This means that when you read an analog input, its range may vary from 0 to 4095.
Note: ADC2 pins cannot be used when Wi-Fi is used. So, if you’re using Wi-Fi and you’re having trouble getting the value from an ADC2 GPIO, you may consider using an ADC1 GPIO instead, that should solve your problem.
Other Useful Functions
There are other more advanced functions to use with the ADC pins that can be useful in other projects.
- analogReadResolution(resolution): set the sample bits and resolution. It can be a value between 9 (0 – 511) and 12 bits (0 – 4095). Default is 12-bit resolution.
- analogSetWidth(width): set the sample bits and resolution. It can be a value between 9 (0 – 511) and 12 bits (0 – 4095). Default is 12-bit resolution.
- analogSetCycles(cycles): set the number of cycles per sample. Default is 8. Range: 1 to 255.
- analogSetSamples(samples): set the number of samples in the range. Default is 1 sample. It has an effect of increasing sensitivity.
- analogSetClockDiv(attenuation): set the divider for the ADC clock. Default is 1. Range: 1 to 255.
- analogSetAttenuation(attenuation): sets the input attenuation for all ADC pins. Default is ADC_11db. Accepted values:
- ADC_0db: sets no attenuation. ADC can measure up to approximately 800 mV (1V input = ADC reading of 1088).
- ADC_2_5db: The input voltage of ADC will be attenuated, extending the range of measurement to up to approx. 1100 mV. (1V input = ADC reading of 3722).
- ADC_6db: The input voltage of ADC will be attenuated, extending the range of measurement to up to approx. 1350 mV. (1V input = ADC reading of 3033).
- ADC_11db: The input voltage of ADC will be attenuated, extending the range of measurement to up to approx. 2600 mV. (1V input = ADC reading of 1575).
- analogSetPinAttenuation(pin, attenuation): sets the input attenuation for the specified pin. The default is ADC_11db. Attenuation values are the same from previous function.
- adcAttachPin(pin): Attach a pin to ADC (also clears any other analog mode that could be on). Returns TRUE or FALSE result.
- adcStart(pin), adcBusy(pin) and resultadcEnd(pin): starts an ADC convertion on attached pin’s bus. Check if conversion on the pin’s ADC bus is currently running (returns TRUE or FALSE). Get the result of the conversion: returns 16-bit integer.
There is a very good video explaining these functions that you can watch here.
Read Analog Values from a Potentiometer with ESP32
To see how everything ties together, we’ll make a simple example to read an analog value from a potentiometer.
For this example, you need the following parts:
You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!
Schematic
Wire a potentiometer to your ESP32. The potentiometer middle pin should be connected to GPIO 34. You can use the following schematic diagram as a reference.
Code
We’ll program the ESP32 using Arduino IDE, so make sure you have the ESP32 add-on installed before proceeding:
- Windows instructions – ESP32 Board in Arduino IDE
- Mac and Linux instructions – ESP32 Board in Arduino IDE
Open your Arduino IDE and copy the following code.
// Potentiometer is connected to GPIO 34 (Analog ADC1_CH6)
const int potPin = 34;
// variable for storing the potentiometer value
int potValue = 0;
void setup() {
Serial.begin(115200);
delay(1000);
}
void loop() {
// Reading potentiometer value
potValue = analogRead(potPin);
Serial.println(potValue);
delay(500);
}
This code simply reads the values from the potentiometer and prints those values in the Serial Monitor.
In the code, you start by defining the GPIO the potentiometer is connected to. In this example, GPIO 34.
const int potPin = 34;
In the setup(), initialize a serial communication at a baud rate of 115200.
Serial.begin(115200);
In the loop(), use the analogRead()function to read the analog input from the potPin.
potValue = analogRead(potPin);
Finally, print the values read from the potentiometer in the serial monitor.
Serial.println(potValue);
Upload the code provided to your ESP32. Make sure you have the right board and COM port selected in the Tools menu.
Testing the Example
After uploading the code and pressing the ESP32 reset button, open the Serial Monitor at a baud rate of 115200. Rotate the potentiometer and see the values changing.
The maximum value you’ll get is 4095 and the minimum value is 0.
Wrapping Up
In this article you’ve learned how to read analog inputs using the ESP32 with the Arduino IDE. In summary:
- The ESP32 DEVKIT V1 DOIT board (version with 30 pins) has 15 ADC pins you can use to read analog inputs.
- These pins have a resolution of 12 bits, which means you can get values from 0 to 4095.
- To read a value in the Arduino IDE, you simply use the analogRead() function.
- The ESP32 ADC pins don’t have a linear behavior. You’ll probably won’t be able to distinguish between 0 and 0.1V, or between 3.2 and 3.3V. You need to keep that in mind when using the ADC pins.
We hope you’ve find this short guide useful. If you want to learn more about the ESP32, enroll in our course: Learn ESP32 with Arduino IDE.
Other ESP32 guides that you may also like:
- ESP32 OLED Display with Arduino IDE
- ESP32 with DHT Temperature and Humidity Sensor using Arduino IDE
- ESP32 Web Server with DHT readings
- 20+ ESP32 Projects and Tutorials
Thanks for reading.
Thanks Rui for another very useful artical. I found the Other Useful Functions particularly useful.
Please check the analogSetWidth(width) they seems to be a typo and I am interested in what this does.
Please explain more the difference between Width, Cycles and Samples. I am sure samples is the number of times a reading is taken, but is the reading returned as an average? What is the limit and do you have a suggested maximum for this?
Also, is the analogSetAttenuation(attenuation) default set to ADC_11db? I am trying to understand this, because if 1V input = ADC reading of 3959 what would happen with a input of 3.3V. My experience says the default must be ADC_0db.
One of the greatest problems with the ESP32 is it may have 18 ADC inputs, but if you use WiFi you cannot use any of the 10 inputs that direct to ADC2.
Yes 10 inputs cannot be used!
What were Espressif thinking when they built the next best IoT device and knobling over half the ADC inputs if you used the typical way to connect to the Internet!
If you desperately need more analog inputs, try disconnecting from the WiFi, using the pins, then reconnecting and publishing any results.
Otherwise, it might be better to consider a simpler WiFi controller and some supporting ADC chips.
If you would like a more linear ADC behavior, you can check my Bitbucket repository at:
bitbucket.org/Blackneron/esp32_adc/
I tried to make an adjustment function for the ADCs of the ESP32.
That’s great! Awesome job.
Thank you so much for sharing. It will certainly be useful for our readers.
Regards,
Sara
thank you!
Thank you! I will do your steps yet but I was wondering why when I receive 1.72V it interprets as 1.51V, and 370mV as 290mV, when all the inputs were tested in laboratory before…
Sara Santos : i’d like to question, I have an error when trying the df-robot tds sensor using the esp32 board, the sensor reading results are zero, the ADC pin used is gpio 34, power5v. I have tried to replace it with another ADC pin but still the results of the tds readings are zero, please advise
Hi.
I’ve never tried a TDS sensor with the ESP32.
But that GPIO should work.
Measure with a multimeter to see if the sensor voltage output is actually changing. This way, you know that the problem is not the sensor.
Regards,
Sara
ok thankyou so much
Hello Sara,
Minor typo on your function summary table:
“resultadcEnd(pin)” -> “adcEnd(pin)” to get the result (or similar).
Otherwise it looks like the function name is “resultadcEnd”.
Hi Rui & co,
Could you advise the best pin(s) to use to get analog readings from the 36pin do-it ESP32?
I am using pin 39 now although I get a constant 4096 from an ldr.
I don’t know what I’d do without Random Nerd Tutorials
David
Hi David.
Try GPIO32 and GPIO 33.
Regards,
Sara
Excellent advice as always. Thank you. I shall modify my pcb.
David
Thnx Sara,
Would using adc2 stop NOW from working reliably? Also does it particularly matter which is assigned first in a sketch – ADC pins, WiFi or NOW?
thank you
David
Hi.
I haven’t tested ADC with ESP-NOW, so I’m not sure.
ESP-NOW doesn’t use Wi-Fi, but it uses the Wi-Fi library, so I don’t know if it conflicts.
Experiment and see what you get.
As for your second question, it doesn’t matter the order as long as the variables are defined before they are called.
Regards,
Sara
My understanding of the ADC2 conflict with WiFi is nothing to do with software, but with internal power routing to the WiFi radio requires power to be diverted from the ADC2 peripheral.
Once you turn the radio on, the power to ADC2 is robbed, and as a result all readings become unreliable.
That’s right.
Thanks for the insight.
Regards,
Sara
Hi, thank you for the great tutorial!
May i know where can i use esp32 simulator as in tinkercad or something like the schematic diagram you shown above?
Hope you have a good day, thank you in advance!
Hi,
Your tutorial on using ESP32 to send digital sensor readings (temperature) to android via bluetooth is perfect. As someone new to the ESP32, I would never have sorted that out on my own. May I ask if you have a tutorial or could recommend one showing how to send a live graph of analog input from the ESP32 to Android device?
Thanks,
Mike
Hi Mike.
We have this tutorial: https://randomnerdtutorials.com/esp32-esp8266-plot-chart-web-server/
You just need to replace the sensor readings with the analog values.
I hope this helps.
Regards,
Sara
HI All
I found this article in Espressif Tech. Ref. Manual that helps calibrate the ADC so its linear 11 db. attenuation mode. Building a lookup table inside the esp32 for this correction is the way to go. I hope this helps.
CONFIG_ADC_CAL_LUT_ENABLE
Use Lookup Tables
Found in: Component config > ADC-Calibration
This option will allow the ADC calibration component to use Lookup Tables to correct for non-linear behavior in 11db attenuation. Other attenuations do not exhibit non-linear behavior hence will not be affected by this option.
Default value:
Yes (enabled)
Thank You For Your Clear Information!
Why would one choose pin 34 vs the VP pin? We’re seeing terrible jitter on our reading of a slide potentiometer even when the slider is not moving. We’re using pin 34 currently.
Would the VP pin with it’s “pre-amp” circuitry help?
Thanks,
RK
Make sure you are reading the correct pin. For example on an Espduino, the analog pins do not conform to the UNO pin sequence A0 through A5. The pin sequence is: A12 (ADC2), A10 (ADC2), A7, A6, A0, A1. So the first pin (next to VIN) is not A0, but A12 which is ADC2 and will not work if WiFi is on. So if you are reading A0, you have to physically connect to the 5th pin (GPIO36), which is A4 on a UNO.
‘adcStart’ was not declared in this scope
Looks like maybe Espressif dropped support for those cool functions??
Hi.
Did you select an ESP32 board when compiling the code?
Regards,
Sara
Hello all,
I am trying to read 7 ADC channels, which are 4 from the ADC1 and 3 from the ADC2.
But it seems like ADC1 is not working. Because I can see correct readings from ADC2 but not from the ADC1 channels.
Why this is happening?
I forgot to mention that, I can read only GPIO 36 and 39 from ADC1 but not the others.
Hi.
What’s your ESP32 board?
Do you have other peripherals connected?
Regards,
Sara
I use ttgo t-beam v1.0. I have same problem. ADC1 not working expect 36 and 39 pins. I have two ttgo both of them have same problem
I ran into a wierd issue when testing the ADC with a 10K potentimeter. I wire the pot to GND, 3.3V and then middle lead to the ADC. When I do an Analog read from the ADC pin, I get results but they are no where near linear. If I turn the pot all the way to the left, the result drops to zero – all the way to the right the result is 4095 – as expected. However, when you rotate the pot from the left (zero position) the number increase and at about 1/3 of the way rotated it is at 4095 and stay there. I have tried this on 4 different esp32 – the most recent being the Sparkfun ESP32 Things Plus. Anyone else seen this issue with the ESP32?
that is expected if you did not modify the gain of ADC or if you did not use voltage divider
the idea is: ADC without any modification would measure from 0V to Vref – for ESP32 it is 1.1V (around). If you use 12bit the value for 0V is 0 and for 1.1V it is 4095. And for 2V it is… also 4095. The ESP32 will not be killed by applying more than 3.3V but the reading is limited to Vref.
There are 2 possible solutions:
1- use voltage divider (2 resistors) and calculate them to provide 1.1V when input voltage is 3.3V – you can use online calculator for such divider i.e. here: https://ohmslawcalculator.com/voltage-divider-calculator
2- use built in functions to extend the range of measured voltage – you will find how to in this article under the section “analogSetAttenuation”
quick question: when you measure input voltage using built in ADC then all is ok – you are measuring the voltage of input pin and it is referenced to built in Vref that is almost 1.1V
but, when I want to use analogue sensor (i.e. photo resistor or transistor or so) then I have 2 variables in case I use unstable power (i.e. battery that is discharging within the time): 1 the resistance of the element (that I will measure using ADC) but also the Vin that will completely destroy the output – consider battery gives 3.6V today but 3 days later it will be 3.3V and so on – how to sort this problem? I cannot find any pin on ESP32 that would provide stable Vref that would serve the purpose of powering the analogue element.
Thank you
Use very low dropout voltage regulator to provide stable 3.3V (Like MIC5219). That’ll keep providing the ESP with 3.3V even when there’s 4.2V in battery upto voltage drops below 3.3V.
Hai, I have a question regarding the analog pin on the esp32, if we will use the analog pin, for example on pin D13, which we write in the program is it 13 or 0 (Because the analog channel on D13 is CH0), ?
Hi.
You use the GPIO number. For example:
analogRead(13) to read GPIO 13.
Regards,
Sara
Hello good Morning,
On pin esp32, when WiFi is on I use pin D15, and pin D14 for analog input, but the sensor can’t be read, is there
D14 is ADC2_2 and D15 is ADC2_3. As clearly stated above, you can’t use ANY ADC2 pins when using wi-fi. You can only use ADC1_0 (VP), ADC1_3 (VN), ADC1_4 (D32), ADC1_5 (D33), ADC1_6 (D34) &/or ADC1_7 (D35).
Hi, great guide, very useful but the functions analogSetCycles() and analogSetSamples() are no longer supported – according to a comment I found on the ESP32 Forum, these functions are not supported from version 1.0.5 – I’m at version 1.0.6 now.
yes these are quite useful but i could not find them in arduino 2.0.1. is there a workaround?
Thanks for this really good tutorial, however, I’m a little bit confused.
At the beginning the tutorial says ” you can measure varying voltage levels between 0V and 3.3V … 3.3V corresponds to 4095″. OK. Later on the “analogSetAttenuation” function is mentioned, saying that the default attenuation is ADC_11db, which results in “extending the range of measurement to up to approx. 2600mV (1V=1575).
If I put both information together I do not understand how I can measure up to 3.3V?!
If the attenuation is actually ADC_11db 3.3 would result in 5197, which exceeds the available range. If the attenuation would be ADC_0db 3.3V would be only 3590 …
Can anyone shed some light on what I’m missing here?
Same unanswered question here.
Other comments above convey this same information. When the esp32 looks at an analog input, if attenuation is set to 0dB, then it maps the values 0-4095 to voltages 0-1100 mV. I’ve seen that the default is 0dB. To change the behavior of the esp32 one must change the attenuation. To get full range 0-4095 mapped to 0-3300 mV the attenuation should be 11 dB. By changing attenuation, I think the esp32 routes the signal through internal voltage divider resistors before reading its value.
What about esp32, adc pin protection against overvoltage ?
Any ideas ?
Hello,
I think you need voltage divider.
You have voltage divider calculator:
https://ohmslawcalculator.com/voltage-divider-calculator
Hello Rui, hello Sara,
thank you very much for all these great tutorials.
I have a question regarding the command: analogSetWidth(width)
It works fine, but what could be the technical benefit, to reduce the sample bits and resolution?
E.g., is it reducing power consumption, cycle time or memory consumption?
Thank you very much.
Äd
It turns out that it is closer to the linearity of reading analog inputs if you use an external voltage source of max 3V, when it comes to the top value, with the lowest value, a resistor can be used for the range 0 – 0.2V
Basically, the ESP32 analog inputs are useless with all these non-linearity reading issues. I have a Personal Weather Station using the Davis 6490 UV Index sensor, and the Davis 6450 Solar Radiation sensor, these sensors’ outputs are analog, guess what? All readings were wrong, so I asked Mr. Google what was the problem, bingo! I came straight to this article.
Here is how I solved this problem. I am using the ADS1115 I2C 16-Bit ADC – 4 Channels, I set it to 16-bits version, and I am able to read down to 1mV, all the way up to 3.2V in 1mV increment.
Has anyone tested how fast the analog reads can happen ?
Can audio rates (48KHz, 2 channels) or faster be expected / experienced.
Update : I can read a single analog signal 2048 samples at a rate of 20000 samples per sec.
How did you achieved this sampling rate ?
Good day. He has such a specific problem. When I connect ESP32 6 sensors to ADC1, I always get only 3 values. The next 3 values are OVF. When I use more than 3 ADC1, it is always on the fourth OVF. I’ve tried absolutely everything. It doesn’t matter which ADC1 pins you use. Don’t know what to do with it? Thank you.
good job.
how to define analogReference(INTERNAL) for esp32? does it need an analog reference?
The AtoD converter of ESP32 is like my musicality. I can tell if the music plays or not. AtoD of ESP can tell if the there is some voltage on a pin or not. It is a really botched up piece of hardware. Even with a dynamically updated lookup table corrections it drifts with temperature +/- 5%. Unusable for single ended precise measurements.
I have problems reading 40mV , it show 0, It is a minimal voltage required to the ADC to read?
Thanks.
Looks like “analogSetCycles” function has been removed from the ADC API.
I am working on hearing aid solution with esp32 and want to use INMP441 mic i2s as input and pass the data to DAC max98357A to read it on earphone. please help me
Hi.
Unfortunately, we don’t have any tutorials about those components.
Regards,
Sara
ESP32-Devkit in USE ADC pin and that show to this pin addressable and non-addressable?
I am trying to set up more then 4 Pot meters to the ADC Channels.
I keep getting cross talk and interference between all of the different potentiometers. Can you do a guide for dealing with this?
I know i have a problem with capacitors but this would be helpful!
I am getting huge noise problems with the A/D inputs on the Wroom ESP32 revc part. I have a 10K pot, connected to gnd and 3.3v pins. wiper is connected to A6 (34), although it doesn’t seem to matter which port I use. If I run in a loop sampling the port everything seems to work properly, but if I don’t touch the pot knob, my readings are all over the place.. usually within 15-20 but sometimes jumps of over 200. I’ve put .1uF cap on analog pin, tried filter cap on 3.3v supply, fiddled with attenuation, makes no difference. Same configuration on Uno is rock solid. This happens with all three processors I tried. Any ideas?
Have you tried turning off the WiFi/Bluetooth radio while sampling?
Can you comment on analogReadMilliVolts() vs analogRead()? I am interested in using the former, knowing that max V is 2.865 but is a lot more precise. Thanks!