12  Emulation & Debugging

12.1 Learning Objectives

By the end of this chapter, you will be able to:

  • Configure QEMU for Raspberry Pi OS emulation without physical hardware
  • Configure Renode for multi-architecture embedded system simulation
  • Apply debugging techniques including breakpoints and variable inspection
  • Interpret serial monitor and plotter output for firmware debugging
  • Leverage logic analyzers and GDB for advanced debugging scenarios
In 60 Seconds

Platform emulation (QEMU, Renode) runs actual compiled firmware binaries on software models of target hardware, enabling high-fidelity testing of complete firmware images including bootloaders, RTOS schedulers, and device drivers. Unlike simplified online simulators, platform emulators model CPU instruction sets and peripheral registers faithfully, allowing debug sessions with GDB and automated test scripts. Renode specifically targets embedded IoT platforms with models for Cortex-M, RISC-V, and major peripheral IPs.

12.2 For Beginners: Emulation & Debugging

Testing and validation ensure your IoT device works correctly and reliably in the real world, not just on your workbench. Think of it like test-driving a car in rain, snow, and heavy traffic before buying it. Thorough testing catches problems before your devices are deployed to thousands of locations where fixing them becomes expensive and disruptive.

“What if you need to simulate an entire Raspberry Pi – operating system and all – without owning one?” asked Max the Microcontroller. “That is where emulators like QEMU come in! QEMU creates a virtual ARM processor on your PC and runs the real Raspberry Pi OS on it. You can develop and test without any physical hardware.”

Sammy the Sensor was impressed. “So I could test my Python sensor scripts on a virtual Raspberry Pi?” Max nodded. “Exactly! And Renode goes even further – it can simulate multiple microcontrollers communicating with each other. You can test an entire IoT network in software.”

Lila the LED described the debugging tools. “The real power is in debugging. You can set breakpoints – pause points where the code stops and you can inspect every variable. Step through line by line to see exactly what happens. It is like watching the code in slow motion with a magnifying glass.” Bella the Battery concluded, “And logic analyzers show you the exact timing of signals between components. If your I2C communication is failing, you can see the exact moment where the clock and data signals get out of sync. These tools turn mysterious hardware bugs into solvable puzzles!”

12.3 Prerequisites

Before diving into this chapter, you should be familiar with:

12.4 QEMU for Raspberry Pi

Estimated time: ~15 min | Advanced | P13.C03.U03

Description: QEMU emulates complete computer systems, including ARM processors used in Raspberry Pi.

Installation:

# Install QEMU
sudo apt install qemu-system-arm

# Download Raspberry Pi kernel and dtb
wget https://github.com/dhruvvyas90/qemu-rpi-kernel/raw/master/kernel-qemu-4.19.50-buster
wget https://github.com/dhruvvyas90/qemu-rpi-kernel/raw/master/versatile-pb-buster.dtb

# Download Raspberry Pi OS image
wget https://downloads.raspberrypi.org/raspios_lite_armhf/images/...

Running Raspberry Pi in QEMU:

qemu-system-arm \
  -kernel kernel-qemu-4.19.50-buster \
  -dtb versatile-pb-buster.dtb \
  -m 256 \
  -M versatilepb \
  -cpu arm1176 \
  -append "root=/dev/sda2 rootfstype=ext4 rw" \
  -hda raspios.img \
  -net nic -net user,hostfwd=tcp::5022-:22 \
  -no-reboot
Try It: QEMU Configuration Explorer

Strengths:

  • Full OS emulation
  • Test software before deploying to Pi
  • No hardware required

Limitations:

  • No GPIO simulation
  • Slower than native
  • Different architecture (some programs may not work identically)

Typical Use Cases:

  • Raspberry Pi software development without hardware
  • Testing OS configurations
  • CI/CD pipelines

12.5 Renode

Description: Open-source simulation framework for embedded systems with support for multiple architectures.

Key Features:

  • Multi-node networks
  • Deterministic execution
  • GDB debugging
  • Scripting support (Python)
  • Peripheral simulation
  • Time-travel debugging

Supported Platforms:

  • ARM Cortex-M
  • RISC-V
  • ESP32
  • STM32
  • Nordic nRF52
  • Many others

Emulation Speed vs Real Hardware: Firmware CI/CD testing on 5 target platforms (ESP32, nRF52, STM32, RISC-V, Cortex-M4):

Physical hardware testing (sequential on device farms): \[T_{\text{hardware}} = 5 \text{ platforms} \times (3\text{min flash} + 8\text{min test suite}) = 55\text{min}\]

Renode emulation (parallel on CI servers): \[T_{\text{emulation}} = \max(5\text{min}, 5\text{min}, 5\text{min}, 5\text{min}, 5\text{min}) = 5\text{min}\]

