Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
c0aa75c
Initial plan
Copilot Dec 25, 2025
d8934bb
Add QEMU-based E2E testing infrastructure with Playwright
Copilot Dec 25, 2025
afbe8fc
Complete QEMU-based E2E testing infrastructure
Copilot Dec 25, 2025
98accba
Add implementation summary for QEMU E2E testing
Copilot Dec 25, 2025
314bf7f
Fix QEMU download URL - update to esp-develop-9.2.2-20250817
Copilot Dec 26, 2025
19df216
Fix missing QEMU dependencies - install libSDL2 and verify libraries
Copilot Dec 26, 2025
129a138
Change port forwarding from 80 to 8080 (non-privileged port)
Copilot Dec 26, 2025
e3f25f6
Add exception decoding and crash analysis for QEMU output
Copilot Dec 26, 2025
0bcd288
Switch to ethernet build to avoid WiFi crashes in QEMU
Copilot Dec 26, 2025
79a1d5c
Switch to esp32_4MB_V4_S_eth build with ESP32-POE ethernet config
Copilot Dec 27, 2025
3c9a68f
Document QEMU DHCP networking and add static IP option
Copilot Dec 27, 2025
a957eed
Fix remaining esp32dev references in scripts
Copilot Dec 27, 2025
104e4ec
(experimental) WLED_DISABLE_WIFI
softhack007 Dec 27, 2025
28a0d00
adding [esp32_16MB_V4_M_eth_debug] for QEMU tests
softhack007 Dec 27, 2025
e5dfccb
Switch to esp32_16MB_V4_M_eth_debug build for QEMU tests
Copilot Dec 27, 2025
3a8b123
Configure QEMU for 16MB flash to match debug build
Copilot Dec 27, 2025
9bda207
Add stack trace analysis and manual exception decoder instructions
Copilot Dec 27, 2025
4beb79b
(experimental) enable open_eth for esp-idf
softhack007 Dec 27, 2025
cee5708
Add WLED_QEMU workaround to skip ethernet hardware init
Copilot Dec 27, 2025
dafd718
Add WLED_QEMU WiFi workaround to prevent crashes
Copilot Dec 27, 2025
949d42f
fix WLED_QEMU build
softhack007 Dec 27, 2025
496b5c3
rename esp32_16MB_V4_M_eth_debug -> esp32_16MB_QEMU_debug
softhack007 Dec 27, 2025
d947fdd
allow to set static IP adress at compile time
softhack007 Dec 27, 2025
4b3f5ed
remove confliciting usermods from QEMU build
softhack007 Dec 27, 2025
b99ebed
Add network verification tests and improve logging in CI
Copilot Dec 27, 2025
f79f0bd
QEMU: manually init ethernet and DHCP via esp-idf
softhack007 Dec 27, 2025
6f7138a
script tinkering
softhack007 Dec 27, 2025
451103e
next try
softhack007 Dec 27, 2025
13e9f60
typo fixo
softhack007 Dec 27, 2025
495cd69
keep registering callback in initConnection()
softhack007 Dec 27, 2025
d541c21
let's try with the tasmota platform
softhack007 Dec 27, 2025
c811a0c
back to standard arduino framework
softhack007 Dec 27, 2025
e47b3ef
typo
softhack007 Dec 27, 2025
2dd31c6
let's try a simpler build
softhack007 Dec 27, 2025
ddd148e
QEMU: remove serial protocols
softhack007 Dec 27, 2025
d1d71f7
Fix confusing exit code 1 in QEMU status check
Copilot Dec 27, 2025
aa580c3
disabling some more wifi calls, and script tinkering
softhack007 Dec 27, 2025
6308df9
shorter wait time (1min), less verbose
softhack007 Dec 27, 2025
db57333
minor e2e script optimization
softhack007 Dec 27, 2025
f0ee919
Add enhanced DHCP/network diagnostics and ping tests
Copilot Dec 28, 2025
01a5df1
Fix HTTP server startup by bypassing uninitialized ETH object in QEMU
Copilot Dec 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions .github/scripts/monitor-qemu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#!/usr/bin/env python3
"""
Monitor QEMU ESP32 serial output and decode exceptions
This script watches the QEMU serial output and uses the ESP32 exception decoder
to translate stack traces into human-readable format.
"""

import sys
import re
import subprocess
import os

def find_elf_file(firmware_dir):
"""Find the ELF file for symbol resolution"""
elf_path = os.path.join(firmware_dir, "firmware.elf")
if os.path.exists(elf_path):
return elf_path
return None

def decode_exception(lines, elf_file):
"""Decode an ESP32 exception using addr2line"""
if not elf_file or not os.path.exists(elf_file):
return None

# Extract addresses from backtrace
addresses = []
for line in lines:
# Look for patterns like: 0x4008xxxx:0x3ffbxxxx
matches = re.findall(r'0x[0-9a-fA-F]{8}', line)
addresses.extend(matches)

if not addresses:
return None

# Use addr2line to decode addresses
try:
# Get the toolchain path from environment or use default
toolchain_prefix = os.environ.get('TOOLCHAIN_PREFIX', 'xtensa-esp32-elf-')
addr2line = f"{toolchain_prefix}addr2line"

cmd = [addr2line, '-e', elf_file, '-f', '-C'] + addresses
result = subprocess.run(cmd, capture_output=True, text=True, timeout=5)

if result.returncode == 0 and result.stdout:
return result.stdout
except Exception as e:
print(f"[Decoder] Error decoding: {e}", file=sys.stderr)

return None

def monitor_output(firmware_dir):
"""Monitor stdin and decode exceptions"""
elf_file = find_elf_file(firmware_dir)

if elf_file:
print(f"[Decoder] Using ELF file: {elf_file}", file=sys.stderr)
else:
print(f"[Decoder] Warning: ELF file not found in {firmware_dir}", file=sys.stderr)
print(f"[Decoder] Exception decoding will not be available", file=sys.stderr)

exception_lines = []
in_exception = False

for line in sys.stdin:
# Print the original line
print(line, end='', flush=True)

# Detect exception start
if 'Guru Meditation Error' in line or 'Backtrace:' in line or 'abort()' in line:
in_exception = True
exception_lines = [line]
print("\n[Decoder] ========== ESP32 EXCEPTION DETECTED ==========", file=sys.stderr)
elif in_exception:
exception_lines.append(line)

# Check if exception block ended
if line.strip() == '' or 'ELF file SHA256' in line or len(exception_lines) > 20:
# Try to decode
decoded = decode_exception(exception_lines, elf_file)
if decoded:
print("\n[Decoder] Decoded stack trace:", file=sys.stderr)
print(decoded, file=sys.stderr)
print("[Decoder] ================================================\n", file=sys.stderr)
else:
print("[Decoder] Could not decode exception (toolchain not available)", file=sys.stderr)
print("[Decoder] ================================================\n", file=sys.stderr)

in_exception = False
exception_lines = []

if __name__ == '__main__':
firmware_dir = sys.argv[1] if len(sys.argv) > 1 else '.pio/build/esp32_16MB_QEMU_debug'
monitor_output(firmware_dir)
108 changes: 108 additions & 0 deletions .github/scripts/run-qemu.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#!/bin/bash
# Run WLED firmware in QEMU ESP32
# This script starts QEMU with the compiled firmware and enables network access
#
# Note: QEMU ESP32 emulation has limitations:
# - Not all peripherals are fully emulated (WiFi, I2C, some GPIOs)
# - Some firmware features may crash in QEMU but work on real hardware
# - This is expected behavior for testing web UI functionality

set -e

FIRMWARE_DIR="${1:-.pio/build/esp32_16MB_QEMU_debug}"
QEMU_DIR="${2:-qemu-esp32}"
HTTP_PORT="${3:-8080}" # Default to 8080 (non-privileged port)

if [ ! -d "$FIRMWARE_DIR" ]; then
echo "Error: Firmware directory not found: $FIRMWARE_DIR"
exit 1
fi

