espressif / arduino-esp32

Arduino core for the ESP32
GNU Lesser General Public License v2.1
13.76k stars 7.44k forks source link

ADC #1804

Closed q-bird closed 5 years ago

q-bird commented 6 years ago

Hi there, I'm using ESP32-WROOM-32D to measure ADC But I see it's not good If I setup 11db (full-scale upto the VDD_A supply - I measure the power voltage about 3.36V) I applied the adc voltage input is 0.99V ==> the expected is around 1206-1207 (0.99/3.36 4096) But the real value that ESP read is just 1102-1104 ==> 11043.36/4096 = 0.906V Error = 10%

adc1_config_channel_atten(ADC1_CHANNEL_6, ADC_ATTEN_11db );
adc1_config_width(ADC_WIDTH_12Bit);

If I setup 0db (full-scale is 1.1V), ESP got 1.06V but the real value is 0.99V

adc1_config_channel_atten(ADC1_CHANNEL_6, ADC_ATTEN_0db );
adc1_config_width(ADC_WIDTH_12Bit);

Is there anyone has the same problem with me? Is the ADC of ESP32 not good or there is some problems with my firmware?

Thanks in advance

MarkusAD commented 6 years ago

ADC_ATTEN_11db gives a full-scale voltage of 3.9V, so at 12bits 4095=3.9V. Although you wont see any readings higher than VDD_A, you should be using 3.9V in your calculation instead of the VDD_A voltage.

q-bird commented 6 years ago

@MarkyAD Thank you for your support! But if I use 3.9V 1104*3.9/4096 = 1.051V it's still different from 0.99V

And what about 0db - 1.1V full-scale? with the range of 0-1.1V + 12bits, I should get a better result when I measure 0.99V right? But in fact the ESP got 1.06V. Then how to know exactly the Vref? they said 1.1V (0db) in datasheet and we follow, but maybe the real Vref is different and we never know?

q-bird commented 6 years ago

@MarkyAD

BTW, I aslo see this note image and this one: image So, what's the 1/3.6 in the first picture and 3.9V in the second one In fact if we use 3.6V for the formula, it's better than 3.9V

Do you have any idea about this point?

MarkusAD commented 6 years ago

Yes but on that page it also states that "At 11dB attenuation the maximum voltage is limited by VDD_A, not the full scale voltage." As to the error offset, I would attribute it to both Vref differences and the non-linear response of the ADC. See this page https://esp-idf.readthedocs.io/en/latest/api-reference/peripherals/adc.html#example-of-reading-calibrated-values regarding ADC calibration.

MarkusAD commented 6 years ago

There are many threads on this in both the esp-idf repository and on esp32.com, search ADC calibration or ADC vref. A thread on esp32.com mentions that Vref variations can typically deviate +/- 100mV from the reference 1.1V, more than enough to account for the error your seeing.

MarkusAD commented 6 years ago

ADC_ATTEN_11db = 3 The input voltage of ADC will be reduced to about 1/3.6

What its showing here is the effect of the attenuator on input voltage. In other words, at -11dB, the input voltage is reduced to 1/3.6 of its actual value (it has to fit in the range of 0-Vref). I tested 3 different esp32, routing Vref to a gpio, and got a range of Vref voltages from 1.18-1.24V. Even such a small sampling shows the variation, and running uncalibrated only puts you in the ballpark of any sort of accuracy.

MarkusAD commented 6 years ago

@q-bird if you need better accuracy, consider using an external adc. There are many adc with spi or i2c (analog devices, microchip, etc) that will work nicely with esp32. Probably most people use the internal adc to read e.g. a potentiometer, light sensor or as a relative indicator of battery voltage where a bit of inaccuracy isnt much of a problem. Anyway you can probably close this, its not really a core issue, and it sounds like your adc is working.

q-bird commented 6 years ago

