Fixing the non linear ADC of an ESP32

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.

  1. 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.

  1. oversampling
 A0Raw  = 0;
  for  (byte n = 0; n < 5; n++)
  {
    A0Raw += analogRead(A0);
    delay (2);
  }
  A0Raw = A0Raw / 5;
  1. 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.

  1. 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!

Thanks. Nice solution.
Normally a external DAC is advised : ADS1115 16-Bit ADC - 4 Channel with Programmable Gain Amplifier : ID 1085 : $14.95 : Adafruit Industries, Unique & fun DIY electronics and kits.
In 2017 they wrote that they would fix it with calibration curves: [Answered] What are the ADC input ranges? - ESP32 Forum. Did they do that ?

One would expect that they would just fix the faulty design and make it work

Using all these methods, I could get a precision of 0.2% over the full range of measurements.

Very interesting @RIN67630; did you find any difference changing to another ESP32?

I agree the ADC is appalling and generally not worth using.

Your "solution" does however impact the sampling time - 2Msps natively, but the 100k/0.1u RC filter gives a time constant of 10msec.

Also the datasheet gives DNL of +-7LSD and INL of +_12LSB; and the ADC still needs calibration

The esp_adc_cal/include/esp_adc_cal.h API provides functions to correct for differences in measured voltages caused by variation of ADC reference voltages (Vref) between chips. Per design the ADC reference voltage is 1100 mV, however the true reference voltage can range from 1000 mV to 1200 mV amongst different ESP32s.

johnerrington:
Very interesting @RIN67630; did you find any difference changing to another ESP32?

Your "solution" does however impact the sampling time - 2Msps natively, but the 100k/0.1u RC filter gives a time constant of 10msec.

Changing ESP changed the results as well. Anyhow I am using a two point calibration routine.

And, yes the workaround is only for slow changing values, the necessary software multisampling/averaging makes it even slower.

Koepel:
...
In 2017 they wrote that they would fix it with calibration curves: [Answered] What are the ADC input ranges? - ESP32 Forum. Did they do that ?

They can't.
The missing first 0.2V at the beginning of the range cannot be fixed by firmware: you need hardware circuitry upfront.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.