Introduction
The NetBurner Real-Time Operating System (NBRTOS) is a full-featured preemptive multitasking RTOS supporting:
- Multiple tasks with 255 priority levels
- Semaphores for resource protection
- Mailboxes for message passing
- FIFOs for flexible queuing
- Message queues for structured communication
- Critical sections for mutual exclusion
The RTOS comes pre-configured as part of the NetBurner development package. Your application begins execution in the UserMain() task.
Priority Levels
To conserve system resources, the default number of tasks is 64, with 64 being the Idle task that will run when all other higher priority tasks are blocked. You can increase the number of tasks up to 255. There are a number of system tasks running at priorities defined in constants.h. The recommended UserMain() priority is MAIN_PRIO, which is a value of 50. When creating additonal tasks:
- Add or subtract from MAIN_PRIO for readability. For example, MAIN_PRIO-1 is a higher priority task, which MAIN_PRIO+1 is a lower priority task.
- Use the helper task function: OSGetNextPrio() to choose. For example, OSGetNextPrio(OSNextPrio::Above)
};
OSNextPrio
What to consider as the 'next' priority when looking for an available priority for task creation.
Definition nbrtos.h:2374
@ Next
Highest priority that is lower than the current task, OSTaskID()->OS_LO_PRIO.
Definition nbrtos.h:2378
@ Above
Lowest priority that is higher than the current task, OSTaskID()->0.
Definition nbrtos.h:2376
@ Maximum
Highest priority currently unused, 0->OS_LO_PRIO.
Definition nbrtos.h:2375
@ Minimum
Lowest priority currently unused, OS_LO_PRIO->0.
Definition nbrtos.h:2379
@ Below
Highest priority that is lower than the current task, OSTaskID()->OS_LO_PRIO.
Definition nbrtos.h:2377
Task Priority Range:
Priority Range: 1 - 255 (Default maximum is 64).
┌─────────────────────────────────────┐
│ Lower Number = Higher Priority │
│ │
│ 1 <--- Highest Priority │
│ 2 │
│ . │
│ . Application Tasks │
│ . │
│ 64 <--- Lowest Priority (Idle) │
│ . │
│ . │
│ . │
│ 254 │
│ 255 <--- Lowest Priority │
└─────────────────────────────────────┘
Important**: Each priority level can only be used by ONE task at a time. Always check return values when creating tasks or changing priorities.
What is a Preemptive RTOS?
Core Principle
The highest priority task ready to run will ALWAYS run.**
This is fundamentally different from time-sliced operating systems like Windows or Unix. In NBRTOS:
- No round-robin scheduling
- No time slices
- Priority-based execution only
Task Execution Flow
Task A (Priority 50) Task B (Priority 51)
┌──────────────┐ ┌──────────────┐
│ Running │ │ Blocked │
│ │ │ │
└──────┬───────┘ └──────────────┘
│
│ Calls blocking function
v
┌──────────────┐ ┌──────────────┐
│ Blocked │ │ Running │
│ │ │ │
└──────────────┘ └──────┬───────┘
│
│ Task A unblocks
v
┌──────────────┐ ┌──────────────┐
│ Running │ │ Blocked │
│ (Higher │ │ (Lower │
│ Priority) │ │ Priority) │
└──────────────┘ └──────────────┘
uint8_t OSSemPend(OS_SEM *psem, uint16_t timeout)
Wait timeout ticks for the value of the semaphore to be non zero. Note: A timeout value of 0 (zero) w...
Definition nbrtos.h:1992
void OSTimeDly(uint32_t to_count)
Delay the task until the specified value of the system timer ticks. The number of system ticks per se...
Definition nbrtos.h:1855
Blocking Scenarios
A higher priority task MUST block for lower priority tasks to execute:
┌─────────────────────────────────────────────┐
│ When Tasks Block │
├─────────────────────────────────────────────┤
│ │
│ 1. Time Delay │
│ │ │
│ v │
│ [Task sleeps
for specified
time] │
│ │
│ 2. Resource Wait │
│ │ │
│ v │
│ [Task waits until resource available] │
│ │
│ 3. I/O Operation │
│ │ │
│ v │
│ [Task waits for data] │
│ │
└─────────────────────────────────────────────┘
#define TICKS_PER_SECOND
System clock ticks per second.
Definition constants.h:49
int read(int fd, char *buf, int nbytes)
Read data from a file descriptor (fd).
time_t time(time_t *pt)
Gets the current system GMT time.
Function Categories
RTOS Blocking Functions
Functions that pend on resources or create time delays:
I/O Functions That Block
Functions that perform read operations or pend on file descriptors:
- select() - Wait for I/O readiness
- read() - Read data (blocks until data available)
- write() - Write data (blocks until space available)
- gets() - Get string from input
- getchar() - Get character
- fgets() - Get string from file
Network Functions That Block
Functions that wait for connections or data:
- accept() - Wait for incoming connection
- UDPPacket() - Wait to receive UDP packet (when configured for receive)
Functions That Unblock Tasks
Functions that post to pending resources:
System Task Priorities
System tasks use predefined priority levels defined in nbrtos/include/constants.h. The exact tasks depend on:
- Your platform (MOD5441X, SOMRT1061, etc.)
Enabled system features (HTTP server, network stack, etc.)
Example**: Calling StartHTTP() creates a system task for web server request handling.
Task Creation
Entry Point: UserMain()
Every NetBurner application begins in the UserMain() task, which is created automatically by the system.
System Boot
│
v
┌─────────────────────┐
│ System Init │
│ - Hardware setup │
│ - RTOS startup │
└──────────┬──────────┘
│
v
┌─────────────────────┐
│ UserMain() starts │
│ - Create tasks │
│ - Main loop │
└─────────────────────┘
void StartHttp(uint16_t port, bool RunConfigMirror)
Start the HTTP web server. Further documentation in the Initialization section Initialization - Syste...
void init()
System initialization. Ideally called at the beginning of all applications, since the easiest Recover...
Task Creation Methods
Method 1: OSTaskCreatewName() - Full Control
Provides complete control over all task parameters with status return value.
Parameters**:
- Task function name
- Optional parameter to pass (NULL if unused)
- Top of task stack
- Bottom of task stack
- Task priority
Task name (string)
Returns**: Status code (OS_NO_ERR on success)
Method 2: OSSimpleTaskCreatewName() - Simplified
Automatically allocates stack space, requires only:
- Task function name
- Task priority
Task name (string)
Note**: Does not return status code
Priority Selection Helper
Use OSGetNextPrio() to automatically find an available priority, but ensure:
- Not higher than critical system tasks (e.g., ETHER_TASK_PRIO)
- Not too low for your real-time requirements
- Refer to constants.h for system priority definitions
Complete Example
#include <init.h>
#include <stdlib.h>
#include <nbrtos.h>
#include <system.h>
#include <utils.h>
const char *AppName = "Task Creation Example";
uint32_t TaskAllParamsStack[USER_TASK_STK_SIZE];
void TaskAllParams(void *pd)
{
uint32_t loopCount = 0;
uint32_t delayTime = (uint32_t)pd;
printf("TaskAllParams delay: %ld seconds\r\n", delayTime);
while (1)
{
printf("TaskAllParams iteration: %ld\r\n", loopCount);
loopCount++;
}
}
void TaskSimple(void *pd)
{
uint32_t loopCount = 0;
uint32_t delayTime = 6;
printf("TaskSimple delay: %ld seconds\r\n", delayTime);
while (1)
{
printf("TaskSimple iteration: %ld\r\n", loopCount);
loopCount++;
}
}
void UserMain(void *pd)
{
uint32_t delayTime = 3;
int returnCode;
printf("Creating TaskAllParams...");
TaskAllParams,
(void *)delayTime,
&TaskAllParamsStack[USER_TASK_STK_SIZE],
TaskAllParamsStack,
"TaskAllParams"
);
printf("Success\r\n");
else
printf("*** Error: %d\r\n", returnCode);
printf("Creating TaskSimple\r\n");
while (1)
{
}
}
#define MAIN_PRIO
Recommend UserMain priority.
Definition constants.h:130
uint8_t OSTaskCreatewName(void(*task)(void *dptr), void *data, void *pstktop, void *pstkbot, uint8_t prio, const char *name, OS_TCB **pRetHandle=NULL)
Create a new task.
#define OSSimpleTaskCreatewName(x, p, n)
Simpler form of creating a new task. Will automatically allocate the default task stack size.
Definition nbrtos.h:1763
#define OS_NO_ERR
No error.
Definition nbrtos.h:58
bool WaitForActiveNetwork(uint32_t ticks_to_wait=120 *TICKS_PER_SECOND, int interface=-1)
Wait for an active network connection on at least one interface.
Protecting Shared Data
Mechanism Selection Guide
Choose the appropriate protection mechanism based on your needs:
┌────────────────────────────────────────────────────┐
│ Data Protection Mechanism │
│ │
│ Need to signal event only (no data)? │
│ │ │
│ └──> Use Semaphore │
│ (32-bit counter) │
│ │
│ Need to pass single 32-bit value/pointer? │
│ │ │
│ └──> Use Mailbox or Queue │
│ (pointer or integer) │
│ │
│ Need to pass structures/objects? │
│ │ │
│ └──> Use FIFO │
│ (linked list of structures) │
│ │
│ Need temporary exclusive access? │
│ │ │
│ └──> Use Critical Section (OSCrit) │
│ (mutex-like protection) │
│ │
│ Need to block all task switching? │
│ │ │
│ (tasks disabled, IRQs enabled) │
│ │
│ Need to block tasks AND interrupts? │
│ │ │
│ └──> Use USER_ENTER/EXIT_CRITICAL │
│ (complete system lock) │
└────────────────────────────────────────────────────┘
void OSUnlock(void)
This function unlocks the OS.
void OSLock(void)
Calling the OSLock function will prevent the OS from changing tasks.
Protection Mechanisms (Ordered by System Impact)
System Impact: Low ──────────────────────────> High
│ │
│ │
┌───────────────┼─────────────────────────────────┼─────┐
│ │ │ │
│ Semaphore │ OSCrit/
OSLock │ CRITICAL
│ Mailbox │ │ SECTION
│ Queue │ │
│ FIFO │ │
│ │ │
└───────────────┴─────────────────────────────────┴─────┘
(Task waits) (Task blocks only) (All blocked)
Detailed Mechanism Comparison
| Mechanism | Purpose | Blocks | IRQ Impact | Best For |
| OSSemPend()/Post() | Signal/protect resource | Task only | None | Event signaling, simple resource protection |
| OSMboxPend()/Post() | Pass pointer message | Task only | None | Passing single message/pointer between tasks |
| OSQPend()/Post() | Message queue (FIFO) | Task only | None | Multiple messages, bounded queue |
| OSFifoPend()/Post() | Linked list queue | Task only | None | Unbounded message passing, complex structures |
| OSCritEnter()/Exit() | Counted mutex | Task only | None | Multi-task resource protection |
| OSLock()/Unlock() | Disable task switch | All tasks | None | Short critical sections |
| USER_ENTER/EXIT_CRITICAL() | Disable all | Tasks + IRQs | All disabled | Absolute protection (use sparingly) |
Important Considerations
Queue vs. FIFO
Queue**:
- Fixed size (declared at compile time)
- Bounded memory usage
- Predictable behavior
Good for embedded systems
FIFO**:
- Dynamic size (linked list)
- Memory allocated by application
- Flexible but requires memory management
Avoid dynamic allocation in embedded systems
Recommendation**: Use static memory pools for FIFO structures to avoid fragmentation.
Semaphores
Concept
A semaphore is a protected counter used for:
- Controlling access to shared resources
- Signaling event occurrence
- Task synchronization
Operation Flow
Task A Semaphore Task B
(Counter)
│ │
├──────────────────> [Count: 0 -> 1] │
│ │
│ [Count: 1] │
│ │
│ [Count: 1 -> 0] <─────────┤
│ │
│ │ (Gets semaphore)
│ │
├──────────────────> [Count: 0 -> 1] │
│ │
uint8_t OSSemPost(OS_SEM *psem)
Increases the value of the semaphore by one. Note: If any higher priority tasks were waiting on the s...
Definition nbrtos.h:1973
Usage Pattern
uint8_t OSSemInit(OS_SEM *psem, long value)
Initializes a semaphore.
Definition nbrtos.h:1955
Semaphores are used to control access to shared resources or or to communicate between tasks in a mul...
Definition nbrtos.h:407
Timeout Specification
Timeout Parameter:
┌──────────────────────────────────────┐
│ 0 = Wait forever │
└──────────────────────────────────────┘
Resource Protection Example
void UserMain(void *pd) {
}
void TaskA(void *pd) {
while (1) {
SerialPort.Write("Task A\r\n");
}
}
Message Queues
Concept
Message queues enable tasks and ISRs to send and receive pointer-sized messages. The pointers typically reference structures or objects containing actual data.
Queue Structure
┌─────────────────────────────────────────┐
│ Message Queue │
│ │
│ Entry 0: [ptr] ──> [Message Data] │
│ │ │
│ Entry 1: [ptr] ──> [Message Data] │
│ │ │
│ Entry 2: [ptr] ──> [Message Data] │
│ │ │
│ Entry 3: [empty] │
│ │ │
│ Entry N: [empty] │
│ │ │
│ v │
│ [Max Size: Defined at Creation] │
└─────────────────────────────────────────┘
^ ^
│ │
Dequeue Enqueue
uint8_t OSQPost(OS_Q *pq, void *msg)
This function posts a message to a Queue.
Definition nbrtos.h:2113
void * OSQPend(OS_Q *pq, uint16_t timeout, uint8_t *err)
Wait timeout ticks for another task to post to the queue.
Definition nbrtos.h:2187
Message Flow
Producer Task Queue Consumer Task
(FIFO)
│ │
├─────────────> [msg1] │
│ │
├─────────────> [msg1][msg2] │
│ │
│ [msg2] <─────────────────┤
│ │
│ │ (Receives msg1)
│ │
├─────────────> [msg2][msg3] │
│ │
Usage Example
typedef struct {
uint32_t sensorId;
float temperature;
uint32_t timestamp;
} SensorReading;
void *SensorQueueStorage[10];
void UserMain(void *pd) {
OSQInit(&SensorQueue, SensorQueueStorage, 10);
}
void SensorTask(void *pd) {
SensorReading reading;
while (1) {
reading.sensorId = 1;
reading.temperature = ReadSensor();
reading.timestamp = Secs;
OSQPost(&SensorQueue, (
void *)&reading);
}
}
void ProcessingTask(void *pd) {
while (1) {
SensorReading *msg = (SensorReading *)
OSQPend(&SensorQueue, 0);
printf("Sensor %lu: %.2fC at %lu\r\n",
msg->sensorId, msg->temperature, msg->timestamp);
}
}
uint8_t OSQInit(OS_Q *pq, void **start, uint8_t size)
A queue functions as a fixed size FIFO for communication between tasks. This function initializes an ...
Definition nbrtos.h:2095
A message queue is an object that enables tasks and interrupt service routines to pend and post point...
Definition nbrtos.h:693
FIFOs
Concept
FIFOs are similar to queues but specifically designed for linked-list-based message passing. Unlike queues with fixed size, FIFOs grow dynamically within available memory.
Structure Requirements
The first element of any FIFO structure MUST be a void * pointer for OS internal linking:
typedef struct MyFifoStruct {
void *next;
uint32_t data1;
float data2;
char message[64];
} MyFifoStruct;
FIFO Architecture
┌──────────────────────────────────────────────┐
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ [next]──┼───>│ [next]──┼───>│ [next]──┼─>NULL
│ │ data1 │ │ data1 │ │ data1 │ │
│ │ data2 │ │ data2 │ │ data2 │ │
│ │ ... │ │ ... │ │ ... │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ ^ │
│ │ │
│ │
│ │ │
│ v │
└──────────────────────────────────────────────┘
uint8_t OSFifoPost(OS_FIFO *pFifo, OS_FIFO_EL *pToPost)
This function posts to a FIFO.
Definition nbrtos.h:2236
OS_FIFO_EL * OSFifoPend(OS_FIFO *pFifo, uint16_t timeout)
This function pends on a FIFO.
Definition nbrtos.h:2267
Memory Management Strategies
Strategy 1: Static Pool (Recommended)
#define FIFO_POOL_SIZE 20
MyFifoStruct fifoPool[FIFO_POOL_SIZE];
bool poolInUse[FIFO_POOL_SIZE] = {false};
MyFifoStruct* AllocateFifoStruct() {
for (int i = 0; i < FIFO_POOL_SIZE; i++) {
if (!poolInUse[i]) {
poolInUse[i] = true;
return &fifoPool[i];
}
}
return NULL;
}
void FreeFifoStruct(MyFifoStruct *ptr) {
int index = ptr - fifoPool;
if (index >= 0 && index < FIFO_POOL_SIZE) {
poolInUse[index] = false;
}
}
Strategy 2: Dynamic Allocation (Not Recommended)
MyFifoStruct *ptr = (MyFifoStruct *)malloc(sizeof(MyFifoStruct));
free(ptr);
Queue vs. FIFO Decision
Choose Queue When: Choose FIFO When:
┌────────────────────┐ ┌────────────────────┐
│ Fixed message count│ │ Variable msg count │
│ Predictable memory │ │ Complex structures │
│ Simple data types │ │ Need flexibility │
│ Bounded behavior │ │ Can manage memory │
└────────────────────┘ └────────────────────┘
FIFO Functions
Critical Sections
OSCrit - Counted Critical Section
A counting mutex that restricts resource access to one task at a time.
Operation Flow
Task A OSCrit Object Task B
(Resource Guard)
│ │
├────────────> [Lock: Task A] │
│ │
│ (Accessing resource) │
│ │
│ [Lock: Task A] <─────────────┤
│ │
│ │ (BLOCKED)
│ │
│ OSCritExit() │
├────────────> [Lock: Released] │
│ │
│ │ (UNBLOCKED)
│ [Lock: Task B] <─────────────┤
│ │
│ │ (Accessing resource)
uint8_t OSCritEnter(OS_CRIT *pCrit, uint16_t timeout)
This function tries to enter or claim the critical section.
Definition nbrtos.h:2320
Counting Behavior
Critical Section Counter:
┌─────────────────────────────────────┐
│ │
│ OSCritExit() -> Count-- │
│ │
│ When Count reaches 0: │
│ -> Resource released │
│ ->
Next task can enter │
│ │
└─────────────────────────────────────┘
Usage Examples
Manual Enter/Exit
OSCritObj linkedListGuard;
void TaskA(void *pd) {
while (1) {
linkedList.Add(data);
OSCritExit(&linkedListGuard);
}
}
C++ Scoped Object (Recommended)
OSCritObj linkedListGuard;
void TaskB(void *pd) {
while (1) {
{
OSCritObj lock(&linkedListGuard);
linkedList.Remove(data);
}
}
}
OSCrit vs. OSLock
┌─────────────────────────────────────────────────┐
├─────────────────────────────────────────────────┤
│ │
│ OSCrit (Resource Mutex): │
│ - Blocks only tasks accessing same resource │
│ - Other tasks can still run │
│ - IRQs not affected │
│ - Good for: Protecting shared data │
│ │
│
OSLock (Task Switch Disable): │
│ - Blocks ALL task switching │
│ - No tasks can preempt │
│ - IRQs still run │
│ - Good for: Brief critical operations │
│ │
└─────────────────────────────────────────────────┘
OSLock Usage Pattern
void QuickOperation() {
globalCounter++;
}
void QuickOperationCpp() {
{
globalCounter++;
}
}
A simple wrapper class that helps use OS locks effectively.
Definition nbrtos.h:2510
Warning**: Keep OSLock sections as short as possible to maintain system responsiveness.
OS Flags
Concept
OSFlags enable monitoring multiple events simultaneously using a 32-bit bitmap where each bit represents a distinct flag or event.
Flag Bitmap Structure
32-bit Flag Register:
┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
│31│30│29│...│ 7│ 6│ 5│ 4│ 3│ 2│ 1│ 0│
└─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
│ │ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ └─> Event 0
│ │ │ │ │ │ │ │ │ └───> Event 1
│ │ │ │ │ │ │ │ └─────> Event 2
│ │ │ │ │ │ │ └───────> Event 3
│ │ │ │ │ │ └─────────> Event 4
...
Flag Operations
Flag Manipulation:
┌────────────────────────────────────────┐
│ │
│ ──────────────────────────────> │
│ [Bit 0 = 1] │
│ │
│ ──────────────────────────────> │
│ [Bit 0 = 1, Bit 2 = 1] │
│ │
│ ──────────────────────────────> │
│ [Bit 2 = 1] │
│ │
└────────────────────────────────────────┘
void OSFlagSet(OS_FLAGS *flags, uint32_t bits_to_set)
This function sets the corresponding bits asserted in bits_to_set of an OS_FLAGS object pointed to by...
Definition nbrtos.h:1480
void OSFlagClear(OS_FLAGS *flags, uint32_t bits_to_clr)
This function clears the bits asserted in bits_to_clr of an OS_FLAGS object pointed to by *flags....
Definition nbrtos.h:1495
Pending Modes
Wait for ANY flag (OR):
┌─────────────────────────────────────┐
│ Mask: 0x0007 (bits 0, 1, 2) │
│ │
│ │
│ Unblocks when: │
│ Bit 0 = 1 OR │
│ Bit 1 = 1 OR │
│ Bit 2 = 1 │
└─────────────────────────────────────┘
Wait for ALL flags (AND):
┌─────────────────────────────────────┐
│ Mask: 0x0007 (bits 0, 1, 2) │
│ │
│ │
│ Unblocks when: │
│ Bit 0 = 1 AND │
│ Bit 1 = 1 AND │
│ Bit 2 = 1 │
└─────────────────────────────────────┘
uint8_t OSFlagPendAll(OS_FLAGS *flags, uint32_t bit_mask, uint16_t timeout)
This function waits a number of time ticks specified by timeout until all the flags indicated by bit_...
Definition nbrtos.h:1551
uint8_t OSFlagPendAny(OS_FLAGS *flags, uint32_t bit_mask, uint16_t timeout)
This function waits a number of time ticks specified by timeout until any of the flags indicated by b...
Definition nbrtos.h:1514
Flag Functions Summary
Usage Example
#define FLAG_SENSOR_READY 0x0001
#define FLAG_NETWORK_UP 0x0002
#define FLAG_DATA_AVAILABLE 0x0004
#define FLAG_ERROR 0x0008
void UserMain(void *pd) {
}
void SensorTask(void *pd) {
while (1) {
if (SensorValid()) {
}
}
}
void ProcessingTask(void *pd) {
while (1) {
&systemFlags,
FLAG_SENSOR_READY | FLAG_NETWORK_UP,
);
if (flags) {
ProcessSensorData();
}
}
}
void MonitorTask(void *pd) {
while (1) {
&systemFlags,
FLAG_ERROR
);
if (flags & FLAG_ERROR) {
printf("Error detected!\r\n");
}
}
}
void OSFlagCreate(OS_FLAGS *pf)
Definition nbrtos.h:1465
uint8_t OSFlagPendAnyNoWait(OS_FLAGS *flags, uint32_t bit_mask)
This function immediately checks to see if any of the flag bits indicated by bit_mask are set; it doe...
Definition nbrtos.h:1532
OSFlags enables a function or task to pend on multiple flags or events.
Definition nbrtos.h:1285
System Configuration
The NetBurner system reads its base configuration from #define statements in nbrtos/include/predef.h and nbrtos/include/constants.h. You change that configuration for one project without editing either shared header.
Where Your Settings Go
Put your definitions in a project-local copy of the matching hook header:
The SDK ships all three hook headers empty. They exist only so a project can replace them. The build system searches the project's overload directory before the SDK, so your copy wins. See Modifying System Files with Overload for how the build finds it.
Why Not Edit predef.h Directly
The overload directory can hold a full copy of predef.h, and before the hook headers existed that was the only way. It has real costs:
- predef.h changes between NNDK releases, so your copy goes stale.
- Your own changes are buried in a 400-line file.
- Every upgrade means a merge.
A hook header holds only your changes, survives an upgrade cleanly, and is short enough to review at a glance. Use it.
- Note
- This shortcut applies to system configuration only. To overload any other system file, nbrtos/source/timezones.cpp for example, you still copy the real system file into the matching path under overload and edit the copy. See Overload Directory & System Files.
Include Order
Order decides whether a definition takes effect. The hook headers sit in different places on purpose:
- predef.h includes predef-overload.h on its last line, after every conditional block in the file has already been evaluated.
- constants.h includes constants-overload.h near the top, before its #ifndef X / #define X default blocks. It includes constants-overload-undefs.h after the header guard closes, for #undef work.
- Each platform user_settings.h includes user_settings-overload.h after all of its settings. Its switches, such as NB_TLS_KEYLOG and the crypto profiles, take effect in user_settings_switches.h, which comes after the hook. So a #define or #undef in user_settings-overload.h always takes effect, switches included. Use #undef first when you change a value that the file already sets.
So in constants-overload.h a plain #define always wins:
#define USER_TASK_STK_SIZE (4096)
In predef-overload.h a plain #define wins for any macro predef.h leaves undefined:
#define NBRTOS_STACKCHECK (1)
#define NBRTOS_TIME (1)
#define ENABLE_SNMP (1)
- Warning
- A macro defined in predef-overload.h cannot switch on an #ifdef block that appears earlier in predef.h. That block has already been skipped.
Working Around the Include Order
When predef.h sets sub-options inside a conditional block, set those sub-options yourself. Multihoming is the clearest case. predef.h contains:
#ifdef MULTIHOME
#define NUM_MULTI_INTERFACES (10)
#else
#define NUM_MULTI_INTERFACES (0)
#endif
By the time predef-overload.h is read, NUM_MULTI_INTERFACES is already (0). Defining MULTIHOME alone does nothing useful. Undefine the sub-option and set it yourself:
#define MULTIHOME
#undef NUM_MULTI_INTERFACES
#define NUM_MULTI_INTERFACES (10)
The VLAN example ships exactly this file. The On-board Cert Generation example does the same for ENABLE_AUTOCERT_REGEN and its AUTO_CERT_GEN_CHECK interval.
The same rule applies to the IPv4-only mode. predef.h reads:
#ifndef IPV4ONLY
#define IPV6 (1)
#define IPV6_COUNTERS (1)
#endif
Defining IPV4ONLY in predef-overload.h will not disable IPv6, because IPV6 is already defined. Undefine it too:
#define IPV4ONLY (1)
#undef IPV6
#undef IPV6_COUNTERS
When to Use #undef
Use a plain #define when the SDK header leaves the macro undefined, which covers most feature switches. Use #undef first only when the SDK header has already defined the macro, as in the three cases above. An unnecessary #undef is harmless, but a missing one produces a redefinition warning or a setting that silently does nothing.
Setup Methods
The build system finds the overload directory by name. Command line builds need no makefile change. NBEclipse projects need the overload include directory added to the compiler include paths, above the $(NNDK_ROOT)/ entries: overload/nbrtos/include for predef-overload.h and constants-overload.h, and overload/libraries/include, in both the C and the C++ include paths, for user_settings-overload.h.
Common Configuration Overrides
Each macro below is real, and each example ships it in its own overload directory:
#define NBRTOS_STACKCHECK (1)
#define NBRTOS_STACKOVERFLOW (1)
#define NBRTOS_STACKUNDERFLOW (1)
#define NBRTOS_TASKLIST (1)
#define NBRTOS_TIME (1)
#define NBRTOS_PRIO_PROMOTION (1)
#define ENABLE_SNMP (1)
#define NB_SSH_SUPPORTED (1)
Read the comment blocks in predef.h for the full list and for what each macro costs. The file groups them under Features, Debugging, and per-subsystem headings.
Verification After Changes
The first time you add a file to the overload directory, clean the build so the system library is rebuilt against it. After that, editing an overloaded file rebuilds the necessary files automatically.
In NBEclipse, select "Clean NetBurner System Library" under the project's "Build Targets", then rebuild.
To confirm a macro took effect, use something it gates. NBRTOS_STACKCHECK, for instance, is what makes OSDumpTCBStacks() link at all: if the definition did not reach the compiler, the build fails with "OSDumpTCBStacks was not declared".
Summary
Key Takeaways
- RTOS Fundamentals
- Highest priority ready task always runs
- Tasks must block for lower priority tasks to execute
- 255 priority levels (1 = highest, 255 = lowest)
- Task Creation
- Data Protection Hierarchy
Semaphore < Mailbox < Queue < FIFO < OSCrit <
OSLock < Critical Section
(Least impact) ──────────────────────────────────> (Most impact)
- Best Practices
- Use predef-overload.h for configuration
- Prefer static memory over dynamic allocation
- Keep critical sections short
- Check return values from task creation
- Match enter/exit calls for counted resources
- Configuration Management
- Never modify the SDK's predef.h directly
- Put your defines in overload/nbrtos/include/predef-overload.h
- Use constants-overload.h for settings that live in constants.h
- #undef first only when the SDK header already defines the macro
- predef.h reads its own conditionals before the overload, so set feature sub-options yourself
- Clean rebuild after adding a file to the overload directory
Quick Reference
Common RTOS Calls:
├── Timing
├── Semaphores
├── Queues
│ ├──
OSQInit(&queue, storage, size)
├── Flags
└── Critical Sections
├── OSCritExit(&crit)
Further Reading
Refer to the following documentation:
- constants.h - System task priorities and constants
- API reference documentation - Complete function details
- Platform-specific guides - Hardware integration
- Example applications - Working implementations