Radio Control Layer (RCL)
NESB PTX Command Handler

Introduction

A PTX device normally acts as the main transmitter in an NESB network. Depending on user configuration, the handler acts similarly to the Generic Tx Command handler, or it can add automatic retransmission of unacknowledged packets.

The following sections describe how the command can be configured and used, its life cycle, and how it fits into the RCL architecture.

Usage

In order to submit a PTX 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_PTX_t command has been initialized and configured.
  3. The Tx buffer has been set up.
  4. 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 built packets so that it is 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.

#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 512
#define FREQUENCY 2470000000
#define SYNCWORD 0xAB0B51DE
#define NESB_ADDR 0xAC9825CB
#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;

Taking this into consideration, packet generation needs to consider how the 11-bit header is placed in the LRF FIFO. the following function shows one way to accomplish this:

void generatePackets(RCL_Buffer_TxBuffer **txBuffers, uint32_t numPkt, uint32_t pktLen,
ExampleRCL_NesbHdrDef *hdrDef, uint32_t address, uint32_t addressLen)
{
union
{
uint32_t value;
uint8_t bytes[4];
} nesbAddress;
union
{
uint16_t value;
uint8_t bytes[2];
} nesbHeader;
uint8_t hdrLen = (hdrDef->numHdrBits + 7) / 8;
uint8_t seqNum = 0; // Start with a seq number equal to zero
uint8_t noAckBit = 0; // Always set the noAckBit to zero
nesbAddress.value = address;
for (int i = 0; i < numPkt; i++)
{
uint8_t *txData;
/* Initialize Tx Buffer */
txData = RCL_TxBuffer_init(txBuffers[i], hdrDef->numPad, hdrLen, pktLen);
/* Build the 11 bit NESB header */
nesbHeader.value = ((pktLen << 3) | (seqNum << 1) | noAckBit);
/* Write NESB header to buffer */
for (int i = 0; i < hdrLen; i++)
{
txData[i] = nesbHeader.bytes[i];
}
/* Write NESB address to buffer. Address comes immediately after header */
for (int i = 0; i < addressLen; i++)
{
txData[hdrLen + i] = nesbAddress.bytes[i];
}
/* Write the payload */
if(pktLen >= addressLen)
{
uint32_t payloadLength = pktLen - addressLen;
for(uint32_t i = 0; i < payloadLength; i++)
{
txData[hdrLen + addressLen + i] = i & 0xFF;
}
}
/* Update packet ID (seq number) for next packet */
seqNum = (seqNum + 1) % 4;
}
}

Command configuration and submitting is similar to generic command handlers.

void runNesbPtx(void)
{
ExampleRCL_NesbHdrDef hdrDef = NESB_hdrDef_DefaultRuntime();
List_List multiBuffers = { 0 };
uint32_t address = NESB_ADDR;
uint32_t addressLen = 4;
/* Set up Tx and Rx buffers considering 4 packets, packet length of 200 bytes, 2 header bytes and 3 padding bytes */
RCL_Buffer_TxBuffer *txBuffers[NUM_PKT];
uint32_t pktBuffer[NUM_PKT][RCL_TxBuffer_len_u32(NUM_PAD, HDR_LEN, PKT_LEN)];
uint32_t rxMultiBuffer[NUM_RX_BUF][MULTI_BUF_SZ / 4];
RCL_Handle h = RCL_open(&rclClient, &LRF_configNesb);
/* Declare command */
RCL_CmdNesbPtx cmd;
/* Command configuration */
cmd.common.scheduling = RCL_Schedule_AbsTime;
cmd.common.runtime.callback = defaultCallback;
cmd.common.runtime.rclCallbackMask.value = RCL_EventLastCmdDone.value | RCL_EventRxEntryAvail.value;
cmd.rfFrequency = FREQUENCY;
cmd.syncWord = SYNCWORD;
cmd.config.hdrConf = 1; // Insert SEQ and NO_ACK from Tx buffer
cmd.config.autoRetransmitMode = 3; // Always listen for ACK and retransmit if missing.
cmd.maxRetrans = 5; // Attempt 5 retransmissions if ACK is not received.
cmd.retransDelay = 50000; // Wait 50000 Systim ticks (i.e. 12.5 ms) between retransmissions.
cmd.common.status = RCL_CommandStatus_Idle;
for (int i = 0; i < NUM_PKT; i++)
{
txBuffers[i] = (RCL_Buffer_TxBuffer *)pktBuffer[i];
}
/* Set up Rx buffer to receive ACK packets */
for (int i = 0; i < NUM_RX_BUF; i++)
{
RCL_MultiBuffer *multiBuffer = (RCL_MultiBuffer *)rxMultiBuffer[i];
RCL_MultiBuffer_init(multiBuffer, MULTI_BUF_SZ);
RCL_MultiBuffer_put(&multiBuffers, multiBuffer);
}
/* Clear multibuffer before starting the operation */
RCL_MultiBuffer *multiBuffer = RCL_MultiBuffer_head(&multiBuffers);
while (multiBuffer != NULL)
{
RCL_MultiBuffer_clear(multiBuffer);
multiBuffer = RCL_MultiBuffer_next(multiBuffer);
}
cmd.rxBuffers = multiBuffers;
/* Generate the packets that will be sent and fill Tx buffer */
generatePackets(txBuffers, NUM_PKT, PKT_LEN, &hdrDef, address, addressLen);
for(int i = 0; i < NUM_PKT; i++)
{
RCL_TxBuffer_put(&cmd.txBuffers, txBuffers[i]);
}
/* Schedule first command considering a start delay of 600 us */
for (int i = 0; i < NUM_PKT; i++)
{
cmd.common.status = RCL_CommandStatus_Idle;
cmd.common.timing.absStartTime = nextTime;
/* Wait for command to conclude */
/* Schedule next command considering the same start delay */
}
/* Check ACK packets */
for (int i = 0; i < NUM_PKT; i++)
{
RCL_Buffer_DataEntry *rxPkt = RCL_MultiBuffer_RxEntry_get(&cmd.rxBuffers, NULL);
if (rxPkt != NULL)
{
/* Do something with the received ACK */
}
}
}

Architecture

The NESB Ptx command handler has a life cycle that depends on several things. If configuration is such that an acknowledgement is expected, the device will automatically switch from Tx mode to Rx mode to listen for an acknowledge coming from an associated PRX device in the network. Furthermore, if retransmission of unacknowledged packets is enabled, the PTX command handler will automatically switch back and forth between Tx mode and Rx mode in order to retransmit the packet. This will go on for a user-defined number of times and with a user-defined retransmission delay. If a valid acknowledgement is received, or if a packet is transmitted and no acknowledgement is expected, the command will conclude not before incrementing a sequence number that is used by the PRX device to differentiate between new packets and retransmitted packets.

Once this has happened, the callback and the command status can be used for error checking and the application can proceed according to its specification.

NESB PTX handler state machine
RCL Event (In) Description
setup Setup has been performed
timerStart Timer-based start signalled
RCL Event (Out) Description
lastCmdDone The RCL is finished with the command
cmdStarted Command handler has accepted and started executing
rxBufferFinished An RX multi-buffer is finished
rxEntryAvail An RX entry has been made available
LRF Event Description
opDone The PBE operation has finished
opError Something went wrong. Cause located in the PBE ENDCAUSE register
rxOk Packet received with CRC OK and not to be ignored by the MCU
rxNok Packet received with CRC error