Integrator Resources

The official home for NAI Support

Not sure where to start? Try Quick Start Guide or ask a question below!

Toggle Components with Visual Button
JavaScript Form Processing

RLY BasicOps

RLY BasicOps Sample Application (SSK 1.x)

Overview

The RLY BasicOps sample application demonstrates how to control relay modules using the NAI Software Support Kit (SSK 1.x). It covers the core relay operations you will need in your own application: opening and closing individual relays, setting all relay states simultaneously via a state word, reading relay position and BIT (Built-In Test) status, clearing latched BIT faults, and configuring BIT interrupts.

This sample supports the following relay module types:

  • KN — non-latching relay module (Gen3)

  • KL — latching relay module (Gen3)

  • RY1 — non-latching relay module (Gen5)

  • RY2 — latching relay module (Gen5)

All relay modules use Form C (SPDT — single pole, double throw) relays. Each relay has three terminals: Common (COM), Normally Open (N.O.), and Normally Closed (N.C.). When a relay is open (de-energized), the COM terminal is connected to N.C. When a relay is closed (energized), the COM terminal switches to N.O.

The Gen5 modules (RY1, RY2) provide additional capabilities not available on older KN/KL modules: separate real-time and latched BIT status registers, the ability to clear latched BIT status, and edge/level interrupt trigger configuration. The sample automatically enables these extra menu commands when it detects an RY1 or RY2 module.

This sample serves as a practical API reference — each menu command maps directly to one or more naibrd_RLY_*() API calls that you can lift into your own code.

Prerequisites

Before running this sample, make sure you have:

  • An NAI board with a relay module installed (KN, KL, RY1, or RY2).

  • SSK 1.x installed on your development host.

  • The sample applications built. Refer to the SSK 1.x build instructions for your platform if you have not already compiled them.

How to Run

Launch the RLY_BasicOps executable from your build output directory. On startup the application looks for a configuration file (default_RLY_BasicOp.txt). On the first run, this file will not exist — the application will present an interactive board menu where you configure a board connection, card index, and module slot. You can save this configuration so that subsequent runs skip the menu and connect automatically. Once connected, a command menu lets you exercise each relay operation.

Board Connection and Module Selection

Note
This startup sequence is common to all NAI sample applications. The board connection and module selection code shown here is not specific to relay modules. For details on board connection configuration, see the First Time Setup Guide.

The main() function follows a standard SSK 1.x startup flow:

  1. Call naiapp_RunBoardMenu() to load a saved configuration file (if one exists) or present the interactive board menu. The configuration file (default_RLY_BasicOp.txt) is not included with the SSK — it is created when the user saves their connection settings from the board menu. On the first run, the menu will always appear.

  2. Query the user for a card index with naiapp_query_CardIndex().

  3. Query for a module slot with naiapp_query_ModuleNumber().

  4. Retrieve the module ID with naibrd_GetModuleID() so downstream code can adapt to the specific relay variant installed.

#if defined (__VXWORKS__)
int32_t RLY_BasicOps(void)
#else
int32_t main(void)
#endif
{
   bool_t stop = FALSE;
   int32_t cardIndex;
   int32_t moduleCnt;
   int32_t module;
   uint32_t moduleID = 0;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

   if (naiapp_RunBoardMenu(CONFIG_FILE) == TRUE)
   {
      while (stop != TRUE)
      {
         stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
         if (stop != TRUE)
         {
            check_status(naibrd_GetModuleCount(cardIndex, &moduleCnt));
            stop = naiapp_query_ModuleNumber(moduleCnt, 1, &module);
            if (stop != TRUE)
            {
               moduleID = naibrd_GetModuleID(cardIndex, module);
               if ((moduleID != 0))
               {
                  Run_RLY_BasicOps(cardIndex, module, moduleID);
               }
            }
         }

         printf("\nType Q to quit or Enter key to restart application:\n");
         stop = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR,
                  inputBuffer, &inputResponseCnt);
      }
   }

   naiapp_access_CloseAllOpenCards();
   return 0;
}
Important

Common connection errors you may encounter at this stage:

  • No board found — verify that the board is powered on and physically connected. Check that the configuration file lists the correct interface and address.

  • Connection timeout — confirm network settings (for Ethernet connections) or bus configuration (for PCI/PCIe). Firewalls and IP mismatches are frequent causes.

  • Invalid card or module index — indices are zero-based for cards and one-based for modules. Ensure the values you pass match your hardware setup.

  • Module not present at selected slot — the slot you selected does not contain a relay module. Use the board menu to verify which slots are populated.

Program Structure

Entry Point

On standard platforms the entry point is main(). On VxWorks the entry point is RLY_BasicOps() — the SSK 1.x build system selects the correct variant via a preprocessor guard:

#if defined (__VXWORKS__)
int32_t RLY_BasicOps(void)
#else
int32_t main(void)
#endif

The startup flow is the same in both cases:

  1. Attempt to load the saved configuration file via naiapp_RunBoardMenu(CONFIG_FILE). If the file does not yet exist, the interactive board menu is presented instead.

  2. Enter a loop that queries for card index and module slot.

  3. Call Run_RLY_BasicOps() to validate the module and enter the interactive command loop.

  4. On exit, close all open board connections with naiapp_access_CloseAllOpenCards().

Module Validation

Before entering the command loop, Run_RLY_BasicOps() calls naibrd_RLY_GetChannelCount() with the module ID. If the channel count is zero, the selected module is not a recognized relay type and the application prints an error. Otherwise, it proceeds into Cfg_RLY_Channel():

void Run_RLY_BasicOps(int32_t cardIndex, int32_t module, uint32_t modid)
{
   int32_t MaxChannel;

   MaxChannel = naibrd_RLY_GetChannelCount(modid);

   if (MaxChannel == 0)
      printf(" *** Module selection not recognized as RLY module. ***\n\n");
   else
      Cfg_RLY_Channel(cardIndex, module, modid, MaxChannel);
}

Application Parameters

The Cfg_RLY_Channel() function populates an naiapp_AppParameters_t struct that is passed to every command handler. Your application will need to track these same values to identify which board, module, and channel you are targeting:

rly_basicOps_params->cardIndex = cardIndex;
rly_basicOps_params->module = module;
rly_basicOps_params->channel = 1;
rly_basicOps_params->maxChannels = naibrd_RLY_GetChannelCount(ModuleID);
rly_basicOps_params->modId = ModuleID;
rly_basicOps_params->displayHex = FALSE;
  • cardIndex — identifies which board in a multi-board system.

  • module — the slot number where the relay module is installed.

  • channel — the currently selected channel (defaults to 1).

  • maxChannels — total channel count for the detected module, retrieved by calling naibrd_RLY_GetChannelCount() with the module ID.

  • modId — the module identifier returned by naibrd_GetModuleID(). API functions use this to apply module-specific behavior.

  • displayHex — toggles between hexadecimal and decimal display (not actively used in this sample).

Command Loop

Cfg_RLY_Channel() drives the interactive command loop. On each iteration it prompts for a channel number, displays relay status for all channels, prints the command menu, and dispatches the user’s selection to the matching handler. The menu is assembled dynamically — base commands are always available, and Gen5-only commands are appended when the module is RY1 or RY2:

/* Update Menu Cmds based on Module ID */
totalMenuCnt = 0;
menuCnt = sizeof(RLY_BasicOpMenuCmds) / sizeof(naiapp_cmdtbl_params_t);
for (i = 0; i < menuCnt; i++)
   menuCmds[totalMenuCnt++] = RLY_BasicOpMenuCmds[i];
if ((ModuleID == NAI_MODULE_ID_RY1) || (ModuleID == NAI_MODULE_ID_RY2))
{
   menuCnt = sizeof(RLY_GEN5_MenuCmds) / sizeof(naiapp_cmdtbl_params_t);
   for (i = 0; i < menuCnt; i++)
      menuCmds[totalMenuCnt++] = RLY_GEN5_MenuCmds[i];
}

The available commands are registered in two tables:

Command Description

Help

Display relay module notes and register reference

Open

Open (de-energize) the relay on the selected channel

CLOse

Close (energize) the relay on the selected channel

Word

Set all relay states simultaneously via a bitmapped state word

ENable

Enable or disable BIT interrupt on the selected channel

Status

Toggle BIT status columns in the status display

The following commands are available only on Gen5 modules (RY1, RY2):

Command Description

R

Reset (clear) latched BIT status on the selected channel

EDge

Set interrupt trigger mode to edge or level

The menu-driven structure is a convenience of the sample application. In your own application, you would call the same underlying naibrd_RLY_*() API functions directly — for example, calling naibrd_RLY_SetRelayState() instead of navigating to the "Open" or "CLOse" menu commands.

Relay Control Operations

This section covers the core relay control commands: opening and closing individual relays, and setting all relays at once using a state word.

Open Relay

To de-energize (open) a relay on a specific channel in your own application, call naibrd_RLY_SetRelayState() with NAI_RLY_STATE_OPEN. When the relay is open, the Common (COM) terminal is connected to the Normally Closed (N.C.) terminal. This is the default (unpowered) state of the relay.

check_status(naibrd_RLY_SetRelayState(cardIndex, module, chan, NAI_RLY_STATE_OPEN));
  • cardIndex — identifies the board.

  • module — the slot containing the relay module.

  • chan — the relay channel to open (1-based).

  • NAI_RLY_STATE_OPEN — directs the relay to its de-energized state (COM connected to N.C.).

At the hardware level, opening a relay removes the drive signal from the relay coil. The relay’s internal spring returns the armature to its resting position, connecting COM to N.C. For latching relay modules (KL, RY2), the relay holds its last commanded state even after power is removed — opening a latching relay requires actively driving the relay to the open position.

Close Relay

To energize (close) a relay on a specific channel in your own application, call naibrd_RLY_SetRelayState() with NAI_RLY_STATE_CLOSE. When the relay is closed, the Common (COM) terminal switches to the Normally Open (N.O.) terminal.

check_status(naibrd_RLY_SetRelayState(cardIndex, module, chan, NAI_RLY_STATE_CLOSE));
  • cardIndex — identifies the board.

  • module — the slot containing the relay module.

  • chan — the relay channel to close (1-based).

  • NAI_RLY_STATE_CLOSE — directs the relay to its energized state (COM connected to N.O.).

At the hardware level, closing a relay drives the relay coil, pulling the armature to connect COM to N.O. The relay physically switches the signal path. On non-latching modules (KN, RY1), the relay will return to its open (COM-N.C.) state if power is lost. On latching modules (KL, RY2), the relay remains in the closed position even without power.

