TL;DR When trying to use some pins of my ESP32 to read analog signals, it turns out those pins have a non-zero voltage, messing up the measurements. Why?

I got myself an Olimex ESP32-POE-ISO (see specs) to run the irrigation of my garden. I am attaching some hunter valves on GPIO0-5 and the plan was to hook up 3 moisture/temperature sensors (Truebner SMT50) to the pins on the other side of the module (see pinout).

However, I ended up pulling my hair out. On some pins (e.g. GPIO14/ADC2_CH6, GPIO32/ADC1_CH4, GPIO33/ADC1_CH5, GPI35/ADC1_CH7) I get proper readings. I've tried both features (moisture and temperature) of each of the 3 sensors on those pins and the values I get look reasonable. So I am ruling out defective sensors.

I have also tried GPIO13/ADC2_CH4, GPIO15/ADC2_CH3, GPI36/ADC1_CH0, GPIO0/ADC2_CH1 and GPIO2/ADC2_CH2, but I always get numbers that are way to high (raw values at 12bit between 2400-3400, corresponding to voltage 1.9V - 2.7V). And in fact, after disconnecting the sensor and measuring with a multimeter I could find that those pins actualy do have such a voltage (measured against the GND pin) while the "good" pins do not have that.

The initialization code looks like this (channel.channel.adc1_id and ...adc2_id contains values like ADC1_CHANNEL_0, ...):

void SensorService::init() {
    ESP_LOGI(TAG, "Initializing sensor service");

    sensorToChannel = getChannelMapping();
    for( const auto& [ idx, channel ] : sensorToChannel) {
        switch (channel.unit) {
            case ADC_UNIT_1:
                adc1_config_channel_atten(channel.channel.adc1_id, ADC_ATTEN_11db);
            case ADC_UNIT_2:
                adc2_config_channel_atten(channel.channel.adc2_id, ADC_ATTEN_11db);
                ESP_LOGW(TAG, "Invalid ADC unit requested");

The reading of raw values like this:

std::optional<unsigned int> SensorService::getRawValue(unsigned int sensorIdx) {
    ESP_LOGV(TAG, "Getting raw value for sensor %d", sensorIdx);

    if (!this->isValidSensorIdx(sensorIdx)) {
        ESP_LOGW(TAG, "Requested non-existing sensor");
        return std::nullopt;

    TargetChannel target = sensorToChannel.at(sensorIdx);
    switch (target.unit) {
        case ADC_UNIT_1:
            return std::make_optional(adc1_get_raw(target.channel.adc1_id));
        case ADC_UNIT_2:
            int value;
            adc2_get_raw(target.channel.adc2_id, ADC_WIDTH_BIT_12, &value);
            return std::make_optional(value);
            ESP_LOGW(TAG, "Invalid ADC unit requested");
            return std::nullopt;

And this works perfectly fine for some pins but not for others.

I also tried a few things I could find in the docs to set the pin explicitly to INPUT and low. But it didn't change anything.

    for (auto const& [ sensorIdx, pin ] : sensorPins) {
        gpio_set_direction(pin, GPIO_MODE_INPUT);
        gpio_set_level(pin, 0);

I am powering and connecting to the board via Ethernet/POE. I am not (knowingly) activating WIFI, RTC, hall sensor anywhere in the code. Not using SD card nor the flash memory for data storage. The values will only be polled via HTTP/Ethernet.

So, my actual question here is, why do some pins (e.g. GPI36, explicitly documented as input-only pin) have non-zero voltage while others haven't? What am I missing?

  • This looks like you have internal pull-ups activated. Try using gpio_set_pull_mode to disable them. – jcaron Jun 9 '20 at 10:49
  • Actually, it's (probably) not the internal pull-ups, but there are external pull-ups on the board. – jcaron Jun 9 '20 at 10:57

It looks like several of the pins have pull-ups on them on the board.

If you look at the schematic, you'll see in the UEXT section:

enter image description here

That there are pull-ups on GPIO16, GPI36, GPIO13 and GPIO5. So this explains the results you see on GPI36 and GPIO13.

GPIO0 also has a pull-up (in the ESP-WROOM-32 MODULE section). GPIO15 and GPIO2 have one each in the SD/MMC Card section.

There are quite a few more pull-ups here and there. Looks like there aren't many pins floating on the board. Also see the Notes section at the bottom of the schematic for additional pull-up/down info related to bootstrapping.

  • Thanks for clarifying. I was afraid it would be something like that. So I'll have to stick to the four pins that I found working. Hard to override hardware pull-ups via software and I won't start scratching tracks on the module. I'll accept your answer since there seems to be no good way of solving my issue. – ErosC Jun 9 '20 at 12:44

Please note that the SMT50 voltage outputs have an output resistance of 10 kOhm (see SMT50 datasheet So if there is a pullup resistance to 3.3V then you will always get voltage levels which are too high. Good to know about the pullups since I plan to start with an ESP32 irrigation control project and SMT50. I will choose the pins without pullup.

  • Note that the pull-ups mentioned on my answer are on the board, not inside the ESP32 itself or a module. The ESP32 does have internal pull-ups (and pull-downs IIRC) but they can be enabled or disabled at will. There are just a few pins where you may need external pull ups or pull downs because they affect boot behaviour, but most ESP32 pins can be used as you like. Not sure why Olimex added so many pull-ups on that board. – jcaron Jun 28 '20 at 15:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.