Zephyr RTOS and its Impact on IoT

 

According to the latest IoT & Embedded Developer Survey Report (2024) by the Eclipse Foundation, Linux remains the most popular choice for developing on Embedded devices. It is closely followed by FreeRTOS and Zephyr RTOS – with Zephyr now even surpassing bare‑metal solutions. This raises an important question: what makes Zephyr stand out compared to the alternatives?

Zephyr RTOS

Zephyr is an open-source Real-Time Operating System (RTOS) specifically created for Embedded devices with limited resources, such as microcontrollers used in IoT, sensors, wearables, and small industrial controllers. Unlike heavier operating systems like Linux, Zephyr is lightweight enough to run effectively even on devices with very small memory footprints (often just tens of kilobytes).

As Zephyr is an RTOS, it comes with basic functionality such as managing system resources via a scheduler, data buffers, and task prioritisation. Furthermore, Zephyr features built-in power management, efficient interrupt handling and integrated libraries such as mbedTLS for security and encryption, and MCUboot for the bootloader, which supports secure boot. Regarding scheduling, Zephyr’s kernel utilises pre-emptive scheduling (with cooperative threads supported) and thread priorities.

 

Zephyr is highly modular –developers can choose which components they need, thus eliminating unnecessary features from the final build. Building a Zephyr application is commonly done with west, with the option to con KConfig. Zephyr utilises logging statements throughout, andfigure libraries an variables with logging can be disabled (for example, via the prj.conf file, which affects all libraries and sub‑modules within the application). If additional features—such as Bluetooth—are required, specific configurations must be added so that the necessary files are included and compiled into the final build. Zephyr is continuously updated with new features and enhanced hardware support, ensuring that the latest technologies are supported natively. Zephyr is not only modular—allowing you to choose which libraries and modules to use—but it also offers excellent portability. It utilises device tree files (.dts) and comes with a wide range of example board files. You can build your application for a specific board configuration without needing to modify your application code.

Overall, Zephyr is a great choice for Embedded projects that require reliability, real-time responsiveness, efficient power management and built-in security 

Alternatives

Before moving on with Zephyr, we should give a fair comparison to some relevant alternatives.

 

Design

 

Summary

 

Bare-Metal

With bare-metal, the code runs directly on the microcontroller without any operating system layer. This means we have direct access to all hardware registers, interrupts, memory management, and timing mechanisms. Developers have to write all the logic themselves without assistance from a provided kernel API or scheduler. The code usually runs inside a single main execution loop.

FreeRTOS

FreeRTOS is a lightweight, minimalistic RTOS popular for Embedded devices because of its simplicity and small memory footprint. It provides core RTOS features such as task scheduling, task priorities, interrupt handling, queues, semaphores, mutexes, and timers. However, FreeRTOS deliberately does not include advanced functionality such as network communication stacks, security layers, file systems, or comprehensive hardware abstraction layers (HALs).

Embedded Linux

Embedded Linux is a powerful, general-purpose operating system commonly used in Embedded systems, often requiring more computational power and even more complex functionality. Embedded Linux systems typically include a Linux kernel combined with user-space applications, drivers, libraries, file systems, and networking capabilities. It offers advanced multitasking, comprehensive memory management (virtual memory, dynamic memory allocation), extensive peripheral support, graphical interfaces, advanced network protocols, security mechanisms (like firewall, TLS/SSL), and powerful debugging and profiling tools.

Choosing the right software design all depends on the complexity of the system and product requirements. Below is a decision tree which could help determine the most suitable design choice for the application in question. Please note that this is only a generic diagram and, of course, does not apply to all situations.

Note the final option could be expanded further for additional alternatives/OSes.

IoT adaptations

Zephyr’s design makes it ideally suited for IoT development. Its native network stack supports IPv4, IPv6, 6LoWPAN, TCP, UDP and higher-level protocols such as HTTP(s). For applications requiring lightweight messaging, Zephyr can integrate with MQTT libraries, facilitating efficient, low-overhead communication between devices and cloud services. Secure boot and the usage of cryptographic libraries like mbedTLS ensures both safe and trusted firmware upgrades and end-to-end data protection.

Zephyr’s configurable logging and debugging allow for comprehensive system monitoring, all without producing any significant resource overhead. These tools enable diagnosing issues quickly and optimise resource utilisation. For instance, it’s simple to utilise Zephyr’s log backend to wrap all existing system and kernel logs into a JSON format, and redirect them to an MQTT topic to the cloud directly!

Temperature sensor example

Consider a practical example: a lightweight temperature sensor designed to report data at regular intervals to a cloud platform (such as Azure, AWS or Google Cloud).

In order to implement the software, we would need to investigate which design to use, and we can utilise our decision tree mentioned earlier for guidance.

 

Q1: Do we want a low memory footprint, with the consequence of writing/importing everything from scratch without an OS?

A: We do want to aim for a low memory footprint, however, even if this can be run as a bare-metal application, we’d still have to manually write or import external temperature drivers, networking stacks and schedulers without a kernel API, possibly increasing development time and maintainability.

Q2: Do we need a full-featured OS (e.g multi-user, networking, advanced file systems)?

A: Again, we could have the file system and extra features provided by Embedded Linux, but there is too much functionality we wouldn’t need. Of course, this would increase the memory footprint of our application, as well as coming with additional maintenance concerns.

Q3: Does the application use advanced connectivity protocols (e.g., Bluetooth, Thread, LTE), or need extensive hardware platform support?

A: Yes, we need to have connectivity to the cloud. Security features such as secure boot and TLS could be viable as well. Zephyr seems like a good fit.

Code example

To demonstrate how simple it is to set up, we can utilise the existing Zephyr’s networking stack, built-in MQTT and JSON libraries, and simply publish the temperature sensor readings on every callback/temperature reading. The basic software for our Zephyr project would be simple, as shown in the brief example C-code below:

 

				
					/**
 * @file main.c
 * @brief Main application file for MQTT and temperature sensor.
 */
/* Zephyr core includes */
#include <zephyr.h>
/* Networking and MQTT includes */
#include <net/mqtt.h>
#include <net/socket.h>
/* Custom (or from Zephyr) temperature driver */
#include "temperature.h" 
/**
 * @brief Initialize MQTT and start its connection thread.
 *
 * This function sets up the MQTT connection and handles the automatic
 * connect/reconnect mechanism using the provided configuration.
 * The configuration includes credentials, IPs, connection parameters, and security details.
 */
void init_mqtt(void)
{
    setup_and_connect_mqtt(config);
}
/**
 * @brief Callback to publish temperature readings.
 *
 * This callback is invoked for each temperature sample.
 * It forwards the temperature reading and publishes it via MQTT.
 *
 * @param temperature Temperature value to be published.
 */
void temperature_callback(int temperature)
{
    publish_mqtt(topic, QOS2, temperature); /* blocking function */
}
/**
 * @brief Initialize temperature sensor and register its callback.
 *
 * This function initializes the temperature sensor and sets up
 * the callback function to handle temperature readings.
 */
void init_temperature_sensor(void)
{
    init_sensor();
    setup_callback(temperature_callback);
}
/**
 * @brief Main entry point.
 *
 * Initializes the MQTT connection and temperature sensor, then enters
 * an infinite loop yielding to other threads.
 *
 * @return int Always returns 0.
 */
int main(void)
{
    init_mqtt();
    init_temperature_sensor();
    while (true) {
        /* Important: yield to other threads */
        k_yield();
    }
    return 0;
}
				
			

Zephyr operates using threads, and when we perform the publish_mqtt call, we block the sensor thread (or the ISR thread, depending on the driver implementation) until execution is complete. As a brief discussion, we can take a look at how we can utilise Zephyr to improve our software further. Most drivers and their callbacks operate within an ISR context, which we do not wish to block.

A common pitfall is waiting for a mutex or semaphore within an ISR, which is what would happen in our example above. We have to wait for a mutex to ensure the hardware is ready to transmit the sensor data to our cloud, as well as waiting for the MQTT QoS 2 acknowledgements to go through. We can detect this issue at runtime with the CONFIG_ASSERT=y configuration, as both k_mutex and k_sem are Zephyr-implemented kernel functions containing an __ASSERT(!arch_is_in_isr()) guard clause. You might ask why there’s no call to these macros in the example code above, but this is because it’s already handled in Zephyr’s low-level implementation of the drivers and API, and would ultimately be called inside the publish_mqtt() function.

To address this issue further in our own code, we could use one of Zephyr’s many helpful tools, such as a message queue (k_msgq). Instead of publishing directly to the cloud within the callback function, we would post the data internally using k_msgq_put(). We can check the return code of k_msgq_put() to determine if the queue is full, and decide whether we want to drop the current temperature sample, or the oldest upon overflow events. A consumer thread in our own code would poll this message queue using k_msgq_get(), and publish the temperature readings to the cloud. Using this technique allow us to not only have full control over overflow events and the action to take, but we would also eliminate the risk of blocking the interrupt thread!

Regardless of the implementation details, we get the idea—it’s simple. We receive the temperature reading, and we can use Zephyr’s API and built-in functionality to publish the temperature data to the cloud service of choice.

Conclusion

While Linux continues to dominate in the development of Embedded devices, Zephyr RTOS offers a lightweight, modular alternative that is great for both IoT and offline applications, which is what contributes to the observed shift towards Zephyr in recent years. Its flexible architecture, robust security features and strong community support provide developers with a great tool for building efficient, scalable and secure Embedded systems.

However, choosing the right platform will always depend on the specific use case and the product requirements. That’s why we’ve developed expertise across multiple software platforms—including Zephyr—to provide value-driven, reliable, and maintainable solutions for future customer projects.

About the author

Eivind earned a bachelor’s degree in Computer Engineering, and a master’s degree in Systems Engineering with Embedded Systems with a thesis on path planning for multiple collaborative UAVs. During his consulting career, he has not only been involved in several different Zephyr projects, but also gained project experience with Embedded Linux and bare-metal programming.

More Posts