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

AR CAN BasicOps

AR CAN BasicOps Sample Application (SSK 1.x)

Overview

The AR CAN BasicOps sample application demonstrates how to perform CAN bus operations on ARINC-429/CAN dual-protocol modules using the NAI Software Support Kit (SSK 1.x). The AR-CAN module provides CAN-FD (Flexible Data-rate) bus connectivity alongside ARINC-429, and this sample focuses on the CAN side. It covers the core CAN operations you will need in your own application: selecting the CAN protocol (CAN A/B or CAN FD), configuring baud rates, transmitting single messages (8-byte and 64-byte), receiving messages, scheduling periodic transmissions, configuring acceptance filters and filter masks, managing transmit and receive FIFOs, and monitoring message counts.

This sample supports AR-CAN module types that provide CAN-FD functionality. It serves as a practical API reference — each menu command maps directly to one or more naibrd_AR_CAN_*() API calls that you can lift into your own code. Consult your AR-CAN module’s manual for hardware-specific specifications.

Prerequisites

Before running this sample, make sure you have:

  • An NAI board with an AR-CAN module installed.

  • 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.

  • A CAN bus connection (loopback cable or external device) for transmit/receive testing.

How to Run

Launch the AR_CAN_BasicOps executable from your build output directory. On startup the application looks for a configuration file (default_ARCAN.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 and a module is selected, the CAN operations menu appears.

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 AR-CAN. 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_ARCAN.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. Enter the CAN command loop via Run_CAN_DisplayCANChoices().

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

   if (naiapp_RunBoardMenu(CONFIG_FILE) == TRUE)
   {
      while (stop != TRUE)
      {
         stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
         userInput.nCardIdx = cardIndex;
         if (stop != TRUE)
         {
            check_status(naibrd_GetModuleCount(cardIndex, &moduleCnt));
            g_moduleCount = moduleCnt;
            stop = naiapp_query_ModuleNumber(moduleCnt, 1, &userInput.nModuleNumber);
            if (stop != TRUE)
            {
               Run_CAN_DisplayCANChoices();
            }
         }
         /* ... quit/restart prompt ... */
      }
   }
   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 an AR-CAN module.

Program Structure

Entry Point

On standard platforms the entry point is main(). On VxWorks the entry point is AR_CAN_BasicOps():

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

Application Parameters

The sample uses a global userInput struct to track the current card, module, channel, protocol, and baud rate settings:

static struct _userInput
{
   int32_t nCardIdx;
   int32_t nModuleNumber;
   int32_t nChannelNumber;
   int32_t nProtocolIndex;
   int32_t nBaudBaseRate;
   int32_t nBaudDataRate;
} userInput;
  • nCardIdx — identifies which board in a multi-board system.

  • nModuleNumber — the slot number where the AR-CAN module is installed.

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

  • nProtocolIndex — 0 for CAN A/B (standard), 1 for CAN FD.

  • nBaudBaseRate — the base (arbitration) baud rate index.

  • nBaudDataRate — the data phase baud rate index (CAN FD only).

Command Loop

Run_CAN_DisplayCANChoices() drives the interactive command loop. It displays the menu, reads the user’s selection, and dispatches to the matching handler:

Command Description

1

Set channel number

2

Set protocol (CAN A/B = 0, CAN FD = 1)

3

Set baud rate (base and data rates)

A

Transmit a single 8-byte message

B

Transmit a single 64-byte CAN FD message

C

Receive a single message

D

Receive message pump (continuous receive)

E

Schedule a periodic message

F

Start the transmit scheduler

G

Stop the transmit scheduler

I

Set a receive acceptance filter

J

Get a receive acceptance filter

K

Set a filter mask

L

Get a filter mask

M

Remove a filter

N

Enable acceptance filters

O

Disable acceptance filters

U

Reset transmit FIFO

V

Reset receive FIFO

W

Enable transmit

X

Disable transmit

Y

Reset drop count

Z

Display FIFO counts and diagnostics

The menu-driven structure is a convenience of the sample application. In your own application, you would call the same underlying naibrd_AR_CAN_*() API functions directly.

Channel and Protocol Configuration

Set Channel

To select which CAN channel to operate on in your own application, simply track the channel number as an integer. The sample stores it in userInput.nChannelNumber. Before performing any operation, the sample validates the channel number with naibrd_AR_CAN_GetChannelCountForModule():

if (chan < 1 || chan > naibrd_AR_CAN_GetChannelCountForModule(card, mod))
{
   /* Invalid channel */
}

Set Protocol

To select between CAN A/B (standard, 8-byte payload max) and CAN FD (flexible data-rate, 64-byte payload max):

/* Protocol index: 0 = CAN A/B, 1 = CAN FD */
int32_t protocolIndex = 1;  /* CAN FD */

The protocol selection affects the maximum payload size and the available data rates. CAN FD supports payloads up to 64 bytes and higher data-phase baud rates.

Set Baud Rate

To configure the base (arbitration) rate and data-phase rate for a channel, call naibrd_AR_CAN_SetBaudRate(). To read the current rates, call naibrd_AR_CAN_GetBaudRate():

naibrd_ar_can_low_rate_type_t origBaseRate;
naibrd_ar_can_high_rate_type_t origDataRate;

/* Read current baud rates */
naibrd_AR_CAN_GetBaudRate(cardIndex, module, channel, &origBaseRate, &origDataRate);

/* Set new baud rates */
naibrd_AR_CAN_SetBaudRate(cardIndex, module, channel,
   (naibrd_ar_can_low_rate_type_t)baseRateIndex,
   (naibrd_ar_can_high_rate_type_t)dataRateIndex);

Base rate options:

  • 0 = 1000K

  • 1 = 500K

  • 3 = 250K

Data rate options:

  • 0 = 2000K (default)

  • 6 = 4000K

  • 7 = 3000K

  • 8 = 2000K

  • 9 = 1000K

Both sides of the CAN bus must use the same baud rates. Consult your AR-CAN module’s manual for the complete list of supported rates.

Important

Common Errors

  • Baud rate mismatch — if transmit and receive devices use different base or data rates, messages will not be received. Ensure all devices on the bus are configured identically.

  • Invalid baud rate index — the sample defaults to safe values if an out-of-range index is entered. In your own code, validate the index before calling the API.

Message Transmit

Transmit a Single 8-Byte Message

To transmit a standard CAN message (up to 8 bytes), call naibrd_AR_CAN_QueueTransmit() to place the message in the transmit FIFO, then call naibrd_AR_CAN_SetTxEnable() to enable the transmitter:

uint8_t payload[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
uint32_t msgId = 0x111444;
bool_t isModeA = FALSE;  /* Extended (29-bit) ID */

/* Queue the message */
status = naibrd_AR_CAN_QueueTransmit(cardIndex, module, channel,
   (naibrd_ar_can_protocol_type_t)protocolIndex, isModeA,
   msgId, &payload[0], STANDARD_CAN_MAX_PAYLOAD);

/* Enable transmit so the message goes out on the bus */
status = naibrd_AR_CAN_SetTxEnable(cardIndex, module, channel, TRUE);
  • protocolIndex — selects CAN A/B or CAN FD.

  • isModeA — TRUE for standard (11-bit) message ID, FALSE for extended (29-bit) message ID.

  • msgId — the CAN message identifier.

  • STANDARD_CAN_MAX_PAYLOAD — 8 bytes for standard CAN.

Transmit a Single 64-Byte CAN FD Message

For CAN FD payloads up to 64 bytes, the call is the same but with the CAN FD protocol and a larger payload buffer:

uint8_t payload[64];
uint32_t msgId = 0x17778888;

/* Fill 64-byte payload */
for (i = 0; i < 64; i++)
   payload[i] = (uint8_t)(i & 0xFF);

/* Queue with CAN FD protocol forced */
status = naibrd_AR_CAN_QueueTransmit(cardIndex, module, channel,
   NAIBRD_AR_CAN_PROTOCOL_FD, FALSE, msgId, &payload[0], CAN_FD_MAX_PAYLOAD);

/* Enable transmit */
status = naibrd_AR_CAN_SetTxEnable(cardIndex, module, channel, TRUE);
Note
64-byte payloads require CAN FD protocol. If CAN A/B is selected, the maximum payload is 8 bytes.

Enable/Disable Transmit

By default, each channel’s transmit capability is disabled. You must enable it before any message will leave the FIFO:

/* Enable transmit */
naibrd_AR_CAN_SetTxEnable(cardIndex, module, channel, TRUE);

/* Disable transmit (messages can still be queued but won't send) */
naibrd_AR_CAN_SetTxEnable(cardIndex, module, channel, FALSE);

Disabling transmit does not clear the FIFO — messages remain queued and will be sent when transmit is re-enabled.

Important

Common Errors

  • Transmit fails silently — the transmit FIFO is not enabled. Call naibrd_AR_CAN_SetTxEnable() with TRUE before transmitting.

  • 64-byte transmit fails — CAN FD protocol must be selected. CAN A/B only supports 8-byte payloads.

  • NAI_ERROR_INVALID_VALUE — invalid channel number. Verify the channel is within range using naibrd_AR_CAN_GetChannelCountForModule().

Message Receive

Receive a Single Message

To read a single message from the receive FIFO, first check whether a message is waiting, then call naibrd_AR_CAN_Receive():

uint8_t rxBuffer[80];
uint32_t msgId = 0;
uint32_t timestamp;
int32_t rxCount = 0;
bool_t msgWaiting = FALSE;

/* Check if a message is available */
naibrd_AR_CAN_GetRxFIFOMessageWaiting(cardIndex, module, channel, &msgWaiting);

if (msgWaiting)
{
   /* Read the message */
   naibrd_AR_CAN_Receive(cardIndex, module, channel, sizeof(rxBuffer),
      &msgId, &timestamp, &rxBuffer[0], &rxCount);
}
  • msgId — the received message’s CAN identifier.

  • timestamp — the hardware timestamp when the message was received.

  • rxBuffer — the payload data.

  • rxCount — the number of payload bytes received.

Receive Message Pump

The message pump continuously polls the receive FIFO and reads all available messages. In your own application, implement a polling loop or use interrupt-driven receive for better efficiency:

do
{
   naibrd_AR_CAN_GetRxFIFOMessageWaiting(cardIndex, module, channel, &msgWaiting);
   if (msgWaiting)
   {
      naibrd_AR_CAN_Receive(cardIndex, module, channel, bufferLen,
         &msgId, &timestamp, &rxBuffer[0], &rxCount);
      receivedMsgCount++;
   }
} while (status == NAI_SUCCESS);

Scheduled Transmissions

Schedule a Periodic Message

To schedule a message for periodic transmission at a fixed interval, call naibrd_AR_CAN_ScheduleMsg():

uint8_t payload[64];
uint32_t msgId = 0x11112222;
int16_t entryIndex = 1;  /* Schedule slot (1-based) */
int16_t rate = 5;         /* Interval in milliseconds between transmissions */
int16_t skew = 5;         /* Delay in milliseconds after scheduler starts */

naibrd_AR_CAN_ScheduleMsg(cardIndex, module, channel,
   entryIndex, rate, skew, NAIBRD_AR_CAN_PROTOCOL_FD,
   FALSE, msgId, &payload[0], CAN_FD_MAX_PAYLOAD);
  • entryIndex — the schedule slot to populate. Multiple schedules can coexist on one channel.

  • rate — the interval in milliseconds between transmissions.

  • skew — the delay after the scheduler starts before this schedule begins transmitting. By assigning different skew values to different schedules, you can stagger transmissions to reduce bus collisions.

Start and Stop the Scheduler

The scheduler does not start automatically after scheduling a message. You must explicitly start it:

/* Start the scheduler -- all configured schedules begin transmitting */
naibrd_AR_CAN_StartScheduler(cardIndex, module, channel);

/* Stop the scheduler -- all schedules halt */
naibrd_AR_CAN_StopScheduler(cardIndex, module, channel);

Stopping the scheduler does not remove configured schedules. Restarting the scheduler resumes all configured schedules.

Acceptance Filtering

Acceptance filters control which incoming CAN messages are placed on the receive FIFO. Filters are not enabled by default — all messages are received until filtering is explicitly enabled. Up to 4 filters can be assigned to each channel (zero-based index).

Set a Filter

To define an acceptance filter based on message ID and the first two payload bytes:

int32_t filterIndex = 0;
uint32_t msgId = 0x17778889;
bool_t isModeA = FALSE;
uint8_t firstTwoBytes[2] = {0x00, 0x00};

naibrd_AR_CAN_SetAcceptFilter(cardIndex, module, channel,
   filterIndex, msgId, isModeA, &firstTwoBytes[0]);

Set a Filter Mask

The filter mask determines which bits of the incoming message are compared against the filter. A 1 in the mask means the corresponding bit must match; a 0 means that bit is ignored:

int32_t maskIndex = 0;
uint32_t maskMsgId = 0x1FFFFFFF;  /* All 29 ID bits must match */
uint8_t maskPayload[2] = {0x00, 0x00};  /* Don't filter on payload */

naibrd_AR_CAN_SetAcceptMask(cardIndex, module, channel,
   maskIndex, maskMsgId, FALSE, &maskPayload[0]);

Get Filter and Mask Configuration

To read back the current filter or mask:

uint32_t readMsgId;
bool_t readModeA;
uint8_t readPayload[2];

naibrd_AR_CAN_GetAcceptFilter(cardIndex, module, channel,
   filterIndex, &readMsgId, &readModeA, &readPayload[0]);

naibrd_AR_CAN_GetAcceptMask(cardIndex, module, channel,
   maskIndex, &readMsgId, &readModeA, &readPayload[0]);

Remove a Filter

To remove a previously configured filter (removes both filter and mask at the specified index):

naibrd_AR_CAN_RemoveAcceptFilter(cardIndex, module, channel, filterIndex);

Enable and Disable Filtering

Filters must be explicitly enabled before they take effect. Disabling filters does not remove them — it just allows all messages through:

/* Enable acceptance filtering */
naibrd_AR_CAN_EnableAcceptFilters(cardIndex, module, channel, TRUE);

/* Disable acceptance filtering (all messages pass through) */
naibrd_AR_CAN_EnableAcceptFilters(cardIndex, module, channel, FALSE);
Important

Common Errors

  • No messages received after configuring filters — the filter message ID does not match any incoming traffic. Double-check the filter value and mask. Set the mask to 0x1FFFFFFF if you need an exact message ID match.

  • Filters configured but not taking effect — filters must be explicitly enabled with naibrd_AR_CAN_EnableAcceptFilters().

FIFO Management

Reset FIFOs

To clear all data from the transmit or receive FIFO:

/* Reset transmit FIFO */
naibrd_AR_CAN_ResetTxFifo(cardIndex, module, channel);

/* Reset receive FIFO */
naibrd_AR_CAN_ResetRxFifo(cardIndex, module, channel);

Resetting the FIFO discards all queued messages. This is useful for error recovery without requiring a full channel reset.

Display FIFO Counts

To monitor FIFO utilization and detect overflow conditions:

int32_t txFIFOCount, txFIFOFrameCount;
int32_t rxFIFOCount, rxFIFOFrameCount;
int32_t dropCount;

naibrd_AR_CAN_GetTxFIFOCount(cardIndex, module, channel, &txFIFOCount);
naibrd_AR_CAN_GetTxFIFOFrameCount(cardIndex, module, channel, &txFIFOFrameCount);
naibrd_AR_CAN_GetRxFIFOCount(cardIndex, module, channel, &rxFIFOCount);
naibrd_AR_CAN_GetRxFIFOFrameCount(cardIndex, module, channel, &rxFIFOFrameCount);
naibrd_AR_CAN_GetDropCount(cardIndex, module, channel, &dropCount);
  • txFIFOCount / rxFIFOCount — the number of entries currently in the FIFO.

  • txFIFOFrameCount / rxFIFOFrameCount — the number of complete CAN messages in the FIFO.

  • dropCount — the number of received messages that were dropped because the receive FIFO was full.

Reset Drop Count

To reset the drop counter after investigating overflow conditions:

naibrd_AR_CAN_ResetDropCount(cardIndex, module, channel);
Important

Common Errors

  • Drop count increasing — the receive FIFO is overflowing. Increase the rate at which your application reads from the FIFO, or reset the FIFO if the data is not needed.

  • Transmit FIFO not empty after sending — messages may be queued but transmit is disabled. Enable transmit with naibrd_AR_CAN_SetTxEnable().

Troubleshooting Reference

This table summarizes common errors and symptoms covered in the sections above. Consult your AR-CAN 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_ARCAN.txt exists, check that it lists the correct interface and address.

Module not present at selected slot

No AR-CAN module installed at the specified slot

Verify hardware configuration and module slot assignment

Transmit fails silently

Transmit FIFO not enabled

Call naibrd_AR_CAN_SetTxEnable(cardIndex, module, channel, TRUE) before transmitting

No messages received

No bus traffic, filters blocking messages, or baud rate mismatch

Verify CAN bus connection; disable filters for initial testing; ensure all bus devices use the same baud rates

64-byte payload rejected

CAN A/B protocol selected (8-byte max)

Switch to CAN FD protocol (index 1)

Baud rate error or no communication

Base or data rate mismatch between devices

Set matching baud rates on all devices on the CAN bus

Drop count increasing

Receive FIFO full; application not reading fast enough

Increase read frequency or reset the receive FIFO

Invalid channel number error

Channel number out of range for the module

Verify channel count with naibrd_AR_CAN_GetChannelCountForModule()

Scheduler not transmitting

Scheduler not started after scheduling messages

Call naibrd_AR_CAN_StartScheduler() after configuring schedules

Filters not taking effect

Filters configured but not enabled

Call naibrd_AR_CAN_EnableAcceptFilters() with TRUE

Full Source

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

Full Source — AR_CAN_BasicOps.c (SSK 1.x)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#if defined (LINUX)
#include <ctype.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#endif

#include "AR_CAN_BasicOps.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_ar_can.h"
#include "naibrd_ether.h"
#include "advanced/nai_ether_adv.h"

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

#define SINGLE_MSG_BUFFER_LEN             80
#define STANDARD_CAN_MAX_PAYLOAD           8
#define CAN_FD_MAX_PAYLOAD                64

static struct _userInput
{
   int32_t nCardIdx;
   int32_t nModuleNumber;
   int32_t nChannelNumber;
   int32_t nProtocolIndex;
   int32_t nBaudBaseRate;
   int32_t nBaudDataRate;
} userInput;

/* Function prototypes */
static void Run_CAN_DisplayCANChoices();
static void CAN_SetChan();
static void CAN_SetProtocol();
static void CAN_SetBaud();
static void CAN_Single8ByteXmit();
static void CAN_Single64ByteXmit();
static void CAN_ReceiveMsg();
static void CAN_ReceiveMsgPump();
static void CAN_ScheduleMsg();
static void CAN_StartSchedule();
static void CAN_StopSchedule();
static void CAN_SetFilter();
static void CAN_GetFilter();
static void CAN_SetFilterMask();
static void CAN_GetFilterMask();
static void CAN_RemoveFilter();
static void CAN_EnableFilters();
static void CAN_DisableFilters();
static void CAN_ResetTxFIFO();
static void CAN_ResetRxFIFO();
static void CAN_EnableTx();
static void CAN_DisableTx();
static void CAN_ResetDropCount();
static void CAN_DisplayCounts();
static bool_t checkChannelNumber(int32_t card, int32_t mod, int32_t chan);

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

   if (naiapp_RunBoardMenu(CONFIG_FILE) == TRUE)
   {
      while (stop != TRUE)
      {
         stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
         userInput.nCardIdx = cardIndex;
         if (stop != TRUE)
         {
            check_status(naibrd_GetModuleCount(cardIndex, &moduleCnt));
            g_moduleCount = moduleCnt;
            stop = naiapp_query_ModuleNumber(moduleCnt, 1, &userInput.nModuleNumber);
            if (stop != TRUE)
            {
               Run_CAN_DisplayCANChoices();
            }
         }
         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;
}

/* ... remaining functions: Run_CAN_DisplayCANChoices, CAN_SetChan, CAN_SetProtocol, */
/* CAN_SetBaud, CAN_Single8ByteXmit, CAN_Single64ByteXmit, CAN_ReceiveMsg, */
/* CAN_ReceiveMsgPump, CAN_ScheduleMsg, CAN_StartSchedule, CAN_StopSchedule, */
/* CAN_SetFilter, CAN_GetFilter, CAN_SetFilterMask, CAN_GetFilterMask, */
/* CAN_RemoveFilter, CAN_EnableFilters, CAN_DisableFilters, CAN_ResetTxFIFO, */
/* CAN_ResetRxFIFO, CAN_EnableTx, CAN_DisableTx, CAN_ResetDropCount, */
/* CAN_DisplayCounts, checkChannelNumber */
/* See the SSK installation directory for the complete source. */

Help Bot

X