Skip to main content
Robotics RTOS Embedded Systems FreeRTOS

RTOS Kernel Architecture for Robotics: Scheduling, Interrupts, and Hardware Control

Akash Verma - VoicePing
RTOS Kernel Architecture for Robotics: Scheduling, Interrupts, and Hardware Control. Teal, blue and green microcontroller, flight-controller and application-processor boards surround a small quadruped and drone. FreeRTOS ESP32 / STM32 / Pico & Pico 2 ↗ Zephyr ESP32 / STM32 / nRF52840 ↗ NuttX / ChibiOS Pixhawk 4 PX4 + NuttX / ArduPilot + ChibiOS ↗ Linux / PREEMPT_RT Raspberry Pi 5 / Jetson Orin / Intel & AMD ↗
Example software alternatives. Board support and RT kernel availability vary.
In this article

In this article

A robot needs the right command while that command can still help. A real-time operating system (RTOS) coordinates the work between sensing and actuation. Follow one joint correction through the RTOS architecture, from sensor input to hardware output.

1. The processor is fast. The robot still reacts too late.

A quadruped detects an uneven step and calculates the right correction

A quadruped steps onto a broken paving stone. An encoder reports a changed joint angle; the inertial measurement unit (IMU) detects the body rotating. The controller calculates a correction—but the robot keeps moving while that command travels to the joints.

On-time correction: joints respond while the robot can recover. Late correction: the posture changes before the joints respond. The same quadruped is shown balanced and tilting at an uneven step.
Illustrative recovery window; the available margin depends on the robot, controller, and terrain.

Correct arithmetic is only part of the job. A command based on an earlier posture may arrive after that posture has disappeared.

The correction reaches the joints after the robot needs it

Suppose this example requires the actuator command to be applied within 1 ms of the sensor sample. Computing the answer in 0.2 ms does not prove that the full response fits.

One sample · two delivery pathsCorrect arithmetic. Different arrival times.
Sample1 ms deadline1.5 ms
On time
0.6 ms
Too late
1.3 ms
Both paths spend 0.2 ms calculating. Waiting and delivery make the difference.
Illustrative timing, not a benchmark. The deadline applies to command application, not just computation.
Four terms for the rest of the pathWhat the timing numbers describe
Period
Time between scheduled updates
Deadline
Latest acceptable completion
Response time
Sample to command application
Jitter
Variation in timing across updates
Sketches show concepts, not measurements or a shared numerical scale.

A fast average can hide occasional late updates. Real-time control requires sufficiently bounded and consistent timing across the complete path. The ROS 2 real-time background develops that distinction.

2. How the controller coordinates each step

How the CPU, memory and drivers turn sensor readings into joint commands

In microcontroller-based embedded systems, the CPU reads measurements, calculates a target, and calls a driver. The driver configures a peripheral; the peripheral delivers a signal to the actuator interface.

The robotics hardware guide explains how motors, transmissions, sensors, and joint loads shape that actuator interface.

Software becomes a physical outputFollow the joint correction
  1. 01SensorIMU / encoderCapture a sample
  2. 02CPU + RAMControl taskCalculate target
  3. 03DriverRegister writesConfigure transfer
  4. 04PeripheralTimer / CANDeliver signal
  5. 05Joint drivePower stageApply command
↶ Encoder / current feedback closes the loop
The CPU issues instructions; peripherals produce signals; the drive supplies motor power.

A PWM driver can write a timer’s compare register; the timer then generates pulses without the CPU toggling every edge. A bus driver instead prepares a transfer, sometimes assisted by DMA. Register write, transfer completion, and command application are different moments.

At instruction level, a memory-mapped register is an address decoded by the peripheral bus. A CPU store changes hardware state rather than an ordinary RAM variable. A timer may latch a new compare value only at its next update boundary; ST’s timer cookbook explains this preload mechanism. A CAN transfer must also wait for bus access. The drive then decides when to apply the command. These waits sit beyond task selection.

Inside a typical MCU controllerDifferent addresses, different jobs
Conceptual microcontroller cutaway: CPU executes instructions, RAM holds working state, flash stores firmware, and timer/I/O produces peripheral signals.
STOREFlashInstructions + constants
WORKRAMSamples, stacks + buffers
CONTROLI/O registersPeripheral state + settings
A register write becomes a physical signal
  1. 01CPU store
  2. 02Timer compare register
  3. 03PWM waveform
  4. 04Motor drive
Conceptual functional cutaway, not a literal die layout. Memory-mapped registers expose peripheral state to CPU loads and stores.

A control pin carries a signal. The motor drive supplies operating power and may run its own faster current loop. The electronics behind physical AI follows that surrounding circuit and power path.

FreeRTOS · conceptual RAM layoutTwo ways to reserve a task's memory
RAM · one shared budget
Globals, buffers + other storage
RTOS heapA Control task · stack + TCBOther kernel objects + free space
Application-reserved storageB Telemetry task · stack + TCB
A Control taskxTaskCreate()

The configured allocator supplies its stack + TCB from the heap.

B Telemetry taskxTaskCreateStatic()

The application supplies its stack array + TCB storage.

Stack
Local variables, calls + saved CPU context
TCB · task control block
Priority, list links + pointer to saved stack context
Each task owns a separate stack and TCB. Either task can use either creation method. This is a conceptual budget; placement varies by build.

Each task needs RAM for its execution stack and its TCB. The stack holds function-call state and saved registers; the TCB tells the kernel where that context lives. A task’s stack is reserved storage, even when the task is Blocked.

