# Debugging I2C Communication with ESP32-Bit-Pirate: A Complete Technical Guide

> Debug I2C communication with ESP32-Bit-Pirate using six powerful commands. Learn how to scan, sniff, ping, dump, write, and recover I2C devices with this technical guide.

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

---

**You can debug I2C communication with ESP32-Bit-Pirate using six built-in commands—`scan`, `sniff`, `ping`, `dump`, `write`, and `recover`—all orchestrated by the `I2cController` class in [`src/Controllers/I2cController.cpp`](https://github.com/geo-tp/ESP32-Bit-Pirate/blob/main/src/Controllers/I2cController.cpp).**

ESP32-Bit-Pirate is an open-source firmware project that transforms ESP32 dev boards into a professional I2C debugging and manipulation tool. The `I2cController` class provides immediate terminal access to bus diagnostics, register inspection, and advanced hardware attacks—all without external logic analyzers or host software.

## Architecture of the I2C Subsystem

The ESP32-Bit-Pirate firmware organizes I2C functionality into a clean layered architecture:

| Layer | Component | Purpose |
|-------|-----------|---------|
| **UI** | `ITerminalView` & `IInput` | Displays formatted results and captures keystrokes to interrupt long-running operations. |
| **Controller** | `I2cController` | Parses sub-commands and coordinates the entire debugging workflow. |
| **Service** | `II2cService` | Wraps ESP-IDF's `TwoWire` driver and adds custom utilities like bit-bang recovery. |
| **Hardware** | [`i2c_sniffer.cpp`](https://github.com/geo-tp/ESP32-Bit-Pirate/blob/main/i2c_sniffer.cpp) | ISR-driven capture of raw SDA/SCL transitions without bus participation. |

The controller validates arguments through `tryParseAddress` and `ArgTransformer`, checks device presence via `i2cService.beginTransmission`, executes the requested operation, and reports through `terminalView`. All I2C-specific routing happens through the single entry point `handleCommand(const TerminalCommand&)`.

## Core Debugging Commands

### Bus Scanning with `i2c scan`

The **bus scanner** iterates addresses `0x01`–`0x7E` and reports any device that ACKs:

```text
> i2c scan
I2C Scan: Scanning I2C bus... Press [ENTER] to stop

Found device at 0x3C
Found device at 0x50

I2C Scan: 

```

Implementation in [`src/Controllers/I2cController.cpp`](https://github.com/geo-tp/ESP32-Bit-Pirate/blob/main/src/Controllers/I2cController.cpp) (lines 71–90):

```cpp
for (uint8_t addr = 1; addr < 127; ++addr) {
    i2cService.beginTransmission(addr);
    if (i2cService.endTransmission() == 0) {
        // Device responded with ACK
        terminalView.println("Found device at 0x" + String(addr, HEX));
    }
}

```

The scan runs asynchronously—press **ENTER** at any time to abort.

### Live Traffic Capture with `i2c sniff`

The **hardware sniffer** records raw bus activity without becoming master or slave, avoiding probe effects:

```text
> i2c sniff
I2C Sniffer: Listening on SCL/SDA... Press [ENTER] to stop.

START 0x3C W
  00  05  01  10
STOP

```

The underlying implementation in [`src/Vendors/i2c_sniffer.cpp`](https://github.com/geo-tp/ESP32-Bit-Pirate/blob/main/src/Vendors/i2c_sniffer.cpp) uses edge-triggered ISRs:

```cpp
i2c_sniffer_begin(state.getI2cSclPin(), state.getI2cSdaPin());
while (i2c_sniffer_available()) {
    // Decode and print each captured transaction
}

```

This operates independently of the ESP-IDF I2C driver, capturing even malformed or out-of-spec traffic that standard drivers would reject.

### Device Presence Testing with `i2c ping`

**Ping** verifies ACK latency and basic connectivity:

```text
> i2c ping 0x3C
Ping 0x3C: I2C Ping: ACK received! Device is present.

```

The `handlePing` method (lines 137–162) wraps `beginTransmission`/`endTransmission` with timing instrumentation.

### Register Inspection with `i2c dump`

**Dump** extracts memory contents with automatic fallback strategies:

```text
> i2c dump 0x50 64
I2C Dump: 0x50 from 0x00 for 64 bytes... Press [ENTER] to stop.

00: FF FF FF FF FF FF FF FF  ?? ?? ?? ?? ?? ?? ?? ??
...

```

The `handleDump` implementation (lines 82–130) attempts `performRegisterRead` first—sending a register pointer then reading sequentially. If the device lacks pointer semantics, it falls back to `performRawRead`. Results render through `printHexDump` with ASCII annotations.

### Advanced Operations: Jam, Glitch, and Recovery

The firmware includes **hardware attack primitives** for security research:

- **`i2c jam`**—drives SCL/SDA to contested states to test error handling
- **`i2c glitch`**—inserts timing violations to bypass authentication
- **`i2c recover`**—executes bit-bang bus recovery when the bus hangs

Bus recovery implementation in `handleRecover` (lines 109–118):

```cpp
i2cService.i2cBitBangRecoverBus();
// Toggles SCL while holding SDA high to force STOP condition

```

Typical recovery workflow:

```text
> i2c jam
I2C Jam: Perturbing bus SCL/SDA... Press [ENTER] to stop.

I2C Reset: Attempting to recover I2C bus...
I2C Reset: SDA released. Bus recovery successful.

```

### Writable Register Probing with `i2c regs`

Identify which registers accept modifications:

```text
> i2c regs 0x50 16
[I2C Registers Summary]
 Tested   : 16
 Readable : 16
 Writable : 4

```

`handleRegs` (lines 88–125) delegates to `probeRegRW` in `II2cService`, performing read-modify-verify cycles across the specified range.

## Configuration and State Management

Runtime I2C parameters live in `GlobalState` and can be modified without recompiling:

```text
> i2c config sda 21
> i2c config scl 22
> i2c config freq 400000

```

The `handleConfig` handler (lines 98–115) immediately reconfigures the service layer. Pin definitions for supported boards (Waveshare S3 Geek, Stamp S3) reside in `src/Boards/`.

## Key Source Files Reference

| File | Location | Responsibility |
|------|----------|----------------|
| [`I2cController.cpp`](https://github.com/geo-tp/ESP32-Bit-Pirate/blob/main/I2cController.cpp) | `src/Controllers/` | Command parsing, workflow orchestration |
| [`i2c_sniffer.cpp`](https://github.com/geo-tp/ESP32-Bit-Pirate/blob/main/i2c_sniffer.cpp) | `src/Vendors/` | ISR-based passive bus monitoring |
| `II2cService.h/cpp` | `src/Services/` | Low-level I2C abstraction layer |
| [`ArgTransformer.cpp`](https://github.com/geo-tp/ESP32-Bit-Pirate/blob/main/ArgTransformer.cpp) | `src/Transformers/` | Hex/decimal argument parsing |
| `GlobalState` | `src/State/` | Runtime configuration persistence |

## Summary

- **ESP32-Bit-Pirate debugging I2C communication** requires no external tools—commands execute directly on the ESP32 hardware.
- The **`I2cController`** class in [`src/Controllers/I2cController.cpp`](https://github.com/geo-tp/ESP32-Bit-Pirate/blob/main/src/Controllers/I2cController.cpp) dispatches all operations through `handleCommand`.
- **Six primary commands** cover discovery (`scan`), observation (`sniff`), health checks (`ping`), memory inspection (`dump`), hardware attacks (`jam`/`glitch`), and recovery (`recover`).
- **Automatic fallback strategies** in `handleDump` adapt to devices with or without register-pointer semantics.
- **Bit-bang recovery** via `i2cBitBangRecoverBus` can unhang buses without power cycling.

## Frequently Asked Questions

### What ESP32 boards are compatible with ESP32-Bit-Pirate I2C debugging?

ESP32-Bit-Pirate targets boards with dedicated SDA/SCL breakout pins, including the **Waveshare S3 Geek** and **Stamp S3**. Pin mappings are defined in `src/Boards/` and can be overridden at runtime via `i2c config`. The firmware uses standard ESP-IDF GPIO so any ESP32-S3 or ESP32 variant should work with appropriate board definitions.

### How does the I2C sniffer avoid interfering with bus traffic?

Unlike active I2C masters, the sniffer implementation in [`src/Vendors/i2c_sniffer.cpp`](https://github.com/geo-tp/ESP32-Bit-Pirate/blob/main/src/Vendors/i2c_sniffer.cpp) configures pins as **inputs with edge-triggered interrupts** only. It never drives SDA or SCL, eliminating probe loading effects. The ISR records timestamped transitions that software later decodes into START, STOP, ACK, and data bytes.

### Can ESP32-Bit-Pirate recover a locked I2C bus without hardware reset?

Yes. The `i2c recover` command executes **clock stretching recovery**—toggling SCL up to 9 times while SDA is held high—to generate a STOP condition that releases any stuck slave. This `i2cBitBangRecoverBus` implementation works even when the ESP-IDF `TwoWire` driver has lost bus arbitration.

### What addressing modes does the I2C scanner support?

The scanner in `handleScan` checks **7-bit addresses from 0x01 to 0x7E** (0x00 is general call, 0x78–0x7F are reserved). 10-bit addressing is not currently implemented in the open-source firmware. Devices responding at any valid address are reported with hexadecimal formatting.