Speedup: \(\frac{55}{5} = 11\times\) faster testing

CI cost (GitHub Actions pricing):

  • Hardware runners: \(5 \times \$0.008/\text{min} \times 55 = \$2.20\) per test run
  • Emulation: \(5 \times \$0.008/\text{min} \times 5 = \$0.20\) per test run

For 100 daily commits: emulation saves \(\$200/\text{day}\) (\(\$6,000/\text{month}\)) while providing deterministic testing (no flaky RF issues). Physical testing still needed for final validation.

12.5.1 Interactive CI Cost Calculator

Key Insight: With daily commits across platforms, emulation provides × faster testing while saving $/month in CI costs. Adjust the sliders above to model your own CI/CD pipeline.

Example: Simulating nRF52 BLE

# Start Renode
renode

# In Renode console
(monitor) mach create "nrf52"
(monitor) machine LoadPlatformDescription @platforms/cpus/nrf52840.repl
(monitor) sysbus LoadELF @path/to/firmware.elf
(monitor) showAnalyzer sysbus.uart0
(monitor) start

Strengths:

  • Deterministic (reproducible)
  • Multi-device simulation
  • Time-travel debugging
  • Free and open source

Limitations:

  • Command-line focused
  • Steep learning curve
  • Requires detailed platform knowledge

Typical Use Cases:

  • Multi-device IoT system testing
  • Wireless network simulation
  • Regression testing
  • Complex debugging

12.6 Debugging in Simulation

Estimated time: ~12 min | Intermediate | P13.C03.U04

12.6.1 Breakpoints and Variable Inspection

Most simulators support debugging similar to traditional software:

Setting Breakpoints:

void loop() {
  int sensorValue = analogRead(A0);  // Set breakpoint here
  Serial.println(sensorValue);
  delay(1000);
}

In Wokwi:

  • Click line number to set breakpoint
  • Run simulation
  • Execution pauses at breakpoint
  • Inspect variables in debugger panel

Variable Watching: Monitor variables in real-time:

  • Add variable to watch list
  • See value change as simulation runs
  • Useful for tracking sensor readings, state machines

12.6.2 Serial Monitor

Essential for debugging embedded systems:

void setup() {
  Serial.begin(115200);
  Serial.println("Starting...");
}

void loop() {
  Serial.print("Temperature: ");
  Serial.println(readTemperature());
  delay(1000);
}

Simulators provide virtual serial monitors showing output in real-time.

Advanced: Serial Plotter

Visualize numeric data:

void loop() {
  int value1 = analogRead(A0);
  int value2 = analogRead(A1);

  Serial.print(value1);
  Serial.print(",");
  Serial.println(value2);

  delay(100);
}

Serial plotter graphs values over time, useful for sensor data analysis.

Try It: Serial Data Visualizer

12.6.3 Logic Analyzer

Some simulators (Wokwi, SimulIDE) include virtual logic analyzers to visualize digital signals:

  • Monitor pin states over time
  • Decode protocols (I2C, SPI, UART)
  • Measure timing (pulse width, frequency)
  • Debug communication issues

12.6.4 GDB Debugging

Advanced simulators (Renode, QEMU) support GDB debugging:

# Start simulator with GDB server
renode --debug

# In another terminal, connect GDB
arm-none-eabi-gdb firmware.elf
(gdb) target remote :3333
(gdb) break main
(gdb) continue
(gdb) step
(gdb) print variable

Full debugging capabilities:

  • Single-step execution
  • Breakpoints (conditional, hardware)
  • Memory inspection
  • Register viewing
  • Stack traces
Try It: GDB Debug Session Simulator

12.7 Testing Strategies

Estimated time: ~12 min | Intermediate | P13.C03.U05

12.7.1 Unit Testing Firmware

Separate business logic from hardware interactions for easier testing:

// Testable: logic separated
float convertToFahrenheit(float celsius) {
  return (celsius * 9.0 / 5.0) + 32.0;
}

// In Arduino code
void loop() {
  float tempC = readSensor();
  float tempF = convertToFahrenheit(tempC);  // Testable function
  display.print(tempF);
}

Test the conversion function in simulation or on PC with unit tests:

// Unit test (can run on PC or simulator)
assert(convertToFahrenheit(0) == 32.0);
assert(convertToFahrenheit(100) == 212.0);

12.7.2 Integration Testing

Test complete system in simulation:

Test Scenarios:

  • Boot sequence
  • Sensor reading
  • Wi-Fi connection
  • Data transmission
  • Error handling
  • State machine transitions

Automated Testing with Renode:

