# ESP32-Bit-Pirate Pinout Diagram for I2C and SPI: Complete GPIO Mapping Guide

> Master ESP32-Bit-Pirate pinout for I2C and SPI communication. Get a complete GPIO mapping guide to connect your peripherals easily and efficiently.

- Repository: [Geo/ESP32-Bit-Pirate](https://github.com/geo-tp/ESP32-Bit-Pirate)
- Tags: pinout-diagram
- Published: 2026-08-02

---

**The ESP32-Bit-Pirate uses fixed GPIO assignments defined in [`src/Adapters/Bpio2Adapter.h`](https://github.com/geo-tp/ESP32-Bit-Pirate/blob/main/src/Adapters/Bpio2Adapter.h): I²C on GPIO 17 (SCL) and GPIO 18 (SDA), SPI on IO0 (CS), IO1 (CLK), IO2 (MOSI), and IO3 (MISO).**

The **ESP32-Bit-Pirate** is an open-source firmware that turns ESP32-S3 development boards into a multi-protocol debugging tool. Whether you're bus-pirating I²C EEPROMs or flashing SPI flash chips, understanding the **pinout diagram for I2C and SPI** is essential for correct wiring. This guide breaks down the exact GPIO mappings, their source locations in the firmware, and how to customize them for your hardware.

---

## Default I²C Pinout on ESP32-S3

The I²C interface uses a dedicated **two-wire bus** with pull-up capable pins on the ESP32-S3.

| Signal | GPIO | Source Location |
|--------|------|-----------------|
| **SCL** | GPIO 17 | [`Bpio2Adapter.h`](https://github.com/geo-tp/ESP32-Bit-Pirate/blob/main/Bpio2Adapter.h) line 24 |
| **SDA** | GPIO 18 | [`Bpio2Adapter.h`](https://github.com/geo-tp/ESP32-Bit-Pirate/blob/main/Bpio2Adapter.h) line 25 |

These values are hardcoded as `I2C_SCL_PIN` and `I2C_SDA_PIN` in the adapter header. The firmware supports **I²C frequencies from 1 kHz to 1 MHz** and **transfers up to 128 bytes** per transaction, defined by `MIN_I2C_FREQUENCY`, `MAX_I2C_FREQUENCY`, and `MAX_I2C_TRANSFER` constants in the same file.

---

## Default SPI Pinout on ESP32-S3

The **SPI interface** shares clock and data lines with the I²C pins but adds dedicated chip-select and MISO lines.

| Signal | GPIO | Pin Alias | Source Location |
|--------|------|-----------|----------------|
| **CLK** | GPIO 17 | IO1 | Comment line 20 (shares I²C SCL) |
| **MOSI** | GPIO 18 | IO2 | Comment line 21 (shares I²C SDA) |
| **MISO** | GPIO 3 | IO3 | Enum line 39 |
| **CS** | GPIO 0 | IO0 | Enum line 39 |

The **IO0–IO3 naming** refers to the logical pin positions on the Bit-Pirate's 10-pin header, not the ESP32-S3 GPIO numbers. This abstraction allows the firmware to present a consistent interface across different board variants.

---

## How the Pinout Is Enforced at Runtime

The **pin-mapping layer** centralizes all hardware definitions. When you execute `mode I2C` or `mode SPI` from the CLI, the firmware:

1. **Loads the protocol-specific mapping** from [`Bpio2Adapter.h`](https://github.com/geo-tp/ESP32-Bit-Pirate/blob/main/Bpio2Adapter.h)
2. **Initializes the ESP-IDC GPIO driver** with the defined pins
3. **Updates the UI via `PinoutTransformer`** — retrieved through `DependencyProvider::getPinoutTransformer()`

The [`ActionDispatcher.cpp`](https://github.com/geo-tp/ESP32-Bit-Pirate/blob/main/ActionDispatcher.cpp) file handles the UI update trigger (see the comment "Show pinout for the mode"). This guarantees that the **web interface always reflects active pin assignments**, eliminating guesswork about which GPIOs are currently in use.

---

## Visual Pinout Reference

The project includes animated documentation for the I²C configuration:

![I²C pinout animation](https://github.com/geo-tp/ESP32-Bit-Pirate/blob/pioarduino/images/i2c.gif)

SPI uses the same physical **IO0–IO3** header positions. For board-specific wiring photos, consult the **"Pinout" section of the hardware-guide wiki**.

---

## Practical Code Examples

### Enter I²C Mode and Scan the Bus

```cpp
// Serial or web terminal
mode I2C
scan

```

The `scan` command probes all 7-bit addresses (0x01–0x7F) using the default GPIO 17/18 pins.

### Read from an I²C EEPROM

```cpp
#include "src/Services/I2cService.h"

I2cService i2c;
i2c.begin();  // Configures GPIO17/18 automatically

uint8_t buffer[4];
i2c.readBytes(0x50, 0x00, buffer, 4);  // Device 0x50, address 0x00

```

The `I2cService` class abstracts the ESP-IDF I²C driver. All pin configuration is delegated to the adapter layer.

### Flash an SPI Chip

```cpp
// CLI mode switch
mode SPI
flash write 0x0000 firmware.bin

```

The `flash` command uses the SPI pins (IO0–IO3) with the **CS on GPIO 0** for chip selection.

---

## Customizing the Pinout for Your Board

Not all ESP32-S3 boards expose GPIO 17/18. Override defaults at **compile-time** via [`platformio.ini`](https://github.com/geo-tp/ESP32-Bit-Pirate/blob/main/platformio.ini):

```ini
build_flags = -DI2C_SCL_PIN=21 -DI2C_SDA_PIN=22

```

After rebuilding, the firmware routes I²C to **GPIO 21 (SCL)** and **GPIO 22 (SDA)**. SPI pins remain fixed on IO0–IO3 unless you modify the enum definitions in [`Bpio2Adapter.h`](https://github.com/geo-tp/ESP32-Bit-Pirate/blob/main/Bpio2Adapter.h) directly.

> **Requirement:** The target board must provide **minimum 8 MB flash** for the firmware image.

---

## Key Source Files Reference

| File | Purpose |
|------|---------|
| [`src/Adapters/Bpio2Adapter.h`](https://github.com/geo-tp/ESP32-Bit-Pirate/blob/main/src/Adapters/Bpio2Adapter.h) | Central pin definitions and protocol limits |
| [`src/Controllers/I2cController.h`](https://github.com/geo-tp/ESP32-Bit-Pirate/blob/main/src/Controllers/I2cController.h) | I²C command handlers (scan, read, write) |
| [`src/Providers/DependencyProvider.cpp`](https://github.com/geo-tp/ESP32-Bit-Pirate/blob/main/src/Providers/DependencyProvider.cpp) | `PinoutTransformer` factory for UI updates |
| [`src/Dispatchers/ActionDispatcher.cpp`](https://github.com/geo-tp/ESP32-Bit-Pirate/blob/main/src/Dispatchers/ActionDispatcher.cpp) | Mode switch and pinout display triggers |

---

## Summary

- **I²C pins:** GPIO 17 (SCL), GPIO 18 (SDA) — defined in [`Bpio2Adapter.h`](https://github.com/geo-tp/ESP32-Bit-Pirate/blob/main/Bpio2Adapter.h) lines 24–25
- **SPI pins:** IO0/CS (GPIO 0), IO1/CLK (GPIO 17), IO2/MOSI (GPIO 18), IO3/MISO (GPIO 3)
- **Frequency range:** 1 kHz – 1 MHz for I²C, with 128-byte maximum transfers
- **Runtime enforcement:** `PinoutTransformer` ensures UI sync with active GPIO configuration
- **Customization:** Override via [`platformio.ini`](https://github.com/geo-tp/ESP32-Bit-Pirate/blob/main/platformio.ini) build flags or direct header edits

---

## Frequently Asked Questions

### Can I use I²C and SPI simultaneously on ESP32-Bit-Pirate?

No. The firmware operates in **single-protocol modes**. Switching with `mode I2C` or `mode SPI` reconfigures the GPIO matrix for the active protocol. The shared CLK/MOSI pins (GPIO 17/18) would create bus conflicts if both protocols ran concurrently.

### Does the pinout work on ESP32 (non-S3) boards?

Partially. The **ESP32-Bit-Pirate targets ESP32-S3 specifically** for its USB-OTG capabilities and memory layout. While the GPIO numbers may function on classic ESP32, the firmware is compiled and tested against S3 silicon. Use the official S3 Dev-Kit, LILYGO T-Display S3, or M5 Atom S3 Lite for guaranteed compatibility.

### How do I verify my wiring matches the active pinout?

Launch the **web UI** after selecting a mode. The `PinoutTransformer` renders the current GPIO mapping based on `DependencyProvider::getPinoutTransformer()`. Cross-reference with the animated I²C GIF in `images/i2c.gif` and the header positions IO0–IO3 documented in the wiki.

### What happens if I override pins to values already used by SPI?

**Avoid overlap.** The adapter does not validate GPIO collisions at compile-time. If you set `I2C_SCL_PIN=0` (SPI CS) or `I2C_SDA_PIN=3` (SPI MISO), runtime behavior is undefined. Keep I²C overrides on GPIOs outside the IO0–IO3 range unless you intend to sacrifice SPI functionality.