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 handlingi2c glitch—inserts timing violations to bypass authenticationi2c 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
I2cControllerclass insrc/Controllers/I2cController.cppdispatches all operations throughhandleCommand. - 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
handleDumpadapt to devices with or without register-pointer semantics. - Bit-bang recovery via
i2cBitBangRecoverBuscan 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →