QEMU Emulator Guide

The ESP32 Distance Sensor project includes full QEMU emulation support, allowing you to develop and test without physical hardware. The emulator includes complete network functionality via a UART-based IP tunnel.

Quick Start

Starting QEMU

For Normal Operation:

# From project root - runs immediately
./tools/qemu/run_qemu.sh

For Debugging:

# Starts QEMU and waits for GDB debugger
./tools/qemu/run_qemu_debug.sh

Or use VS Code tasks:

  • Run QEMU (No Debug) - Start emulator immediately

  • Run QEMU Debug - Start with GDB support, then press F5 to attach debugger

Both scripts automatically:

  • ✅ Build the project if needed (incremental, fast)

  • ✅ Start the network stack (TUN device, bridge)

  • ✅ Create Unix domain sockets for UART communication

  • ✅ Launch QEMU with proper configuration

Stopping QEMU

./tools/qemu/stop_qemu.sh

Or use VS Code task: Stop QEMU

Accessing the Web Interface

Once QEMU is running:

# Direct access to emulated ESP32
curl http://192.168.100.2/

# Via HTTP proxy (if configured)
curl http://localhost:8080/

The web interface should be accessible at http://192.168.100.2 in your browser.

Architecture Overview

┌────────────────────────────────────────────────────────────┐
│                    Host System (Linux)                     │
│                                                            │
│  ┌──────────┐           ┌──────────────────┐              │
│  │ Browser  │──────────▶│   tun0 Device    │              │
│  │          │    HTTP   │  192.168.100.1   │              │
│  └──────────┘           └──────────────────┘              │
│                                  │                         │
│                         ┌────────▼─────────┐               │
│                         │   TUN Bridge     │               │
│                         │   (Python)       │               │
│                         └────────┬─────────┘               │
│                                  │ Unix Socket             │
│                         temp/esp32-uart1.sock              │
└──────────────────────────────────┼────────────────────────┘
                                   │
                                   │ QEMU chardev
┌──────────────────────────────────┼────────────────────────┐
│                   ESP32 QEMU     │                        │
│                                  ▼                        │
│  ┌────────────┐    ┌──────────┐    ┌──────────────┐      │
│  │    Web     │    │   lwIP   │    │    UART1     │      │
│  │   Server   │◄──►│  Stack   │◄──►│   Driver     │      │
│  │   :80      │    │ 192.168. │    │              │      │
│  │            │    │ 100.2/24 │    └──────────────┘      │
│  └────────────┘    └──────────┘                          │
│                                                           │
│  Console Logs (ESP_LOG*)                                 │
│         │                                                 │
│         ▼                                                 │
│  ┌──────────────┐      Unix Socket                       │
│  │    UART0     │──────temp/esp32-uart0.sock────────────▶│
│  └──────────────┘                                        │
└───────────────────────────────────────────────────────────┘

Network Configuration

IP Addresses

Component

IP Address

Description

Host TUN device

192.168.100.1/24

Gateway for emulated ESP32

ESP32 QEMU

192.168.100.2/24

Emulated device IP

Communication Channels

Channel

Location

Purpose

UART0 Socket

temp/esp32-uart0.sock

Console output (ESP_LOG*)

UART1 Socket

temp/esp32-uart1.sock

Network tunnel (IP packets)

GDB Port (debug)

TCP 3333

Debugger connection (when -d)

Note

This project uses Unix domain sockets for UART communication instead of TCP ports. This eliminates “port already in use” errors and provides better performance.

How It Works

UART-Based IP Tunnel

The emulator uses UART1 as a network interface:

  1. Ethernet Frame Encapsulation: IP packets are wrapped in Ethernet frames

  2. Length Prefix Protocol: Each frame is prefixed with 2-byte length (big-endian)

  3. UART Transport: Frames are transmitted over UART1 (115200 baud)

  4. TUN Bridge: Python script bridges UART ↔ TUN device

Network Stack

Application (Web Server, your code)
         ↓
    lwIP TCP/IP Stack
         ↓
Custom UART Network Interface (netif_uart_tunnel_sim.c)
         ↓
      UART1 Driver
         ↓
   QEMU Serial Device
         ↓
    TUN Bridge (Python)
         ↓
      Linux TUN Device
         ↓
     Host Network Stack

Frame Format

