sanoRTOS is a minimal Real-Time Operating System (RTOS) designed for ARM Cortex-M and RISC-V microcontrollers. This implementation provides a simple yet effective API for task management, synchronization, and communication, enabling efficient and predictable multitasking in embedded systems.
-
Priority-Based Preemptive Scheduling
Efficient task management with support for preemptive scheduling based on task priority levels. -
Optional Priority Inheritance
Prevents priority inversion during mutex acquisition by temporarily elevating the priority of lower-priority tasks. -
Symmetric Multiprocessing (SMP) Support
Fully supports multi-core systems with per-task core affinity configuration for optimal load balancing. -
Dynamic Stack Overflow Detection
Runtime monitoring of task stacks to catch and handle stack overflows proactively. -
Configurable Tick Rate
Easily adjustable tick frequency to match application-specific timing and power requirements. -
Task Synchronization Primitives
Includes mutexes, semaphores, condition variables, and event objects for safe and efficient coordination between tasks. -
Inter-Task Communication
Enables message passing between tasks using message queues, stream buffers, message buffers, and mailboxes. -
Minimalistic and Lightweight Design
Designed for embedded systems with limited resources — small footprint, fast context switches, and no unnecessary bloat.
The detailed API reference, including example code for each kernel object, lives in API_REFERENCE.md.
-
Clone the Repository:
- Open a terminal and clone the sanoRTOS repository
git clone https://github.com/pdlsurya/sanoRTOS.git
- Open a terminal and clone the sanoRTOS repository
-
Open STM32Cube IDE:
- Launch STM32Cube IDE and create a new STM32F4 project or open an existing one.
-
Include sanoRTOS in Project:
- Right-click on your project and select Properties.
- Go to C/C++ Build > Settings.
- Under Tool Settings, go to MCU GCC Compiler > Include paths and add the path to sanoRTOS/include and sanoRTOS/ports/arm/stm32f4/include directory.
-
Add Source Files:
- Navigate to C/C++ General > Paths and Symbols.
- In the Source Location tab, click on Link folder and add the path to sanoRTOS/source and sanoRTOS/ports/arm/stm32f4 directory by selecting Link to folder in the filesystem.
-
Edit stm32xxxx_it.c file:
-
STM32 initializes the SysTick timer during its clock initialization process and defines the
SysTick_HandlerISR function for the implementation of the delay function in the Core > Src > stm32xxxx_it.c file. Hence, theSysTick_HandlerISR function cannot be redefined inside the sanoRTOS. Instead, callsanoRTOS_SysTickHook()from the SDK'sSysTick_Handler()implementation. -
Moreover, sanoRTOS includes definition for
PendSV_Handlerused for task scheduling and context switching. STM32 also defines this ISR in stm32xxxx_it.c file; Hence, we need to remove the definition of this ISR from the stm32xxxx_it.c file to avoid multiple definition error.
-
-
Clone the Repository:
- Open a terminal and clone the sanoRTOS repository
git clone https://github.com/pdlsurya/sanoRTOS.git
- Open a terminal and clone the sanoRTOS repository
-
Set Up Your Pico Project:
- You can either create a new CMake-based Pico SDK project or add sanoRTOS to an existing one. Add sanoRTOS folder to your project folder
- sanoRTOS supports both ARM and RISC-V cores of the RP2350.
- Include sanoRTOS in Your CMakeLists.txt:
- Update your CMakeLists.txt to include sanoRTOS headers and sources as follows:
file(GLOB_RECURSE SANORTOS_SRCS
sanoRTOS/source/*.c
sanoRTOS/ports/arm/rp2350/port.c)
#for Hazard3 RISC-V cores, add source files from risc-v port of rp2350 (sanoRTOS/ports/riscv/rp2350/port.c and sanoRTOS/ports/riscv/rp2350/port.S)
target_include_directories(project_name PRIVATE
sanoRTOS/include
sanoRTOS/ports/arm/rp2350/include #for RISC-V port replace this line with sanoRTOS/ports/risc-v/rp2350/include
)
target_sources(project_name PRIVATE ${SANORTOS_SRCS})
- Add sanoRTOS to your Pico SDK project and include the RP2040 ARM port files.
file(GLOB_RECURSE SANORTOS_SRCS
sanoRTOS/source/*.c
sanoRTOS/ports/arm/rp2040/port.c)
target_include_directories(project_name PRIVATE
sanoRTOS/include
sanoRTOS/ports/arm/rp2040/include
)
target_sources(project_name PRIVATE ${SANORTOS_SRCS})
target_link_libraries(project_name PRIVATE pico_stdlib pico_multicore hardware_sync)- The RP2040 port uses:
SysTick_Handler()from the sanoRTOS port for the scheduler tickPendSV_Handler()from the sanoRTOS port for context switchingmulticore_launch_core1()whenCONFIG_SMPis enabled- RP2040 hardware spin lock
PICO_SPINLOCK_ID_OS1to serialize RTOS CAS operations on Cortex-M0+
#include "sanoRTOS/config.h"
#include "sanoRTOS/scheduler.h"
#include "sanoRTOS/task.h"
//Include MCU-specific header files here
// Define two tasks with 1024-byte stacks.
// These tasks are assigned to any available core using AFFINITY_CORE_ANY.
//
// On multicore MCUs (e.g. RP2350), sanoRTOS can schedule tasks on different cores
// depending on the affinity setting. AFFINITY_CORE_ANY allows the scheduler to choose any core.
// This can help distribute load across both cores and enable true parallel execution.
//
// On single-core MCUs, this setting is safely ignored — all tasks will run on the only available core.
TASK_DEFINE(task1, 1024, firstTask, NULL, 1, AFFINITY_CORE_ANY);
TASK_DEFINE(task2, 1024, secondTask, NULL, 1, AFFINITY_CORE_ANY);
taskHandleType *dynamicTask = NULL;
// Task 1 function – user-defined logic inside this loop
void firstTask(void *args) {
while (1) {
// Task1 code to run repeatedly
}
}
// Task 2 function – user-defined logic inside this loop
void secondTask(void *args) {
while (1) {
// Task2 code to run repeatedly
}
}
// Dynamically created task function
void thirdTask(void *args) {
while (1) {
// Dynamic task code to run repeatedly
}
}
int main() {
// Perform MCU-specific initializations here
// Example: initialize GPIO, UART, peripherals, etc.
// Start tasks
taskStart(&task1);
taskStart(&task2);
// Create and start a dynamic task
int ret = taskCreate(&dynamicTask,
"dynamicTask",
1024,
thirdTask,
NULL,
1,
AFFINITY_CORE_ANY);
if (ret != RET_SUCCESS) {
// Handle task creation failure
while (1) {
}
}
// Start the sanoRTOS scheduler (will not return)
schedulerStart();
// Control should never reach here if scheduler is working correctly
return 0;
}This RTOS includes ports for the following Espressif RISC-V based MCUs:
- ESP32-C6
- ESP32-P4
Unlike most ESP32 projects, these ports do not use Espressif's ESP-IDF.
Instead, they are built on top of custom ESP32 RISC-V bare-metal SDK developed specifically for low-level control and minimal system overhead.
The bare-metal SDK provides:
- Startup code and linker scripts
- Register definitions and peripheral structures
- Interrupt and exception handling
- Basic hardware initialization
- Minimal HAL required for RTOS operation
This SDK allows the RTOS to run without the ESP-IDF framework, making the implementation closer to traditional embedded RTOS ports.
- The RTOS ports assume the system is initialized using the corresponding bare-metal SDK.
- ESP-IDF is not required to build or run these ports.
This project is licensed under the MIT License-see the LICENSE file for details.