Online tech learner logo
Online Tech Learner
  • Please enable News ticker from the theme option Panel to display Post

Lies, Damn Lies and Analog Inputs (comparing ADCs on ESP32, Pico and Arduino)

[ad_1]

After some inconsistent and unreliable results reading an analog input from an ESP32 board, I decided to get all scientific and do some experimenting with an ESP32, a Raspberry Pi Pico and an Arduino Uno R3.

Method

My test setup was a bench power supply providing the reference voltage to be measured by the test board. The output of the bench PSU had a dummy load of a 470Ω resistor and a 100nF capacitor in parallel (the latter largely for superstitions reasons) as the voltage output looks extremely stable on a DMM voltmeter.

This output from the bench power supply was then applied directly to an analog input and GND of the board being tested.

I was particularly interested in three things:

* finding any dead-zones at each end of the analog input voltage range

* measuring the reproducibility of the readings

* linearity through the range

Another time, I’d like to look at the input impedance of the ADC (analog to digital convertor) and the effects of how rapidly you sample. But I’ll leave that for another day.

To measure the reproducibility of the readings, each time the test voltage was changed, 100 readings would be taken, and the mean and standard deviation of that set recorded. That way, when it came to plotting the readings from the boards, I could add some error bars.

For the ESP32 and Pico, I used MicroPython and for the Arduino Uno, I used Arduino C. The Arduino readings were scaled up to 16bit unsigned values (max value 65536) to be consistent with the MicroPython version. In all cases, the default ADC settings were used.

ESP32

For this experiment, I used an ESP32 Lite (sometimes also called LOLIN32 Lite). These are a ubiquitous low-cost ESP32 board, with built-in WiFi and Bluetooth.

Analog input maximum voltage 1.0V

Here’s the plot

The red error bars show +- 3 standard deviations (SDs) from 100 samples. Nearly all of the sample values would fall within 3 SDs and 60% would fall within 1 SD.

There is a sizeable dead zone until the voltage rises to about 0.05V and a lot of noise around the readings, evidenced by the large error bars. But it retains pretty good linearity once you get past that up to the 1V upper limit.

Raspberry Pi Pico

The Raspberry Pi Pico uses Raspberry Pi’s RP2040 chip. It’a another popular, good value board.

It’s maximum analog input voltage is the full 3.3V supply range.

Here’s the plot for the Pico – 

This is considerably better than the ESP32, with smaller 3 x SD error bars, a small dead zone at the low voltage end and some slight tail-off in linearity at the 3.3V end.

Arduino Uno R3

Despite its age, the Arduino Uno R3 (not the fancy new one) is still my go-to board for any experimentation or early stage project work that doesn’t need a specific microcontroller. I’ll admit, it’s partly out of familiarity and inertia on my part.

And here are the results.

And there we have it. Very little deviation between readings and great linearity across the whole range, right up to 5V. The Uno with it’s ancient Atmega328 is streets ahead of the other two boards.

Conclusion

On looking at the documentation in MicroPython and learning that the analog readings for a Pico and ESP32 come at a massive 16 bit precision (a number between 0 and 65536) it’s easy think that their analog inputs are much better than the paltry 10 bits of an Arduino (0 to 1023 reading range). But this is to confuse precision with accuracy. It’s why pure megapixels is not the best way to judge a camera. So much depends on the lens.

So, if you are trying to get decent accuracy and reproducibility from your analog readings, then you probably want to take a set of readings and average them — or use an Arduino Uno R3!

Test Programs

ESP32

from machine import ADC, Pin

from time import sleep

from math import sqrt

analog = ADC(12)

p = 0.05

n = 100

while True:

    readings = []

    for i in range(0, n):

        readings.append(analog.read_u16())

        sleep(p)

    total = 0

    for i in range(0, n):

        total += readings[i]

    mean = total / n

    dist_tot = 0

    for i in range(0, n):

        dist = readings[i] – mean

        dist_tot += dist * dist

    

    print(mean, sqrt(dist_tot / n))

    

    input(‘Press enter to read again’)

Pico

from machine import ADC, Pin

from time import sleep

from math import sqrt

analog = ADC(28)

p = 0.05

n = 100

while True:

    readings = []

    for i in range(0, n):

        readings.append(analog.read_u16())

        sleep(p)

    total = 0

    for i in range(0, n):

        total += readings[i]

    mean = total / n

    dist_tot = 0

    for i in range(0, n):

        dist = readings[i] – mean

        dist_tot += dist * dist

    

    print(mean, sqrt(dist_tot / n))

    

    input(‘Press enter to read again’)

Arduino

int p = 50;

const int n = 100;

unsigned int readings[n];

void setup() {

  Serial.begin(9600);

}

void loop() {

  if (Serial.available()) {

    Serial.read();

    Serial.println(“measuring”);

    for (int i = 0; i < n; i++) {

      readings[i] = analogRead(A0) * 64; // 16 bit not 10

      delay(p);

    }

    float total = 0.0;

    for (int i = 0; i < n; i++) {

      total += float(readings[i]);

    }

    float mean = total / n;

    float dist_total = 0.0;

    for (int i = 0; i < n; i++) {

      float dist = float(readings[i] – mean);

      dist_total += (dist * dist);

    }

    float sd = sqrt(dist_total / n);

    Serial.print(mean); Serial.print(‘ ‘); Serial.println(sd);

  }

}

[ad_2]

Source link

administrator

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *