ESP32/Pins and signals/12. Digital I/O
Your chip
Your language
Pins and signals · 12 of 81

Digital I/O

A pin is a switch you control, or a question you ask — never a power supply and never a voltmeter. Plus the part nobody mentions, which is that a few pins on every ESP32 are already busy before your code starts.

/esp32/digital-io · arduino · S3

Two directions, and one thing a pin is not

As an output the pin is a switch — it connects the wire to 3.3 V or to 0 V. As an input it is a comparator asking one question: is this above or below the threshold. It cannot tell you how much voltage is there; that needs the ADC.

The current limit follows from the switch idea. Roughly 20 mA is fine, 40 mA is the absolute maximum, and a motor wants 200 mA — which is why a motor never goes on a pin.

The pins that are already busy

Power-on to the first line of setup()
GPIO 4, 5, 13, 18, 19, 21, 22, 23
Usable as output
yes
Moves before setup()
no
GPIO 4, 5, 13, 18, 19, 21, 22, 23. Floating, then whatever your sketch sets. This is what everyone assumes every pin does, and it is the right kind of pin for a relay, a MOSFET, or anything else that must not twitch.

Safe pins to start with

On a classic ESP32 dev board: 4, 5, 13, 16, 17, 18, 19, 21, 22, 23, 25, 26, 27, 32, 33. Those do what you expect at boot and in your sketch.

Avoid GPIO 0, 2, 12 and 15 unless you know what they do at reset, avoid 6–11 entirely, and remember 34–39 are input-only. The S3, C3, C6 and P4 have their own lists — the pinout page for your chip has them.

On your S3
ChipXtensa LX7 · 2 × 240 MHz
Board settingESP32S3 Dev Module
Default I2CSDA 8 · SCL 9
Watch out forThe port vanishes after upload

The code

A button to ground and an LED. INPUT_PULLUP is doing the work — without it the pin floats and reads randomly, which is the first bug almost everybody writes.

button_and_led.ino
const int BUTTON = 4;      // pick an ordinary pin, not 0, 2, 12 or 15
const int LED    = 18;

void setup() {
  Serial.begin(115200);
  pinMode(BUTTON, INPUT_PULLUP);   // internal resistor to 3V3
  pinMode(LED, OUTPUT);
}

void loop() {
  bool pressed = digitalRead(BUTTON) == LOW;   // LOW means pressed
  digitalWrite(LED, pressed ? HIGH : LOW);
}

With a pull-up, a pressed button reads LOW. That inversion feels wrong for about a week and then feels obvious.

When it does not work

The input flickers between 0 and 1 with nothing connected

The pin is floating. An unconnected input is not 0 — it is an antenna. Use INPUT_PULLUP, or add a 10 kΩ resistor to 3V3 or ground yourself.

pinMode(OUTPUT) has no effect on this pin

On the original ESP32, GPIO 34 to 39 are input-only. There is no output driver and no internal pull-up. The call is accepted and does nothing, which is the most confusing possible failure.

A relay clicks every time the board resets

You have it on a strapping pin, or on a pin the bootloader drives. Move it to an ordinary one and add a pull-down resistor so it stays off during the first 250 ms.

The LED is dim, or the board resets when it lights

A pin can pass about 20 mA comfortably and 40 mA once. An LED without a resistor asks for much more than that. Anything bigger — a motor, a strip, a relay coil — needs a transistor.

Where this goes next

On and off is only two brightnesses. The next page is how the chip fakes everything in between.

PWM with LEDC

Edit this page — content/esp32/digital-io.mdx

Discuss this article

Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.

Browse ESP32 on the forum