Note
Consult your module’s manual for relay switching time specifications, contact ratings, and maximum switching frequency.

Set State Word

To set all relay states simultaneously in your own application, call naibrd_RLY_SetGroupRaw() with the NAI_RLY_RAW_GROUP_RELAYSTATE group type. This writes a bitmapped word where each bit corresponds to one relay channel — a 1 energizes (closes) the relay, and a 0 de-energizes (opens) it. This is useful when you need to switch multiple relays in a single operation rather than commanding them one at a time.

uint32_t stateword = 0;

/* Each bit maps to a channel: bit 0 = channel 1, bit 1 = channel 2, etc. */
/* A value of 1 energizes (closes) the relay */
stateword = (atoi((const char *)inputBuffer)) & 0xF;

check_status(naibrd_RLY_SetGroupRaw(cardIndex, module, 1,
               (nai_rly_raw_group_t)NAI_RLY_RAW_GROUP_RELAYSTATE, stateword));
  • cardIndex — identifies the board.

  • module — the slot containing the relay module.

  • 1 — the group number (relay modules have a single group).

  • NAI_RLY_RAW_GROUP_RELAYSTATE — specifies that this write targets the relay state register.

  • stateword — a bitmapped value where bit N controls channel N+1. The sample masks the input to 4 bits (& 0xF), covering up to 4 relay channels.

For example, a state word of 0x5 (binary 0101) closes channels 1 and 3 while opening channels 2 and 4. Consult your module’s manual for the exact channel count and state word width.

Important

Common Errors

  • Relay does not switch — verify you are addressing the correct channel number. Channel numbers are 1-based. Also confirm the module is powered and the relay is not mechanically stuck.

  • NAI_ERROR_NOT_SUPPORTED — the selected module is not a recognized relay type. Verify the module ID.

  • State word has no effect on some channels — the state word is masked to the number of channels on the module. Bits beyond the channel count are ignored.

Status Display

This section covers reading relay state and position information, and managing BIT status.

Relay Status

The sample’s Display_RLY_Status() function reads and displays four pieces of information for every channel on the module:

  1. Relay Setting — the commanded state (open or closed), read via naibrd_RLY_GetRelayState().

  2. Relay Position — the actual physical position of the relay, read via naibrd_RLY_GetRelayPosition(). This is a hardware readback that confirms whether the relay actually switched.

  3. BIT Status (Real-time) — the current mismatch status between the commanded setting and the position readback, read via naibrd_RLY_GetStatus() with NAI_RLY_BIT_STATUS_REALTIME. A "Nogo" indication means the relay’s physical position does not match its commanded state.

  4. BIT Status (Latched) — a latched record of any BIT fault that has occurred since the last clear. Read via naibrd_RLY_GetGroupRaw() with NAI_RLY_RAW_GROUP_BIT_LATCHED_STATUS. This captures transient faults that may have resolved by the time you check.

/* Read latched BIT status for all channels as a bitmapped word */
check_status(naibrd_RLY_GetGroupRaw(cardIndex, module, 1,
               (nai_rly_raw_group_t)NAI_RLY_RAW_GROUP_BIT_LATCHED_STATUS,
               &BITstatusWord));

/* Read per-channel state and position */
check_status(naibrd_RLY_GetRelayState(cardIndex, module, displaychan, &relaysetting));
check_status(naibrd_RLY_GetRelayPosition(cardIndex, module, displaychan, &relayposition));

/* Read real-time BIT status for the channel */
check_status(naibrd_RLY_GetStatus(cardIndex, module, displaychan,
               NAI_RLY_BIT_STATUS_REALTIME, &BITstatus));

/* Extract latched status for this channel from the group word */
BITstatuslatched = (BITstatusWord >> (displaychan - 1)) & 0x1;

The distinction between the setting and the position is important: the setting is what you commanded, and the position is what the hardware reports. If these disagree, BIT flags a fault. A transient mismatch can occur during the relay’s switching time (typically a few milliseconds) — this is normal. A persistent mismatch may indicate a hardware issue.

Note
On Gen3 modules (KN, KL), only latched BIT status is available. The real-time status register is a Gen5 (RY1, RY2) feature. The sample reads both registers on Gen5 modules. Consult your module’s manual for the BIT register layout on your specific hardware.

Toggle Status Display

The Status command toggles whether BIT status columns are shown in the status display. This is a display-only toggle within the sample — it does not change any hardware state. The toggle is controlled by the bShowBIT global variable:

bShowBIT = (~bShowBIT) & 0x1;

Clear Latched BIT Status (Gen5 Only)

To clear the latched BIT status on a specific channel in your own application, call naibrd_RLY_ClearStatus(). This resets the latched fault record for that channel, so you can detect new faults going forward. The real-time BIT status is not affected — it continues to reflect the current mismatch state.

check_status(naibrd_RLY_ClearStatus(cardIndex, module, chan, NAI_RLY_BIT_STATUS_LATCHED));
  • cardIndex — identifies the board.

  • module — the slot containing the relay module.

  • chan — the channel whose latched status to clear (1-based).

  • NAI_RLY_BIT_STATUS_LATCHED — specifies that the latched status register is the target.

This command is available only on Gen5 modules (RY1, RY2). On Gen3 modules (KN, KL), the R menu command does not appear.

Important

Common Errors

  • BIT status shows "Nogo" persistently — the relay’s physical position does not match its commanded state. This may indicate a stuck relay, a wiring issue, or a relay that has exceeded its mechanical life. Consult your module’s manual for diagnostic procedures.

  • Latched status shows "Nogo" but real-time shows "Go" — a transient mismatch occurred (e.g., during switching) and was captured by the latch. Clear the latched status with naibrd_RLY_ClearStatus() and monitor for recurrence.

  • Clear command not available — you are running on a Gen3 module (KN or KL). The clear command is only available on Gen5 modules (RY1, RY2).

Interrupt Configuration

This section covers enabling BIT interrupts and configuring the interrupt trigger mode. Interrupts allow your application to be notified when a BIT fault occurs, rather than polling the status registers.

Interrupt Enable

To enable or disable the BIT interrupt on a specific channel in your own application, call naibrd_RLY_SetInterruptEnable(). When enabled, the module generates an interrupt whenever the BIT status condition is active on that channel. Interrupts are disabled by default at power-on.

/* Enable BIT interrupt on the selected channel */
check_status(naibrd_RLY_SetInterruptEnable(cardIndex, module, chan,
               NAI_RLY_BIT_STATUS_LATCHED, enable));
  • cardIndex — identifies the board.

  • module — the slot containing the relay module.

  • chan — the channel to configure (1-based).

  • NAI_RLY_BIT_STATUS_LATCHED — specifies the interrupt source is the latched BIT status.

  • enable — 1 to enable interrupts, 0 to disable (power-on default).

This command is available on all supported relay modules (KN, KL, RY1, RY2).

Interrupt Edge/Level Trigger (Gen5 Only)

To configure whether the BIT interrupt fires on edge or level in your own application, call naibrd_RLY_SetEdgeLevelInterrupt(). This determines whether the interrupt triggers once when a fault transitions from inactive to active (edge), or continuously as long as the fault remains active (level).

/* Set interrupt trigger mode: 0 = edge, 1 = level */
check_status(naibrd_RLY_SetEdgeLevelInterrupt(cardIndex, module, chan,
               NAI_RLY_BIT_STATUS_LATCHED, trigmode));
  • cardIndex — identifies the board.

  • module — the slot containing the relay module.

  • chan — the channel to configure (1-based).

  • NAI_RLY_BIT_STATUS_LATCHED — specifies the interrupt source.

  • trigmode — 0 for edge triggering (interrupt fires once on fault onset), 1 for level triggering (interrupt fires continuously while fault is active).

Edge triggering is useful when you want a single notification per fault event. Level triggering is useful when your interrupt handler needs to keep running as long as the fault persists — for example, if your handler performs corrective action and needs to retry until the fault clears.

This command is available only on Gen5 modules (RY1, RY2). On Gen3 modules (KN, KL), the EDge menu command does not appear.

Important

Common Errors

  • Interrupt not firing — verify that interrupts are enabled on the channel with naibrd_RLY_SetInterruptEnable(). Also confirm that the interrupt vector and steering are configured correctly using naibrd_RLY_SetGroupInterruptVector() and naibrd_RLY_SetGroupInterruptSteering() (not demonstrated in this sample — refer to the Interrupts API Guide).

  • Interrupt fires repeatedly when not expected — check the trigger mode. Level triggering causes repeated interrupts as long as the BIT fault is active. Switch to edge triggering if you only need a single notification.

  • Edge/Level command not available — you are running on a Gen3 module (KN or KL). The edge/level configuration is only available on Gen5 modules (RY1, RY2).

Troubleshooting Reference

This table summarizes common errors and symptoms covered in the sections above. For detailed context on each entry, refer to the relevant section. Consult your module’s manual for hardware-specific diagnostic procedures.

Error / Symptom Possible Causes Suggested Resolution

No board found or connection timeout

Board not powered, incorrect or missing configuration file, network issue

Verify hardware is powered and connected. If default_RLY_BasicOp.txt exists, check that it lists the correct interface and address. If it does not exist, the board menu will appear — configure and save your connection settings.

Module not recognized as RLY module

Selected slot does not contain a relay module, or module ID is not KN, KL, RY1, or RY2

Verify hardware configuration and module slot assignment. Use the board menu to confirm which slots are populated.

Relay does not switch (setting changes but position does not)

Wrong channel addressed, relay mechanically stuck, relay exceeded mechanical life, insufficient power to relay coil

Verify the channel number is correct (1-based). Check hardware wiring. Consult the module manual for relay life specifications.

Relay returns to open state after being closed

Module is non-latching type (KN or RY1) and lost power, or another command overrode the state

Non-latching relays require continuous power to hold the closed state. Use a latching module (KL or RY2) if state must be retained across power cycles.

BIT status shows persistent "Nogo"

Relay position readback does not match commanded state — possible hardware fault or stuck relay

Inspect relay hardware. Try commanding the relay to the opposite state and back. Consult the module manual for diagnostic procedures.

Latched BIT status shows "Nogo" but real-time shows "Go"

A transient mismatch occurred (e.g., during relay switching) and was captured by the latch

Clear the latched status with naibrd_RLY_ClearStatus() (Gen5 only) and monitor for recurrence.

Gen5 commands (R, EDge) not appearing in menu

Module is a Gen3 type (KN or KL) — these commands are only available on RY1 and RY2

Verify the module type. Gen5 commands require an RY1 or RY2 module.

Interrupt not firing

Interrupt not enabled on the channel, or interrupt vector/steering not configured

Enable interrupts with naibrd_RLY_SetInterruptEnable(). Configure interrupt vector and steering per the Interrupts API Guide.

NAI_ERROR_NOT_SUPPORTED

Feature not available for this module type or command called on a non-relay module

Check your module type against the feature requirements. Gen5-only features require RY1 or RY2.

Full Source

The complete source for this sample is provided below for reference. The sections above explain each part in detail.

Full Source — RLY_BasicOps.c (SSK 1.x)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>

/* Common Sample Program include files */
#include "include/naiapp_boardaccess_menu.h"
#include "include/naiapp_boardaccess_query.h"
#include "include/naiapp_boardaccess_access.h"
#include "include/naiapp_boardaccess_display.h"
#include "include/naiapp_boardaccess_utils.h"

/* naibrd include files */
#include "nai.h"
#include "naibrd.h"
#include "functions/naibrd_rly.h"
#include "advanced/nai_ether_adv.h"

static const int8_t *CONFIG_FILE = (const int8_t *)"default_RLY_BasicOp.txt";

#define K6_VER_4 0x3420u /* "4 " */

/* Function prototypes */
void Run_RLY_BasicOps(int32_t cardIndex, int32_t module, uint32_t modid);
void Cfg_RLY_Channel(int32_t cardIndex, int32_t module, uint32_t ModuleID, int32_t MaxChannel);
bool_t Display_RLY_Status(int32_t cardIndex, int32_t module, int32_t chan, uint32_t ModuleID);
nai_status_t Configure_RLY_StateWord(int32_t paramCount, int32_t* p_params);
nai_status_t Configure_RLY_Interrupt_Enable(int32_t paramCount, int32_t* p_params);
nai_status_t Configure_RLY_Interrupt_Trigger(int32_t paramCount, int32_t* p_params);
static nai_status_t DisplayHelp(int32_t paramCount, int32_t* p_params);
bool_t bShowBIT = TRUE;

static const int32_t DEF_RLY_CHANNEL = 1;

/****** Command Table *******/
enum rly_basicops_commands
{
   RLY_BASICOP_CMD_HELP,
   RLY_BASICOP_CMD_OPEN,
   RLY_BASICOP_CMD_CLOSE,
   RLY_BASICOP_CMD_SETWORD,
   RLY_BASICOP_CMD_INTERRUPT_ENABLE,
   RLY_BASICOP_CMD_TOGGLE,
   RLY_BASICOP_CMD_CLEAR,              /* RY1/RY2 only */
   RLY_BASICOP_CMD_INTERRUPT_TRIG,     /* RY1/RY2 only */
   RLY_BASICOP_CMD_COUNT
};

/****** Command Tables *******/
naiapp_cmdtbl_params_t RLY_BasicOpMenuCmds[] = {
   {"Help",    "RLY Notes",                     RLY_BASICOP_CMD_HELP,               DisplayHelp},
   {"Open",    "RLY Open Relay",                RLY_BASICOP_CMD_OPEN,               NULL},
   {"CLOse",   "RLY Close Relay",               RLY_BASICOP_CMD_CLOSE,              NULL},
   {"Word",    "RLY Set Word",                  RLY_BASICOP_CMD_SETWORD,            Configure_RLY_StateWord},
   {"ENable",  "RLY Select Interrupt Enable",   RLY_BASICOP_CMD_INTERRUPT_ENABLE,   Configure_RLY_Interrupt_Enable},
   {"Status",  "RLY Toggle Status Display",     RLY_BASICOP_CMD_TOGGLE,             NULL},
};

naiapp_cmdtbl_params_t RLY_GEN5_MenuCmds[] = {
   {"R",   "RLY Reset BIT Status",              RLY_BASICOP_CMD_CLEAR,              NULL},
   {"EDge",    "RLY Select Interrupt Trig",     RLY_BASICOP_CMD_INTERRUPT_TRIG,     Configure_RLY_Interrupt_Trigger}
};

/**************************************************************************************************************/
/**
<summary>
The purpose of the RLY_BasicOps is to illustrate the methods to call in the naibrd library to perform basic
 operations with the relay modules for configuration setup, controlling the drive outputs, and reading
 the channels.

The following system configuration routines from the nai_sys_cfg.c file are called to assist with the configuration
setup for this program prior to calling the naibrd RLY routines.
 - ClearDeviceCfg
 - QuerySystemCfg
 - DisplayDeviceCfg
 - GetBoardSNModCfg
 - SaveDeviceCfg
</summary>
*/
/**************************************************************************************************************/
#if defined (__VXWORKS__)
int32_t RLY_BasicOps(void)
#else
int32_t main(void)
#endif
{
   bool_t stop = FALSE;
   int32_t cardIndex;
   int32_t moduleCnt;
   int32_t module;
   uint32_t moduleID = 0;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

   if (naiapp_RunBoardMenu(CONFIG_FILE) == TRUE)
   {
      while (stop != TRUE)
      {
         /* Query the user for the card index */
         stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
         if (stop != TRUE)
         {
            check_status(naibrd_GetModuleCount(cardIndex, &moduleCnt));

            /* Query the user for the module number */
            stop = naiapp_query_ModuleNumber(moduleCnt, 1, &module);
            if (stop != TRUE)
            {
               moduleID = naibrd_GetModuleID(cardIndex, module);
               if ((moduleID != 0))
               {
                  Run_RLY_BasicOps(cardIndex, module, moduleID);
               }
            }
         }

         printf("\nType Q to quit or Enter key to restart application:\n");
         stop = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
      }
   }

   printf("\nType the Enter key to exit the program: ");
   naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
   naiapp_access_CloseAllOpenCards();

   return 0;
}

