Sample Task

Tasks are written in C and built on a standard template using library functions to access shared memory and other services. This section documents task structure using the sampleTask.c program that’s distributed with the system. We’ll go through the program section by section.

Well behaved tasks

In order to be well-behaved, a task needs to have a unique ID. This ID is used to claim write access to shared memory data elements. The ID values are defined in vecs.h as follows:

#define SERVER_ID 1

#define CTL_ID  2

#define WEBEDIT_ID 3

#define LOGGER_ID 4

#define SQLLOGGER_ID 5

#define ONEWIRE_ID 6

#define PID_ID 7

#define NETIO_ID 8

#define USR1_ID 20

#define USR2_ID 21

#define USR3_ID 22

#define USR4_ID 23

The system also maintains a matching list of shared memory variables that tells each task how often they should run. The ‘System’ tab provides a mechanism for setting task frequency. Each task should access one of these ‘period’ variables.


The system includes this supporting structure for up to four user-created tasks, named USR1 through USR4. This example will be written to run as USR1, so its task ID will be USR1_ID and it will run at an interval determined by usr1_period.

Task Outline

Almost all tasks have the same basic structure:

Perform startup initialization

Eternal Loop: {

        Perform periodic initialization if needed

        Read data from shared memory

        Do some computation

        Write results to shared memory

        Sleep until next cycle


Task Support API

The system provides a set of functions that simplify writing a well behaved task. These functions handle interaction with shared memory as well as sleep management.

This  sample task uses most of the task support API:

get_shm(taskid) - get link to shared memory

getNow() - get current timestamp

reloadRequired(taskid) - true if this task needs to reload data due to changes

claimElement(elementid,taskid) - claim write access to a shared memory data element

clearReloadFlag(taskid) - clear reload flag for this task

elementValue(elementid) - get value of data element from shared memory

setElement(elementid,value,diskflag) - set data element value in shared memory

timedSleep(processname,period,starttime) - sleep until next cycle is due

Sample Task Function

This task will perform the following functions:

Screenshot of control panel with sampleTask running:

Sample Task Code

This section contains the complete code for sampleTask, with each section described in detail.

Includes and defines

Like all C programs, you’ll need to include header files appropriate to the functions that you use. Additionally, you’ll need to include ‘nfcs.h’. Each process also has a name that’s used for status and error logging - in this case, ‘sample’.

#include <stdio.h>

#include <fcntl.h>            // For file I/O

#include "nfcs.h"

// Choose task name here. Used for status and error logging.



Like all C programs, a function named ‘main’ is required. In this case all of the code is in this single function. We’ll need a variable to keep track of timing - start_usec in this example. We also have variables to hold the temperatures from shared memory and the values that we’ll write to shared memory.


 // Variable for calculating sleep interval

 unsigned long long start_usec;

 // We need element ID values for elements that we'll use.

 // Normally will get list from disk file. In this case we'll hard code.

 int temp1_id = 2;

 int temp2_id = 3;

 int diff_id = 4;

 int max_id = 5;

 // Variables to store shared memory values. Not necessary but can improve readability.

 float temp1, temp2, diff, max;


There are four initializations that could be necessary:

  1. Establish shared memory link
  2. Initialize cycle timer
  3. Claim shared memory variables that this task will write to
  4. Read task-specific data from disk file(s)

The first two happen only once, when the program is first run. The second two could happen repeatedly, and are handled in the main loop.

Initializing shared memory and cycle timer:

 // Get shared memory link.


 start_usec = getNow();

Controller tasks typically run forever with a timed sleep after each pass through the main loop.

 // Do forever

 while (1){

At the start of each cycle, check to see if there has been a change that requires us to refresh our shared memory or disk file data. For instance, the user could have changed element numbers. There’s a shared memory flag that we check to see if we need to re-initialize. This is always invoked the very first pass through. At the very least, we should claim the shared memory elements that we are going to write to. Claiming elements will generate warnings if other tasks have already claimed them, although it is legal for more than one process to write to the same element.

   // Each cycle, check to see if we should respond to config changes in shared memory.

   // If we read a disk file, we should do that here too. Must use valid ID here as well

   if (reloadRequired(USR1_ID)){

     // Claim our elements (the ones we'll write to)



     // Clear our bit in the reload flag



Do the actual work

Now that we’re through with any initialization, we need to do the actual task. Typically this involves reading some data from shared memory, performing some computation, and writing values back into shared memory. In this example, we’ll read two temperature values as ‘temp1’ and ‘temp2’. We’ll calculate the difference and determine the larger of the two temperatures.

   // Read values from shared memory using elementValue() function

   temp1 = elementValue(temp1_id);

   temp2 = elementValue(temp2_id);

   // Calculate diff

   diff = temp1 - temp2;

   // Determine value for max

   if(temp1 > temp2){

     max = temp1;


     max = temp2;


Post the Results

Once the calculations are complete, the resulting values will be written back into two shared memory variables using the setElement() function. This function requires three arguments:

  1. The ID of the element to be written
  2. The value to be written
  3. A ‘disk write’ flag

The disk write flag tells the controller whether to update the elements.csv file on disk with the new value. Writing to disk means that the value will persist across system restarts. This would be appropriate for a user-defined variable such as ‘Top Floor Setpoint’, but would be pointless for this example. We’ll use a value of ‘0’ to indicate that the disk file does not need to be written.

   // Update shared memory with new values using setElement.

   // setElement needs element ID, value, and disk write flag.

   setElement(diff_id, diff, 0);

   setElement(max_id, max, 0);


Once the work is done, we need to sleep until it’s time to run again. Our sleep interval is determined by a system configuration value - in this case, usr1_period which can be set on the controller’s ‘System’ tab.

   // Sleep until next cycle. Select appropriate period for your ID.

   timedSleep(PROCESSNAME, config->usr1_period, &start_usec);