Hi, Is this function used to route the Vref to a GPIO pin? Is there only one Vref inside the ESP and is it used for both ADC1 and ADC2? adc2_vref_to_gpio(GPIO_NUM_25);

I'm using ADC1, not ADC2 but I can't use adc1_vref_to_gpio, it's an invalid function. So I used adc2_vref_to_gpio(GPIO_NUM_25); instead, and got the Vref = 1.09V (measured by meter). Will the ADC1 also refer to this Vref?

BTW, many thanks for your support! I will close this!

MarkusAD commented 6 years ago

Now I'm questioning my own sanity (or lack thereof) on this one. The documentation mentions 3 different voltages or scales...

I took this to mean "full-scale = 3.9V = 4095 but you will never see a raw reading that corresponds to more than VDD_A volts". I tested a 1V reference with VDD_A=3.35V and got raw values around 1103, plugging these in I get 3 different voltages:

(full scale) 1103 3.9 / 4096 = 1.0502 (1/3.6 input) 1103 3.6 / 4096 = 0.9694 (VDD_A) 1103 * 3.35 / 4096 = 0.9021

The 3.6volt formula is closest, but why mention the 3.9V full-scale at all if its not important. Which is it lol.

MarkusAD commented 6 years ago

@q-bird yes thats what I used to measure Vref, its the same for adc1 and 2.

q-bird commented 6 years ago

@MarkyAD

So in this case of ESP32, what's the sense of 12bits I still don't know :) In theory, with 12bits we will have 4096 points for the range of 0-3.6V (in case 11db) So one adc step (unit) will correspond with 3.6/4096 = 0.000879V...

And if we select 0db, meaning 1.1V, one adc step can read upto 1.1/4096 = 0.00027V which we expected

Based on your result if we calculate the expected Vref to have exactly 1V: Vref = 1V*4096/1103 = 3.71V (between 3.6 and 3.9)... But of course it will be wrong with another input voltage such as 0.5V or 2V...

MarkusAD commented 6 years ago

I had been using the "3.9v full-scale" calculation in a project and just accepted the 0.05V offset. I didn't worry about it much as it wasn't all that critical, plus I got the same 0.05V offset with the 0db mode and known 1.1V full scale, which is why I blamed Vref.

lbernstone commented 6 years ago
#include <esp_adc_cal.h>
#define REF_VOLTAGE 1100

const uint8_t TMP_PIN = 33;
esp_adc_cal_characteristics_t *adc_chars = new esp_adc_cal_characteristics_t;

uint16_t avgAnalogRead(uint8_t pin, uint16_t samples = 8) {
  adc1_channel_t chan;
  switch (pin) {
    case 32:
      chan = ADC1_CHANNEL_4;
    case 33:
      chan = ADC1_CHANNEL_5;
    case 34:
      chan = ADC1_CHANNEL_6;
    case 35:
      chan = ADC1_CHANNEL_7;
    case 36:
      chan = ADC1_CHANNEL_3;
    case 39:
      chan = ADC1_CHANNEL_0;
  }
  uint32_t sum = 0;
  for (int x=0; x<samples; x++) {
    sum += adc1_get_raw(chan);
  }
  sum /= samples;
  return esp_adc_cal_raw_to_voltage(sum, adc_chars);
}

void setup() {
    Serial.begin(115200);
    pinMode(TMP_PIN, INPUT);
    adc1_config_width(ADC_WIDTH_BIT_11);
    adc1_config_channel_atten(ADC1_CHANNEL_5,ADC_ATTEN_DB_11);
    esp_adc_cal_value_t val_type = 
           esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_11, REF_VOLTAGE, adc_chars);
}

void loop() {
    Serial.println(avgAnalogRead(TMP_PIN));
    delay(5000);
}
MarkusAD commented 6 years ago

@lbernstone Nice, with that code and the code in examples/peripherals/adc in esp-idf I was able to get measurements to within 3mV of actual after substituting my measured Vref. Maybe the cal_characterize and raw_to_voltage functions can be exposed/wrapped in arduino ide somehow?

