# How to Set Up ESP32-Bit-Pirate for I²C Communication: Complete Configuration Guide

> Set up ESP32-Bit-Pirate for I2C communication easily. Enable I2C in your sketch, include the Wire library, and connect peripherals to default SDA SCL pins for seamless integration.

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

---

**Enable I²C on ESP32-Bit-Pirate by defining `I2C_DISPLAY` in your sketch, including the Arduino Wire library, and connecting peripherals to the default SDA (GPIO 21) and SCL (GPIO 22) pins.**

The ESP32-Bit-Pirate firmware provides native support for I²C peripherals through the standard Arduino **Wire** library. This article explains how to configure I²C communication using the repository's built-in examples, with specific reference to the `scannerGraphic.ino` sketch that demonstrates an **Adafruit SSD1306** OLED display over I²C.

## Architecture of I²C Support in ESP32-Bit-Pirate

The I²C subsystem in this repository follows a layered architecture that abstracts hardware details while maintaining flexibility for different peripheral types.

### Core Components

- **Wire library** — The foundation I²C driver for ESP32, automatically initialized by peripheral libraries such as *Adafruit_SSD1306*
- **Compile-time peripheral selection** — Display type chosen via macros in `scannerGraphic.ino` (lines 45-47)
- **Display driver abstraction** — `Adafruit_SSD1306` instance receiving a reference to the Wire object (line 123)

### Pin Mapping Defaults

The ESP32-Bit-Pirate board routes I²C to Arduino-compatible pins with these default assignments:

| Signal | ESP32 GPIO | Function |
|--------|-----------|----------|
| SDA | 21 | Serial data line |
| SCL | 22 | Serial clock line |

No additional pin configuration is required unless you need to remap the I²C bus to alternate GPIOs.

## Enabling I²C in Your Sketch

Follow these steps to activate I²C mode in the graphic scanner example or your own code.

### Step 1: Select I²C Display Mode

In `lib/RF24/examples/scannerGraphic/scannerGraphic.ino`, modify lines 45-47:

```cpp
#define I2C_DISPLAY      // Enable this for I²C OLED
// #define SPI_DISPLAY    // Comment out SPI mode

```

Only one display macro may be defined at compile time.

### Step 2: Install Required Libraries

Using the Arduino Library Manager or PlatformIO, install:

- **Adafruit GFX Library** — Base graphics primitives
- **Adafruit SSD1306** — OLED display driver

These dependencies are also declared in the repository's [`platformio.ini`](https://github.com/geo-tp/ESP32-Bit-Pirate/blob/main/platformio.ini).

### Step 3: Hardware Connection

Connect your I²C peripheral to the board's header:

- SDA → Pin 21 (or labeled SDA header)
- SCL → Pin 22 (or labeled SCL header)
- VCC → 3.3 V
- GND → Ground

### Step 4: Address Configuration

The default OLED address is **0x3D** for 128×64 displays. For 128×32 displays, change to **0x3C**:

```cpp
#define SCREEN_ADDRESS 0x3C   // For 128×32 OLED variants

```

## I²C OLED Implementation Example

The complete initialization pattern from `scannerGraphic.ino` demonstrates proper setup:

```cpp
// Choose I²C display (comment out SPI_DISPLAY)
#define I2C_DISPLAY

#include <Wire.h>
#include <Adafruit_SSD1306.h>

// OLED configuration
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3D   // 0x3C for 128×32 displays

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void setup() {
  // Initialize OLED; Wire is started internally by begin()
  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    while (true);   // Halt initialization on failure
  }
  display.clearDisplay();
  // Continue with radio and scanner setup...
}

```

**Critical detail:** The call to `display.begin()` internally invokes `Wire.begin()`—no explicit `Wire.begin()` is required in your setup code when using the Adafruit library.

## Direct Wire Library Usage

For non-display I²C peripherals, use the Wire library directly. This pattern applies to the PN532 NFC reader examples in the repository:

```cpp
#include <Wire.h>

void setup() {
  Wire.begin();                 // SDA=GPIO21, SCL=GPIO22
  Serial.begin(115200);
}

void loop() {
  Wire.beginTransmission(0x50); // Target device address
  Wire.write(0x00);             // Register/sub-address
  Wire.endTransmission();

  Wire.requestFrom(0x50, 1);    // Request 1 byte
  if (Wire.available()) {
    uint8_t val = Wire.read();
    Serial.println(val);
  }
  delay(1000);
}

```

This approach is used in `lib/PN532/examples/readMifare/readMifare.ino` for NFC communication over I²C.

## Key Source Files Reference

| File | Purpose |
|------|---------|
| `lib/RF24/examples/scannerGraphic/scannerGraphic.ino` | Primary example showing I²C/SPI display selection |
| `lib/PN532/examples/readMifare/readMifare.ino` | NFC reader using direct Wire library calls |
| [`platformio.ini`](https://github.com/geo-tp/ESP32-Bit-Pirate/blob/main/platformio.ini) | Global dependencies and build configuration |
| `lib/Adafruit_SSD1306/` | OLED driver library (bundled) |
| `lib/Adafruit_GFX/` | Graphics primitives library (bundled) |

## Troubleshooting ESP32-Bit-Pirate I²C Setup

| Symptom | Cause | Solution |
|---------|-------|----------|
| Display won't initialize | Wrong I²C address | Try 0x3C instead of 0x3D |
| Compilation error "SPI_DISPLAY redefined" | Both macros active | Comment out `#define SPI_DISPLAY` |
| No signal on SDA/SCL | Wrong pins used | Verify GPIO 21/22 connection |
| Wire.begin() causes crash | Called twice | Remove explicit call when using Adafruit library |

## Summary

- **ESP32-Bit-Pirate I²C setup** requires selecting the `I2C_DISPLAY` macro and including [`Wire.h`](https://github.com/geo-tp/ESP32-Bit-Pirate/blob/main/Wire.h)
- Default pins **GPIO 21 (SDA)** and **GPIO 22 (SCL)** need no configuration
- The **Adafruit SSD1306** library handles `Wire.begin()` internally—do not call it explicitly
- Repository examples in `lib/RF24/examples/` and `lib/PN532/examples/` provide working templates
- Address **0x3D** is standard for 128×64 OLEDs; use **0x3C** for 128×32 variants

## Frequently Asked Questions

### Does ESP32-Bit-Pirate support custom I²C pin remapping?

Yes. While the default SDA/SCL pins are GPIO 21 and 22, you can specify alternate pins by calling `Wire.begin(uint8_t sda, uint8_t scl)` before initializing your peripheral. This overrides the default mapping without modifying the board's physical headers.

### Why does my OLED show nothing even with correct wiring?

The most common cause is an **incorrect I²C address**. Many 128×64 OLED modules ship configured for **0x3C** rather than **0x3D**. Scan the I²C bus with a simple sketch to detect the actual address, or try both values in `SCREEN_ADDRESS`.

### Can I use both I²C and SPI peripherals simultaneously?

Yes, but not for the display subsystem in the scanner example—the **compile-time macros enforce mutual exclusion** between `I2C_DISPLAY` and `SPI_DISPLAY`. For other combinations (I²C OLED + SPI radio), modify the sketch to remove the `#ifndef` guard that prevents both defines.

### Is the Wire library included in the ESP32-Bit-Pirate firmware?

The **Wire library is part of the ESP32 Arduino core**, not bundled separately in this repository. The [`platformio.ini`](https://github.com/geo-tp/ESP32-Bit-Pirate/blob/main/platformio.ini) configuration ensures it's available during compilation. The repository includes wrapper examples demonstrating proper integration patterns.