┌───────────────┬──────────────────────────────────────────┐
│ Frame Length  │         Ethernet Frame                   │
│   (2 bytes)   │    (14-byte header + IP packet)          │
│  Big Endian   │                                          │
├───────────────┼──────────────────────────────────────────┤
│  [HI] [LO]    │ [DST_MAC:6][SRC_MAC:6][TYPE:2][IP DATA]  │
└───────────────┴──────────────────────────────────────────┘

Example: ICMP Echo Request (98 bytes)

Length: 0x00 0x62 (98 bytes)
Ethernet:
     Dst MAC: 02:00:00:00:00:02 (ESP32)
     Src MAC: 02:00:00:00:00:01 (Host)
     Type:    0x08 0x00 (IPv4)
IP Packet:
     Src IP: 192.168.100.1
     Dst IP: 192.168.100.2
     Protocol: ICMP

Testing Network Connectivity

Ping Test

# Ping the emulated ESP32
ping -c 4 192.168.100.2

Expected output:

64 bytes from 192.168.100.2: icmp_seq=1 ttl=64 time=5.2 ms
64 bytes from 192.168.100.2: icmp_seq=2 ttl=64 time=3.8 ms

HTTP Test

# Direct access
curl http://192.168.100.2/

# Via proxy
curl http://localhost:8080/

Monitor Network Traffic

# Watch TUN device traffic
sudo tcpdump -i tun0 -n

# Monitor UART traffic in QEMU logs
# Look for "RX:" and "TX:" messages

Troubleshooting

QEMU Won’t Start

Problem: Script fails to start QEMU

Solutions:

# Check if QEMU is already running
ps aux | grep qemu-system-xtensa

# Stop any existing QEMU instances
./tools/qemu/stop_qemu.sh

# Clean old socket files
rm -f temp/esp32-uart*.sock

# Rebuild and try again
idf.py build
./tools/qemu/run_qemu.sh

Network Not Working

Problem: Can’t ping or access web server

Checks:

# 1. Verify TUN device exists
ip addr show tun0

# 2. Check TUN bridge is running
ps aux | grep serial_tun_bridge

# 3. Verify UART1 socket exists
ls -l temp/esp32-uart1.sock

# 4. Restart everything
./tools/qemu/stop_qemu.sh
./tools/qemu/run_qemu.sh

No Console Output

Problem: No logs from QEMU

Solution:

# Use the UART0 terminal viewer
./tools/qemu/uart0_terminal.sh

This connects to the UART0 Unix socket and displays ESP_LOG* output.

Debugging with GDB

Problem: Want to debug but QEMU runs too fast

Solution:

Use the debug script and VS Code integration:

  1. Start QEMU in debug mode: ./tools/qemu/run_qemu_debug.sh (or use VS Code task)

  2. QEMU will wait for debugger connection

  3. Press F5 in VS Code to attach debugger

  4. Set breakpoints and step through code

See Debugging Guide for complete GDB debugging workflow.

Advanced Usage

Debugging Network Issues

Enable verbose logging in netif_uart_tunnel_sim.c:

// Temporarily change log level
#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE

Then rebuild and watch detailed packet flow:

idf.py build
./tools/run-qemu-network.sh

Custom Network Configuration

Edit main/components/netif_uart_tunnel/netif_uart_tunnel_sim.c:

// Change IP address
#define ESP32_IP "192.168.100.2"
#define GATEWAY_IP "192.168.100.1"
#define NETMASK "255.255.255.0"

Running QEMU

Two modes available:

  1. Normal Mode (runs immediately):

    ./tools/qemu/run_qemu.sh
    
  2. Debug Mode (waits for GDB):

    ./tools/qemu/run_qemu_debug.sh
    

Both modes use the same QEMU instance with Unix socket communication. Debug mode adds the -d flag to enable GDB support and wait for debugger attachment.

Technical Deep Dive

For detailed information about the network implementation, packet flow, and lwIP integration, see QEMU Network Internals.

Known Limitations

  • UART Speed: Limited to 115200 baud (adequate for HTTP, slow for large transfers)

  • No WiFi Simulation: Uses direct IP connectivity instead of WiFi AP/STA modes

  • Browser Caching: Web interface may cache old versions (use Ctrl+F5 to refresh)

  • QEMU Performance: Slower than real hardware, but sufficient for testing

  • HTTPS Not Working: SSL/TLS support in QEMU is work in progress

Next Steps