ESP32 shows wrong voltage

Hi everyone,

I’m currently trying to use a CO2 sensor. It runs with a 5V heating voltage (passed directly from USB) and outputs a test voltage, which is fed into an impedance converter:

I then take its output into my microcontroller, which shows a voltage of around 75mV for an ESP32. However, if I measure it with a voltmeter, I get 200mV - which is an expected value considering the datasheet.

Example code for the ESP32:

void setup() {
  Serial.begin(115200);
  pinMode(35, INPUT);
}

float voltage = 0;
void loop() {
  voltage = (analogRead(35)*3300.0)/4096.0;
  Serial.println(voltage);
}

What am I missing here? I’m still a beginner, so please be patient with me :wink:
Thanks a lot already!

CO2_Sensor.pdf (529 KB)

If i'm correct, by default the ESP32 uses attenuation on the analog in.

Don't see code for Nano or a schematic so can't comment on that.

I found that, especially for the A:D converter, that using the ESP32 AD API is a lot more accurate then using the ESP32 Arduino core.

ESP#@ AD API

#include 

void setup()
{
  // https://dl.espressif.com/doc/esp-idf/latest/api-reference/peripherals/adc.html
  // set up A:D channels
  adc1_config_width(ADC_WIDTH_12Bit);
  adc1_config_channel_atten(ADC1_CHANNEL_6, ADC_ATTEN_DB_11);
}
//
void TaskAnalogVoltRead_LIDAR( void *pvParameters )
{
  // String localBuffer;
  // localBuffer.reserve ( StringBufferSize300 );
  int iBit = 1;
  float ADbits = 4095.0f;
  float offSET = 1.0f;
  float r1 = 100800.0f; // R1 in ohm, 100k = 100,800.0 //
  float r2 = 38780.0f; // R2 in ohm, 38k = 38000.0 //actual 38780K
  float uPvolts = 3.3f;
  // ADC1 channel 0 is GPIO36
  // ADC1 channel 1 is GPIO37
  // ADC1 channel 6 is GPIO34
  // https://dl.espressif.com/doc/esp-idf/latest/api-reference/peripherals/adc.html
  // to get resistor R2 go to:
  // http://www.ohmslawcalculator.com/voltage-divider-calculator
  //   used 12 volts for the input voltage to calculate R2, used 100K for R1
  for (;;)
  {
    // group handle, WaitForBits, ClearOnExitBit, WaitForAllBits, TicksToWait
    xEventGroupWaitBits( eg, evtAnalogVoltReadTask_LIDAR, pdTRUE, pdTRUE, portMAX_DELAY );
    //    // read the input
    iBit = iBit << 1;
    if ( iBit == 1073741824 )
    {
      if ( xSemaphoreTake( sema_AnalogVoltRead_LIDAR, xSemaphoreTicksToWait ) == pdTRUE )
      {
        ptrVbatt_LIDAR[0] += ( ((( uPvolts * adc1_get_raw(ADC1_CHANNEL_6)) / ADbits) / r2 * ( r1 + r2)) + offSET );
        
      } // if ( xSemaphoreTake( sema_AnalogVoltRead_LIDAR, xSemaphoreTicksToWait ) == pdTRUE )
      iBit = 1;
    } // if ( iBit == 1073741824 )
  }
  vTaskDelete( NULL );
}

Some code removed but the basics of using the ESP32 AD API

Thanks for the replies!
I corrected the title, it is working correctly on an Arduino Nano - I just tested it. Therefore, the issue is only the ESP32.

Using the following code:

#include <driver/adc.h>

void setup() {
  Serial.begin(115200);
  adc1_config_width(ADC_WIDTH_BIT_12);
  adc1_config_channel_atten(ADC1_CHANNEL_7,ADC_ATTEN_DB_0);
}

float voltage = 0;

void loop() {

for (int i=0;i<1000;i++)
  voltage += (adc1_get_raw(ADC1_CHANNEL_7)*1100.0)/4096.0;
voltage /= 1000;

Serial.print(voltage);
Serial.println("mV");
}

I still get measurements around 120mV, while the Multimeter comes close to 200mV… Could this be an issue with the impedance converter?

Off the top of my head, when I look at the code in loop(), you might consider using a delay of sort before making another reading allowing the A:D a larger sampling time. Consider you are not asking the CPU core1 to do very much and the A:D may need more time between readings. I'd try a delay of 100uS using, delayMicroseconds or something like that. If the readings improve then you know you are on the right track.

You might try using the A:D calibration procedure as per the ESP32 AD API.

https://randomnerdtutorials.com/esp32-adc-analog-read-arduino-ide/

Adding a delay doesn't change anything. I'm afraid the calibration goes a bit over my head, but

"Per design the ADC reference voltage is 1100 mV, however the true reference voltage can range from 1000 mV to 1200 mV amongst different ESP32s."

tells me that this issue should be in the range of +-10% at a maximum.

However, I managed to find a potentiometer and did some measurements:

ESP: 125mV Voltmeter: 190mV ESP: 500mV Voltmeter: 509mV ESP: 1000mV Voltmeter: 950mV

This matches the results from using the CO2 sensor. In the middle range of ~500mV, the measurements seem to be quite accurate. Could the ADC really be THAT non-linear?

I guess I could just use an amplifier then?

x0RRY: Could the ADC really be THAT non-linear?

Yes, they really are not great. particularly at the ends (closer to 0V and closer to 3.3V) and above 1V.

This problem is well known and has already been mentioned here.

runaway_pancake: https://randomnerdtutorials.com/esp32-adc-analog-read-arduino-ide/