void Run_RLY_BasicOps(int32_t cardIndex, int32_t module, uint32_t modid)
{
   int32_t MaxChannel;

   MaxChannel = naibrd_RLY_GetChannelCount(modid);

   if (MaxChannel == 0)
      printf(" *** Module selection not recognized as RLY module. ***\n\n");
   else
      Cfg_RLY_Channel(cardIndex, module, modid, MaxChannel);
}

void Cfg_RLY_Channel(int32_t cardIndex, int32_t module, uint32_t ModuleID, int32_t MaxChannel)
{

   bool_t bQuit = FALSE;
   bool_t bContinue = TRUE;
   bool_t bCmdFound = FALSE;
   int32_t chan, defaultchan = 1;
   int32_t cmd;
   naiapp_cmdtbl_params_t menuCmds[RLY_BASICOP_CMD_COUNT];
   int32_t menuCnt, totalMenuCnt;
   int32_t i;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

   naiapp_AppParameters_t  rly_basicops_params;
   p_naiapp_AppParameters_t rly_basicOps_params = &rly_basicops_params;
   rly_basicOps_params->cardIndex = cardIndex;
   rly_basicOps_params->module = module;
   rly_basicOps_params->channel = 1;
   rly_basicOps_params->maxChannels = naibrd_RLY_GetChannelCount(ModuleID);
   rly_basicOps_params->modId = ModuleID;
   rly_basicOps_params->displayHex = FALSE;

   /* Basic operation sample for relay modules.
   - User selection of channel
   - Set relay state, open or closed
   - read status information (BIT)
   */
   while (bContinue)
   {
      printf("    \r\n\r\n");
      printf("Channel selection \r\n");
      printf("================= \r\n");
      defaultchan = DEF_RLY_CHANNEL;
      bQuit = naiapp_query_ChannelNumber(MaxChannel, defaultchan, &chan);

      /* Update Menu Cmds based on Module ID */
      totalMenuCnt = 0;
      menuCnt = sizeof(RLY_BasicOpMenuCmds) / sizeof(naiapp_cmdtbl_params_t);
      for (i = 0; i < menuCnt; i++)
         menuCmds[totalMenuCnt++] = RLY_BasicOpMenuCmds[i];
      if ((ModuleID == NAI_MODULE_ID_RY1) || (ModuleID == NAI_MODULE_ID_RY2))
      {
         /* Added menu for Gen 5 Relay modules */
         menuCnt = sizeof(RLY_GEN5_MenuCmds) / sizeof(naiapp_cmdtbl_params_t);
         for (i = 0; i < menuCnt; i++)
            menuCmds[totalMenuCnt++] = RLY_GEN5_MenuCmds[i];
      }

      naiapp_utils_LoadParamMenuCommands(totalMenuCnt, menuCmds);
      while (bContinue)
      {
         Display_RLY_Status(cardIndex, module, chan, ModuleID);

         naiapp_display_ParamMenuCommands((int8_t *)"RLY Basic Operation Menu");
         printf("\n Type RLY command or %c to quit : ", NAI_QUIT_CHAR);
         bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
         if (!bQuit)
         {
            if (inputResponseCnt > 0)
            {
               bCmdFound = naiapp_utils_GetParamMenuCmdNum(inputResponseCnt, inputBuffer, &cmd);
               if (bCmdFound)
               {
                  printf("  <%s>", menuCmds[cmd].cmdstr); /*Echo back acknowledgment of command selection*/
                  switch (cmd)
                  {
                     case RLY_BASICOP_CMD_OPEN:
                        check_status(naibrd_RLY_SetRelayState(cardIndex, module, chan, NAI_RLY_STATE_OPEN));
                        break;
                     case RLY_BASICOP_CMD_CLOSE:
                        check_status(naibrd_RLY_SetRelayState(cardIndex, module, chan, NAI_RLY_STATE_CLOSE));
                        break;
                     case RLY_BASICOP_CMD_CLEAR:
                        /*clear latched bit status on selected channel*/
                        check_status(naibrd_RLY_ClearStatus(cardIndex, module, chan, NAI_RLY_BIT_STATUS_LATCHED));
                        break;
                     case RLY_BASICOP_CMD_TOGGLE:
                        bShowBIT = (~bShowBIT) & 0x1;
                        break;
                     case RLY_BASICOP_CMD_SETWORD:
                     case RLY_BASICOP_CMD_HELP:
                     case RLY_BASICOP_CMD_INTERRUPT_ENABLE:
                     case RLY_BASICOP_CMD_INTERRUPT_TRIG:
                        RLY_BasicOpMenuCmds[cmd].func(APP_PARAM_COUNT, (int32_t*)rly_basicOps_params);
                        break;
                     default:
                        printf("Invalid command entered\n");
                        break;
                  }
               }
               else
                  printf("Invalid command entered\n");
            }
         }
         else
            bContinue = FALSE;
      }
   }
}
bool_t Display_RLY_Status(int32_t cardIndex, int32_t module, int32_t chan, uint32_t ModuleID)
{
   nai_rly_state_t BITstatus = 0;
   uint32_t BITstatusWord = 0;
   nai_rly_state_t BITstatuslatched = 0;
   nai_rly_state_t relaysetting;
   nai_rly_state_t relayposition;
   int32_t MaxChannel;
   uint8_t i = 0;
   uint8_t displaychan = 0;
   printf("\n\n");
   printf("\n === Setting Channel %d ===\n\n", chan);
   MaxChannel = naibrd_RLY_GetChannelCount(ModuleID);
   if (bShowBIT)
   {
      printf("Chan   Relay Setting       Relay Position       BIT Status  Latched Status \n");
      printf("---- ------------------- -------------------- ------------ ---------------- \n");
   }
   else
   {
      printf("Chan   Relay Setting       Relay Position     \n");
      printf("---- ------------------- -------------------- \n");
   }
   /*read bit status raw data register, covering all channels
     Two BIT status registers are available with RY1 and RY2 modules, latched and dynamically updating real time status. Prior modules KN and KL provide only
     latched register.
     This function will retrieve the latched values for all channels.
     If GetBITStatus is used, only the first channel will show the latched status, and the other channels will show the present status only. */
   if (bShowBIT)
      check_status(naibrd_RLY_GetGroupRaw(cardIndex, module, 1, (nai_rly_raw_group_t)NAI_RLY_RAW_GROUP_BIT_LATCHED_STATUS, &BITstatusWord));
   for (i = 0; i < MaxChannel; i++)
   {
      displaychan = i + 1;
      printf(" %2d ", displaychan);

      check_status(naibrd_RLY_GetRelayState(cardIndex, module, displaychan, &relaysetting));
      if (relaysetting == 1)
      {
         printf("     Closed          ");
      }
      else
         printf("      Open           ");

      check_status(naibrd_RLY_GetRelayPosition(cardIndex, module, displaychan, &relayposition));
      if (relayposition == 1)
         printf(" Closed (COM - N.O.) ");
      else
         printf("  Open (COM - N.C.)  ");

      if (bShowBIT)
      {
         BITstatuslatched = (BITstatusWord >> (displaychan - 1)) & 0x1;
         check_status(naibrd_RLY_GetStatus(cardIndex, module, displaychan, NAI_RLY_BIT_STATUS_REALTIME, &BITstatus));

         switch (BITstatus)
         {
            case 0:
               printf("    Go       ");
               break;
            case 1:
               printf("    Nogo     ");
               break;
            default:
               printf("  Unknown    ");
               break;
         }
         printf("   ");
         switch (BITstatuslatched)
         {
            case 0:
               printf("     Go         ");
               break;
            case 1:
               printf("     Nogo       ");
               break;
            default:
               printf("   Unknown        ");
               break;
         }
      }
      printf("\n");
   }
   return FALSE;
}
nai_status_t Configure_RLY_StateWord(int32_t paramCount, int32_t* p_params)
{
   /*Set word for relay to change all relay settings concurrently*/
   bool_t bQuit = FALSE;
   bool_t bUpdateOutput = FALSE;
   uint32_t stateword = 0;
   p_naiapp_AppParameters_t p_ad_params = (p_naiapp_AppParameters_t)p_params;
   int32_t cardIndex = p_ad_params->cardIndex;
   int32_t module = p_ad_params->module;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

#if defined (WIN32)
   UNREFERENCED_PARAMETER(paramCount);
#endif

   /* Set the relay setting (open or closed) on the selected relay.
        Closed = Common (COM) to Normally Open (N.O.)
        Open   = Common (COM) to Normally Closed (N.C.)
   */
   printf("\nEnter the relay state word to set:  Range [0 to 15) \n Channel Bitmapped position, 1 energizes relay \n > ");
   bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
   if (!bQuit)
   {
      if (inputResponseCnt > 0)
      {
         stateword = (atoi((const char *)inputBuffer)) & 0xF;
         bUpdateOutput = TRUE;
      }
   }
   if (!bQuit)
   {
      if (bUpdateOutput)
         check_status(naibrd_RLY_SetGroupRaw(cardIndex, module, 1, (nai_rly_raw_group_t)NAI_RLY_RAW_GROUP_RELAYSTATE, stateword));
   }
   return (bQuit) ? NAI_ERROR_UNKNOWN : NAI_SUCCESS;
}