@q-bird make sure when you measure your external reference voltage and the internal Vref that you zero out any lead resistance in your test instrument using relative mode, this was at least part of the offset I was getting.

lbernstone commented 6 years ago

They will be wrapped when @me-no-dev gets some spare cycles. raw_to_voltage doesn't follow Arduino standards, so probably will just be a separate method.

q-bird commented 6 years ago

Hi @lbernstone @MarkyAD

I used your code to test @lbernstone But I changed something: remove pinMode(TMP_PIN, INPUT); and increase the resolution to 12bits

With 0.99V input, the result is: sum = 1103 but esp_adc_cal_raw_to_voltage(sum, adc_chars) = 1023

When we calculate 1103/4096(3.36 to 3.9) and 1023/4096(3.36 to 3.9) Which one is better? will use 3.9V in the formula in this case?

MarkusAD commented 6 years ago

@q-bird esp_adc_cal_raw_to_voltage() already converts to voltage for you so you dont have to calculate it. 1023 = 1023mV = 1.023V. You should be substituting your measured Vref (also in mV) in the line with esp_adc_cal_characterize() where it says 1100.

lbernstone commented 6 years ago

I find 12 bit to be too jittery in most cases. You need to figure out whether you need precision or accuracy. Take more samples to improve accuracy. If you need that extra bit of precision, get a dedicated ADC.

q-bird commented 6 years ago

Thank you both of you @MarkyAD and @lbernstone (BTW, with 11db I got 1023mV, and with 2_5db I got 1001-1004mV better than 11db like the theory :) )

JohnMacrae commented 6 years ago

This is an interesting discussion. Using a DOIT v1 board, I have found that the sketch is ineffective at switching outputs. e.g. selecting TMP_PIN = 32 and only a voltage applied to pins 36 and 39 returns a value.

Can you satisfy my curiosity please. adc1_config_channel_atten(ADC1_CHANNEL_5,ADC_ATTEN_DB_11); is not changed when the TMP_PIN is selected - I would have expected it to but perhaps I misunderstand it?

q-bird commented 6 years ago

Hi @9H5G,

Can you try this?

JohnMacrae commented 6 years ago

Hi @q-bird, thanks for this. I'd expected the parameters should match the pin and tried what you suggested. However, pins 36 and 39 still returned readings. It was only by commenting out sum += adc1_get_raw(chan); and substituting sum += adc1_get_raw(ADC1_CHANNEL_7); that correct operation on pin 35 was achieved. Correct operation of all other pins confirmed. Thanks :)

NoelWalters commented 6 years ago

Just starting to get involved with analogue in on the esp 32 so this thread should be very useful.

For reference, according to my calculator (square root of (10 to the power of (decibels over 10))): 11db = voltage ratio of 3.548 to 1 6db = voltage ratio of 1.995 to 1 2.5db = voltage ratio of 1.334 to 1

so it looks like the 3.9 ratio could be a typing error

standarddeviant commented 6 years ago

@lbernstone FYI, the switch case in avgAnalogRead needs break statements. Switch statements in C 'fall through', meaning if you switch to the case where pin=33, you're then subsequently executing all the cases below it...

I appreciate the code, but caught this bug because I put a default case at the bottom, where it just returns 42, and that's all that I get until I put in break statements.

chegewara commented 6 years ago

@NoelWalters Those are negative values:

-11db -6db -2.5db

Reference voltage value measured by esp32 on ADC pins is 1.1V. This way you have:

DaveCalaway commented 5 years ago

@lbernstone I edited your code and i added the Arduino's functions where was possible.

