[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