Using the internal ADC
Posted: Sun Feb 17, 2019 10:52 am
It is well known that the internal ADC of the ESP8266 is not brilliant but still usable in some less demanding applications. Theoretically it is a 10 bit converter but normal use gives much more variable results than one would expect.
I have done some initial measurements to characterise it a bit more with a view to optimising its use.
Test set up was an ESP-12F (i.e. raw Analog input) programmed under Arduino. I set up a voltage source giving a reading of about 940 and tested with different resistors in series between the source and the ADC pin; 15K, 115K, 375K, 1M, 10M.
Sketch was in a 30mSec loop and in each iteration it took 17 ADC measurements in a fast internal loop. iteration. The loop calculated a simple average and a moving average. I captured the raw data and the averages of about 300 loops for each resistor value and analysed them in excel.
Here Ave0 is the average value for the first measurement in each loop, Ave1_16 is a simple average of the next 16 samples, and AveRoll is a moving average where the result is combined with a previous value in the ratio of 1:15. The STD values are the standard deviations for these three statistics. For a normal distribution one expects to see values within +/- 2 STD of the average 95% of the time so it is quite a good measure of the quality of the conversion.
First comment is on the effect of source resistance. This starts to have an effect when it starts getting above 115K. The isolated measurements Ave0 show a distinct reduction and the STD grows. This effect is serious at 375K, bad at 1M and unusable at 10M.
The effect on the AVE1_16 is much less severe and even less on the rolling average, but the STD is still rising from 375K.
Part of the reason for the drop in AVE0 can be seen from a sample of the raw results.
This shows just 1 loops raw results for 3 different source resistance, but the nature of the result is consistent across the loops. In particular, the first analogRead is lower than the rest and this gets worse at higher source resistances. This is probably some effect of a sampling capacitor internal to the chip needing to charge up.
This means 2 things can be helpful in using the ADC. First the source resistance should be kept as low as possible, ideally below 100K. This is the case on NodeMCU units where there is already a medium resistance divider.
Second, the reliability of the readings can be significantly improved by discarding an initial reading and then averaging the results. The 16 sample averaging used here makes the ADC more like a 9 bit converter.
Two example functions are:
where readADC returns a simple average and getFilteredADC returns an even smoother one at the expense of lag in responding to changes.
Note the raw conversion time of analogRead in Arduino is about 125uSec so the readADc still only takes about 2mSec to execute.
Other interesting characteristics would be supply voltage sensitivity, temperature stability and linearity. I may try to get some more data if I get some time.
I have done some initial measurements to characterise it a bit more with a view to optimising its use.
Test set up was an ESP-12F (i.e. raw Analog input) programmed under Arduino. I set up a voltage source giving a reading of about 940 and tested with different resistors in series between the source and the ADC pin; 15K, 115K, 375K, 1M, 10M.
Sketch was in a 30mSec loop and in each iteration it took 17 ADC measurements in a fast internal loop. iteration. The loop calculated a simple average and a moving average. I captured the raw data and the averages of about 300 loops for each resistor value and analysed them in excel.
Here Ave0 is the average value for the first measurement in each loop, Ave1_16 is a simple average of the next 16 samples, and AveRoll is a moving average where the result is combined with a previous value in the ratio of 1:15. The STD values are the standard deviations for these three statistics. For a normal distribution one expects to see values within +/- 2 STD of the average 95% of the time so it is quite a good measure of the quality of the conversion.
First comment is on the effect of source resistance. This starts to have an effect when it starts getting above 115K. The isolated measurements Ave0 show a distinct reduction and the STD grows. This effect is serious at 375K, bad at 1M and unusable at 10M.
The effect on the AVE1_16 is much less severe and even less on the rolling average, but the STD is still rising from 375K.
Part of the reason for the drop in AVE0 can be seen from a sample of the raw results.
This shows just 1 loops raw results for 3 different source resistance, but the nature of the result is consistent across the loops. In particular, the first analogRead is lower than the rest and this gets worse at higher source resistances. This is probably some effect of a sampling capacitor internal to the chip needing to charge up.
This means 2 things can be helpful in using the ADC. First the source resistance should be kept as low as possible, ideally below 100K. This is the case on NodeMCU units where there is already a medium resistance divider.
Second, the reliability of the readings can be significantly improved by discarding an initial reading and then averaging the results. The 16 sample averaging used here makes the ADC more like a 9 bit converter.
Two example functions are:
Code: Select all
int readADC() {
int a;
int i;
a = analogRead(A0);
a = 0;
for(i=0;i<16;i++) {
a += analogRead(A0);
}
return a>>4;
}
float filteredADC;
float getFilteredADC() {
int a = analogRead(A0);
filteredADC = (filteredADC * 15 + analogRead(A0)) / 16;
return filteredADC;
}
where readADC returns a simple average and getFilteredADC returns an even smoother one at the expense of lag in responding to changes.
Note the raw conversion time of analogRead in Arduino is about 125uSec so the readADc still only takes about 2mSec to execute.
Other interesting characteristics would be supply voltage sensitivity, temperature stability and linearity. I may try to get some more data if I get some time.