if [ ! -f "${QEMU_DIR}/qemu-system-xtensa" ] && [ ! -f "${QEMU_DIR}/bin/qemu-system-xtensa" ]; then
echo "Error: QEMU not found at ${QEMU_DIR}/qemu-system-xtensa or ${QEMU_DIR}/bin/qemu-system-xtensa"
echo "Please run setup-qemu.sh first"
exit 1
fi

# Determine QEMU binary location
if [ -f "${QEMU_DIR}/qemu-system-xtensa" ]; then
QEMU_BIN="${QEMU_DIR}/qemu-system-xtensa"
else
QEMU_BIN="${QEMU_DIR}/bin/qemu-system-xtensa"
fi

# Check for required firmware files
BOOTLOADER="${FIRMWARE_DIR}/bootloader.bin"
PARTITIONS="${FIRMWARE_DIR}/partitions.bin"
FIRMWARE="${FIRMWARE_DIR}/firmware.bin"

if [ ! -f "$BOOTLOADER" ]; then
echo "Error: Bootloader not found: $BOOTLOADER"
exit 1
fi

if [ ! -f "$FIRMWARE" ]; then
echo "Error: Firmware not found: $FIRMWARE"
exit 1
fi

echo "Starting QEMU ESP32 with WLED firmware"
echo "Firmware directory: $FIRMWARE_DIR"
echo "HTTP will be accessible at: http://localhost:${HTTP_PORT}"

# Create a merged flash image as QEMU expects
FLASH_IMAGE="/tmp/wled_flash.bin"
echo "Creating flash image at $FLASH_IMAGE"

# Create a 16MB flash image (0x1000000 bytes) for esp32_16MB_QEMU_debug
dd if=/dev/zero of="$FLASH_IMAGE" bs=1M count=16 2>/dev/null

# Write bootloader at 0x1000
if [ -f "$BOOTLOADER" ]; then
dd if="$BOOTLOADER" of="$FLASH_IMAGE" bs=1 seek=$((0x1000)) conv=notrunc 2>/dev/null
fi

# Write partitions at 0x8000
if [ -f "$PARTITIONS" ]; then
dd if="$PARTITIONS" of="$FLASH_IMAGE" bs=1 seek=$((0x8000)) conv=notrunc 2>/dev/null
fi

# Write firmware at 0x10000
dd if="$FIRMWARE" of="$FLASH_IMAGE" bs=1 seek=$((0x10000)) conv=notrunc 2>/dev/null

echo "Flash image created successfully"

# Run QEMU ESP32
# Note: ESP32 in QEMU has limited peripheral support
# Network configuration uses user-mode networking with port forwarding
# -nic user,model=open_eth,id=lo0,hostfwd=tcp:127.0.0.1:PORT_HOST-:PORT_GUEST # for port forwarding
# -global driver=timer.esp32.timg,property=wdt_disable,value=true # disables TG watchdog timers
echo "Starting QEMU..."
${QEMU_BIN} \
-nographic \
-machine esp32 \
-drive file=${FLASH_IMAGE},if=mtd,format=raw \
-nic user,model=open_eth,id=lo0,hostfwd=tcp::${HTTP_PORT}-:80 \
-global driver=timer.esp32.timg,property=wdt_disable,value=true \
-serial mon:stdio &

QEMU_PID=$!
echo "QEMU started with PID: $QEMU_PID"
echo $QEMU_PID > qemu.pid

# Wait for QEMU to initialize
echo "Waiting for QEMU to initialize (30 seconds)..."
sleep 30

# Check if QEMU is still running
if ! kill -0 $QEMU_PID 2>/dev/null; then
echo "Error: QEMU process died"
exit 1
fi

echo "QEMU is running"
echo "To stop QEMU: kill $QEMU_PID"
echo "Or use: kill \$(cat qemu.pid)"

# Wait for QEMU process
wait $QEMU_PID
101 changes: 101 additions & 0 deletions .github/scripts/setup-qemu.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#!/bin/bash
# Setup QEMU ESP32 emulation environment
# This script downloads and sets up QEMU for ESP32

set -e

QEMU_DIR="qemu-esp32"

echo "Setting up QEMU ESP32..."

# Create directory for QEMU
mkdir -p ${QEMU_DIR}

# Check if QEMU is already installed
if [ -f "${QEMU_DIR}/qemu-system-xtensa" ]; then
echo "QEMU ESP32 already installed"
echo "QEMU binary: ${QEMU_DIR}/qemu-system-xtensa"
exit 0
fi

# Try multiple QEMU sources in order of preference
echo "Attempting to download QEMU ESP32..."

# List of potential QEMU download URLs to try
# Using the latest stable releases from Espressif
QEMU_URLS=(
"esp-develop-9.2.2-20250817|https://github.com/espressif/qemu/releases/download/esp-develop-9.2.2-20250817/qemu-xtensa-softmmu-esp_develop_9.2.2_20250817-x86_64-linux-gnu.tar.xz"
"esp-develop-9.1.0-20240606|https://github.com/espressif/qemu/releases/download/esp-develop-9.1.0-20240606/qemu-xtensa-softmmu-esp_develop_9.1.0_20240606-x86_64-linux-gnu.tar.xz"
"esp-develop-9.0.0-20231220|https://github.com/espressif/qemu/releases/download/esp-develop-9.0.0-20231220/qemu-xtensa-softmmu-esp_develop_9.0.0_20231220-x86_64-linux-gnu.tar.xz"
)

DOWNLOAD_SUCCESS=false

for ENTRY in "${QEMU_URLS[@]}"; do
VERSION="${ENTRY%%|*}"
URL="${ENTRY##*|}"

echo "Trying version ${VERSION}..."
echo "URL: ${URL}"

if wget --spider -q "${URL}" 2>/dev/null; then
echo "Found available version: ${VERSION}"
echo "Downloading from ${URL}..."

if wget -q "${URL}" -O qemu.tar.xz; then
echo "Download successful, extracting..."
if tar -xf qemu.tar.xz -C ${QEMU_DIR} --strip-components=1; then
rm qemu.tar.xz
DOWNLOAD_SUCCESS=true
echo "QEMU ESP32 version ${VERSION} installed successfully"
break
else
echo "Extraction failed, trying next source..."
rm -f qemu.tar.xz
fi
else
echo "Download failed, trying next source..."
rm -f qemu.tar.xz
fi
else
echo "Version ${VERSION} not available, trying next..."
fi
done

if [ "$DOWNLOAD_SUCCESS" = false ]; then
echo "ERROR: Could not download QEMU ESP32 from any source"
echo "Please check https://github.com/espressif/qemu/releases for available versions"
exit 1
fi

# Make QEMU executable (try both possible locations)
if [ -f "${QEMU_DIR}/qemu-system-xtensa" ]; then
chmod +x ${QEMU_DIR}/qemu-system-xtensa
QEMU_BIN="${QEMU_DIR}/qemu-system-xtensa"
elif [ -f "${QEMU_DIR}/bin/qemu-system-xtensa" ]; then
chmod +x ${QEMU_DIR}/bin/qemu-system-xtensa
# Create symlink for easier access
ln -sf bin/qemu-system-xtensa ${QEMU_DIR}/qemu-system-xtensa
QEMU_BIN="${QEMU_DIR}/bin/qemu-system-xtensa"
else
echo "ERROR: Could not find qemu-system-xtensa binary"
exit 1
fi

echo "QEMU ESP32 setup complete"
echo "QEMU binary: ${QEMU_BIN}"

# Verify QEMU can run by checking for required libraries
echo "Verifying QEMU dependencies..."
if ! ldd "${QEMU_BIN}" | grep -q "not found"; then
echo "All required libraries found"
${QEMU_BIN} --version
else
echo "WARNING: Missing required libraries:"
ldd "${QEMU_BIN}" | grep "not found"
echo ""
echo "Install missing dependencies with:"
echo " sudo apt-get update"
echo " sudo apt-get install -y libsdl2-2.0-0 libpixman-1-0 libglib2.0-0"
exit 1
fi
Loading
Loading