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

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.

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 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:

> 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 (lines 71–90):

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:

> 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 uses edge-triggered ISRs:

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:

> 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:

> 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):

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

Typical recovery workflow:

> 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:

> 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:

> 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 src/Controllers/ Command parsing, workflow orchestration
i2c_sniffer.cpp src/Vendors/ ISR-based passive bus monitoring
II2cService.h/cpp src/Services/ Low-level I2C abstraction layer
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 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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →