GPIO initialization on the ESP32 in ESP-IDF

This is just a quick post on how not to initialize a GPIO in ESP-IDF. A tutorial on Embedded Explorer discusses GPIO use in ESP-IDF and suggests initialization in this way:

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"

#define LED_PIN     GPIO_NUM_32
#define BUTTON_PIN  GPIO_NUM_36

void app_main(void)
{
   gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);   
   gpio_set_direction(BUTTON_PIN, GPIO_MODE_INPUT);
   
   while(1) {       
      if (gpio_get_level(BUTTON_PIN) == 0) {  // If button is pressed
         gpio_set_level(LED_PIN, 1);         // Turn the LED on
      } else {
         gpio_set_level(LED_PIN, 0);         // Turn the LED off
      }
      
      vTaskDelay(1); // Add 1 tick delay (10 ms) so that current task does not starve idle task and trigger watchdog timer
   }
}

This of course works, but I wanted to clear up matters because I think this takes some major shortcuts. Here is what I would suggest instead:

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_err.h"

#define LED_PIN     GPIO_NUM_32
#define BUTTON_PIN  GPIO_NUM_36   // classic ESP32: input-only, no internal pullups

static void gpio_init(void)
{
   // LED output
   gpio_config_t out_conf = {
      .pin_bit_mask = 1ULL << LED_PIN,
      .mode = GPIO_MODE_OUTPUT,
      .pull_up_en = GPIO_PULLUP_DISABLE,
      .pull_down_en = GPIO_PULLDOWN_DISABLE,
      .intr_type = GPIO_INTR_DISABLE
   };
   ESP_ERROR_CHECK(gpio_config(&out_conf));

   // Button input (assume external pull-up; pressed pulls to GND)
   // GPIO36 can't do internal pulls on classic ESP32, though
   gpio_config_t in_conf = {
      .pin_bit_mask = 1ULL << BUTTON_PIN,
      .mode = GPIO_MODE_INPUT,
      .pull_up_en = GPIO_PULLUP_DISABLE,     
      .pull_down_en = GPIO_PULLDOWN_DISABLE,
      .intr_type = GPIO_INTR_DISABLE
   };
   ESP_ERROR_CHECK(gpio_config(&in_conf));
}

void app_main(void)
{
   gpio_init();

   while (1) {
      int pressed = (gpio_get_level(BUTTON_PIN) == 0);
      gpio_set_level(LED_PIN, pressed);

      vTaskDelay(pdMS_TO_TICKS(10));
   }
}

The problem with the original code which is simply calling gpio_set_direction() is that in more complex code, other portions of the code may be doing something to your GPIO configuration; so it’s better to be explicit about the configuration. It’s just a good habit to specific exactly what you intend in the GPIO configuration. Never assume prior state. This isn’t Arduino.