FreeRTOS is the most widely deployed real-time operating system in the world. It runs on everything from simple microcontrollers in consumer electronics to safety-critical industrial controllers, and Amazon’s stewardship since 2017 has added a well-maintained AWS IoT connectivity layer that makes it a natural fit for connected device development. If you’re building a connected embedded system and haven’t chosen an RTOS yet, this is the one to understand.

What FreeRTOS Actually Is (and Isn’t)

FreeRTOS is a real-time operating system kernel — not a full OS in the Linux sense, but a minimal scheduler that runs on hardware with as little as 4KB of RAM. It provides the primitives you need for concurrent embedded development: tasks (like threads), queues, semaphores, mutexes, and timers.

The “real-time” part matters. FreeRTOS uses a priority-based preemptive scheduler, meaning higher-priority tasks preempt lower-priority ones. This gives you deterministic timing guarantees that a general-purpose OS like Linux cannot provide — critical for controlling actuators, sampling sensors at precise intervals, or handling protocol timing requirements.

Amazon’s FreeRTOS (now called FreeRTOS, having dropped the “Amazon” prefix) adds libraries on top of the kernel: OTA update management, AWS IoT Core connectivity via MQTT, HTTPS, and TLS, shadow device state management, and fleet provisioning. These libraries are what make it competitive with cloud-vendor alternatives for connected device development.

The Task Model

Everything in FreeRTOS is a task. Tasks are independently scheduled functions that run concurrently under the scheduler. Creating a task looks like this:

void vSensorTask(void *pvParameters) {
    for (;;) {
        // Read sensor
        float temperature = read_temperature_sensor();
        
        // Publish to queue for the MQTT task to handle
        xQueueSend(xSensorQueue, &temperature, portMAX_DELAY);
        
        // Wait 1000ms before next reading
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

// In your main function or initialisation code:
xTaskCreate(
    vSensorTask,        // Task function
    "SensorTask",       // Task name (for debugging)
    configMINIMAL_STACK_SIZE + 128,  // Stack size in words
    NULL,               // Parameters
    2,                  // Priority (higher = more urgent)
    NULL                // Task handle (optional)
);

The vTaskDelay call is important — it yields the CPU to other tasks for the specified period rather than spinning. A task that never delays blocks lower-priority tasks from running, which is the most common source of hard-to-diagnose bugs in new FreeRTOS code.

Queues: The Right Way to Share Data

Global variables are the intuitive way to share data between tasks, but they’re not safe in an RTOS environment — the scheduler can switch tasks at any point, leaving shared data in inconsistent state. FreeRTOS queues solve this correctly:

// Define a queue for temperature readings
QueueHandle_t xSensorQueue;

// Initialisation (before scheduler starts):
xSensorQueue = xQueueCreate(10, sizeof(float));

// Producer task (sensor reader):
float temperature = read_sensor();
xQueueSend(xSensorQueue, &temperature, portMAX_DELAY);

// Consumer task (MQTT publisher):
float received_temp;
if (xQueueReceive(xSensorQueue, &received_temp, portMAX_DELAY) == pdTRUE) {
    publish_to_aws(received_temp);
}

Queues are thread-safe and can be used from interrupt service routines via the xQueueSendFromISR variant. For simple mutual exclusion (protecting a shared resource like an SPI bus), use a mutex instead.

Connecting to AWS IoT Core

The FreeRTOS libraries include a coreMQTT agent that handles MQTT connectivity to AWS IoT Core with TLS authentication. The connection model uses X.509 certificates — each device has a unique certificate provisioned during manufacturing or via fleet provisioning at first boot.

#include "core_mqtt.h"
#include "core_mqtt_agent.h"

// Configure MQTT connection parameters
MQTTAgentConnectArgs_t connectArgs = {
    .pHostName = AWS_IOT_ENDPOINT,
    .port = AWS_MQTT_PORT,
    .pNetworkCredentials = &networkCredentials,
    .enableSessionResumption = false,
};

// Connect (TLS handshake, MQTT CONNECT packet)
MQTTStatus_t status = MQTTAgent_Connect(
    &mqttAgentContext,
    &connectArgs,
    MQTT_TIMEOUT_MS
);

// Publish a sensor reading
MQTTPublishInfo_t publishInfo = {
    .qos = MQTTQoS1,
    .retain = false,
    .pTopicName = "factory/sensor/temperature",
    .topicNameLength = strlen("factory/sensor/temperature"),
    .pPayload = jsonPayload,
    .payloadLength = payloadLength,
};

MQTTAgent_Publish(&mqttAgentContext, &publishInfo, &commandContext, NULL, MQTT_TIMEOUT_MS);

The coreMQTT library handles reconnection on network interruptions, which is essential for devices in real-world environments where connectivity is unreliable.

Memory Management: Pick the Right Heap Scheme

FreeRTOS ships five memory allocation schemes (heap_1 through heap_5). The choice matters more than most tutorials explain:

  • heap_1: simplest — allocates memory but never frees it. Good for systems where tasks and queues are created at startup and never destroyed.
  • heap_2: allows deallocation but can fragment. Avoid for long-running systems.
  • heap_4: first-fit with adjacent block merging — the right default for most applications. Handles fragmentation reasonably well.
  • heap_5: like heap_4 but spans multiple non-contiguous memory regions — needed for microcontrollers with fragmented RAM (common in STM32 and ESP32 devices).

For most IoT applications, heap_4 is the right starting point. Set configTOTAL_HEAP_SIZE to something appropriate for your hardware — on an ESP32 with 320KB of RAM, 200KB is a reasonable allocation for FreeRTOS heap given you also need space for your stack and static variables.

FreeRTOS vs Zephyr: When to Choose Each

If you’re evaluating both: FreeRTOS is simpler and has a shallower learning curve, better AWS integration, and an enormous community. Zephyr has a more complete driver ecosystem, stronger security architecture (memory protection, privilege separation), and is better suited to safety-critical applications where certification matters.

For a connected sensor device publishing to AWS IoT Core, FreeRTOS is the pragmatic choice. For an industrial safety controller or a device that needs PSA Certified or IEC 61508 compliance, Zephyr’s architecture is worth the steeper learning curve.

Getting Started

The fastest path to a working FreeRTOS device in 2026 is an ESP32 development board (£8 to £15) and the ESP-IDF development environment, which uses FreeRTOS as its underlying RTOS and exposes most FreeRTOS APIs directly alongside Espressif’s hardware abstraction layer.

The FreeRTOS documentation at freertos.org is genuinely good — the kernel developer guide covers every API with clear explanations of blocking behaviour and timing guarantees. Amazon’s own FreeRTOS documentation covers the AWS connectivity libraries and OTA update workflow.

The combination of a capable microcontroller, solid AWS connectivity libraries, and thorough documentation makes FreeRTOS the most accessible entry point to connected embedded development that actually works reliably in production.