Sivanesan
21 July 2026

Choosing between bare-metal (superloop) and RTOS for an embedded GUI application is not a question of which is technically superior — both work, and both are in production across millions of embedded GUI devices. The decision depends on application complexity, the number of concurrent tasks the firmware must manage, real-time constraints beyond the GUI, and memory budget. Understanding where each model succeeds and where it creates engineering pain is the foundation for making the right choice for a specific project.

Bare-Metal (Superloop) Architecture

In a bare-metal embedded GUI architecture, the main loop calls the GUI task function on each iteration, alongside all other application logic:

int main(void) {
    HAL_Init();
    display_init();
    SGUI_Init();
    SGUI_LoadScreen(SCREEN_HOME);

    while (1) {
        /* Poll all subsystems in round-robin */
        process_uart_input();       /* ~0.1ms if data available */
        update_sensor_values();     /* ~0.2ms */
        SGUI_Task();                /* Process touch + state machine */
        SGUI_Render();              /* Render dirty regions */
        SGUI_HAL_FlushDisplay();    /* Transfer to display */
    }
}

When bare metal works well:

  • Simple GUI application with one main function (display sensor data, respond to button presses)
  • Communication stack is polling-based (no interrupt-driven data with hard deadlines)
  • RAM is extremely constrained — RTOS kernel adds 2–8 KB RAM overhead; every KB counts on very small MCUs
  • GUI is the primary (or only) complex subsystem — no concurrent heavy computation tasks
  • Team does not have RTOS experience — bare metal is simpler to debug and reason about

Where bare metal causes problems:

  • Any loop iteration that blocks (waiting for UART byte, waiting for SPI transfer) delays the GUI render loop and causes visible frame drops
  • Interrupt service routines that do significant processing (protocol decoding) delay the main loop unpredictably, causing GUI jitter
  • Multiple concurrent subsystems (CAN receive + Modbus master + GUI + data logging) require careful time-slicing logic that grows complex as the system evolves

RTOS Architecture

With an RTOS (FreeRTOS, Azure RTOS, Zephyr), each subsystem runs as an independent task with its own stack and priority. The RTOS preemptive scheduler ensures high-priority tasks run when needed regardless of what lower-priority tasks are doing:

/* Each subsystem is an independent RTOS task */
void can_receive_task(void *pvParameters)  { /* Priority 4 */ }
void modbus_master_task(void *pvParameters) { /* Priority 3 */ }
void gui_render_task(void *pvParameters)    { /* Priority 3 */ }
void data_log_task(void *pvParameters)      { /* Priority 1 */ }

When RTOS is the right choice:

  • CAN, Ethernet, Modbus, or other communication stacks run concurrently with the GUI — each needs its own execution context and priority
  • Any subsystem has a hard real-time deadline that cannot be delayed by GUI rendering (motor control loop, safety monitor, PWM generation)
  • The application is expected to grow in complexity — RTOS architecture scales gracefully; superloop complexity grows non-linearly
  • Target MCU is STM32H7, i.MX RT, or similar with 512 KB+ RAM — RTOS overhead is a small fraction of available memory

RTOS overhead to plan for:

  • FreeRTOS kernel: ~5–10 KB ROM, 2–4 KB RAM (kernel data structures, task list)
  • Per-task stack: 1–16 KB depending on task complexity. GUI task: 8 KB minimum. Communication tasks: 2–4 KB.
  • Total RTOS overhead for a 4-task application: approximately 2–4 KB kernel RAM + 4 × stack size

Head-to-Head Comparison

Criterion Bare Metal RTOS
RAM overhead Near-zero (no kernel) 2–4 KB kernel + per-task stacks
Multi-task support Manual time-slicing Preemptive, priority-driven
Real-time responsiveness Worst-case = full loop period Bounded by task priority
Debug complexity Low — linear execution Higher — concurrent tasks, deadlock risk
Scalability Poor for complex systems Scales well with added subsystems
Communication stacks Polling only or careful ISR design Independent task per protocol

Sparklet on Both Architectures

Sparklet supports both bare-metal and RTOS operation. The GUI loop API (SGUI_Task() + SGUI_Render()) is called from the main superloop in bare-metal deployment or from a dedicated FreeRTOS task in RTOS deployment. The same application code works in both configurations — no architecture-specific API differences. See RTOS support details for all supported RTOS platforms, or request the evaluation package which includes both bare-metal and FreeRTOS sample projects.

Subscribe to our Blog


For further information on how your personal data is processed, please refer to the Sparklet Privacy Policy.