The ADC of the ESP32 has a – fully justified – bad reputation.
- The advertised 12 bits are practically 9 bit +3 bit noise.
- The V/ADC relation is not linear
- The first 0.21V of the input (by 11dB attenuation) are ignored
I am proposing here a solution to get an acceptable result despite of all that drawbacks.
- usefull settings:
// Settings for ADC
analogSetWidth(11); // 11Bit resolution
analogSetAttenuation(ADC_0db);
11 bit or 10 bit is much more realistic
It is the attenuator that is non-linear, the best is to switch it off and to use a resistor divider.
- oversampling
A0Raw = 0;
for (byte n = 0; n < 5; n++)
{
A0Raw += analogRead(A0);
delay (2);
}
A0Raw = A0Raw / 5;
- input circuitry:
-Use a resistive voltage divider and a decoupling capacitor c2 to stabilize the input to the ADC.
as well as
-Feed a minimal voltage supplement of ~0.07V into the ADC to get rid if the ADC offset:
In my circuit I am sourcing the feed from 5V, (which has for me the best stability) eliminating potential ripple through R4,C1 and injecting a small residual current into the ADC through R3.
Shouldn't you have a stable 5V, you might use 3,3V as well, in that case the value of R3 is 390K instead of 560K.
Without any input you should measure ~ 0.07V at the input of the ADC, which just slightly overcompensates the missing beginning of the ADC measurement.
- final adjustment:
float adc2volt(unsigned int adc)
{
float volt = map(pwm, ADC0V, 2047, 0, VOLT_MAX);
volt = volt / 1000;
return volt;
}
Whereas ADC0V is the value of the ADC without any input (for me, it was 47) and VOLT_MAX the voltage when the ADC shows 2047.
Using all these methods, I could get a precision of 0.2% over the full range of measurements.
Enjoy!