nai_status_t Configure_RLY_Interrupt_Enable(int32_t paramCount, int32_t* p_params)
{
   /*Enable interrupt for selected channel
      - '0' will disable interrupt (power on default)
      - '1' will enable interrupt for selected channel
   Bitmapped per channel, LSB = Ch.1 */
   bool_t bQuit = FALSE;
   bool_t bUpdateOutput = FALSE;
   nai_rly_state_t enable = 0;
   p_naiapp_AppParameters_t p_ad_params = (p_naiapp_AppParameters_t)p_params;
   int32_t cardIndex = p_ad_params->cardIndex;
   int32_t module = p_ad_params->module;
   int32_t chan = p_ad_params->channel;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

#if defined (WIN32)
   UNREFERENCED_PARAMETER(paramCount);
#endif

   printf("\nEnter the selection for interrupt enable:  'Enable'  or 'Disable' \n > ");
   bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
   if (!bQuit)
   {
      if (inputResponseCnt > 0)
      {
         switch (toupper(inputBuffer[0]))
         {
            case 'E':
               printf(" <Enable>\n\n");
               enable = 1;
               break;
            case 'D':
               printf(" <Disable>\n\n");
               enable = 0;
               break;
         }
         bUpdateOutput = TRUE;
      }
   }
   if (!bQuit)
   {
      if (bUpdateOutput)
         check_status(naibrd_RLY_SetInterruptEnable(cardIndex, module, chan, NAI_RLY_BIT_STATUS_LATCHED, enable));
   }
   return bQuit;
}
nai_status_t Configure_RLY_Interrupt_Trigger(int32_t paramCount, int32_t* p_params)
{
   /*Configuration for interrupt triggering.  Options are Edge and Level triggering.
   Bitmapped per channel, LSB = Ch.1
      - Edge  '0' will trigger interrupts only once when BIT status indication goes active from a previously inactive state.
      - Level '1' will set level triggering, where the interrupt will be triggered on an ongoing basis as long as BIT status indication remains active.*/
   bool_t bQuit = FALSE;
   bool_t bUpdateOutput = FALSE;
   nai_rly_state_t trigmode = 0;
   p_naiapp_AppParameters_t p_rly_params = (p_naiapp_AppParameters_t)p_params;
   int32_t cardIndex = p_rly_params->cardIndex;
   int32_t module = p_rly_params->module;
   int32_t chan = p_rly_params->channel;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

#if defined (WIN32)
   UNREFERENCED_PARAMETER(paramCount);
#endif

   printf("\nEnter the selection for interrupt triggering mode:  'Edge'  or 'Level' \n > ");
   bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
   if (!bQuit)
   {
      if (inputResponseCnt > 0)
      {
         switch (toupper(inputBuffer[0]))
         {
            case 'E':
               printf(" <Edge>\n\n");
               trigmode = 0;
               break;
            case 'L':
               printf(" <Level>\n\n");
               trigmode = 1;
               break;
         }
         bUpdateOutput = TRUE;
      }
   }
   if (!bQuit)
   {
      if (bUpdateOutput)
         check_status(naibrd_RLY_SetEdgeLevelInterrupt(cardIndex, module, chan, NAI_RLY_BIT_STATUS_LATCHED, trigmode));
   }
   return (bQuit) ? NAI_ERROR_UNKNOWN : NAI_SUCCESS;
}

