RLY BasicOps
Edit this on GitLab
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:
-
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. -
Query the user for a card index with
naiapp_query_CardIndex(). -
Query for a module slot with
naiapp_query_ModuleNumber(). -
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:
|
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:
-
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. -
Enter a loop that queries for card index and module slot.
-
Call
Run_RLY_BasicOps()to validate the module and enter the interactive command loop. -
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 callingnaibrd_RLY_GetChannelCount()with the module ID. -
modId— the module identifier returned bynaibrd_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
|
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:
-
Relay Setting — the commanded state (open or closed), read via
naibrd_RLY_GetRelayState(). -
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. -
BIT Status (Real-time) — the current mismatch status between the commanded setting and the position readback, read via
naibrd_RLY_GetStatus()withNAI_RLY_BIT_STATUS_REALTIME. A "Nogo" indication means the relay’s physical position does not match its commanded state. -
BIT Status (Latched) — a latched record of any BIT fault that has occurred since the last clear. Read via
naibrd_RLY_GetGroupRaw()withNAI_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
|
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—1to enable interrupts,0to 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—0for edge triggering (interrupt fires once on fault onset),1for 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
|
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 |
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 |
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 |
|
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;
}