/*
   ESP32 ADC multi readings

   All time ADC pins:
   ADC1_CH0,ADC1_CH03,ADC1_CH04
   ADC1_CH05,ADC1_CH06,ADC1_CH07

   Use only without WiFi:
   ADC2_CH0,ADC2_CH01,ADC2_CH02,ADC2_CH03
   ADC2_CH04,ADC2_CH05,ADC2_CH06
   ADC2_CH07,ADC2_CH08,ADC2_CH09

   Arduino espressif doc: https://goo.gl/NpUo3Z
   Espressif doc: https://goo.gl/hcUy5U
   GPIO: https://goo.gl/k8FGGD
*/

#include <esp_adc_cal.h>

// Command to see the REF_VOLTAGE: espefuse.py --port /dev/ttyUSB0 adc_info
// or dc2_vref_to_gpio(25)
#define REF_VOLTAGE 1128

byte pins[6] = {36, 39, 34, 35, 32, 33};

esp_adc_cal_characteristics_t *adc_chars = new esp_adc_cal_characteristics_t;

// ========= analogRead_cal =========
int analogRead_cal(uint8_t channel, adc_atten_t attenuation) {
  adc1_channel_t channelNum;

  /*
     Set number of cycles per sample
     Default is 8 and seems to do well
     Range is 1 - 255
   * */
  // analogSetCycles(uint8_t cycles);

  /*
     Set number of samples in the range.
     Default is 1
     Range is 1 - 255
     This setting splits the range into
     "samples" pieces, which could look
     like the sensitivity has been multiplied
     that many times
   * */
  // analogSetSamples(uint8_t samples);

  switch (channel) {
    case (36):
      channelNum = ADC1_CHANNEL_0;
      break;

    case (39):
      channelNum = ADC1_CHANNEL_3;
      break;

    case (34):
      channelNum = ADC1_CHANNEL_6;
      break;

    case (35):
      channelNum = ADC1_CHANNEL_7;
      break;

    case (32):
      channelNum = ADC1_CHANNEL_4;
      break;

    case (33):
      channelNum = ADC1_CHANNEL_5;
      break;
  }

  adc1_config_channel_atten(channelNum, attenuation);
  return esp_adc_cal_raw_to_voltage(analogRead(channel), adc_chars);
}

// ========= SETUP =========
void setup() {
  Serial.begin(115200);

  // -- Fixed for the all adc1 ---
  // 4095 for 12-bits -> VDD_A / 4095 = 805uV  too jittery
  // 2048 for 11-bits -> 3.9 / 2048 = 1.9mV looks fine
  /*
     Set the resolution of analogRead return values. Default is 12 bits (range from 0 to 4096).
     If between 9 and 12, it will equal the set hardware resolution, else value will be shifted.
     Range is 1 - 16
  */
  analogReadResolution(11); // https://goo.gl/qwUx2d

  // Calibration function
  esp_adc_cal_value_t val_type =
    esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_11, REF_VOLTAGE, adc_chars);
}

// ========= LOOP =========
void loop() {
  // ====== Read all pins on ADC_1 ======
  for (byte i = 0; i < 6; i++) {
    Serial.print("Pin ");
    Serial.print(pins[i]);
    Serial.print(": ");
    /*
      The maximum voltage is limited by VDD_A
      - 0dB attenuaton (ADC_ATTEN_DB_0) gives full-scale voltage 1.1V
      - 2.5dB attenuation (ADC_ATTEN_DB_2_5) gives full-scale voltage 1.5V
      - 6dB attenuation (ADC_ATTEN_DB_6) gives full-scale voltage 2.2V
      - 11dB attenuation (ADC_ATTEN_DB_11) gives full-scale voltage 3.9V
    */
    // Default is 11db, full scale
    Serial.print(analogRead_cal(pins[i], ADC_ATTEN_DB_11));
    Serial.println(" mV");
    delay(4000);
  }
  Serial.println("----------");
}

It works.

mixaobo commented 5 years ago

i measure voltage battery (lipo 18650). 3.7 at normal/4.2 max. so 4.2v is harm to esp32 then i run voltage divider +4.2 -->27k -->measure here (3.3v) --> 10k --> ground. so which atten Attenuation level should i use? for example if i use for (int x=0; x<samples; x++) { sum += adc1_get_raw(chan); } sum /= samples; return esp_adc_cal_raw_to_voltage(sum, adc_chars); } if my battery is 4.2v then i will see 3.3v in monitor? "The maximum voltage is limited by VDD_A" @DaveCalaway

DaveCalaway commented 5 years ago

The VDD_A is the voltage applied to the ESP32 board, in most case is 3.3V (not 3.9V). For safety resone, I advise you to use a voltage divider, https://is.gd/U2iKhd, with: Vin= 4.2 R1= 3570 ohm R2 = 11500 ohm

The Vout is around 3.2V. Don't try to measure 3.3V, or higher, with a 3.3V as VDD_A.

You can use "ADC_ATTEN_DB_11".

mixaobo commented 5 years ago

The VDD_A is the voltage applied to the ESP32 board, in most case is 3.3V (not 3.9V). For safety resone, I advise you to use a voltage divider, https://is.gd/U2iKhd, with: Vin= 4.2 R1= 3570 ohm R2 = 11500 ohm

The Vout is around 3.2V. Don't try to measure 3.3V, or higher, with a 3.3V as VDD_A.

You can use "ADC_ATTEN_DB_11".

my currently setup is powered up esp32 board by usb cable, so it's will be 3.3v. +battery-->27k-->measure here-->10k-->ground . 11db atten and 11bit resolution, i get 3.66v, seem good; then i use lifepo4 battery. 3.32v -->measurehere -->ground, 11db attenation, i always get 2047 adc value, i think i set 3.9 as max scale so any voltage above 3.9 i get 2047, but i applied 3.32v i still got 2047, plz correct me, i dont know how inter adc work correctly. should i always need voltage divider aplied voltage <3.3v to GPIO36 to use internal ADC, for example if i want to use 1.1 scale, voltage input <1.1v like 0.9v ` float R1 = 27000.0; // resistance of R1 (1.1v at GPIO36) float R2 = 10000.0; // resistance of R2

for (int i = 0; i < 100; i++)
{
    sum += adc1_get_voltage(ADC1_CHANNEL_0);
    delayMicroseconds(1000);
}
// calculate the voltage
voltage = sum / (float)100;
Serial.println(voltage);
voltage = (voltage * 3.9) / 2047.0; //for internal 3.9v reference
// use if added divider circuit
voltage_in = voltage / (R2/(R1+R2));`
mixaobo commented 5 years ago

The VDD_A is the voltage applied to the ESP32 board, in most case is 3.3V (not 3.9V). For safety resone, I advise you to use a voltage divider, https://is.gd/U2iKhd, with: Vin= 4.2 R1= 3570 ohm R2 = 11500 ohm

The Vout is around 3.2V. Don't try to measure 3.3V, or higher, with a 3.3V as VDD_A.

You can use "ADC_ATTEN_DB_11".

so esp32 voltage operator 3.0 -> 3.6. so the 11db 3.9 max scale is useless because we can use 3.9v to esp32. then the max scale is always 3.3 ?

DaveCalaway commented 5 years ago

Use my function to read the mV correctly, https://github.com/espressif/arduino-esp32/issues/1804#issuecomment-475281577 : analogRead_cal(pins[i], ADC_ATTEN_DB_11);

You can't read a value up to the VDD_A, in your case, the 3v3, see the datasheet:

Screenshot 2019-07-04 at 08 51 52

If you want to use the 1.1v , aka 0dB attenuation (ADC_ATTEN_DB_0), you must have a voltage divider with the Vout under 1.1V. Something like 1V or 0.9V.

Screenshot 2019-07-04 at 08 51 52
mixaobo commented 5 years ago

Use my function to read the mV correctly, #1804 (comment) : analogRead_cal(pins[i], ADC_ATTEN_DB_11);

You can't read a value up to the VDD_A, in your case, the 3v3, see the datasheet:

Screenshot 2019-07-04 at 08 51 52

If you want to use the 1.1v , aka 0dB attenuation (ADC_ATTEN_DB_0), you must have a voltage divider with the Vout under 1.1V. Something like 1V or 0.9V.

Screenshot 2019-07-04 at 08 51 52

i run voltage divider from 4.6-->100k-->measure here (0.978)-->27k-->ground. just use this code . 11 width, 0db.

for (int i = 0; i < 100; i++)
{
    sum += adc1_get_voltage(ADC1_CHANNEL_0);
    delayMicroseconds(1000);
}
// calculate the voltage
voltage = sum / (float)100;
Serial.println(voltage);
voltage = (voltage * 1.1) / 2047.0; //for internal 1.1v reference
// use if added divider circuit
voltage_in = voltage / (R2/(R1+R2));`

When i applied 3.6v the voltage_in i get is 3.0; --> 0.6 eror. i will test with your code later

DaveCalaway commented 5 years ago

When i applied 3.6v the voltage_in i get is 3.0; --> 0.6 eror. i will test with your code later

This is the reason: https://is.gd/gsyEoR My function take care about the no linearity :rocket:

mixaobo commented 5 years ago

When i applied 3.6v the voltage_in i get is 3.0; --> 0.6 eror. i will test with your code later

This is the reason: https://is.gd/gsyEoR My function take care about the no linearity 🚀

Thank you for the code. it's work now :D last question. i also have lipo 4.2v max and lifepo 3.6v max. as i see when the measure voltage above 3.3v it is notaccuracy much? (measure 3.32v but receive 3144 at monitor); is it?. should i run voltage divider and use 1.1 max scale and

I suggest you use the internal 1.1v. Because for 3.3v if your battery becomes low the board would feed on ADC pin which is not desirable and you need to avoid that

mixaobo commented 5 years ago

When i applied 3.6v the voltage_in i get is 3.0; --> 0.6 eror. i will test with your code later

This is the reason: https://is.gd/gsyEoR My function take care about the no linearity 🚀

i found your code always fail for the first time run. do you have any idea? i'm doing with deep sleep so it's always incorrect, link here,

DaveCalaway commented 5 years ago

After deep sleep, probably, you need to wait some mS before starting the first reading. It's a warm up.

mixaobo commented 5 years ago

After deep sleep, probably, you need to wait some mS before starting the first reading. It's a warm up.

i try to put delay but nothing happen. so i add 1 line: analogRead(channel) in top of analogRead_cal() and it work. dont know why also :D

DaveCalaway commented 5 years ago

i try to put delay but nothing happen. so i add 1 line: analogRead(channel) in top of analogRead_cal() and it work. dont know why also :D

Very interesting, is it works after the deep sleep also? or you need to wait some cycles?

MacLeod-D commented 5 years ago

Maybe you may want to test:

https://github.com/MacLeod-D/ESP32-ADC

You may calibrate your own ESP32( the nonlinear 11DB curve) with this program.
All you need is a wire from pin 25 to pin 35 and a normal voltmeter.

Calibration is done automatically.
It produces a LookUpTable[4096] to correct your values like:
CalibratedValue = LookUpTable[Read Value];

I do NOT use a fitted curve but a real LookUpTable with 4096 values !

I get correct values +/- 0.01V compared to the voltmeter. :D

markterrill commented 5 years ago

@MacLeod-D but +/- 0.01V with what full scale voltage? 1.1V ? with a 0.02v step that's only 55 LSBs...

Am I missing something?

MacLeod-D commented 5 years ago

Full scale voltage. The +/- 0.01 V are NOT absolute precision but the delta from expected (linearized) value. For example if you have steps 0.7 and 0,9 that means you get 0.7 +/- 0.01V and 0.8 +/- 0.1V And that is ment with linearity.