# Renode test script
Execute "runMacro $reset"
Start

WaitForString "System ready" timeout=10

SendKey "Key_A"
WaitForString "Button A pressed"

SendKey "Key_B"
WaitForString "Button B pressed"

# Test passes if all WaitForString succeed

12.7.3 Fuzz Testing

Simulate random inputs to find edge cases:

void loop() {
  // In simulator, randomize sensor input
  int sensor = random(0, 1024);

  // Ensure code doesn't crash on any input
  processValue(sensor);
}

Simulators allow rapid iteration through thousands of random scenarios.

Try It: Fuzz Testing Simulator

12.8 Limitations of Simulation

Estimated time: ~10 min | Intermediate | P13.C03.U06

12.8.1 Timing Differences

Simulators may not perfectly match real-time behavior:

  • Interrupt timing
  • Clock accuracy
  • RTOS scheduling
  • Hardware delays

Impact: Real-time critical applications (motor control, audio) may behave differently on physical hardware.

Mitigation: Validate timing-critical code on physical hardware.

12.8.2 Missing Peripherals

Not all hardware features are simulated:

  • Specific sensor models
  • Proprietary communication protocols
  • Custom hardware

Impact: Cannot fully test systems with unsupported components.

Mitigation: Use closest available component or mock behavior.

12.8.3 Analog Behavior

Digital simulation of analog circuits has limitations:

  • Component tolerances
  • Temperature effects
  • Noise and interference
  • Power supply variations

Impact: Analog sensor readings may differ from simulation.

Mitigation: Plan for calibration and testing with physical sensors.

12.8.4 Physical Interactions

Simulation cannot capture:

  • Mechanical vibrations
  • Environmental factors (temperature, humidity)
  • EMI/RFI interference
  • Real-world network conditions

Impact: Deployed devices may encounter issues not seen in simulation.

Mitigation: Field testing with pilot deployments.

12.8.5 Performance Differences

Simulator performance is not equal to real hardware:

  • May be faster (ideal execution)
  • May be slower (overhead of simulation)
  • Different memory characteristics

Impact: Performance-critical code needs hardware validation.

Mitigation: Profile on real hardware.

12.8.6 Worked Example: Choosing the Right Debug Approach for a Fleet Tracker

Scenario: Your startup is building a GPS/cellular fleet tracker (ESP32 + u-blox NEO-M9N GPS + SIM7600 LTE modem). During field testing, 3 out of 50 prototype units lose GPS fix after exactly 72 hours of continuous operation, requiring a power cycle to recover. The bug does not appear in your simulation environment.

Step 1: Assess which tools can reproduce the bug

Debug Approach Can Reproduce? Why / Why Not Cost
Wokwi simulation No No real GPS/LTE hardware, no 72-hour timing effects $0
QEMU emulation No Emulates CPU but not GPS module’s internal state machine $0
Renode simulation Unlikely Could model GPS timeout but missing NEO-M9N peripheral model $0
JTAG debugger on bench Unlikely Bug needs 72 hours to trigger; JTAG connection prevents sleep modes $45 (ESP-Prog)
Serial logging to SD card Yes Log GPS state machine transitions for 72+ hours, review after failure $12 (SD card module)
Logic analyzer on I2C/UART Partial Shows GPS-to-ESP32 communication breakdown but not root cause $150 (Saleae Logic 8)
Field unit with crash dump Yes ESP32 core dump to flash partition, retrieve after failure $0 (firmware change)

Step 2: Design a staged debug strategy

Stage 1 (0 cost, 1 day): Enable ESP32 core dump to flash. Redeploy to 3 affected units. Wait for crash and retrieve dump via OTA.

Stage 2 (if core dump shows no crash): Add SD card logging of GPS NMEA sentences, ESP32 heap usage, and LTE modem AT command responses. Sample every 60 seconds. 72 hours at 1 sample/min = 4,320 entries, ~2 MB on SD card.

Stage 3 (if still unclear): Connect Saleae logic analyzer to GPS UART lines on one bench unit and run for 72+ hours with continuous capture at 9600 baud.

Step 3: What the investigation revealed

The SD card logs showed the root cause: the u-blox NEO-M9N GPS module enters a “periodic power-saving mode” after 72 hours when the host does not poll PMTK messages. The ESP32 firmware was using only NMEA passthrough and never sent the keep-alive UBX-CFG-PM2 command. In simulation, GPS data was mocked as always-available, so this power management behavior was invisible.

Debug Tool Time to Find Bug Total Cost
Simulation only Never (can’t reproduce) $0
SD card logging (actual approach) 4 days (72h wait + 1 day analysis) $12
Logic analyzer ~5 days (72h wait + 2 days decode) $150
Replacing GPS module (trial and error) Unpredictable, wastes money $35/unit

