.. highlight:: text

NESB PRX Introduction
#####################
A Primary Receiver (PRX) |DEVICE| 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 <https://dev.ti.com/tirex/explore/content/simplelink_lowpower_f3_sdk_7_20_00_29/docs/rcl/html/nesb_prx_handler.html>`_
    or :docs_root:`Radio Control Layer <rcl/html/index.html>`.

.. need to find the doc-tree for these links, a bit difficult to figure this out though. 
Summary
-------
To start developing a program with NESB PRX start with the rfPacketRx example provided in the |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.

.. code-block:: C

    /* 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).

.. code-block:: c

    /***** 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:

.. code-block:: C

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

.. code-block:: C

    /***** 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.

.. code-block:: c

    /***** 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. 

.. code-block:: c 

    #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 <https://e2e.ti.com/support/wireless-connectivity/bluetooth-group/bluetooth/f/bluetooth-forum>`_.