FreeRTOS task memory · conceptual layoutTwo objects per task. Two ways to supply them.
DYNAMIC CREATIONxTaskCreate()
Configured heap allocatorAllocates task storage
Task stackCalls + saved registers
TCBPriority + stack pointer + list links
STATIC CREATIONxTaskCreateStatic()
Application-provided storageNo heap allocation for these objects
StackType_t[]Reserved stack array
StaticTask_tReserved TCB storage
Example stack only768 elements × 4 bytes = 3,072 bytesFor a 32-bit StackType_t; TCB, queues, and other buffers are extra.
Both paths consume RAM. Actual addresses, stack growth, TCB size, and allocator layout depend on the port and build. This is not a universal RAM map.

With upstream FreeRTOS, xTaskCreate() obtains the stack and TCB through the configured allocator. xTaskCreateStatic() accepts application-provided storage. Both still consume RAM; static creation removes the heap allocation for those objects. Stack depth uses StackType_t elements in upstream FreeRTOS, while ESP-IDF task-creation APIs use bytes. Check the task API and IDF port differences before copying a stack size.

That storage budget helps explain the platform split. A small FreeRTOS kernel and a Linux system carry different services; compare what each number includes before using it to select a chip.

Software footprint · published reference pointsFreeRTOS and Linux have different memory budgets
Examples from different builds, not a controlled performance or size comparison
What is counted?FreeRTOS referenceEmbedded Linux reference
Kernel code / storage≈9–18 KBUsable kernel configurations, quoted by Richard Barry in 2022.FreeRTOS size discussion ↗≈2–4 MBEmbedded kernel storage guidance in a 2017 Bootlin presentation.Bootlin kernel-size reference ↗
RAM in a published example21,944 bytes ≈22 KBOne STM32F3 Crane application: stacks, buffers, objects, and application data included.2026 study · Table 11 ↗32 MB or more2022 guidance for a more realistic small Linux system; very basic systems can use less.Bootlin RAM guidance ↗
And Linux + PREEMPT_RT? These Linux figures do not measure an RT build. Size the actual kernel, drivers, userspace, and locked working memory. There is no fixed RT memory increment.
Reference points, not minimum requirements. Compiler, architecture, features, libraries, and application storage change the result. A Linux kernel image and a complete firmware application are different measurement scopes; do not calculate a like-for-like size ratio from these rows.

Memory choices: MCU firmware, Linux, and allocation

In MCU-based embedded systems, on-chip SRAM holds task stacks, samples, and buffers, with firmware commonly stored in flash. An application processor with external DRAM and an MMU can support Linux processes, virtual memory, and a larger driver stack. Neither memory capacity alone establishes a timing guarantee.

Prepare critical working storage before the loop. FreeRTOS supports static or dynamic object allocation ; developers still size stacks, queues, and buffers. Its heap-management guide explains the storage schemes.

Inside the RTOS kernel: how tasks, interrupts and scheduling work together

RTOS is an operating-system category. FreeRTOS and Zephyr are implementations that supply scheduling and synchronization. Developers configure those mechanisms and implement their application tasks.

Two ways to organize the same workFrom a call sequence to waiting tasks
Superloop
SenseControlCommunicate
Repeat the sequence. A long call delays later calls.
RTOS
Sensor taskControl taskTelemetry task
Events make tasks Ready; the scheduler chooses eligible work.
Either design needs bounded work. An RTOS provides task execution and synchronization mechanisms.

To follow FreeRTOS task scheduling, use a preemptive, single-core configuration for this walkthrough. A control task waits for fresh data while sensing and telemetry get their own execution contexts.

One control taskBlocked → Ready → Running
BlockedWaiting for a sample
Sample / event
ReadyEligible for the CPU
Dispatch allowed
RunningCPU executes task
Running → Blocked Wait for next sampleRunning → Ready Preempted by eligible work
Ready means runnable, not already executing. A blocked task first needs its waiting condition resolved.

The kernel stores task state in a task control block (TCB). pxTopOfStack locates its saved context; uxPriority ranks it; list items connect it to state and event lists. An RTOS context switch resumes the task at its saved execution point.

Simplified single-core FreeRTOSStored state tells the scheduler what can run
Task control block (TCB)

Priority 3 + Ready / waiting-list links

saved stack pointer
Control task's stack · RAMLocal variables · function calls
Saved CPU context
Ready tasks · highest priority first
P3Control← selected
P2Sensor processing
P1Telemetry
P0Idle
The TCB points to context saved on the task's stack. The highest-priority eligible Ready task is selected when switching is permitted. Priorities are illustrative.

When switching is permitted, the highest-priority eligible Ready task is selected. A high-priority task that never blocks can starve lower-priority work. See the FreeRTOS task implementation .

For the single-core model, Ready lists are indexed by priority. Blocking on a queue removes a task from the Ready set and registers it as a waiter; a finite timeout also needs delayed-list bookkeeping. An event or timeout makes it eligible again. The kernel protects these list updates so an interrupt cannot observe a half-modified scheduling structure. Priority lookup may use a port-optimized bitmap where configured; it is not one universal algorithm on every chip.

Now let the sensor assert its data-ready interrupt. Follow the complete handoff below: Ready does not yet mean Running.

