UART, I2C and SPI — what the three buses are, and how to check a pin before you wire it
Question this page answers: “UART vs I2C vs SPI — what’s actually different, and before I wire a device to a pin, how do I know that pin is legal, free and safe on my board?”
The one-line differences
| Bus | Lines | Who talks when | Typical use |
|---|---|---|---|
| UART | TX, RX (async, both directions) | both sides whenever (agreed baud) | console/serial, modems |
| I2C | SDA, SCL (+shared bus, addresses) | controller addresses a peripheral | low-speed sensors, config EEPROMs |
| SPI | MOSI, MISO, SCK, CS (+1 per device) | controller selects, full duplex | fast flash, displays, SD |
UART is the typical point-to-point choice (two devices); I2C hangs many devices on two shared wires; SPI is usually the fastest of the three but spends an extra select wire per device.
Where they hide on these boards
- ESP32-S3-DevKitC-1 — the USB-to-UART bridge talks to the chip over UART0 (U0TXD/U0RXD on GPIO43/44 in the header table). That is why “the serial console” works without any wiring: the bridge is permanently wired to those pins. Several J3 pins also carry FSPI* names — the chip’s flash-SPI function group.
- ESP32-DevKitC — D0/D1/D2/D3/CMD/CLK are an SPI port in action: the SPI flash wired to the chip. On WROVER variants the same SPI bus carries PSRAM — which is why GPIO16/17 disappear there (see the board page).
- Pico / Pico 2 — both boards’ SDK headers default UART0 to GP0/1 and I2C0 to GP4/5 (pico.h and pico2.h, commit
079c6f39). What PIO, the programmable state machines, can do is a different question — a software implementation of the protocol, separate from the hardware UART/I2C/SPI blocks.
Before you wire: the three-layer pin check
“Can I use GP_n for my bus?” is three separate questions. Check them in order, because each layer can veto the pin on its own:
- The SDK default — a software convention.
PICO_DEFAULT_I2C_SDA_PIN 4means the SDK’s board header chose GP4 for you; you can change it in code. Defaults are per-board files, and they prove nothing about which pins are legal — nor do they configure anything: the macro names a default choice, and initializing the controller and selecting pin functions is your application’s (or framework’s) job. - The chip’s mux table — the hardware fact. A hardware UART/I2C/SPI block exists only on a fixed set of mux pins per chip. For RP2040 this site pins the official io_bank0.h: UART0 TX/RX sits on GP0/1, with GP12/13 and GP16/17 as further UART0 options — never on an arbitrary GP pin. For RP2350 this site has not yet pinned the official mux table to a fixed source, so this page does not list its legal combinations — check the RP2350 datasheet; the Pico 2 board header’s defaults (GP0/1, GP4/5) are layer-1 facts, not proof that other pins are illegal or legal.
- The board’s wiring — what actually reaches the header, and what is already occupied. This is the board page’s claim table: physical header position, board-internal functions (LED, VBUS sense…), per revision.
And before current flows: all four boards’ GPIO are 3.3 V logic (see 3.3 V and 5 V), share a common ground with the peripheral, and an I2C bus needs pull-ups to 3.3 V on SDA/SCL (open-drain lines) — many sensor modules carry them on-board; check yours rather than assuming.
Worked example: GP4/GP5 for an I2C sensor
The check, done honestly for both generations:
- Layer 1 —
pico.handpico2.hsetPICO_DEFAULT_I2C_SDA_PIN 4,PICO_DEFAULT_I2C_SCL_PIN 5: on both boards the SDK’s default pin choice for I2C0 is GP4/5 — the macros name default pins, they do not initialize the controller or mux the pin functions. - Layer 2 — GP4/GP5 are the documented defaults for both generations’ chips. For RP2040 the pinned mux table confirms I2C0 on GP4/5; for RP2350 this site has not verified a second-layer mux source, so the header default is not recycled here as mux proof — the full legal-pin set stays datasheet territory.
- Layer 3 — the Pico and Pico 2 records show GP4/GP5 brought out on the J1 header with no board-internal function on them.
Conclusion: on both generations GP4/5 is the documented default pair, and both boards bring it out unoccupied — wiring the module there means following the SDK’s default instead of fighting it. That is a head start, not a guarantee: the controller still needs initializing, pull-ups to 3.3 V, a common ground and the right bus address before the first transaction.
The counter-example: GP25 is not a free pin
The mirror-image mistake: picking a pin because it is near the sensor and skipping layer 3. On both the Pico and the Pico 2, GP25 is wired to the on-board LED and is not brought out on the J1/J2 header (fixed records on both board pages) — the SDK will let you write the code, and layers 1 and 2 never complain, but there is no header pin to wire to, and driving that line as a bus fights the board’s own LED. The same layer vetoes harder on the ESP32-S3-DevKitC-1: GPIO43/44 look like “UART0’s default pins”, and they are — permanently occupied by the USB-to-UART bridge. Use another UART instance for your own link, or you will fight the console for the pins.
The one trap to remember
Bus controllers are cheap; pins are the scarce resource. The flash-SPI pins on the DevKitC are a bus you must not share; the Pico’s “default” pins can move — within each peripheral’s legal mux pins for that chip; the DevKitC-1’s U0TXD/U0RXD are already taken by the bridge.
What this page cannot tell you
- Electrical/protocol details (pull-up values, clock stretching, SPI modes 0-3) — chip datasheet territory; this page is the orientation layer.
- Which UART/I2C/SPI hardware instances exist on each chip — see the chip pages’ fixed-source records and vendor datasheets.
- The RP2350’s full legal mux combinations — not yet pinned to a fixed official source here; until then this page asserts only its documented defaults.
Sources
- MCUID data records: ESP32-S3-DevKitC-1 (U0TXD/U0RXD, FSPI* pin names), ESP32-DevKitC (flash-SPI lines, WROVER GPIO16/17 condition), Pico and Pico 2 (GP4/GP5 header presence, GP25 on-board LED)
- pico-sdk pico.h / pico2.h board headers — SDK defaults: UART0 GP0/1, I2C0 SDA 4 / SCL 5 on both generations (commit
079c6f39) - pico-sdk io_bank0.h (RP2040 IO bank function select) — the RP2040 hardware mux assignments, cited for RP2040 only (commit
079c6f39) - Pin numbers, GPIO numbers and pin functions — the companion page on numbering systems