Skip to main content
Use Embedder to inspect RTOS dependencies, plan the port, adapt one service at a time, and compare the result against the original firmware. Start by defining the behavior the application must preserve: startup order, deadlines, data flow, recovery, and memory limits. This workflow applies to RTOS changes on an existing board. The example below covers FreeRTOS to Zephyr. If the MCU or board also changes, use Platform migrations to track the hardware work alongside the RTOS port.

Prepare the migration

Follow the Quickstart if you are new to Embedder, then open the firmware workspace. Run /init if needed and record the following in EMBEDDER.md:
  • Source RTOS version, vendor SDK or fork, toolchain, and build configuration.
  • Destination RTOS release, board target, required drivers, and middleware dependencies.
  • Build, test, flash, and observation commands, including the connected board and probe or port.
  • Application interfaces, timing tolerances, RAM, flash, and power budgets, and accepted behavior changes.
Confirm destination support for the exact board and required peripheral features, including DMA or asynchronous transfers where used. Supply the board revision, schematics, and relevant hardware documentation, and identify any board or driver work needed. Keep a reproducible source build and its firmware artifact so you can rerun the original behavior.

Capture the source behavior

In Act mode, ask Embedder to run the existing tests and a bounded hardware check before changing code:
Choose pass conditions for normal operation, peak load, and recovery from a failed peripheral or timed-out request. Use the same workload and measurement method for both RTOS implementations.

Review the porting plan

Run /plan and ask Embedder to trace RTOS use through the codebase, including wrappers, middleware, interrupt handlers, build variants, and generated files. The plan should identify:
  • Tasks, priorities, stacks, timers, synchronization, and shared-resource ownership.
  • Blocking calls, timeouts, interrupt context, and assumptions about scheduling.
  • Startup, allocation, drivers, power management, and build configuration.
  • Affected files, implementation order, checks for each stage, and unresolved dependencies.
Review which application interfaces remain stable and which need to change. Approve the plan to begin implementation in Act mode. Establish a bootable destination image and an observation path before moving dependent services.

Example: FreeRTOS to Zephyr

For this example, keep the existing board and migrate a sensor application with a periodic acquisition task, a sample queue, and a UART reporting task. Replace these details and limits with your project’s requirements:

Establish the Zephyr application

Ask Embedder to prepare the target build using the selected Zephyr release and board target. Review CMakeLists.txt, prj.conf, and any board overlay, along with the workspace’s pinned dependencies. Zephyr uses Kconfig for software configuration and devicetree for hardware description; follow the selected release’s application setup. Start with a minimal image that boots and produces console output. Then enable the sensor bus and UART, checking pins, clocks, and device readiness. Inspect the generated .config and zephyr.dts for the application image to confirm the effective configuration; a conventional single-image build places them under build/zephyr/. See Zephyr’s devicetree checks.

Review the behavior behind each API

Ask Embedder to explain each proposed replacement against the versions in your workspace:
  • Tasks and priorities. Move task entry points to Zephyr threads and explicitly map their relative urgency. FreeRTOS uses larger numbers for higher task priority; Zephyr uses smaller numbers, with negative values for cooperative threads and nonnegative values for preemptible threads. Include system threads in the priority review, and check time slicing and startup order. See FreeRTOS task scheduling and Zephyr threads.
  • Stacks and allocation. Upstream FreeRTOS xTaskCreate takes a depth in StackType_t elements, often called words; ESP-IDF uses bytes. Zephyr’s K_THREAD_DEFINE takes bytes. Use Zephyr’s stack-definition or stack-allocation APIs, then measure stack usage again under load. Check heap, static allocation, and allocation-failure behavior too. See FreeRTOS task creation and Zephyr thread APIs.
  • Queues and synchronization. A fixed-size sample queue may fit k_msgq. Check item size, copy versus pointer ownership, capacity, return values, and full-queue behavior. Review mutex inheritance and notification or event-bit semantics separately before choosing replacements. See Zephyr message queues.
  • Periods and timeouts. Preserve the acquisition task’s periodic deadline and overrun policy. Replacing a delay-until loop with a relative sleep after each iteration’s work can introduce drift. Convert tick-based waits deliberately and check rounding, no-wait, and indefinite-wait behavior against the chosen API. See Zephyr kernel timing.
  • Timers and interrupt handlers. FreeRTOS software timer callbacks run in a timer service task and must not block; Zephyr timer expiry functions run in interrupt context. Defer work that needs thread context to a suitable worker or thread. Audit every FromISR call and critical section for the destination API’s context rules. Zephyr treats kernel API use from zero-latency interrupts as undefined behavior. See FreeRTOS software timers, Zephyr timers, and interrupt handling.
  • Deferred work. A shared workqueue can delay processing behind other work. Submitting the same work item while it is already queued does not retain another event. If every sample matters, preserve samples in a queue or buffer and verify the consumer’s latency. See Zephyr workqueues.
  • Drivers and middleware. Decide which existing code can remain behind the application interface and which should use Zephyr drivers or subsystems. Check ownership of interrupts, clocks, DMA, and the system tick before retaining vendor HAL initialization.

Port one complete data path

After reviewing the plan, use /act and start with sensor acquisition through UART output:
Bring over additional services only after their dependencies work. Keep the original firmware build available until the destination passes the agreed checks.

Verify the migrated firmware

Build and flash the destination image on the confirmed board, then repeat the baseline tests. For the sensor example:
Use hardware scripts for repeatable bench checks and Debug mode for faults or hangs. A successful build and boot leave scheduling, timing, and recovery behavior to verify under load. See Automated testing for host and target checks. Choose instruments for the requirement: a logic analyzer for bus transactions, an oscilloscope for signal quality, or a power analyzer for current consumption. Record logging, instrumentation, and debugger settings that can affect the result. Test the ported subsystems together under expected peak load; strict timing requirements may also need worst-case analysis. Finish by updating EMBEDDER.md with the destination setup and verified commands. Record intentional behavior changes, remaining test gaps, and how to rebuild the source baseline before retiring the old RTOS configuration.
Last modified on September 18, 2026