Comparison

Bare Metal vs RTOS Programming

A thermostat that reads temperature every second and toggles a relay has no need for FreeRTOS — a 200-line bare-metal loop on a PIC16 does the job with 2 kB flash. A drone flight controller reading IMU at 1 kHz, running a PID loop at 500 Hz, managing telemetry, and handling a failsafe timer simultaneously needs deterministic multitasking — FreeRTOS on an STM32F4 gives exactly that without hand-rolling a scheduler. The question is always whether application complexity justifies the OS overhead.

ECE, EI

Side-by-side comparison

ParameterBare MetalRTOS Programming
Execution ModelSingle super-loop or state machine; one threadMultiple tasks with separate stacks; preemptive/cooperative scheduling
SchedulerNone — developer controls all timingPriority-based preemptive (FreeRTOS, Zephyr) or cooperative
Flash OverheadZero OS overheadFreeRTOS kernel: ~5–10 kB flash (Cortex-M minimal config)
RAM OverheadGlobal/stack variables onlyEach task needs stack (min 128 words); FreeRTOS heap ~2 kB minimum
Task IsolationNone — all code shares one stackEach task has independent stack; stack overflow detection optional
Inter-task CommunicationGlobal variables (no synchronisation primitives)Queues, semaphores, mutexes, event groups (FreeRTOS API)
Real-Time DeterminismDeterministic if single task; shared ISR disrupts loop timingPriority-based — highest-priority ready task always runs within tick period
Debugging ComplexitySimple — linear execution, UART printf worksHarder — race conditions, stack overflow, priority inversion possible
Tick ResolutionSoftware delay loops or hardware timerConfigurable: FreeRTOS default 1 ms; configurable to 100 µs
Example TargetPIC16F877A, ATmega328 (Arduino), STM32F0STM32F4/H7 with FreeRTOS, ESP32 with FreeRTOS, Zephyr on nRF52840

Key differences

Bare-metal super-loop gives zero scheduling overhead — the entire chip resources run your code. The problem: if two tasks need different time slices (IMU at 1 kHz, Bluetooth at 10 Hz), hand-rolled timing with flags and timers becomes fragile and unmaintainable above about four concurrent activities. FreeRTOS on an STM32F4 uses 5 kB flash and 2 kB RAM for the kernel, creates tasks with independent 512-word stacks, and ensures a 100 µs-period IMU task always preempts a 10 Hz Bluetooth task deterministically. Priority inversion — where a low-priority task blocks a high-priority one via a shared resource — is a real RTOS pitfall that bare-metal avoids entirely by having no scheduler, but also avoids the problem by having no concurrent tasks at all.

When to use Bare Metal

Use bare-metal programming for simple, single-concern devices with low task count and tight resource constraints. Example: a gas sensor alarm on a PIC16F1937 (8 kB flash, 512 B RAM) reads an ADC, compares to a threshold, and drives a buzzer — a 150-line super-loop with no OS overhead is the right and only sensible choice.

When to use RTOS Programming

Use RTOS when three or more concurrent activities with different timing requirements must be managed deterministically. Example: an STM32F407 drone ESC controller runs four FreeRTOS tasks: IMU sampling (1 kHz, priority 4), PID control (500 Hz, priority 3), telemetry (10 Hz, priority 2), and CLI (priority 1), all managed by FreeRTOS with 10 µs tick resolution.

Recommendation

For fewer than three concurrent tasks or severely resource-constrained MCUs (< 8 kB RAM), choose bare-metal — it is faster to code, easier to debug, and wastes nothing. For three or more concurrent activities with different timing requirements, choose FreeRTOS or Zephyr. The overhead of FreeRTOS (5 kB flash, 2 kB RAM) pays back immediately in maintainability and correctness once complexity crosses that threshold.

Exam tip: Examiners ask students to list three FreeRTOS inter-task communication primitives and their use cases — know: queue (data transfer), binary semaphore (event signalling), mutex (shared resource exclusive access with priority inheritance).

Interview tip: An embedded systems interviewer will ask you to explain priority inversion and how FreeRTOS handles it — state that a low-priority task L holds a mutex needed by high-priority task H; medium-priority M runs instead of L because M > L, starving H; FreeRTOS mutexes implement priority inheritance, temporarily raising L's priority to H's until the mutex is released.

More Embedded Systems comparisons