Lesson: Simulation excels for functional testing and rapid iteration (catching 80% of bugs in minutes), but timing-dependent hardware interactions require real hardware debugging. The optimal strategy is simulation for development speed, with targeted real-hardware debugging for bugs that only manifest over extended operation. Budget $200-500 per developer for basic hardware debug tools (logic analyzer + JTAG probe + SD card logger) – this investment pays for itself the first time a simulation-invisible bug appears.

12.10 Summary

  • QEMU enables full Raspberry Pi OS emulation for software development and CI/CD integration without physical hardware
  • Renode provides deterministic multi-architecture simulation with time-travel debugging for complex embedded systems
  • Debugging techniques include breakpoints, variable watching, serial monitors, logic analyzers, and GDB integration
  • Testing strategies span unit testing (separated logic), integration testing (full system), and fuzz testing (random inputs)
  • Simulation limitations include timing differences, missing peripherals, idealized analog behavior, and absence of physical environmental factors

12.11 Knowledge Check

12.12 Concept Relationships

How This Connects

Builds on: Online Hardware Simulators provides entry-level simulation; Programming Paradigms covers development tools.

Relates to: Network Simulation Tools for system-level validation; Testing Automation for CI/CD integration.

Leads to: HIL Testing bridges emulation and real hardware; Field Testing validates in production environments.

Part of: The complete simulation stack from hardware (Wokwi) → OS (QEMU) → network (NS-3) → field trials.

12.13 See Also

Documentation:

Related Tools:

  • Docker for reproducible test environments
  • OpenOCD for JTAG debugging
  • Valgrind for memory leak detection

Advanced Topics:

  • Time-travel debugging with RR: rr-project.org
  • Deterministic replay for bug reproduction

12.14 Try It Yourself

Challenge: Debug ESP32 Firmware Crash Using GDB in Renode

Scenario: Your ESP32 firmware crashes after exactly 47 minutes. Serial prints don’t help (they change timing, bug disappears). Use Renode + GDB to catch the crash.

Steps (90 minutes): 1. Setup Renode with ESP32 platform 2. Load firmware ELF file with debug symbols 3. Set watchpoint on hard fault handler 4. Run at 10× speed (Renode determinism) to trigger crash in 4.7 minutes 5. Examine backtrace when watchpoint hits 6. Inspect variables to find null pointer

What to Observe:

  • Deterministic replay: crash happens at exact same instruction every time
  • GDB shows full call stack and register state at crash point
  • No Heisenbug effect (unlike serial debugging)

Expected Outcome: Find the bug (buffer overflow in 47th MQTT reconnect attempt) that serial debugging couldn’t catch.

Bonus: Set conditional breakpoint: break mqtt_reconnect if reconnect_count > 45

Common Pitfalls

QEMU’s STM32 or nRF52 models vary in completeness: some peripherals (timers, UART) are well-modeled; others (USB, crypto accelerators, radio peripherals) may be absent or incomplete. Firmware that uses unsupported peripherals in QEMU silently skips or crashes without hardware-equivalent behavior. Before adopting QEMU for a target MCU, audit which peripherals your firmware uses against the QEMU model’s documented peripheral support list.

The most valuable use of platform emulation is automated regression testing: run the full firmware test suite against a Renode emulation model for every CI commit in <5 minutes, without hardware. This requires: creating Renode platform scripts for the target hardware, writing test scripts using Robot Framework or pytest-robot, and integrating with the CI pipeline. Teams that use emulation only for occasional manual debugging miss its primary value as a continuous quality gate.

Emulators like QEMU with AddressSanitizer (for user-space emulation) or Renode with memory access tracing can detect stack overflows, heap corruption, and NULL pointer dereferences that are impossible to detect on real hardware without JTAG debugging. For IoT firmware with dynamic memory allocation (RTOS heaps, ring buffers), run emulation-based memory corruption analysis. Enable memory access watchpoints at known-bad addresses and run the firmware through its full operational sequence.

Emulation tests run manually once per week provide much weaker quality assurance than running automatically on every commit. Renode provides a CI-friendly headless mode: renode-test –headless –include regression_suite.robot; configure this as a required CI check before merge. Teams that run emulation tests manually “when we remember to” will have inconsistent coverage and miss regressions introduced between manual test runs.

12.15 What’s Next

Continue to Simulation-Driven Development to learn about comprehensive development workflows, testing pyramids, hardware-in-the-loop testing, best practices, and CI/CD integration for simulation-based IoT development.

Previous Current Next
Online Hardware Simulators Emulation & Debugging Network Simulation Tools