ADC outputs > 3.3V - is this a bug?

I’ve just used the ADC with ESPHome for the first time with a LDR (light sensor).

I used the 11dB attenuation option on an ESP32. I powered the device with 5V over USB, and used the 3.3V generated on-board to connect the LDR with a 47k resistor to make a voltage divider. I measured the input voltage at the top of the divider and it’s at about 3.288V. I have it configured like this diagram, except replace 5V with 3.288V and R1 is 47kOhm.

The weird thing is ESPHome is reporting voltages above 3.3V when the LDR resistance is very high!!! I will investigate further.

@patfelst any luck? I’ve got a similar issue. When I attach the ADC pin to the 3.3v pin I get a reading of 3.9v when using 11dB attenuation.

I ended up going for 6dB attenuation, I can’t recall the outcome of the 11dB. From my code, it looks like I used a calibration function to straighten the ESP32’s non-linear ADC. I might look into the 11dB thing again over the weekend. Here’s the code I ended up with, it might give you some ideas to investigate:

  #******************************************************************************
  # LDR Light Sensor
  #******************************************************************************
  - platform: adc
    pin: GPIO35
    name: "LDR Voltage"
    internal: true
    # https://bit.ly/ESP32_ADC_Attenuation
    # 6 dB attenuation (ADC_ATTEN_DB_6) gives full-scale voltage 2.2 V
    # 11 dB attenuation (ADC_ATTEN_DB_11) gives full-scale voltage 3.9 V (see note below)
    # Note: At 11 dB attenuation the maximum voltage is limited by VDD_A (3.30V), not the full scale voltage.
    attenuation: '6db'
    id: ldr_voltage
    update_interval: 200ms
    filters:
      - sliding_window_moving_average:
          window_size: 10
          send_every: 5
      # For maximum accuracy, use the ADC calibration APIs and measure voltages within these recommended ranges.
      # 6 dB attenuation (ADC_ATTEN_DB_6) between 150 to 1750 mV
      # 11 dB attenuation (ADC_ATTEN_DB_11) between 150 to 2450 mV
      - calibrate_linear:
          # Map from SENSOR -> TRUE value
          - 0.135 -> 0.193
          - 2.06 -> 1.776
          - 2.185 -> 1.872
      - lambda: |-
          float raw_volts = id(ldr_voltage).raw_state;
          int raw_adc_count = (raw_volts * 4095.0 / 3.90);
          float current = (3.28 - x) / 47200.0;
          //ESP_LOGW("adc", "3.28-x = %.3f, current = %.1fuA", 3.28 - x, current * 1E6);
          id(ldr_resistance) = (x / current);
          //ESP_LOGI("adc", "Raw ADC count [%u], Raw ADC Voltage [%.3fV], Filtered ADC Voltage [%.3fV], Resistance [%.0fkΩ]", raw_adc_count, raw_volts, x, id(ldr_resistance) / 1000.0);
          return x;
1 Like

Yes. That is to be expected.

The documents state:

0db for a full-scale voltage of 1.1V (default)

2.5db for a full-scale voltage of 1.5V

6db for a full-scale voltage of 2.2V

11db for a full-scale voltage of 3.9V <-------###########

So is it always necessary to do some kind of calibration when using an ADC sensor, or is there some other method?

Does this look right, or is there a better way to use the ADC sensor?

sensor:
  - platform: ntc
    sensor: resistance32
    calibration:
      - 7.15kOhm -> 212°F
      - 31.6kOhm -> 130.3°F
      - 98.2kOhm -> 77.0°F
    name: test NTC Temperature
    
  - platform: resistance
    id: resistance32
    sensor: sensor32
    configuration: UPSTREAM
    resistor: 11.88kOhm
    name: Resistance Sensor
    
  - platform: adc
    id: sensor32
    pin: GPIO33
    attenuation: 11db
    update_interval: 2s
    filters:
    - calibrate_polynomial:
       degree: 2
       datapoints:
          # Map from SENSOR -> TRUE value
          - 3.90 -> 3.31
          - 3.65 -> 3.04
          - 3.09 -> 2.73
          - 2.51 -> 2.28
          - 2.13 -> 1.95
          - 1.61 -> 1.51
          - 1.34 -> 1.28
          - 1.10 -> 1.08
          - 0.60 -> 0.66
          - 0.14 -> 0.25
          - 0.00 -> 0.00

It’s pretty much always required. The ADC is non-linear at the extreme ends of its range.

2 Likes

that makes sense, I didn’t read it that way. So the limit is 3.3V but should be scaled to 3.9V. Thanks