Accessible Embedded Games with STM32, Zephyr, and Lua

Accessible Embedded Games with STM32, Zephyr, and Lua

Building complex embedded systems does not always mean industrial controllers or medical devices.
Sometimes it means designing human-centered applications.
This post presents my project alyn — an accessible game platform built on STM32, Zephyr RTOS, Lua scripting, and a 32×32 RGB LED matrix. In addition to the embedded firmware, I also developed a Windows-based emulator of the board.
This emulator allows developers to program and run Lua scripts on a PC, simulating board operation before deployment to the STM32 hardware.


🎯 Project Goals

The goal was to create a game system for children with special needs, deployed in a swimming pool environment.

Requirements included:

  • Flexible game rules – configurable without recompiling firmware.
  • Real-time responsiveness – handling input devices, LEDs, and displays.
  • Visual feedback – colorful and dynamic, using a 32×32 RGB LED panel.
  • Lightweight footprint – running on STM32 MCUs with tight constraints.
  • Support for Hebrew fonts – creating and rendering custom bitmap fonts in Hebrew for localized messages.
  • Windows-based emulator – enabling developers to program and run Lua scripts on a PC to simulate board operation before deployment.

⚙️ Why Zephyr RTOS?

I selected Zephyr RTOS instead of FreeRTOS because it brings a Linux-like development model:

  • Device Tree for hardware abstraction → cleaner code, easier portability.
  • Kconfig build system → compile-time toggling of drivers and subsystems.
  • Threading and priorities → isolation between critical I/O tasks and Lua scripts.
  • Ecosystem → active upstream, long-term support, and tooling (west, CMake).

This gave me a scalable, maintainable foundation for the system.


🔌 Why Lua?

Most embedded projects hardcode all logic in C or C++.
Here I wanted non-embedded developers (educators, therapists) to extend games without firmware changes.

  • Lua was compiled as a static library inside the Zephyr app.
  • APIs were exposed from C++ into Lua:
    • led_set(pattern)
    • score_increment()
    • get_input("button1")

This meant a game designer could edit a .lua script and reload behavior — without touching firmware.


🖼️ RGB LED Panel Integration (32×32)

To make the games engaging, I integrated a 32×32 RGB LED matrix panel, inspired by this STM32F4 plasma project.

How the Panel Works

  • Each row of LEDs is driven using shift registers for the RGB channels, latched and multiplexed across rows.
  • Data lines include R0/G0/B0 and R1/G1/B1 (for top and bottom halves), plus control lines: Latch (STB), Output Enable (OE), and row select (A–D).
  • SPI with DMA was used to transfer pixel data into the shift registers, reducing CPU load.
  • A timer interrupt handled row scanning and synchronized frame refresh (~64 Hz full-frame).

Color Depth & Brightness

  • Implemented using bit-angle modulation (software PWM): multiple passes per frame modulate brightness levels.
  • Achieved ~4 bits per color channel (16 brightness levels), enough for smooth plasma-like animations.

Performance Enhancements

  • Double buffering ensured that one frame buffer could be updated while another was being displayed — preventing flicker or tearing.
  • Floating-point math was used for visual effects (sine waves, plasma), though fixed-point would be more efficient.
  • Careful thread priorities in Zephyr guaranteed that LED scanning never missed a refresh deadline, while Lua handled high-level game logic.

Why It Mattered

The LED panel gave children instant visual feedback: bright colors, animations, and patterns that made the game more engaging.
It also served as a test case for combining strict real-time tasks (LED refresh) with flexible scripting (Lua) on a resource-constrained MCU.


🧩 Architecture Overview

The diagram below shows the overall system architecture:
STM32 hardware with Zephyr drivers, the Lua scripting engine, and the RGB LED panel working together to provide real-time interaction.

graph TD subgraph "Hardware Layer" A[STM32 MCU] --> B[Zephyr Drivers] B --> C[RGB LED Panel Driver] end subgraph "Zephyr RTOS" C --> D[Device Tree + Kconfig] D --> E[Lua VM Thread] D --> F[C++ Core Logic] end subgraph "Scripting Layer" E --> G["Lua Scripts (Game Rules)"] end subgraph "User Interaction" F --> H[LEDs, Display, RGB Panel] F --> I[Input Devices] end

🗂️ Directory Layout

alyn/
├── src/
│   ├── main.cpp          # Zephyr application entry
│   ├── game_api.cpp      # C++ ↔ Lua bindings
│   └── drivers/          # Custom peripheral drivers
├── scripts/
│   └── demo_game.lua     # Game rules (LED sequences, scoring)
├── CMakeLists.txt
└── prj.conf              # Zephyr build configuration

🛠️ Key Technical Points

  1. Lua Integration

    • Stripped Lua to minimal modules (math, string).
    • Allocated interpreter heap via Zephyr memory pool.
  2. Thread Isolation

    • Lua interpreter ran in its own Zephyr k_thread.
    • Priority lower than GPIO/LED ISR handlers → no blocking of real-time events.
  3. LED Panel Driver

    • Used SPI + DMA for efficient pixel updates.
    • Implemented row scanning in a timer ISR.
    • Double-buffered frame updates with V-Sync to avoid artifacts.
  4. Debugging

    • Redirected Lua print() to Zephyr logging subsystem.
    • UART shell allowed interactive Lua commands during runtime.

📈 Lessons Learned

  • RTOS + scripting + real-time display = challenging but rewarding.
  • Double buffering is a must for flicker-free RGB panels.
  • Zephyr’s device tree abstraction made it easier to port the same driver code to multiple STM32 boards.
  • Accessible design → engaging visuals matter as much as technical correctness when the end users are children.

✅ Conclusion

This project was an experiment in combining embedded RTOS discipline, Lua scripting flexibility, and RGB LED panel control.
It showed how Zephyr + Lua + STM32 + LED matrix can provide a unique blend of real-time control, visual feedback, and human-centered design.

👉 Source code is available here: GitHub: yairgd/alyn



Comments
comments powered by Disqus




Archives

2025 (7)
2022 (3)
2021 (9)
2020 (18)
2014 (4)