You have to calibrate: measure the full scale voltage. So full scale output may be 3.05 V instead of 3.3 V, That means expected halfscale output will be 1.525 V and THAT value is met +/- 0.01 V And this should be true even if you only have 5 steps - meaning 0.61 V per step (3.05/5).

Mathematically: If you have a straight line going through (0,0) you have the expression y=x*m where y is the output value, x the input value and m=(fullscale-output) / (fullscale-input) That should be true for any ADC or DAC. Error is the delta from calculated y to measured y. 1) There will be delta because of noise! 2) And another comes from non-linearity. Here we try to correct the last one!

Example: 10 bit (1023 steps) Measured full scale: 3.069 V This method will gtive you 1.53V output for input of 511 instead of maybe 1.3V without correction because of the converters deviation from limearity.

+/- 0.01V are NOT the stepsize!

markterrill commented 5 years ago

Cheers @MacLeod-D, I now understand, thanks for your explanation!

From observations the ESP32 vRef may vary based on the PSU, noise on the supply, battery etc. Would it make sense to use a simple independent ADC that has its own internal vRef to measure the ESP32 vREF? Then all the ESP ADC channels could be used reliably?

stale[bot] commented 5 years ago

[STALE_SET] This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs. Thank you for your contributions.

stale[bot] commented 5 years ago

[STALE_DEL] This stale issue has been automatically closed. Thank you for your contributions.

Bwanna commented 4 years ago

@lbernstone I edited your code and i added the Arduino's functions where was possible.

/*
   ESP32 ADC multi readings

   All time ADC pins:
   ADC1_CH0,ADC1_CH03,ADC1_CH04
   ADC1_CH05,ADC1_CH06,ADC1_CH07

   Use only without WiFi:
   ADC2_CH0,ADC2_CH01,ADC2_CH02,ADC2_CH03
   ADC2_CH04,ADC2_CH05,ADC2_CH06
   ADC2_CH07,ADC2_CH08,ADC2_CH09

   Arduino espressif doc: https://goo.gl/NpUo3Z
   Espressif doc: https://goo.gl/hcUy5U
   GPIO: https://goo.gl/k8FGGD
*/

#include <esp_adc_cal.h>

// Command to see the REF_VOLTAGE: espefuse.py --port /dev/ttyUSB0 adc_info
// or dc2_vref_to_gpio(25)
#define REF_VOLTAGE 1128

byte pins[6] = {36, 39, 34, 35, 32, 33};

esp_adc_cal_characteristics_t *adc_chars = new esp_adc_cal_characteristics_t;

// ========= analogRead_cal =========
int analogRead_cal(uint8_t channel, adc_atten_t attenuation) {
  adc1_channel_t channelNum;

  /*
     Set number of cycles per sample
     Default is 8 and seems to do well
     Range is 1 - 255
   * */
  // analogSetCycles(uint8_t cycles);

  /*
     Set number of samples in the range.
     Default is 1
     Range is 1 - 255
     This setting splits the range into
     "samples" pieces, which could look
     like the sensitivity has been multiplied
     that many times
   * */
  // analogSetSamples(uint8_t samples);

  switch (channel) {
    case (36):
      channelNum = ADC1_CHANNEL_0;
      break;

    case (39):
      channelNum = ADC1_CHANNEL_3;
      break;

    case (34):
      channelNum = ADC1_CHANNEL_6;
      break;

    case (35):
      channelNum = ADC1_CHANNEL_7;
      break;

    case (32):
      channelNum = ADC1_CHANNEL_4;
      break;

    case (33):
      channelNum = ADC1_CHANNEL_5;
      break;
  }

  adc1_config_channel_atten(channelNum, attenuation);
  return esp_adc_cal_raw_to_voltage(analogRead(channel), adc_chars);
}