nai_status_t DisplayHelp(int32_t paramCount, int32_t* p_params)
{
   /*register references*/
   uint32_t ModuleID;
   uint32_t ModuleVer;
   uint32_t ModuleRev;
   uint32_t ModInfo_Special;
   p_naiapp_AppParameters_t p_ad_params = (p_naiapp_AppParameters_t)p_params;
   int32_t cardIndex = p_ad_params->cardIndex;
   int32_t module = p_ad_params->module;

#if defined (WIN32)
   UNREFERENCED_PARAMETER(paramCount);
#endif

   naibrd_GetModuleInfo(cardIndex, module, &ModuleID, &ModuleVer, &ModuleRev, &ModInfo_Special);
   printf("\n\n\n");
   printf(" ===================================================================\n");
   printf(" Relay module has Form C relays, single pole double throw (SPDT) \n");
   printf(" A BIT status indication shows a mismatch detected between   \n");
   printf(" the relay setting and the position readback.                \n");
   printf(" Latched status retains any transient BIT indications that may have occurred. \n\n");
   if ((ModuleID == NAI_MODULE_ID_RY1) || (ModuleID == NAI_MODULE_ID_RY2))
   {
      printf(" SET_POSITION           0x1000  Set Relay position   (1:N.C.- COM) LSB=Ch.1  \n");
      printf(" READ_POSITION          0x1018  Read Relay position  (1:N.C.- COM) LSB=Ch.1  \n");
      printf(" INDUCE_BIT             0x1004  Force BIT fail       (1:forced)    LSB=Ch.1  \n");
      printf(" RELAY TYPE             0x1008  Relay config (fixed) (1:Latching, 0:Nonlatching) \n");
      printf(" INTERRUPT_SELECT_EDGE  0x080C  Edge/Level interrupt (1:Level)    LSB=Ch.1  \n");
      printf(" INTERRUPT_ENABLE       0x0808  BIT Interrupt Enable (1:Enable)    LSB=Ch.1  \n");
      printf(" BIT_STATUS_LATCHED     0x0804  Latched BIT status   (1:fault)     LSB=Ch.1  \n");
      printf(" BIT_STATUS_REALTIME    0x0800  Realtime BIT status  (1:fault)     LSB=Ch.1  \n");
   }
   else
   {
      printf(" NAI_REG_RLY_SET_POSITION                            0x000 \n");
      printf(" NAI_REG_RLY_READ_POSITION                           0x002 \n");
      printf(" NAI_REG_RLY_BIT_STATUS                              0x0D0  Same as latched status on KN/KL\n");
      printf(" NAI_REG_RLY_BIT_STATUS_LATCHED                      0x0D0 \n");
      printf(" NAI_REG_RLY_INDUCE_BIT                              0x0D2 \n");
      printf(" NAI_REG_RLY_BIT_INT_ENAB                            0x0E8 \n");
      printf(" NAI_REG_RLY_INT_VECTOR_BIT                          0x3E0 \n");
   }
   return NAI_SUCCESS;
}

Help Bot

X