NESB PTX Introduction¶
A PTX on the CC23xx acts as the main transmitter in an NESB network. Depending on user configuration, the handler acts similarly to the Generic Tx Command handler, and an advantage of NESB is that it can automatic retransmit unacknowledged packets.
The following describes how to configure and use NESB, with example code demonstrating its use.
Note
For more information related to NESB PTX please visit NESB PTX Command Handler
Summary¶
To start developing a program with NESB PTX start with the rfPacketTx example provided in the SimpleLink Low Power F3 SDK found in: example->rtos->CC23xx->prop_rf folder. We will be modifying this example to provide functionality with NESB.
Initial synchronization¶
In order to submit a PTX command, the following steps must have taken place:
RCL has been initialized (see
RCL_init()) and a handle must exist (seeRCL_open()).The
RCL_CMD_NESB_PTX_tcommand has been initialized and configured.The Tx buffer has been set up.
An RX buffer has been set up to receive the ACK (if configuration demands it).
Once these steps have been completed, RCL_Command_submit and RCL_Command_pend are called to
effectively send 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.
Program flow¶
Initialize and open RCL.
- Set RCL settings.
Set frequency.
Set PHY.
Set Syncword.
Set scheduling.
Set status.
Set FS.
Set TX_POWER.
Set auto retransmit mode to ACK.
If set to ACK, set numbers of transmits, and delay.
Set
hdrConf.
Set up the
multibuffer.Clear the
multiBufferfor use later.Set NESB status update.
Set global variable (stats) for use in debugging.
Set RCL callback.
Set
txPacketstructure.Set up green GPIO (
CONFIG_GPIO_GLED).Set number of packets (100).
In a for loop, create a packet with random payload.
Set packet to transmit.
Submit RCL tx command.
Pend on command completion.
Callback toggle
CONFIG_GPIO_GLED.If ACK received, clear for now, and clear
multibuffer.Repeat steps 11 - 17 until 100 packets are sent.
Example code snippets¶
In order to use NESB you first need to include the RCL libraries and drivers. These include, but are not limited to:
RCL.h
RCL_Scheduler.h
rcl/commands/generic.h
/* 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>
Next you will need to define the packet configuration, such as how long the packets will be, the number of entries, pad bytes, packet TX interval, and more.
/* Packet TX Configuration */
#define MAX_LENGTH (30U) // Length, match with RX
#define NUM_DATA_ENTRIES (2U) // Number of data entries
#define NUM_PAD_BYTES (3U) // Number of pad bytes
/* Header length */
#define HDR_LEN (2U)
#define PACKET_INTERVAL 500000 /* Set packet interval to 500000us or 500ms */
#define FS_OFF (1U) // 0: On, 1: Off
#define FREQUENCY (2470000000U) // 2470 MHz
#define TX_POWER (5U)
#define TX_CONFIG_SYNCWORD (0xABF95EB6)
#define TX_CONFIG_ADDR (0xFE1E3C)
#define TX_CONFIG_AUTO_ACK (3) // 3: Always listen for ACK and retransmit if missing.
#define TX_CONFIG_MAX_RETRANS (40)
#define TX_CONFIG_RETRANS_DELAY (50000)
#define NESB_HDR_NUM_PAD (3) // numPad
#define NESB_HDR_LEN (2) // (numHdrBits + 7) / 8
#define NESB_ADDR_LEN (3)
#define NESB_NO_ACK_BIT (0)
Now let’s define some variable declarations to be used within the code.
/***** Variable declarations *****/
RCL_CmdNesbPtx txCmd; // RCL Commands
RCL_StatsNesb nesbStats; //stats for use in debug
#if defined(USE_NESB) && (TX_CONFIG_AUTO_ACK > 0)
#define NUM_DATA_ENTRIES (1)
#define BUFF_STRUCT_LENGTH (32)
RCL_MultiBuffer * nesbAckMultiBuffer; //define the multibuffer
uint32_t nesbAckBuffer[NUM_DATA_ENTRIES][BUFF_STRUCT_LENGTH/4];
#endif
/* RCL Client used to open RCL */
static RCL_Client rclClient;
/* TX packet buffer */
uint32_t packet[NUM_DATA_ENTRIES][3 + ((MAX_LENGTH + 10)/ 4)];
/* Counters for RCL event callback */
volatile uint32_t gCmdDone = 0; // Command done
In order to respond to the ACK (acknowledgement) from the RX device, we can use a callback function, this will also control the blinking that shows each packet being transmitted.
/***** Callback Functions *****/
void defaultCallback(RCL_Command *cmd, LRF_Events lrfEvents, RCL_Events rclEvents)
{
if (rclEvents.lastCmdDone)
{
gCmdDone += 1;
GPIO_toggle(CONFIG_GPIO_GLED); //toggles the green led, this is what blinks 50 times
}
if (cmd->cmdId == RCL_CMDID_NESB_PTX && rclEvents.rxEntryAvail) //red if rx ACK
{
// ack received, just clear it for now
RCL_CmdNesbPtx *pNesbTx = (RCL_CmdNesbPtx *) cmd;
RCL_MultiBuffer * multiBuffer = RCL_MultiBuffer_head(&(pNesbTx->rxBuffers));
RCL_MultiBuffer_clear(multiBuffer); //clear ACK response
}
}
Inside the main thread we first initialize the RCL, and assign a handle and then open the RCL through the use of the following lines.
/* Initialize and open RCL */
RCL_init();
RCL_Handle rclHandle = RCL_open(&rclClient, &LRF_configNesb);
txCmd = RCL_CmdNesbPtx_DefaultRuntime();
Then we can set the relative configurable features such as frequency, PHY, syncword for the RCL. In order for the TX side to communicate successfully with the RX side you are required to match these, as if they are not matched the TX/RX cannot communicate with each other.
txCmd.rfFrequency = FREQUENCY; //2470000000U
txCmd.common.phyFeatures = RCL_PHY_FEATURE_SUB_PHY_1_MBPS_NESB;
txCmd.syncWord = TX_CONFIG_SYNCWORD;
We also need to set how commands are sent to the RCL, ACK mode, and FS mode. ACK mode 3 will always listen for ACK and retransmit if missing.
/* Start command as soon as possible */
txCmd.common.scheduling = RCL_Schedule_Now;
txCmd.common.status = RCL_CommandStatus_Idle;
txCmd.config.fsOff = FS_OFF; // Turn off FS
txCmd.txPower.dBm = TX_POWER;
txCmd.config.autoRetransmitMode = TX_CONFIG_AUTO_ACK; //set to 3 for auto ACK
if (TX_CONFIG_AUTO_ACK > 0)
{
txCmd.maxRetrans = TX_CONFIG_MAX_RETRANS; //set the (max) number of retransmit
txCmd.retransDelay = TX_CONFIG_RETRANS_DELAY; //set the delay
}
txCmd.config.hdrConf = 1;
Now that we have completed our setup, and defined our settings we need to setup the
RX buffer (within TX) to receive ACK packets from the RX device, as well as clearing the multibuffer for smooth operation.
/* Set up Rx buffer to receive ACK packets */
uint32_t i;
for (i = 0; i < NUM_DATA_ENTRIES; i++)
{
RCL_MultiBuffer *multiBuffer = (RCL_MultiBuffer *)nesbAckBuffer[i];
RCL_MultiBuffer_init(multiBuffer, BUFF_STRUCT_LENGTH);
RCL_MultiBuffer_put(&txCmd.rxBuffers, multiBuffer);
}
/* Clear multibuffer before starting the operation */
RCL_MultiBuffer *multiBuffer = RCL_MultiBuffer_head(&txCmd.rxBuffers);
while (multiBuffer != NULL)
{
RCL_MultiBuffer_clear(multiBuffer);
multiBuffer = RCL_MultiBuffer_next(multiBuffer);
}
Next we need to set the callback to trigger on last command done.
/* Callback triggers on last command done */
txCmd.common.runtime.callback = defaultCallback;
txCmd.common.runtime.rclCallbackMask.value = RCL_EventLastCmdDone.value;
txCmd.common.runtime.rclCallbackMask.value |= RCL_EventRxEntryAvail.value;
Now let’s set up the RCL TX buffer packet, as well as the green LED config.
The LED is setup in such a fashion that it toggles in the defaultCallback pending on last command completion,
so when testing the LED blinks 50 times, which indicates 100 packets sent.
RCL_Buffer_TxBuffer *txPacket = (RCL_Buffer_TxBuffer *)&packet;
/* setup the GPIO for green LED */
GPIO_setConfig(CONFIG_GPIO_GLED, GPIO_CFG_OUT_STD | GPIO_CFG_OUT_LOW);
GPIO_write(CONFIG_GPIO_GLED, CONFIG_GPIO_LED_OFF);
In order to send the bytes we make use of a for loop, this for loop is defined around the number of packets,
100 in this case. The for loop executes 100 times,
filling each packet and sending it to the CC23xx RX side. Alternatively you could change the
for loop to a while(1) loop to send packets forever.
RCL_TxBuffer_init (txData) will initialize a TX buffer entry for use by the RCL,
consisting of the number of padding bytes in front of a packet,
number of header bytes to hold, and the number of payload bytes to hold.
for (i = 0; i < numPkt; i++) //alternatively you could make this a while(1) to loop forever
/* Create packet with random payload, similar to generatebytes in previous example */
uint8_t *txData;
txData = RCL_TxBuffer_init(txPacket, NUM_PAD_BYTES, HDR_LEN, MAX_LENGTH);
static uint8_t seqNum = 0;
union
{
uint32_t value;
uint8_t bytes[4];
} nesbAddress;
union
{
uint16_t value;
uint8_t bytes[2];
} nesbHeader;
uint32_t payloadLength = MAX_LENGTH - NESB_ADDR_LEN;
uint32_t pkt_len = payloadLength + NESB_ADDR_LEN;
RCL_Buffer_TxBuffer *txBuffer = (RCL_Buffer_TxBuffer *)&packet[0];
txData = RCL_TxBuffer_init(txBuffer, NESB_HDR_NUM_PAD, NESB_HDR_LEN, pkt_len);
nesbHeader.value = (txCmd.config.hdrConf) ? ((pkt_len << 3) | (seqNum << 1) | NESB_NO_ACK_BIT) : ((pkt_len << 3) | NESB_NO_ACK_BIT);
/* Write NESB header to buffer */
for (int i = 0; i < NESB_HDR_LEN; i++)
{
txData[i] = nesbHeader.bytes[i];
}
/* Write NESB address to buffer */
nesbAddress.value = TX_CONFIG_ADDR;
for (int i = 0; i < NESB_ADDR_LEN; i++)
{
txData[NESB_HDR_LEN + i] = nesbAddress.bytes[i];
}
seqNum = (seqNum + 1) % 4;
/* Write NESB payload */
populateTestBytes(&txData[NESB_HDR_LEN + NESB_ADDR_LEN], payloadLength);
/* Set packet to transmit */
RCL_TxBuffer_put(&txCmd.txBuffers, txPacket);
txCmd.common.status = RCL_CommandStatus_Idle;
/* Submit command */
RCL_Command_submit(rclHandle, &txCmd);
/* Pend on command completion */
RCL_Command_pend(&txCmd);
//sleep for some time...
usleep(PACKET_INTERVAL);
}
Finally, the populateTestBytes function fills the packets with a random payload ready to be used during transmit.
static void populateTestBytes(void *input, uint32_t len)
{
// incrementing bytes
{
//static uint8_t byte = 0x00;
uint8_t byte = 0x00;
uint8_t *bytePointer = (uint8_t *)input;
for(uint32_t i = 0; i < len; i++)
{
bytePointer[i] = byte & 0xFF;
byte += 1;
}
}