Monday, January18, 2021
Privacy Policy

ESP32 Low Noise Voltage Measurements

Solving the noise and linearity issues with the ESP32

Resource id #18

The ESP32 has many great features. One of them is multiple ADC inputs. The problem is that these inputs are not very good. They suffer from noise and non linearity. The software I will present overcomes these problems in what I think is a unique way. The first thing to deal with is the noise. Once we get quiet readings, we can linearize them for our application. The application I have developed reads battery voltage on a boat so the range of 12 to 14 volts is the most important. More on that later but first we need quiet readings.

There are three kinds of noise that disturb the reading. One is electrical noise so putting a .1 micro farad capacitor near the input pin is useful. The next is random spike noise. The problem with them is that you can't really get rid of this noise by averaging. These spikes are large and do not at all resemble what we think of as random noise. We need to reject them before averaging what is left. The technique I used is very simple to implement. I sum 8 readings and then subtract the largest and smallest and divide what is left by 6. That is a common technique for dealing with data that has outliers. I then take 100 of these combined readings and average them. Thus a single point represents 800 measurements of which 600 are used and 200 are ignored. This produces readings that have noise in the .01 volt range.

The choice of 8 readings is important. You would like to have a lot of readings but you don't want more than one spike in the readings. With 8, most samples do not have a spike and tossing out the high and low does not bias the result. More than 8 and you start to see more than one spike in a sample. So averaging 8 gives 6 samples that can then be averaged to get rid of the more traditional gaussian noise.

Once I have a quiet reading, I use a third order curve fit to linearize it. I picked readings in the region I cared about and then used Excel to curve fit the data. I extracted the coefficients using an obscure Excel function called LINEST. From there it was a simple matter of solving the equation.

The final thing is that each individual ESP32 has a different gain. I have three units that I rotate in as I make changes to the code. Each has it's own gain. I have a defined constant UNIT which tells the linearization function which gain correction to use.

The final thing that could be done is to have a stable voltage reference which is measured to take out any temperature effects. I did not do that. What you would do is have a separate ADC input on the same ADC channel to read the reference voltage and scale all the readings by that value. I have not found that necessary (yet).

Actually, there is one more thing that could be a problem but isn't. I used 1% resistors so worst case could have a 2% error between my two ADC channels. Luckily they are much closer and that is not a problem. I could also calibrate each channel independently but again, it is not a problem.

The code is below.

The ADC is very fast and 100 readings takes very little time given I am reading a DC voltage.
const int samples = 100; //there will be 8 times this many readings. Recommend at least 2

This is the main function called for reading voltages
double readVoltage(int bat1Pin){
  // Reading voltage
  double bat1Value = 0;
  int x = 0;
  for(x = 0; x < samples; x++){
    bat1Value += analogReadAverage(bat1Pin);
  bat1Value /= samples;
  double linValue = lin(bat1Value);
  Serial.println("Battery " + String(bat1Pin) + " is " + String(linValue) + " Volts");
  return linValue;
Third order curve fit linearization. Note that there is an offset term so this function will not read 0 volts. Not a problem in this application.

double lin(float batvalue){
  double gain = 1;
  switch (UNIT){
    case 1:
      gain = 999999;
    case 2:
      gain = .992;
    case 3:
      gain = .9874;
      gain = 1;
  double returnValue = gain * (-9.42459E-11 * pow(batvalue, 3) +2.79259E-07 * pow(batvalue, 2) + batvalue*0.004223291+0.478405313);
  return returnValue;
//Read 8 values and toss the high and low. Average the rest.

double analogReadAverage(int bat1Pin){
  int i;
  int maxValue = 0;
  int minValue = 5000;
  double sumValue = 0;
  double result;
  int reading;
  for(i = 0; i<8; i++){
    reading = analogRead(bat1Pin);
    sumValue += (double)reading; 
    if(reading > maxValue) maxValue = reading;
    if(reading < minValue) minValue = reading;
  result = (sumValue - (double)maxValue - (double)minValue) / 6;
  return result;

Cookie Policy:
This website uses cookies to save your settings. No personal information is saved. I do not collect statistics on your visit. You can disable cookies in your browser if you like but it is not recommended for this site. I do not sell cookies. Go to a bakery for that. In fact I do not sell anything. To disable cookies from, please refer to the Help button in your browser.
Privacy Policy:
I do not sell or share any user data or anything else for that matter. The only personal information I save is in the site log which has a line for each page view which includes the IP address your browser sends in the header as well as which page you requested. I use this to block hackers and other bad actors. I do not use this raw data to create profiles on users. I periodically delete the log files. Google supplies the ads on this site. Because I do not track who you are, I cannot customize how these ads are served. They may be personalized to improve the ad experience. If you do not want personalized ads, please adjust the settings on the Google site HERE. NOTE: The best I can determine, this site is not subject to CCPA but I am doing my best to comply anyway.
The information on this web site has not been checked for accuracy. It is for entertainment purposes only and should be independently verified before using for any other reason. There are five sources. 1) Documents and manuals from a variety of sources. These have not been checked for accuracy and in many cases have not even been read by anyone associated with I have no idea of they are useful or accurate, I leave that to the reader. 2) Articles others have written and submitted. If you have questions on these, please contact the author. 3) Articles that represent my personal opinions. These are intended to promote thought and for entertainment. These are not intended to be fact, they are my opinions. 4) Small programs that generate result presented on a web page. Like any computer program, these may and in some cases do have errors. Almost all of these also make simplifying assumptions so they are not totally accurate even if there are no errors. Please verify all results. 5) Weather information is from numerous of sources and is presented automatically. It is not checked for accuracy either by anyone at or by the source which is typically the US Government. See the NOAA web site for their disclaimer. Finally, tide and current data on this site is from 2007 and 2008 data bases, which may contain even older data. Changes in harbors due to building or dredging change tides and currents and for that reason many of the locations presented are no longer supported by newer data bases. For example, there is very little tidal current data in newer data bases so current data is likely wrong to some extent. This data is NOT FOR NAVIGATION. See the XTide disclaimer for details. In addition, tide and current are influenced by storms, river flow, and other factors beyond the ability of any predictive program.