NESB PRX Introduction

A Primary Receiver (PRX) CC23xx normally acts as the main receiver in an NESB network. Depending on user configuration, the handler acts similarly to the Generic Rx Command handler, or it can add automatic acknowledgement and address filtering.

The following guide is an introduction into how to use NESB protocol with TI devices using RCL command structure.

Note

For more information regarding NESB, or RCL please visit: NESB Command Handlers or Radio Control Layer.

Summary

To start developing a program with NESB PRX start with the rfPacketRx example provided in the SimpleLink Low Power F3 SDK example->rtos->CC23xx->prop_rf folder. We will be modifying this example to provide functionality with NESB.

Example Usage

In order to submit a PRX command, the following steps must have taken place:

  1. RCL has been initialized (See RCL_init()) and a handle must exist (See RCL_open()).

  2. The RCL_CMD_NESB_PRX_t command has been initialized and configured.

  3. A Multibuffer has been set up to receive the incoming packets.

RCL_Command_submit and RCL_Command_pend are called to effectively receive a packet, and then wait for the command to conclude.

As with any command handler, the application must build packets so that they are compatible with the internal packet format used in the LRF. Having said this, NESB packets also have a specific packet format that needs to be considered at the application level.

This can be accomplished by using a struct to define the various fields that need to be considered when building or checking the content of the packet.

Steps

  1. Import the rfPacketRx

  2. Implement required libraries and drivers for NESB configuration

  3. Implement defines, and variable declarations

  4. Implement and match the defines such as packet length, structure, and syncword (among others) with rfPacketTx side

  5. Create the hdrdef structure for the packets

  6. Setup the NESB PRX callback

  7. Implement the runNesbPrx() function

  8. Adapt main.c to account for changes

Example code usage

The RCL driver library is needed to use NESB functions, found below are the drivers included in this example.

/* TI Drivers */
#include <ti/drivers/GPIO.h>
#include <ti/drivers/rcl/RCL.h>
#include <ti/drivers/rcl/RCL_Scheduler.h>
#include <ti/drivers/rcl/commands/generic.h>
#include "ti_drivers_config.h"

Next we will need to provide definitions such as maximum packet length, and number of entries for NESB packet structure. These will need to match between TX and RX in order for packets to be successfully received between the devices. We also need to define the packet length, padding bytes, multibuffer size, frequency, and syncword (among other definitions).

/***** Defines *****/
/* Packet RX Configuration */
#define MAX_LENGTH              (30U) // Max packet length
#define NUM_DATA_ENTRIES        (8U)  // Number of data entries

/* RCL buffer length */
#define BUFF_STRUCT_LENGTH      (2048U)

/* Indicates if FS is off */
#define FS_OFF                  (1U)  // 0: On, 1: Off

/* Indicates if RX packet is not stored */
#define DISCARD_RX_PACKET       (0U)  // 0: Store received packets in rxBuffers, 1: Discard packets
/* Number of packets to initialize for multi buffer */
#define NUM_OF_PACKETS          (1U)

#define PKT_LEN 200
#define HDR_LEN 3
#define NUM_PKT 4
#define NUM_PAD 3
#define NUM_RX_BUF 1
#define MULTI_BUF_SZ 2048
#define FREQUENCY 2470000000
#define SYNCWORD 0xABF95EB6
#define NESB_ADDR 0xFE1E3C

The hdrdef structure is as follows:

#define NESB_hdrDef_Default()   \
{                               \
    .numHdrBits = 11,           \
    .numLenBits = 8,            \
    .lenPos = 3,                \
    .lenOffset = 0,             \
    .numPad = 3,                \
}
#define NESB_hdrDef_DefaultRuntime() (ExampleRCL_NesbHdrDef) NESB_hdrDef_Default()

typedef struct {
    uint8_t numHdrBits;
    uint8_t numLenBits;
    uint8_t lenPos;
    int8_t lenOffset;
    uint8_t numPad;
} ExampleRCL_NesbHdrDef;

Now let’s define variable declarations, which will be used later in the main function(s) of the code. Since these are defined as globals you can easily see the data while in debugging mode.

/***** Variable Declarations *****/
/* RCL Commands */
RCL_CmdGenericRx    rxCmd;
RCL_CmdNesbPrx cmd;// RX command
RCL_StatsGeneric    stats;              // Statistic command

/* RCL Client used to open RCL */
static RCL_Client  rclClient;

RCL_MultiBuffer *multiBuffer;

/* Counters for RCL event callback */
volatile uint32_t gCmdDone = 0;         // Command done
volatile uint32_t gPktRcvd = 0;         // Packet received

/* Buffer used to store RCL packet */
uint32_t buffer[NUM_DATA_ENTRIES][BUFF_STRUCT_LENGTH/4];

Next let’s define the NESB PRX callback, this callback will check the last received command, check for a received entry, and then shift the data in a global structure, and clear the entry to move onto the next entry.

