ESP32-Bit-Pirate UART to Bit-Bang Conversion: Architecture, Implementation, and Usage

The ESP32-Bit-Pirate firmware converts UART signals to bit-bang output by reconfiguring the same GPIO pins from hardware UART mode to input-only sampling mode, enabling raw sniffing of RX/TX lines as hex or ASCII streams.

The ESP32-Bit-Pirate project implements a Bus-Pirate-style analysis tool that bridges standard UART communication with bit-bang signal inspection. This dual-mode architecture allows developers to interact with serial devices while simultaneously capturing and analyzing raw line activity. The implementation spans multiple abstraction layers, from command dispatching through low-level ESP-IDF driver integration.


Architecture Overview

The firmware organizes UART functionality across five distinct layers:

Layer Key Component Primary Responsibility
Command Dispatcher ActionDispatcher::dispatchCommand Routes terminal input to UART controller
Controller UartController::handleCommand Implements all UART subcommands
Hardware Abstraction IUartService Wraps ESP-IDF UART configuration and I/O
Bit-Bang Engine UartSnifferService Raw GPIO sampling for sniffing operations
User Interface DeviceView / WebTerminalView Renders pin-out diagrams and terminal output

Each layer maintains clean separation: the controller manages state and user interaction, the service layer handles hardware, and the sniffer provides the bit-bang conversion capability.


How UART to Bit-Bang Conversion Works

Configuration Phase

Before any conversion occurs, the user establishes baseline UART parameters:

uart config

This triggers UartController::handleConfig(), which collects baud rate, data bits, parity, stop bits, and GPIO assignments via state.getUartRxPin() and state.getUartTxPin(). The configuration persists in the State object for subsequent operations.

Bit-Bang Sniffing Implementation

The core conversion happens in UartSnifferService, triggered by:

uart sniff raw

The execution flow in src/Controllers/UartController.cpp proceeds as follows:

  1. UartController::handleSniffRaw() retrieves current UART settings from State
  2. The controller invokes UartSnifferService::sniffRaw(rxPin, txPin, baudRate)
  3. The sniffer service reconfigures both pins as input-only UART ports via configurePorts()
  4. Continuous polling captures bytes from both lines simultaneously
  5. Output is interleaved with [RX] and [TX] tags, displayed as formatted hex

The same GPIO pins serve dual purposes: hardware UART peripheral during normal operation, raw digital inputs during bit-bang sampling. This pin reuse pattern is central to the conversion architecture.

For ASCII-oriented inspection, use:

uart sniff txt

This calls UartSnifferService::sniffText(), filtering non-printable characters while maintaining the same [RX]/[TX] line tagging.

Bridge Mode (Transparent Pass-Through)

The uart bridge command creates live bidirectional forwarding without bit-bang conversion:

uart bridge

UartController::handleBridge() implements a polling loop that copies UART→terminal and terminal→UART using uartService.read() and uartService.write(). Pins retain their hardware UART configuration—no reconfiguration occurs, preserving signal integrity for active communication.


Auto-Baud Detection

When line parameters are unknown, the auto-baud feature scans for valid signaling:

uart autobaud

UartController::handleAutoBaud() iterates common baud rates (9600, 19200, 38400, 57600, 115200, etc.), invoking IUartService::detectBaudByEdge() to measure pulse widths on the RX pin. Upon successful detection, the user may persist the rate to configuration.


Advanced UART Commands

Command Handler Function Purpose
uart spam "payload" interval_ms UartController::handleSpam() Repeated transmission for stress testing
uart trigger "match_hex" "response_hex" UartController::handleTrigger() Pattern-matching auto-responder
uart xmodem send/receive Via IUartService File transfer protocol support

All advanced commands build upon the same IUartService infrastructure established during initial configuration.


Complete Command Reference


# Configure UART parameters and GPIO selection

uart config
→ UartController::handleConfig()
→ IUartService::configure(baud, config, rxPin, txPin, inverted)

# Raw bit-bang sniffing with hex output

uart sniff raw
→ UartController::handleSniffRaw()
→ UartSnifferService::sniffRaw(...)

# Text-mode sniffing with ASCII filtering

uart sniff txt
→ UartController::handleSniffTxt()
→ UartSnifferService::sniffText(...)

# Transparent bridge without bit-bang conversion

uart bridge
→ UartController::handleBridge()

# Automatic baud rate detection

uart autobaud
→ UartController::handleAutoBaud()

# Repeated payload transmission

uart spam "Hello World" 500
→ UartController::handleSpam()

# Triggered response on pattern match

uart trigger "0x55 0xAA" "\x00\x01\x02"
→ UartController::handleTrigger()

Key Source Files


Summary

  • ESP32-Bit-Pirate implements UART to bit-bang conversion by reconfiguring shared GPIO pins between hardware UART and input-only sampling modes
  • The UartSnifferService provides raw line inspection with [RX]/[TX] tagged output in hex or ASCII formats
  • uart sniff raw and uart sniff txt activate bit-bang mode; uart bridge maintains transparent hardware pass-through
  • Auto-baud detection, spam testing, and trigger responses extend the core sniffing capability
  • All functionality routes through UartController with clean separation between command handling, hardware abstraction, and UI rendering

Frequently Asked Questions

How does ESP32-Bit-Pirate switch between UART and bit-bang modes?

The firmware uses pin reuse rather than mode switching. The same RX/TX GPIOs configured for hardware UART are reconfigured as input-only UART ports when sniffing begins. UartSnifferService::configurePorts() in src/Services/UartSnifferService.cpp handles this transition without physical pin changes.

What is the difference between uart sniff raw and uart bridge?

uart sniff raw activates bit-bang sampling: pins become inputs, data is captured as raw bytes, and output is formatted with [RX]/[TX] tags. uart bridge maintains hardware UART configuration, creating transparent terminal-to-device pass-through without signal analysis.

Can I sniff both directions simultaneously?

Yes. UartSnifferService::sniffRaw() polls both configured pins continuously, queuing bytes from each line separately. The output interleaves both directions with source identifiers, enabling full-duplex analysis on a single terminal.

Does auto-baud detection modify my saved configuration?

No. UartController::handleAutoBaud() tests rates ephemerally. Only explicit user confirmation persists the detected baud rate to State via the configuration flow.

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 →