APIs, concepts, guides, and more
Create a Real-Time Task

Learn how to create a Real-Time Task (RTTasks).

PREMIUM FEATURE BETA

Recommended
This guide will help you integrate RTTasks into your application. If you're new to RTTasks or want to better understand how they work, refer to the following resources:

Real-Time Tasks Function Library

This project, located in the examples/ folder under your RMP install directory (e.g., C:/RSI/X.X.X/examples/ or /rsi/examples/), is a CMake template for building a task functions library for use with Real-Time Tasks (RTTasks).

πŸ”Ή Understand the architecture

Before writing code, make sure you understand how the pieces fit together:

  • RTTaskManager (Firmware) – Distributed with RMP, the manager runs inside the real-time operating system, loads the RTTaskFunctions library, and schedules the functions according to their configured periods and priorities.
  • RTTask (Execution Unit) β€” Represents an individual real-time function instance managed by the RTTaskManager. Each task runs at a defined priority and period.
  • RTTaskFunctions library (Real-Time Logic) – This C++ project produces a shared library of deterministic task functions and the accompanying GlobalData definition. It cannot run on its own. The RTTaskManager loads and executes the functions from this library at runtime.
  • GlobalData (Shared Memory Bridge) β€” A structured data block defined in the RTTaskFunctions project that both environments can safely access. RTTasks read or write GlobalData members inside the real-time loop, while the user application reads or updates those values from the host side using the RealTimeTasks API.
  • User application – Runs in the non-real-time environment using RapidCode (C++, C#, or Python) or RapidCodeRemote (any gRPC-capable language). It communicates with the RTTaskManager to submit, monitor, and control tasks. It can also read and write to GlobalData through methods in the RealTimeTasks API. All high-level logic remains here, outside the real-time environment.

Keeping these boundaries clear prevents common mistakes and confusion. The RTTaskFunctions project used in this guide defines the RTTaskFunctions library and the GlobalData. It will not be runnable on its own. You will need to start the RTTaskManager separately and submit the tasks to it. You can start the RTTaskManager through the RealTimeTasks API, the rsiconfig utility, or the command line. For more information on using the RealTimeTasks API refer to the Real-Time Tasks concept page in the documentation.

⚠️ Important Note

These samples focus on demonstrating API usage. They may not include the complete safety logic required for your machine. When working with real hardware always wire an external emergency stop, verify limits, and start with conservative motion constants.

βœ… Prerequisites

Before generating or building Real-Time Tasks, make sure your development environment meets these requirements:

  • Windows hosts:
    • Visual Studio 2022 with the β€œDesktop development with C++” workload and CMake integration component (β€œC++ CMake tools for Windows”).
    • Install either the INtime Runtime (CDEV) or the INtime SDK if you plan to deploy or run RTTasks on an INtime node. Without either you can still build and test RTTasks on Windows, but it will not have real-time performance.
  • Linux hosts:
    • A recent GCC or Clang toolchain.
    • The distribution’s standard build tools (for example, install build-essential on Debian/Ubuntu).
  • CMake 3.15 or newer

For environment setup and Visual Studio generator scripts, see the documentation for the C++ sample applications. For conceptual background and runtime behavior, refer to the Real-Time Tasks concept page in our docs.

πŸš€ Quickstart

To get up and running quickly, follow these steps:

  • Copy the RTTaskFunctions (found in the examples folder) into your project
  • Add any required global variables to src/rttaskglobals.h
  • Define your task functions in src/rttaskfunctions.cpp, using Increment as a template
  • Build the shared library using CMake
  • In your application, create an RTTaskManager and use TaskSubmit to launch your tasks from the shared library

For a more detailed walkthrough, continue below.

βš™οΈ Set up your project

The examples folder in your RMP installation includes a CMake project called RTTaskFunctions. This serves as a starting point for building a shared library containing RTTask functions. Copy this folder into your project directory and rename it appropriately.

Here is an overview of the key files:

πŸ“‚ Layout

RTTaskFunctions/
β”œβ”€β”€ CMakeLists.txt # CMake setup for building the shared library
└── src/
β”œβ”€β”€ rttaskglobals.h # Defines Global variables
└── rttaskfunctions.cpp # Task function implementations

How it works

The template project takes all the source files (.h, .cpp) in the src/ directory and compiles them into a shared library for the target platform (Windows/INtime or Linux). It automatically includes the RMP headers, links the RMP libraries, and applies necessary configuration for use with RTTasks. The output is a .dll/rsl/.so file that will be loaded by the RTTaskManager. There is no standalone executable produced by this project, so you must launch a manager instance separately.

By default, the resulting library is named RTTaskFunctions and is output to the default RMP install directory (e.g., /rsi or C:/RSI/X.X.X). If these paths or names are modified, you must specify them explicitly when submitting a task from your application. Otherwise, they will be discovered automatically.

If you want to change any of the default behavior or are interested in learning more about how it works, then look at the CMakeLists.txt located in the root of the project folder.

πŸ“œ Create an RTTask functions library

In this guide, you’ll build a simple application that moves an axis based on the value of an analog input. It will use one global variable and two RTTasks. The first task, CalculateTarget, reads the analog input and calculates a target position, storing it in the global variable targetPosition. The second task, FollowTarget, moves the axis to the specified target.

Create a global variable

Open src/rttaskglobals.h. This file is where the global variables are defined and registered before being exposed to the host through the RealTimeTasks API.

To add a new global of type double called targetPosition:

  • Add RSI_GLOBAL(double, targetPosition) to the GlobalData struct
  • Add REGISTER_GLOBAL(targetPosition) to the GlobalMetaDataMap

The result should be:

struct GlobalData
{
GlobalData() { std::memset(this, 0, sizeof(*this)); }
GlobalData(GlobalData&& other) { std::memcpy(this, &other, sizeof(*this)); }
// A shared integer counter
RSI_GLOBAL(int64_t, counter);
// Shared double variables
RSI_GLOBAL(double, average);
//... other globals
RSI_GLOBAL(double, targetPosition);
};
inline constexpr GlobalMetadataMap<RSI::RapidCode::RealTimeTasks::GlobalMaxSize> GlobalMetadata(
{
REGISTER_GLOBAL(counter),
//... other globals
REGISTER_GLOBAL(average),
REGISTER_GLOBAL(targetPosition),
});

Create a task function

Open src/rttaskfunctions.cpp. This is the file where the task functions are defined. A task function must follow this template:

RSI_TASK(FunctionName)
{
...
}

We will add a new task function called CalculateTarget that will read the analog input, scale it to a value between 0 and 1, and store it in the global created in the previous step.

// This task reads the analog input value from the network node, scales it to
// a value between 0 and 1, and stores it in the targetPosition variable
RSI_TASK(CalculateTarget)
{
constexpr int NODE_INDEX = 0; // The network node with the analog input
constexpr int ANALOG_INDEX = 0; // The index of the analog input to use
constexpr int ANALOG_MAX = 65536; // Max value of the analog input
constexpr int ANALOG_ORIGIN = 42800; // The value to treat as the "origin" of the analog input
// Read the raw analog input value
int32_t analogInVal = RTNetworkNodeGet(NODE_INDEX)->AnalogInGet(ANALOG_INDEX);
// Shift the value by the origin
int32_t shiftedVal = analogInVal - ANALOG_ORIGIN;
// Make sure the value is between 0 and ANALOG_MAX using modulo
int32_t modVal = (shiftedVal + ANALOG_MAX) % ANALOG_MAX;
// Scale the value to be between 0 and 1 and store it in targetPosition
data->targetPosition = double(modVal) / ANALOG_MAX;
}

Then we will add another task function called FollowTarget that will move the axis towards the target position.

// This task moves the axis to the target position if it is not within the tolerance
// of the target position already.
RSI_TASK(FollowTarget)
{
constexpr int AXIS_INDEX = 0; // The index of the axis to move
constexpr double TOLERANCE = 0.02; // The tolerance for the position difference
// Check if the axis is within the tolerance of the target position
if (abs(RTAxisGet(AXIS_INDEX)->ActualPositionGet() - data->targetPosition) > TOLERANCE)
{
// Move the axis to the target position
RTAxisGet(AXIS_INDEX)->MoveSCurve(data->targetPosition);
}
}

Once the task functions have been added, build the project to produce a new library.

πŸš€ Launch your tasks

In your user application, written in C++, C#, or Python with RapidCode, or in any gRPC-capable language using RapidCodeRemote, create an RTTaskManager instance after configuring your RapidCode objects. Submit your tasks to the manager, which will load the shared library built in the previous steps. You can also start the manager from utilities like rsiconfig if you prefer to manage tasks outside of application code.

// The path to the RMP install folder
std::snprintf(parameters.RTTaskDirectory,
RMP_INSTALL_PATH);
// On Windows/INtime, use PlatformType::INtime for real-time or PlatformType::Windows for debugging
// On Linux, Platform does not need to be set
// For INtime managers, set the node you want the manager to run on
std::snprintf(parameters.NodeName,
"NodeA");
// For Linux, set the CPU core you want the manager to run on
parameters.CpuCore = 3;
// Create the RTTaskManager
RTTaskManager manager = RTTaskManager::Create(parameters);
static RTTaskManager Create(const RTTaskManagerCreationParameters &parameters)
Create a new RTTaskManager instance.
Interface for managing real-time tasks firmware. See Real-Time Tasks for more information.
Definition rttask.h:559
The RealTimeTasks namespace.
Definition rttask.h:36
RTTaskManagerCreationParameters specifies all the information required to create and configure an RTT...
Definition rttask.h:320
int32_t CpuCore
[Linux] CPU core to which the manager should be pinned (-1 for no pinning).
Definition rttask.h:349
PlatformType Platform
Platform on which the manager firmware will run.
Definition rttask.h:342
static constexpr int32_t DirectoryLengthMaximum
Maximum length of the directory path.
Definition rttask.h:322
char NodeName[NameLengthMaximum]
[INtime] Name of the node on which the manager will run. By default, this is set to (and verified as)...
Definition rttask.h:346
char RTTaskDirectory[DirectoryLengthMaximum]
Path to the directory containing the real-time task libraries.
Definition rttask.h:339
static constexpr int32_t NameLengthMaximum
Maximum length of name fields (node name, user label).
Definition rttask.h:325

Next, run the Initialize task one time, to get access to RapidCode objects and initialize your global variables.

// If you use the default library name and directory, you can omit the last two arguments
RTTaskCreationParameters initParams("Initialize");
params.Repeats = RTTaskCreationParameters::RepeatNone; // Don't repeat this task
RTTask task = manager.TaskSubmit(initParams);
constexpr int timeoutMs = 5000;
initTask->ExecutionCountAbsoluteWait(1, timeoutMs); // Wait for the task to execute once.
int64_t ExecutionCountAbsoluteWait(int64_t count=ExecutionCountDefault, int32_t timeoutMs=ExecutionCountWaitTimeoutMillisecondsDefault)
Wait for the task to reach a specific execution count.
Interface for controlling and monitoring a single real-time task. See RTTaskManager::TaskSubmit and R...
Definition rttask.h:435
RTTask TaskSubmit(const RTTaskCreationParameters &parameters)
Submit a new task to the manager using creation parameters.
RTTaskCreationParameters specifies all the information required to create and configure a real-time t...
Definition rttask.h:126
static constexpr int32_t RepeatNone
Special value to indicate the task should not repeat.
Definition rttask.h:143

Then submit the tasks created in the previous section.

RTTaskCreationParameters calcParams("CalculateTarget");
calcParams.Repeats = RTTaskCreationParameters::RepeatForever; // Repeat this task infinitely
calcParams.Period = 10; // Run the task every 10 samples
RTTask calcTask = manager->TaskSubmit(calcParams);
RTTaskCreationParameters followParams("FollowTarget");
followParams.Period = 10;
RTTask followTask = manager->TaskSubmit(followParams);
static constexpr int32_t RepeatForever
Special value to indicate the task should repeat forever.
Definition rttask.h:140

While your tasks are running, you can use RTTask::StatusGet and RTTaskManager::GlobalValueGet to monitor your tasks.

FirmwareValue value = manager->GlobalValueGet("targetPosition");
std::cout << "Target position: " << value.Double << std::endl;
RSI::RapidCode::FirmwareValue GlobalValueGet(int32_t offset)
Read a GlobalTag by its offset. (internal use).
Union representing a generic RMP firmware value with multiple data types, stored in 64-bits.
Definition rsi.h:468
double Double
Double precision (64-bit) floating-point.
Definition rsi.h:477

At the end of your program, stop the tasks and manager.

followTask->Stop();
calcTask->Stop();
manager->Shutdown();
void Stop()
Stop the task from executing.
void Shutdown()
Shutdown the RTTaskManager firmware.