Skip to main content

Debugging Embedded Graphics with Wokwi and AI: Solving a Raylib Color Mapping on ESP32

·7 mins·
ESP32-S3 Raylib Wokwi CI/CD Graphics Debugging Simulation AI
Author
Juraj Michálek
Software Manager at Espressif Systems (Czech) s.r.o.
Table of Contents
Learn how Wokwi simulation and AI-assisted debugging solved a subtle RGB565 color mapping bug when porting Raylib to ESP32, turning hours of hardware debugging into an efficient iterative workflow with automated visual testing.

Introduction
#

Porting graphics libraries to embedded systems often involves subtle bugs that are hard to diagnose - especially when dealing with color formats, endianness, and framebuffer handling. This article describes how we used Wokwi simulation combined with Warp’s AI capabilities to iteratively solve a color mapping bug in the raylib ESP-IDF component, turning what could have been hours of trial-and-error into an efficient debugging workflow.

The Challenge: Porting Raylib to ESP32
#

Raylib is a popular game programming library that recently gained software rendering support, making it suitable for embedded devices without GPUs. We were porting it to ESP32 devices (specifically the ESP32-S3-BOX-3) using ESP-IDF, wrapping the upstream raylib library as an ESP-IDF component.

The initial port compiled and ran, but there was a problem: instead of showing solid colors, the display showed vertical yellow/blue stripes.

Wokwi - ESP32-S3-BOX-3 - Raylib

The framebuffer format was clearly wrong, but determining exactly where and how required multiple iterations.

Enter Wokwi: Hardware Simulation for Bug Hunting
#

Rather than flash to physical hardware for every test, we used Wokwi - a powerful hardware simulator that can emulate ESP32 boards with displays. Wokwi is particularly valuable for bug hunting in embedded graphics applications because it provides:

  1. Screenshot Capability: Capture display output as PNG files at any point in execution - crucial for visual debugging
  2. Integrated Debugger: Step through code, set breakpoints, and inspect variables without physical hardware
  3. Fast Iteration Cycles: No need to physically flash devices between tests
  4. Reproducible Testing: Same simulation conditions every time, eliminating hardware variability
  5. IDE Integration: Works seamlessly with VS Code and CLion for professional development workflows
  6. CI/CD Ready: Automated testing in GitHub Actions with screenshot validation

Our Wokwi setup included:

wokwi-cli \
  --diagram-file diagram.json \
  --screenshot-part esp \
  --screenshot-time 5000 \
  --screenshot-file screenshot.png \
  --timeout 6000 \
  boards/esp-box-3

The AI-Assisted Debugging Loop
#

Here’s where it got interesting. Instead of manually analyzing each screenshot and making educated guesses, we used Warp’s AI agent with vision capabilities to create a feedback loop:

1. Initial Problem Identification
#

Developer: “The screen should have a red background, but as you can see, it’s not true. It has some kind of Yellow, blue, vertical line patterns.”

AI Action: Read the screenshot, identified the vertical stripe pattern indicating likely color format mismatch or byte-order issues.

2. Hypothesis Generation
#

The AI analyzed the code and proposed several theories:

  • RGB565 color format mismatch
  • Endianness issues (big-endian vs little-endian)
  • Incorrect framebuffer copy method
  • RGBA vs RGB internal format confusion

3. Iterative Testing with Wokwi
#

For each hypothesis, we followed this pattern:

1. AI proposes code change
2. Build firmware
3. Run Wokwi simulation with screenshot capture
4. AI reads the screenshot to verify if colors improved
5. Adjust based on results

This tight feedback loop was possible because:

  • Wokwi provided consistent, reproducible screenshots
  • Warp AI could read images directly
  • Build times were reasonable (~30 seconds)
  • No physical hardware connection needed

4. The AI iterations: Direct Framebuffer Access
#

Since we established AI feedback loop, the AI was able to iterate over the problem. Thanks to provided feedback from developer, it was able to drive several iterations of the debugging process.

The AI discovered that the original code used rlCopyFramebuffer(), which performed:

RGB565 → RGBA8888 → RGB565

This double conversion was corrupting colors. The debug process:

Iteration 1: Added debug logging to print pixel values

ESP_LOGI("RAYLIB", "First pixel = 0x%04X (should be RED=0xF800)", framebuffer[0]);

Wokwi serial output showed: 0x28E6 (wrong!) instead of 0xF800 (red)

Iteration 2: Switch to direct framebuffer access using swGetColorBuffer()

uint16_t *sw_framebuffer = (uint16_t *)swGetColorBuffer(&sw_width, &sw_height);
memcpy(framebuffer, sw_framebuffer, screen_width * screen_height * sizeof(uint16_t));

New pixel values: 0xE147 (closer, but still wrong)

Iteration 3: Add byte-swapping for SPI LCD endianness

for (int i = 0; i < total_pixels; i++) {
    uint16_t pixel = framebuffer[i];
    framebuffer[i] = (pixel >> 8) | (pixel << 8);  // Swap bytes
}

Screenshot verification: Wokwi showed solid green (test was at 5 seconds when green was displayed) ✅

ESP32-S3-BOX-3 Hello World

The Complete Workflow in Action
#

Here’s what a typical debugging iteration looked like. Note the iterative loop - we went through this cycle multiple times before finding the correct solution:

Debugging workflow diagram showing the iterative loop between AI analysis, code changes, Wokwi simulation, and verification

The key insight is the feedback loop (highlighted in yellow): AI proposes changes → build → Wokwi captures screenshot → AI verifies → if not fixed, iterate again. This cycle ran multiple times, with each iteration getting closer to the solution.

Key Insights
#