The complete handoff · all five stagesFrom sensor interrupt to control task
  1. 1
    Wait
    Control
    Blocked
    CPU
    Telemetry task

    The control task waits for a fresh sample. Lower-priority telemetry can use the CPU.

  2. 2
    Interrupt
    Control
    Blocked
    CPU
    Sensor ISR

    The data-ready interrupt preempts task execution. The ISR acknowledges the sensor event.

  3. 3
    Wake
    Control
    Ready
    CPU
    Sensor ISR

    An ISR-safe kernel operation wakes control. It is eligible, but the ISR is still executing.

  4. 4
    Switch
    Control
    Ready
    CPU
    PendSV / context switch

    After higher-priority exceptions permit it, the port saves the outgoing context and restores the selected task.

  5. 5
    Run
    Control
    Running
    CPU
    Control task

    Exception return resumes control. It can consume the sample and calculate the joint correction.

Cortex-M example with preemption enabled and kernel API interrupt-priority rules satisfied. No tick boundary is required. Ready does not yet mean Running.

On the Cortex-M4F port, a requested switch can pend PendSV, a low-priority exception. Hardware first saves an exception frame; the port saves the remaining task context, selects another TCB, and restores that task. The process stack pointer (PSP) follows task storage; handler code uses the main stack pointer (MSP).

Cortex-M4F example · basic integer frameA context switch changes the saved stack pointer
Outgoing task stack · PSP
Hardware exception frameR0–R3 · R12 · LR · PC · xPSR
Port-saved task contextR4–R11 · EXC_RETURN
TCB.pxTopOfStack → saved context
  1. SavePendSV saves remaining registers onto the outgoing task stack.
  2. SelectvTaskSwitchContext() chooses the next eligible task under kernel protection.
  3. RestoreLoad the next TCB's stack pointer and restore its port-saved registers.
  4. ReturnException return restores the hardware frame; execution resumes at its saved PC.
PSP
Task stack pointer
MSP
Handler stack pointer
BASEPRI
Interrupt-priority masking threshold
Conceptual order, not assembly. Floating-point use can add saved state; alignment and lazy FPU stacking affect the frame. ESP32 uses different ports.

The port raises BASEPRI while selecting the next task to mask the interrupt priorities that may access kernel data. Higher-urgency interrupts can remain enabled, but must not call the affected FreeRTOS APIs. Pending PendSV does not force a switch through an active higher-priority handler. This is specifically the Cortex-M4F port , not ESP32’s Xtensa or RISC-V assembly.

Priority change versus equal-priority turnsPreemption and time slicing are separate
Preemption
Telemetry P1Control P3 wakes → runs
A higher-priority task can take over between ticks.
Time slicing
Task A · P1Task B · P1Task A · P1
Equal-priority Ready tasks take turns at ticks.
The tick also advances timeouts. The scheduler can respond to events independently of periodic time slicing.

Preemption can respond between ticks. Time slicing gives equal-priority Ready tasks turns at ticks. They have separate FreeRTOS configuration switches .

Inside the context switch: registers, stacks, and other schedulers

A Cortex-M exception saves part of the CPU context. The port saves the remaining task context and stores its stack pointer; exception return restores the selected instruction stream. Memfault’s walkthrough covers PSP/MSP, exception frames, BASEPRI masking, and floating-point context.

An ISR must use the appropriate ISR-safe kernel operation and request the switch when required by the API/port. It need not wait for the next tick. On applicable Cortex-M ports, interrupts above the permitted API-call threshold must not call FreeRTOS APIs.

FreeRTOS task-management examples expand the single-core model. Zephyr scheduling offers different ready-queue implementations and cooperative/preemptive thread classes.

How queues, semaphores and mutexes coordinate tasks and shared resources

RTOS task synchronization has two jobs here: deliver the sensor sample and wake the waiting control task. Choose the kernel object by what needs to cross that boundary.

Pick the object by its meaningData, availability, or ownership?
Task communication and resource protection
Kernel objectCarriesUse in the robot
QueueDATABuffered messagesTimestamped sensor samples
Binary semaphore0 OR 1One available permitTransfer-complete signal
Repeated events may coalesce
Counting semaphore0 … NSeveral permitsCount available buffer slots
MutexOWNERSHIPExclusive resource accessProtect task-level bus access
Basic priority inheritance
A semaphore is a synchronization object with a count and waiting-task state, not a FIFO payload queue. Mutexes are not ISR signaling objects.

A sample becoming available can move its waiting receiver Blocked → Ready. CPU access comes next. A semaphore carries availability, not the sample itself; a mutex adds ownership and basic priority inheritance.

Inside FreeRTOS, a queue combines a circular payload buffer, an item count, and lists of tasks waiting to send or receive. Sending copies the configured item bytes and may release a waiting receiver. Semaphores reuse queue machinery with zero-sized data items: the count and waiting lists matter, not a payload buffer. A mutex additionally tracks its holder so the kernel can temporarily raise that owner’s priority. The queue implementation makes these relationships concrete.

Queue freshness, event counts, and ownership

A binary semaphore holds at most one permit, so repeated events may coalesce. A counting semaphore tracks multiple available permits. FreeRTOS also provides task notifications, useful for a single receiving task when their semantics fit the event.

Decide whether a full control queue rejects data, overwrites old data, or reports an overrun. An old backlog may be less useful than the latest complete sample. Pointer messages also need explicit buffer ownership. Read the queue examples and shared queue/semaphore implementation .

3. Why a critical correction can still be delayed

Why a ready control task cannot always run immediately

A high-priority task can be eligible yet unable to take over the CPU. It can also be waiting on a dependency. Raising its task priority alone does not automatically remove either wait.

Two different waitsReady-but-delayed is different from Blocked
READYThe task could run

The CPU is in protected work or interrupt handling. Dispatch must wait.

Protected workSwitchControl
BLOCKEDThe task cannot run yet

A required sample, resource, or timeout condition is unresolved.

WaitResource arrivesReady
Priority chooses among eligible tasks. It cannot remove a dependency or make a protected CPU region preemptible.

Critical sections, interrupt masking, scheduler suspension, and ISR execution can postpone task dispatch—including in FreeRTOS. Protected work has no special “highest task priority”; it temporarily restricts when switching can happen.

Why task priority alone is insufficientWhich mechanism prevents the handoff?
Kernel delay sources and their effect on control execution
MechanismWhat the CPU or kernel doesWhat control waits for
Non-RT Linux spinlockspin_lock() prevents local task preemption; IRQs may remain enabled.Protected region to finish, then an allowed reschedule.
Interrupt maskingMasked interrupt sources cannot enter their handlers yet.Unmasking, event handling, and subsequent dispatch.
ISR executionHandler context occupies the CPU outside ordinary task selection.An eligible exception return / context switch.
Scheduler suspensionvTaskSuspendAll() postpones task switching, without itself masking interrupts.Scheduler resumption and deferred bookkeeping.
Mutex dependencyA task blocks on a resource another task owns.Owner progress and release; PI can reduce interference.
The first four can postpone dispatch or event delivery. A mutex waiter is Blocked until its dependency is resolved. Protected work is not a special highest-priority task.

Consider a Linux driver holding spinlock_t on a non-PREEMPT_RT CPU. An enabled interrupt can wake a higher-priority control thread and set a rescheduling request. On returning from the interrupt, the CPU may still have to finish the protected driver section before switching. The control thread is Ready; the remaining protected work is part of its response-time budget.

vTaskSuspendAll() in FreeRTOS is different from masking interrupts: ISRs can still execute, but task switching is postponed until scheduler resumption processes deferred work. A Cortex-M critical section instead uses port-specific interrupt masking. Raising a task’s priority does not undo either mechanism. The Linux locking rules explain the corresponding spinlock and IRQ distinctions.

Priority inheritance · one CPU · one mutexLet the mutex owner borrow High's priority
3HighRobot control
2MediumLogging
1LowDevice setup

Larger number = higher priority

Low owns the mutex. High requests it and becomes Blocked.
Without inheritanceMedium is Ready and outranks Low
CPU execution order
  1. Medium · 2Logging runs
  2. Low · 1Finish + unlock
  3. High · 3Take mutex + control
High waitsMedium's work + Low's remaining work
With inheritanceThe kernel boosts Low from 1 → 3
CPU execution order
  1. Low · 1 → 3Finish + unlock
  2. High · 3Take mutex + control
  3. Medium · 2Logging runs later
High waitsLow's remaining work; Medium cannot preempt Low
Low temporarily inherits High's priority because High is waiting on its mutex. In this example, Low returns to priority 1 after unlocking. Blocks show execution order, not duration.

With a mutex supporting priority inheritance, its owner temporarily inherits the priority of a higher-priority task waiting for it. This helps the owner finish and release the resource. It does not remove the critical work, cure deadlock, or bound a device that never responds.

Kernel protection, interrupt priority, and multicore locks

In a non-PREEMPT_RT Linux configuration, acquiring a normal spinlock prevents task preemption on that CPU. IRQ-disabling variants add local interrupt masking. The Linux locking rules distinguish those mechanisms.

Task priority and hardware interrupt priority are separate. Port-specific masking thresholds govern which interrupts may call the kernel. On multicore systems, local protection alone may not protect a resource another CPU can access.

Keep resource hold times bounded and analyze lock chains. The FreeRTOS resource-management chapter explains mutex ownership and basic priority inheritance.

How FreeRTOS and desktop Linux differ—and what PREEMPT_RT changes

An RTOS vs Linux comparison must account for the complete response path. Both kernels manage task states, priorities, preemption, and interrupts; the distinction is how the configured system controls interference across that path.

Kernel architecture · three configured systemsWhat sits between the control task and hardware?
FreeRTOSSingle-core MCU example
High-priority control task
FreeRTOS kernel
  • TCBs + priority Ready lists
  • Highest eligible priority selected
  • ISR-safe APIs wake tasks
  • Remaining delay: critical sections can defer switches
Linux without PREEMPT_RTPreemption model varies
Configured control thread
Linux kernel
  • SCHED_FIFO / SCHED_RR available
  • Hard IRQ work can defer dispatch
  • spinlock_t disables task preemption locally
  • Remaining delay: protected paths postpone switching
Linux + PREEMPT_RTSame Linux application platform
Configured RT control thread
Linux RT kernel
  • Most IRQ work runs in threads
  • spinlock_t uses RT-mutex-based PI
  • More kernel paths are preemptible
  • Remaining delay: raw locks + low-level paths remain protected
All three reach hardware through drivers → I/O → actuators
Preemption permits a handoff; it does not remove the work. FreeRTOS has its own scheduler. PREEMPT_RT changes Linux.
PI = priority inheritance. All three still require bounds for interrupts, locks, execution, and device delays.

PREEMPT_RT is a Linux configuration, not a FreeRTOS feature. It places more kernel work under scheduler control through threaded interrupts and PI-aware sleeping locks.

A typical threaded IRQ path keeps a short hard-interrupt entry that acknowledges or masks the source, then wakes an IRQ thread. That thread competes according to scheduling policy and priority. PREEMPT_RT changes normal spinlock_t to an RT-mutex-based implementation: a contender can sleep, and priority inheritance helps its owner progress. raw_spinlock_t, low-level entry paths, and other explicitly protected regions remain non-preemptible. See how RT kernels differ .

Illustrative traces · not benchmark resultsSame calculation. Different waiting budgets.
Sense · 70 µsWaiting · variesCompute · 200 µsTransport + apply · 210 µs

Scale: 0–1,500 µs · dashed line: 1,000 µs deadline

Linux without PREEMPT_RTSCHED_FIFO control thread
Wait 800 µs1,280 µs total · 280 µs late
Linux + PREEMPT_RTSCHED_FIFO control thread
Wait 120 µs600 µs total · 400 µs margin
FreeRTOSHigh-priority task · preemption enabled
Wait 120 µs600 µs total · 400 µs margin
Only the waiting allowance changes in this example. Actual ordering and latency depend on hardware, configuration, and load; the diagram establishes no ranking or guarantee.

Real-time Linux on a suitably configured PC can perform robot control. Ordinary Ubuntu cannot simply be assumed to meet a strict deadline; neither can an unvalidated FreeRTOS system. Open-source licensing is independent of that timing question.

Memory can add another delay even after scheduling permits execution. With virtual memory, an instruction may fault because a page needs allocation, a copy-on-write copy, or a file/swap read. Only some faults involve storage. A typical MCU FreeRTOS system uses resident memory without demand paging, although allocation, flash/cache access, buses, and devices can still stall it.

Memory residency is a separate timing concernA Ready task can still wait on memory
Typical MCU FreeRTOS
  1. Reserved RAM
  2. Reuse task stack + buffers
  3. No demand-page fault step
Linux virtual-memory access
Translate virtual address
Resident + permittedAccess RAM
or
Recoverable page faultAllocate, copy or read a page → retry instruction
Prepare Linux control memory: preallocate → prefault → lock required mappings → reuse. A TLB miss alone is not a page fault; not every fault reads storage.
Invalid accesses may fail instead. Static creation and residency are different issues. PREEMPT_RT does not eliminate faults, and MCU memory can still incur cache, bus, or device stalls.

For Linux control threads, prepare critical buffers and stack pages before starting the loop, prefault the needed mappings, and use appropriate memory locking while checking errors and limits. mlockall() does not make future allocation or copy-on-write harmless. PREEMPT_RT itself does not eliminate page faults; the memory-locking manual explains these constraints.

Linux priorities, page faults, and PREEMPT_RT trade-offs

Linux provides normal application policies and real-time policies such as SCHED_FIFO and SCHED_RR. A nice value is not a replacement for configuring those policies. The scheduling manual defines the interfaces.

Page faults may introduce work before an address becomes usable. Preallocate critical storage, prefault pages, and use appropriate memory locking while checking failures and limits. mlockall() helps keep memory resident; it does not fix CPU, lock, cache, or device delays.

Threaded work and different locking can add overhead or reduce throughput for some workloads. Priorities, interrupt placement, power management, CPU placement, and drivers need care. The cost is workload-dependent, not a fixed percentage. Read the RT theory , kernel differences , and hardware constraints .

Now give the whole correction one budget. Add distinct bounds for sensing, interference, computation, transport, and output application; include higher-priority work that interrupts computation.

Illustrative timing budget · two complete examplesHow much waiting can the correction afford?
Sensing 70 µsCompute 200 µsTransport 120 µsApply output 90 µs

480 µs fixed + 120 µs waiting = 600 µs total

Within the 1,000 µs deadline · 400 µs remaining

480 µs fixed + 800 µs waiting = 1,280 µs total

Past the 1,000 µs deadline · 280 µs late

Sensing + waiting/interference + computation + transport + output application ≤ deadline. The fixed stages total 480 µs. Only the waiting allowance changes between these examples.

A budget model, not a robot simulation or a measured guarantee. Valid bounds must come from the actual system.

With 120 µs of waiting, the example totals 600 µs, leaving 400 µs before its 1,000 µs deadline. With 800 µs of waiting, the same fixed work totals 1,280 µs and arrives 280 µs late. These are teaching values, not board measurements.

Make the output boundary observableOne event, six timestamps
  1. 0 µsSample acquiredSensor timestamp
  2. 70 µsISR entryGPIO / trace marker
  3. 190 µsControl startsTask marker
  4. 390 µsComputation endsDriver call boundary
  5. 510 µsTransport doneBus capture
  6. 600 µsOutput appliedDrive / output marker
600 µs − 0 µs = total response. Task-start latency alone would miss computation and output delivery.
Illustrative trace matching the default budget. The actuator interface determines how application of a command can be observed.

Measure the actual output boundary. Scheduler latency alone does not reveal when a drive applies a command, and the largest observed delay in one run is not a worst-case proof.

Verify the path on hardware

Timestamp sensor acquisition, ISR entry, task start, computation end, transfer completion, and output application. GPIO markers and a logic analyzer can help; the markers must identify the same event and clock domain. Stress competing communication, storage, and interrupts.

Canonical’s cyclictest guide measures a scheduling-related part of the path. Linux real-time application monitors help diagnose faults and waiting patterns on supported kernels. ROS 2 real-time background , PREEMPT_RT internals , and Zephyr mutex documentation provide further depth.

4. From local control to a complete robot

Building the control firmware and exposing it through a custom SDK

The FreeRTOS vs Linux choice starts with the hardware that must sense and act. RAM, CPU architecture, peripheral drivers, and timing requirements determine which software can do that job. These are example choices for supported targets, with two options per platform.

Hardware capacity ≠ software footprintWhich software fits which chip?
Two example software choices per platform; application roles illustrate how the hardware is used
Chip / boardTwo software optionsWhy the software fitsRobot role + hardware reason
ESP32 / S3 / C3Wireless MCU
Hundreds of KB of on-chip SRAM; capacity varies by chip.
  1. ESP-IDF FreeRTOS
  2. Zephyr
Compact task kernels leave room for stacks and radio buffers. IDF integrates chip drivers and services.Wireless sensing / robot I/O. Radios exchange data; timers and GPIO handle local signals.
RP2040 / RP2350Pico / Pico 2 MCU
264 KB / 520 KB SRAM ↗
  1. Bare-metal C/C++
  2. FreeRTOS
Bare-metal code avoids kernel task overhead; FreeRTOS adds scheduling and synchronization within SRAM.Encoders / pulse generation. PIO, timers, and DMA capture or generate signals without CPU work on every edge.
STM32 F4 / F7 / H7Cortex-M MCU families
Flash and SRAM vary by part.
  1. FreeRTOS
  2. Zephyr
Configurable MCU kernels organize control, sensing, and communication without a full Linux process environment.Motor / joint controller. ADCs, timers, DMA, and buses connect feedback sampling to actuator commands.
Nordic nRF52840Wireless Cortex-M4 MCU
256 KB RAM · 1 MB flash ↗
  1. Zephyr via nRF Connect SDK
  2. Bare-metal + nrfx
Zephyr integrates wireless services; standalone drivers suit simpler peripheral firmware. Both need a planned RAM budget.Battery-powered sensors. Radio and sleep modes suit intermittent sensing; bare-metal nrfx alone is not a Bluetooth stack.
STM32F765Pixhawk 4 flight controller
512 KB RAM · 2 MB flash ↗
  1. PX4 + NuttX
  2. ArduPilot + ChibiOS
Board-specific flight firmware combines an RTOS, sensor drivers, task stacks, and buffers.Drone stabilization. IMU sampling, estimation, and control produce timely ESC commands.
BCM2712Raspberry Pi 5 application SoC
GB-scale external DRAM ↗
  1. Raspberry Pi OS
  2. Ubuntu Linux
An MMU and external DRAM support processes, virtual memory, filesystems, and Linux drivers.ROS 2 / cameras / gateway. Multicore compute and camera/network I/O serve concurrent host workloads.
NVIDIA OrinJetson CPU / GPU modules
GB-scale DRAM; module-dependent.
  1. Jetson Linux
  2. Jetson Linux + PREEMPT_RT
Linux supplies GPU and device services; an RT kernel changes CPU dispatch and interrupt handling on supported releases.Vision / AI inference. GPU compute processes frames and tensors; CPU real-time tuning does not bound every GPU operation.
Intel / AMDPC application processors
External DRAM + MMU.
  1. Ubuntu Linux
  2. Ubuntu + PREEMPT_RT
A broad process and driver platform handles large applications; RT configuration reduces kernel-induced waiting.Development / planning / validated control. CPU capacity serves complex workloads; direct control still needs suitable I/O and timing validation.
Memory capacity is one constraint, not the complete selection rule. Hardware figures describe available memory, not required OS memory. Check the exact board, port, drivers, and release; the example applications still require measured timing.

Give sensing and feedback control explicit wake-ups, priorities, execution limits, and enough stack. Keep parsing, network traffic, logging, and storage from holding resources needed by the critical loop.

Local RTOS firmware · illustrative data flowSeparate communication from the control loop
Incoming commandsWhen a host message arrives
Command taskParse + validate the SDK command
Latest targetFixed-size mailboxExample: 30°
Latest target → control taskRead the latest valid target
Local controlAt each scheduled update
Sensor sampleMeasured joint positionExample: 27°
Control taskTarget − measured = 3° errorCompute the correction
Driver → jointApply the actuator command
Control task → state snapshotPublish a copy of measured state
Outgoing feedbackLower-priority telemetry task
State snapshotMeasured position, status + timestamp
Telemetry taskSend feedback to the host SDK
Control does not wait for a new message or for telemetry. An expired target follows the configured timeout policy.
Arrows show data handoffs between tasks. Mailbox and snapshot access must take bounded time; slow parsing or telemetry must not hold resources needed by the control loop.

The kernel supplies scheduling and synchronization. Your firmware supplies the control behavior and drivers; a custom SDK exposes commands and measured state.

Here is the earlier interrupt handoff expressed as a small FreeRTOS pattern. A one-item queue keeps the latest complete sample. Create the queue and task before enabling the interrupt; acquire the sample through a bounded hardware path.

Cortex-M / FreeRTOS application patternKeep acquisition short. Run control in its task.
INTERRUPT CONTEXT
void sample_isr(void) {
  BaseType_t wake = pdFALSE;
  Sample s = read_ready_sample();
  xQueueOverwriteFromISR(
    sample_q, &s, &wake);
  portYIELD_FROM_ISR(wake);
}

Copy one completed sample; request a switch when a higher-priority task wakes.

TASK CONTEXT
void control_task(void *arg) {
  Sample s;
  for (;;) {
    xQueueReceive(sample_q, &s,
                  portMAX_DELAY);
    Target t = target_snapshot();
    Command u =
      control_step(s, t);
    actuator_write(u);
  }
}

Block between samples. Use a coherent target and a bounded output path.

Setup contract: a one-item queue, valid ISR API priority, initialized hardware, and bounded helper functions. Add freshness checks, overruns, and fault handling for the actual controller.
Schematic C pattern, not complete firmware. Types and application helpers are omitted; ISR acquisition must not hide a blocking bus transaction. Yield macros are port-specific.

The ISR copies a completed sample and requests a switch if required. The task waits without consuming CPU, reads a coherent target snapshot, computes the next output, and calls the driver. Copy a multi-field target as one consistent version using message passing or a bounded task-level lock; do not hold that lock across slow I/O.

For a periodic loop, use an appropriate timer or xTaskDelayUntil() rather than accumulating work time with repeated relative delays. Tick resolution must fit the period; sub-tick requirements need a suitable hardware timing source.

Set configUSE_PREEMPTION, the priority range, allocation options, and stack sizes in the supplied kernel configuration. Then implement the tasks and their synchronization. You normally configure and port the scheduler rather than writing Ready-list selection yourself. When running FreeRTOS on ESP32 with a supported dual-core ESP-IDF configuration, the scheduler also considers core affinity. Pinning a task does not isolate shared memory or buses; a local interrupt mask does not stop the other core. Shared data needs the target’s supported cross-core synchronization.

An example contract, not a vendor APISend intent. Return measured state.
Command → controller
Joint
joint_3
position_rad
0.15
sequence
42
Expiry
Defined validity window
Controller → host
Measured position
Radians
Sample timestamp
Declared clock domain
Applied sequence
Which command was used
Status
Fresh / stale / fault
Document ranges, queue policy, acknowledgements, units, clock synchronization, and timeout behavior.
An acknowledgement can confirm receipt without proving when a joint applied the target.

Define units, accepted ranges, timestamps, sequence numbers, acknowledgements, freshness, and timeout behavior. Receipt is not proof of physical execution.

At the wire boundary, define serialization explicitly: field widths, byte order, framing, and integrity checks where needed. Avoid sending a raw C structure whose padding may differ on the host. The controller validates a packet, publishes a complete target, and reports the sequence actually applied. A timeout can select a defined hold, ramp-down, or fault response appropriate to the mechanism. The SDK should expose that state, rather than disguising receipt as successful motion.

For a custom robot, scope these interfaces alongside sensing and actuator integration. VoicePing’s custom robotics development approach starts with the task and its operating environment.

ESP-IDF, upstream FreeRTOS, and multicore choices

ESP-IDF integrates its FreeRTOS implementation, drivers, and startup path. Supported multicore targets add affinity and shared-resource considerations. Upstream FreeRTOS also supports SMP; you do not implement all multicore scheduling manually.

Follow the IDF documentation for the target and release . Core pinning can reduce some competition, but cannot isolate shared buses, memory, or interrupt effects. Define clock synchronization and whether a timestamp describes acquisition or transmission, then validate the configured system.

How a kernel becomes a product platform. Customization can happen in the kernel port, in application firmware, or at the external SDK boundary. A company does not need to rewrite the scheduler to make a distinct controller.

Documented product examples · different customization layersReuse the kernel. Build the product around it.
01 · KERNEL + PLATFORMEspressifESP-IDF on supported ESP targets
FreeRTOSTarget port + SMP integration + hardware driversApplication tasks + network / peripheral services

What developers customizeTask priorities, affinity, buffers, synchronization, and application behavior.

02 · CONTROL FIRMWAREBitcrazeCrazyflie flight application
FreeRTOS tasksSensor-ready → estimator → controller → motor outputSetpoints, parameters, logging + board support

What developers customizeControl algorithms and drivers, platform configuration, and application modules.

03 · ROBOT SDKUnitreePublic SDK2 Go2 interface
rt/lowcmd → robotqdqkpkdtaurt/lowstate → application

What developers customizeHost control logic using motor targets and measured state. The SDK does not identify the joint-controller OS.

Kernel adaptation, application firmware, and host SDKs are distinct layers. Field and topic names refer to the cited Go2 example; model interfaces can differ.

Espressif documents a hardware-specific FreeRTOS implementation and integration. Bitcraze’s Crazyflie shows the other common route: use FreeRTOS tasks and add a sensor-to-estimator-to-controller-to-motor pipeline. Its stabilizer documentation and firmware source expose that application layer.

Unitree illustrates the public-interface layer. The SDK2 Go2 example publishes low-level motor commands and subscribes to state. Its Python SDK documents Cyclone DDS . This establishes an extensible robot interface; it does not establish that the hidden joint controllers use FreeRTOS. Keep the documented SDK behavior separate from assumptions about internal firmware.

Connecting local RTOS control with ROS 2 in quadrupeds and drones

Place that controller in the robot. A host can plan motion while local controllers close feedback loops and joint drives regulate current. Allocate each loop where its timing can be validated.

One possible quadruped architecturePut each loop where its timing can be met
A host computer sends targets to a microcontroller and then a joint drive. Teal arrows show outgoing commands; indigo arrows return measured feedback.
Targets →← Measured feedback
HOSTLinux + ROS 2Plan motion
CONTROLLERLocal RTOSExecute feedback loop
JOINT DRIVEDrive firmwareRegulate current + power
Arrows show communication, not power wiring. This split is a design choice; validated Linux/PREEMPT_RT control can also sit above actuator-level loops.

ROS 2 is middleware; an RTOS is an operating-system kernel or platform. They have different responsibilities and often work together. Selected loops can also run on a suitable Linux/PREEMPT_RT host; ROS 2’s real-time design discussion explains the application-level constraints.

Linux is useful on the host because it supplies processes, camera/network drivers, robotics libraries, and deployment tools. PREEMPT_RT improves kernel responsiveness while retaining that platform. A ROS 2 callback still needs a suitable executor/thread policy, bounded work, and prepared memory; an RT kernel does not automatically make every callback real-time. FreeRTOS offers a smaller execution environment for local firmware, rather than a drop-in home for the host’s whole Linux software stack.

Choose the timing requirement before the kernel. A vision or mapping host may need throughput and a broad driver stack while a separate MCU closes the fast loop. A Linux host that directly owns a strict control deadline needs an appropriate real-time configuration and end-to-end validation. A PC can run PREEMPT_RT; ordinary desktop workloads often do not need its tighter latency objective.

Application choice · desktop host or deadline owner?What you gain—and what you must budget
General-purpose Linux hostVision · planning · UI · developmentBalance responsiveness, throughput, and power. A separate controller can own fast feedback.
Linux + PREEMPT_RT control hostValidated loops with strict response requirementsKeep the Linux ecosystem while reducing kernel-induced waiting. Prepare the application and I/O path too.
PREEMPT_RT mechanisms and application trade-offs
Cost or obligationWhy it arisesApplication decision
Possible throughput costIRQ-thread dispatch and PI-lock bookkeeping add work.Measure the real workload; there is no universal overhead percentage.
More configurationTask/IRQ priorities, affinity, and shared resources interact.Validate control alongside networking, storage, and housekeeping.
Starvation riskA continuously runnable RT thread can deprive lower-priority work.Bound execution and blocking. This risk also exists without PREEMPT_RT.
Energy trade-offsSome latency tuning restricts deep idle or frequency changes.Balance power and thermals; enabling PREEMPT_RT alone does not mandate this tuning.
Driver assumptionsNormal spinlocks and interrupt execution contexts change.Review custom drivers that assume a spinlock always disables preemption.
A PC can run PREEMPT_RT. Its value depends on the timing requirement; neither kernel choice automatically grants the application RT priority or a deadline guarantee.

These costs are workload-dependent. IRQ threads and priority-inheritance locking add bookkeeping; CPU placement and driver assumptions need review. Disabling deep sleep or frequency changes can trade energy efficiency for response time, but that is a tuning choice rather than an automatic effect of PREEMPT_RT. Canonical’s latency guide describes those practical trade-offs. A CPU-hogging real-time thread can starve other work even on a non-RT kernel.

This coexistence also appears in developer surveys. The two categories below cover different hardware roles; neither is a benchmark or a recommendation for a particular MCU.

Eclipse Foundation · 2025 developer surveyDifferent device roles use different software stacks
Constrained devices0–100% · reported use
  • Linux/Raspbian45%
  • FreeRTOS32%
  • Bare-metal21%
  • Zephyr RTOS17%
  • ThreadX17%
  • ROS 27%
  • Contiki / Contiki-NG5%
  • Huawei LiteOS5%
IoT gateways & edge nodes0–100% · reported use
  • Linux/Raspbian62%
  • Android23%
  • Windows19%
  • Bare-metal12%
  • ThreadX10%
  • Zephyr9%
Multiple systems are reported; these percentages are not exclusive market shares or deadline tests. Source labels are retained: ROS 2 is middleware, and Raspbian is a Linux distribution.
Adapted layout from the 2025 Eclipse IoT and Embedded Developer Survey Report, p. 15. © Eclipse Foundation AISBL and contributors. Chart adaptation: CC BY-SA 4.0; values and labels retained.

Read “constrained” relative to an application’s RAM, flash, CPU, power, and I/O budget. An ESP32-class controller and a Linux edge computer can face different constraints. A survey’s broad Linux result does not establish that desktop Linux fits an ESP32. Memory capacity, MMU support, drivers, and measured loop timing must be checked for the actual board.

The same path in a flight controllerIMU sample → stabilization → motor command
  1. IMUMeasure motion
  2. EstimatorEstimate attitude
  3. ControllerCompute correction
  4. ESCDrive motor phases
  5. MotorsChange thrust
PX4 applicationNuttX on supported flight boardsArduPilot applicationChibiOS on many STM32 targets
The flight application uses the RTOS and board drivers; an optional companion computer supplies higher-level targets.

The same pattern supports drone stabilization. On supported flight boards, PX4 commonly uses NuttX; ArduPilot uses ChibiOS on many STM32 targets. Follow the PX4 flight-stack overview , system architecture , and ArduPilot porting guide .

Back to the quadruped

We can now follow the uneven-step correction all the way to the joint. The kernel organizes execution; the complete sensing, control, and actuation path determines whether it arrives in time.

Return to the uneven stepOne correction. A complete timing path.
01Sense the changeIdentify sample time and freshness.
02Make control eligibleResolve the event or resource wait.
03Run within the budgetBound interference and computation.
04Apply before the deadlineInclude transfer and drive timing.
The useful result is a timely physical correction, supported by the whole controller architecture.

For your next controller, trace one sensor event to the physical output. Name the deadline, account for each wait, and verify that boundary on hardware.

To take that controller into an application, explore VoicePing’s robotics engineering approach and define the hardware, control interfaces, and acceptance criteria together.

Share this article
Video
0:00 0:00