// ========= SETUP =========
void setup() {
  Serial.begin(115200);

  // -- Fixed for the all adc1 ---
  // 4095 for 12-bits -> VDD_A / 4095 = 805uV  too jittery
  // 2048 for 11-bits -> 3.9 / 2048 = 1.9mV looks fine
  /*
     Set the resolution of analogRead return values. Default is 12 bits (range from 0 to 4096).
     If between 9 and 12, it will equal the set hardware resolution, else value will be shifted.
     Range is 1 - 16
  */
  analogReadResolution(11); // https://goo.gl/qwUx2d

  // Calibration function
  esp_adc_cal_value_t val_type =
    esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_11, REF_VOLTAGE, adc_chars);
}

// ========= LOOP =========
void loop() {
  // ====== Read all pins on ADC_1 ======
  for (byte i = 0; i < 6; i++) {
    Serial.print("Pin ");
    Serial.print(pins[i]);
    Serial.print(": ");
    /*
      The maximum voltage is limited by VDD_A
      - 0dB attenuaton (ADC_ATTEN_DB_0) gives full-scale voltage 1.1V
      - 2.5dB attenuation (ADC_ATTEN_DB_2_5) gives full-scale voltage 1.5V
      - 6dB attenuation (ADC_ATTEN_DB_6) gives full-scale voltage 2.2V
      - 11dB attenuation (ADC_ATTEN_DB_11) gives full-scale voltage 3.9V
    */
    // Default is 11db, full scale
    Serial.print(analogRead_cal(pins[i], ADC_ATTEN_DB_11));
    Serial.println(" mV");
    delay(4000);
  }
  Serial.println("----------");
}

It works.

Hi, I could use assistance in understanding how to apply the outputs of this program. My project is measuring temp with a Thermistor using an external 3.25v ref. The serial output of the above program gives:

Pin 36: 142 mV Pin 39: 142 mV Pin 34: 142 mV Pin 35: 152 mV Pin 32: 493 mV Pin 33: 152 mV

@lbernstone - How do I incorporate this information into a sketch? Again, I do apologize for not being able to figure this out. Have tried researching and the brain is getting a bit scrambled. If I can see one example, maybe with an explanation of the parameters, I should be able to go from there. Thank you!!!

lbernstone commented 4 years ago

https://www.google.com/search?q=arduino%20read%20analog%20thermistor%20voltage%20example

Bwanna commented 4 years ago

https://www.google.com/search?q=arduino%20read%20analog%20thermistor%20voltage%20example

Thank you for the link to google. Maybe my question was not very clear. I have SUCCESSFULLY created a project that measures multiple temps using thermistors and discovered (like this issue reveals) an inconsistency between actual voltage into the GPIO and what the MCU is using as a measured voltage. After trying the program provided here, I obtained the output readings as shown.

Since many of the calibration approaches I've researched seem to use a 'correction' factor' is seemed reasonable that the output of this program did something similar - but I don't know. Hence my question about how to incorporate this information. I would GREATLY appreciate if you could spare a moment to help me understand how to use this information. Is it a correction factor? Is it a precise measurement of a certain ref voltage?

Thank you again.

lbernstone commented 4 years ago

https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/adc.html

alambin commented 3 years ago

Hi. I tried solution, suggested by lbernstone and then extended by DaveCalaway. In my case I use ADC2 channel 3 (pin 15). I used adc2_vref_to_gpio(), figured out that Vref is 1.07 and adapter constant in code. Before applying this solution I noticed that ADC gives value 4095 in case there is 3.09V on ADC input. So, further increase of input from 3.09 to 3.28V is not captured by ADC - it is always 4095. Unfortunately after applying solution, mentioned above, I didn't see any difference. Is that solution helps to make results linear in case input is close to the maximum value? Or it makes result more linear only in case input is quite far from max value? Maybe I'm doing something wrong? Or may be there is another solution for my issue? I would appreciate any info.