Why This Worked Well
#

  1. Visual Feedback is Critical: Color bugs can’t be debugged with logs alone - you need to see the actual output. Wokwi’s screenshot capability made this trivial
  2. Wokwi’s Debugging Power: The integrated debugger would have allowed stepping through framebuffer operations if screenshots weren’t sufficient
  3. Wokwi’s Consistency: Same simulation every time meant we could isolate variables and prove fixes definitively
  4. AI Vision + Code Understanding: The AI could correlate visual output with code changes by reading Wokwi screenshots
  5. Fast Iteration: ~2 minutes per cycle (build + simulate + analyze) vs. hours with physical hardware
  6. Professional Development Environment: Wokwi’s VS Code and CLion integration meant we could debug just like with physical hardware

Limitations We Hit
#

  1. Screenshot Timing: Had to carefully time captures to match test sequence (red at 2.7s, green at 3.9s, etc.). The alternative approach is to use Wokwi Scenario files, which allows triggering screenshot based on the log message
  2. Endianness Assumptions: Even AI made wrong guesses about byte order initially, requiring multiple iterations

Lessons for Embedded Development
#

1. Simulation-First Development with Wokwi
#

Don’t wait until hardware is available. Wokwi provides a simulated of MCUs:

  • Screenshot capture for visual debugging - essential for graphics bugs
  • Integrated debugger with breakpoints and variable inspection
  • IDE integration with VSCode and CLion for professional workflows
  • Test display output without physical devices
  • Capture exact visual state for comparison and regression testing
  • CI/CD integration for automated visual testing

2. AI as Debugging Partner
#

The AI helped in ways that went beyond code completion:

  • Pattern recognition: Spotted vertical stripes → byte alignment issues
  • Cross-reference: Connected pixel values to RGB565 bit packing
  • Hypothesis testing: Proposed specific code changes to test theories
  • Visual verification: Confirmed fixes by reading screenshots

3. Structured Debugging Workflow
#

The key was having a repeatable process:

Observe → Hypothesize → Code → Build → Simulate → Capture → Verify → Repeat

Each step was automated or AI-assisted, keeping developer focus on problem-solving. Wokwi’s screenshot capability was the key enabler - without it, we’d need physical hardware and manual camera captures for each test.

The Complete Solution
#

The final fix involved three key changes:

// 1. Direct framebuffer access (no conversion)
extern void *swGetColorBuffer(int *w, int *h);
uint16_t *sw_framebuffer = (uint16_t *)swGetColorBuffer(&sw_width, &sw_height);

// 2. Direct memory copy (RGB565 → RGB565)
memcpy(framebuffer, sw_framebuffer, screen_width * screen_height * sizeof(uint16_t));

// 3. Byte swap for SPI LCD endianness
for (int i = 0; i < total_pixels; i++) {
    uint16_t pixel = framebuffer[i];
    framebuffer[i] = (pixel >> 8) | (pixel << 8);
}

CI/CD Integration
#

Once the fix was validated, we automated the visual testing:

# .github/workflows/test-example.yml
- name: Simulate & take a screenshot
  run: |
    wokwi-cli \
      --screenshot-part "esp" \
      --screenshot-time 5000 \
      --screenshot-file "screenshot-${{ matrix.board }}.png" \
      "raylib/examples/hello/boards/${{ matrix.board }}"

- name: Flip screenshot vertically
  run: |
    convert "screenshot-${{ matrix.board }}.png" -flip "screenshot-${{ matrix.board }}.png"

- name: Upload Screenshot
  uses: actions/upload-artifact@v4
  with:
    name: screenshot-${{ matrix.board }}
    path: screenshot-${{ matrix.board }}.png

Now every commit produces screenshots showing correct color rendering across all supported boards.

ESP32-S3-BOX-3 Hello World

▶️ Run online Wokwi ESP32-S3-BOX-3 (320x240)

Note: The time based approach might not be suitable for all the cases. Wokwi can watch for keyword in the log message to trigger screenshot:

steps:
  - wait-serial: 'Display initialized'

  - wait-serial: 'Counter: 0'
  - take-screenshot:
      part-id: 'lcd'
      compare-with: 'screenshots/counter-0.png'

Conclusion
#

The combination of Wokwi simulation and AI-assisted debugging created a powerful workflow for solving a subtle embedded graphics bug. What traditionally might take days of trial-and-error with physical hardware was resolved in an afternoon through:

  • Visual verification via Wokwi screenshots
  • AI code analysis connecting visual symptoms to root causes
  • Rapid iteration without physical hardware constraints
  • Automated regression testing via CI/CD

This approach isn’t limited to graphics - any embedded system with visual, audio, or sensor output that can be simulated could benefit from this debugging methodology.

You can test the final result in web browser or flash it to ESP32-S3-BOX-3 using ESP-Launchpad.

▶️ Run online Wokwi ESP32-S3-BOX-3 (320x240)

Try it with ESP Launchpad

Resources
#

Related

Integrating Embedded Swift and SDL3 on ESP32
·11 mins
ESP32-P4 ESP32-C6 ESP32-C3 Swift CI/CD SDL Graphics Filesystem Experimental
Debugging with ESP-IDF VS Code extension: Part 2
·8 mins
Debugging ESP-IDF ESP32-C3 ESP32-S3 Vscode
This two-part guide shows how to set up VS Code with the ESP-IDF extension to debug Espressif boards using JTAG. In this second part, we will debug a simple project using gdb through Espressif’s VSCode extension. We will explore the debugging options for navigating the code and inspecting the variables.
Debugging with ESP-IDF VS Code extension: Part 1
·9 mins
Debugging ESP-IDF ESP32-C3 ESP32-S3
This two-part guide shows how to set up VS Code with the ESP-IDF extension to debug Espressif boards using JTAG. This first part covers the debugging process, hardware setup and connections, and starting the openOCD server.