/***** Callback Functions *****/
void defaultCallback(RCL_Command *cmd, LRF_Events lrfEvents, RCL_Events rclEvents)
{
    if (rclEvents.lastCmdDone)
    {
        gCmdDone += 1;
    }
    if (rclEvents.rxEntryAvail)
    {
        gPktRcvd += 1; //used in global stats to indicate packets received
        GPIO_toggle(CONFIG_GPIO_RLED); //Toggle led
        if (cmd->cmdId == RCL_CMDID_NESB_PRX)
        {
            RCL_CmdNesbPrx  * nesbRxCmd     =   (RCL_CmdNesbPrx *) cmd;
            List_List consumedBuffers = { 0 };

            uint32_t timestamp  =  nesbRxCmd->stats->lastTimestamp;


            uint8_t * pDataEntry;
            pDataEntry = (uint8_t * ) RCL_MultiBuffer_RxEntry_get( &nesbRxCmd->rxBuffers, &consumedBuffers );

            RCL_MultiBuffer_ListInfo listInfo;
            RCL_MultiBuffer_ListInfo_init( &listInfo, &consumedBuffers );

            while (pDataEntry) //while we have data to process
            {
                RCL_Buffer_DataEntry * pRclBufDataEntry = (RCL_Buffer_DataEntry *) pDataEntry;

                const uint32_t headerLen = 2, addrLen = 3; //packet has metadata in it, length of packet, and address, etc

                uint32_t startOfData_i;
                uint32_t lengthOfData;

                startOfData_i =
                        sizeof(pRclBufDataEntry->length)    +
                        sizeof(pRclBufDataEntry->numPad)    +
                        pRclBufDataEntry->numPad            +
                        headerLen  +  addrLen ;
                // startOfData_i should now index to the first data byte.

                //just looking at the data itself in the packet from TX

                lengthOfData =
                        pRclBufDataEntry->length            -
                        sizeof(pRclBufDataEntry->numPad)    -
                        pRclBufDataEntry->numPad            -
                        (headerLen  +  addrLen);

                uint8_t * startOfData  =  &pDataEntry[startOfData_i];
                pDataEntry = (uint8_t *) RCL_MultiBuffer_RxEntry_next( &listInfo );
            }
            RCL_MultiBuffer * multiBuffer   =   RCL_MultiBuffer_head(&(nesbRxCmd->rxBuffers));
            RCL_MultiBuffer_clear(multiBuffer);
        }
    }

}

Now let’s initialize RCL, and set up the configuration settings for NESB (match the TX side). Optional is to set up a useful global “stats” for use in debugging, to show that we received 100 packets ok. In RCL_MultiBuffer_put(&cmd.rxBuffers, multiBuffer); we take the data entry, and prepare to re-transmit the entry.

#define NUM_RX_BUF 1
#define MULTI_BUF_SZ 2048

void runNesbPrx(void)
{
    uint32_t rxMultiBuffer[NUM_RX_BUF][MULTI_BUF_SZ / 4];
    List_List multiBuffers = { 0 };

    RCL_init();
    RCL_Handle rclHandle = RCL_open(&rclClient, &LRF_configNesb);

    /* Declare command */
    cmd = RCL_CmdNesbPrx_DefaultRuntime();
    RCL_StatsNesb rxStats;
    rxStats = RCL_StatsNesb_DefaultRuntime();

    /* Command configuration */
    cmd.common.scheduling = RCL_Schedule_Now;
    cmd.common.status = RCL_CommandStatus_Idle;
    cmd.common.runtime.callback = defaultCallback;
    cmd.config.discardRxPackets = DISCARD_RX_PACKET;
    cmd.config.fsOff = 1U;
    cmd.common.runtime.rclCallbackMask.value = RCL_EventLastCmdDone.value | RCL_EventRxEntryAvail.value;
    cmd.rfFrequency = FREQUENCY;
    cmd.syncWordA = SYNCWORD;
    cmd.syncWord[0].address = NESB_ADDR; // Set NESB address
    cmd.addrLen = 4; // Consider 4 bytes for the NESB address
    cmd.syncWord[0].seqValid = 1; // Only accept packets with sequence number and CRC different from the previous one
    cmd.syncWord[0].autoAckMode = 3; // Enable auto-acknowledgement regardless of received NO_ACK
    cmd.common.timing.relGracefulStopTime = 0;
    cmd.config.repeatNok = 1; // Does radio want to restart the RX if it has received a packet with bad (Nok) CRC
    cmd.config.repeatOk = 1; // Does radio want to restart RX or do we just consider RX as complete

    /* Stats configuration, useful for debugging */
    rxStats.config.accumulate = 1;
    rxStats.nTx = 0;
    rxStats.nRxOk = 0;
    rxStats.nRxNok = 0;
    rxStats.nRxIgnored = 0;
    rxStats.nRxAddrMismatch = 0;
    rxStats.nRxBufFull = 0;

    stats.config.accumulate = 1; //how often stats update.
    stats.config.activeUpdate = 1;

    /* Set RX command statistics structure */
    cmd.stats = &stats;
    GPIO_setConfig(CONFIG_GPIO_RLED, GPIO_CFG_OUT_STD | GPIO_CFG_OUT_LOW);
    GPIO_write(CONFIG_GPIO_RLED, CONFIG_GPIO_LED_OFF);
    /* Set up Rx (multi)buffer */

    cmd.rxBuffers = multiBuffers;

    for(int i = 0; i < NUM_OF_PACKETS; i++)
    {
        multiBuffer = (RCL_MultiBuffer *) buffer[i];
        RCL_MultiBuffer_init(multiBuffer, BUFF_STRUCT_LENGTH);
        RCL_MultiBuffer_put(&cmd.rxBuffers, multiBuffer);
    }
    cmd.common.status = RCL_CommandStatus_Idle;
    RCL_Command_submit(rclHandle, &cmd);

    /* Wait for command to conclude  */
    RCL_Command_pend(&cmd);
    while(1)
    {

    }

}

For more help

For any help or questions regarding NESB please visit TI E2E.