BOTA Sender tutorial

Table of contents Table of contents

This short tutorial shows how to use the BOTA Sender API in order to start and manage a BOTA transfer. For the purpose of this tutorial we will assume that we want to write a functional OS thread, that initializes and handles the BOTA Sender service. It will also start and manage a single transfer.

The general scheme of the BOTA Sender thread

BOTA API follows an object-oriented style of programming. In order to use the BOTA Sender service we need its instance: BotaSender. First, the instance needs to be initialized through a call to BOTA_SENDER_Init. Next, the service is handled through a repeating call to BOTA_SENDER_Proc. Once the service is not needed, it can be deinitialized by a call to BOTA_SENDER_Deinit.

Let's start to write some thread code. In general the BOTA service thread will look like this:

#include "bota_sender.h" // the one and only header file needed to use the BOTA Sender service
// BOTA Sender instance
// Time provider function needed for BOTA. This function should return the number of milliseconds passing.
static uint64_t botaTimeFunc(void) {
return GetTickCount();
}
// The BOTA Sender thread
void BotaThread(void* arg) {
// Initialize BOTA service
while (1) {
// Process the BOTA service
// Add some delay - in general anywhere near 10ms-100ms is sufficient
Sleep(10);
}
// Deinitialize the BOTA service
}
BotaSender botaSender
[bota_example_01_sender_service]
Definition: bota_example_01_sender_service.c:7
void BotaThread(void *arg)
Definition: bota_example_01_sender_service.c:15
#define BOTA_DEFAULT_PORT
Default UDP port number for the BOTA service.
Definition: bota_recipient/include/bota_defs.h:96
BOTA service for the sender.
void BOTA_SENDER_Proc(BotaSender *botaSender)
void BOTA_SENDER_Deinit(BotaSender *botaSender)
BotaResult BOTA_SENDER_Init(BotaSender *botaSender, uint16_t port, BotaTimeFunc timeFunc)
Structure describing BOTA Sender instance.
Definition: bota_sender.h:178

The above code initializes and runs the service, but since there are no transfers defined it will mostly be IDLE code.

Starting a transfer

In order to start a BOTA transfer a call to BOTA_SENDER_StartTransfer is needed. But this call requires couple of things. We will discuss them one by one below:

Bulk memory

BOTA Sender accesses the data to send through a callback 'read' function. The user is expected to provide an implementation of such function, allowing to access the data that should be send. Let's look at a simple example:

static uint8_t bulkToSend[] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
"veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
"commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
"velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
"occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
"mollit anim id est laborum.";
static void readFunc(BotaMemoryAddr addr, void* dst, size_t size, BotaTransferId transferId) {
memcpy(dst, bulkToSend + addr, size);
}
uint32_t BotaMemoryAddr
Address in the Bulk memory.
Definition: bota_recipient/include/bota_defs.h:106
uint16_t BotaTransferId
BOTA transfer identifier used to identify and distinguish the transfers.
Definition: bota_recipient/include/bota_defs.h:78

In the above example the 'botaSenderReadFunc' is the read function that reads from a static memory buffer 'bulkToSend'.

In order to define the Bulk Memory during the start of the transfer, the user code is expected to provide:

  • 'readFunc' (as shown above)
  • 'readAddr' - additional read offset in the bulk memory (for the above read function this address should be 0)
  • 'size' - total size (in number of bytes) of the data to be sent

Recipient IPv6 address

The recipient(s) of the transfer are defined using IPv6 address. This can be an unicast address or group address. If unicast addressing is used, then there is only one expected recipient. In case of group addressing there might potentially be many recipients. The recipient address is provided as 'recipientAddr' in the call to BOTA_SENDER_StartTransfer.

Transfer information

When the transfer is started we can pass additional information describing it. This information is user-defined and can contain up to 32 bytes. In the above example we've used a simple string containing "testTransfer".

Transfer space

In order to handle multiple recipients the BOTA Sender instance needs additional operational memory called 'transfer space'. This memory is provided through the following arguments of the BOTA_SENDER_StartTransfer call:

  • transferSpace - pointer to the transfer space size
  • transferSpaceSize - size (in number of bytes) of the transfer space

You can use the BOTA_SENDER_GetRequiredTransferSpaceSize call to determine how much memory is needed in order to handle a given number of recipients. The code below illustrates this idea:

// Get required transfer space assuming that we will handle 100 recipients
size_t transferSpaceSize = BOTA_SENDER_GetRequiredTransferSpaceSize(100);
// Allocate the necessary memory
void* transferSpace = malloc(transferSpaceSize);
size_t BOTA_SENDER_GetRequiredTransferSpaceSize(size_t maxNumberOfRecipients)
Determines how much user memory is needed to handle the transfer to the given recipient/recipients.

Callbacks

As an option we can define two additional callbacks, that will inform about some important events during the BOTA transfer:

  • onTransferAccepted - is a callback that will be called each time a new recipient accepts an incoming transfer
  • onTransferFinished - is a callback that will be called once the transfer is done

Some exemplary callback implementation is shown below:

static void onTransferAcceptedCallback(BotaTransferId transferId, const EMBENET_IPV6* recepientAddr) {
printf("Transfer was accepted\n");
}
static void onTransferFinishedCallback(BotaTransferId transferId) {
printf("Transfer was finished\n");
// explicitly end the transfer removing it from the transfer list
}
BotaResult BOTA_SENDER_EndTransfer(BotaSender *botaSender, BotaTransferId transferId)
Ends a BOTA transfer.

Ending the transfer

Note that we must explicitly end the transfer by a call to BOTA_SENDER_EndTransfer. In the above example this is done in the onTransferFinished callback.

The BOTA_SENDER_EndTransfer can also be called to end and ongoing transfer.

Putting it all together to manage a transfer

The example below merges all the above code into a thread that runs the BOTA Sender service and starts a single transfer.

#include "bota_sender.h" // the one and only header file needed to use the BOTA Sender service
// BOTA Sender instance
// Time provider function needed for BOTA. This function should return the number of milliseconds passing.
static uint64_t botaTimeFunc(void) {
return GetTickCount();
}
static uint8_t bulkToSend[] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
"veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
"commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
"velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
"occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
"mollit anim id est laborum.";
static void readFunc(BotaMemoryAddr addr, void* dst, size_t size, BotaTransferId transferId) {
memcpy(dst, bulkToSend + addr, size);
}
static void onTransferAcceptedCallback(BotaTransferId transferId, const EMBENET_IPV6* recepientAddr) {
printf("Transfer was accepted\n");
}
static void onTransferFinishedCallback(BotaTransferId transferId) {
printf("Transfer was finished\n");
// explicitly end the transfer removing it from the transfer list
}
// The BOTA Sender thread
void BotaThread(void* arg) {
// Initialize BOTA service
// Get required transfer space assuming that we will handle 100 recipients
size_t transferSpaceSize = BOTA_SENDER_GetRequiredTransferSpaceSize(100);
// Allocate the necessary memory
void* transferSpace = malloc(transferSpaceSize);
// This variable will hold the transfer identifier
BotaTransferId transferId;
// Start the transfer
BotaResult result;
result = BOTA_SENDER_StartTransfer(&botaSender, // sender instance
readFunc, // read function
0, // read address
sizeof(bulkToSend), // total size of the bulk data to send
NULL, // recipient address
BOTA_DEFAULT_PORT, // recipient port
"lorem ipsum", // additional transfer information
// (user defined)
12, // size (in bytes) of the additional
// transfer information (up to 32 bytes)
transferSpace, // transfer space
transferSpaceSize, // transfer space size
onTransferAcceptedCallback, // on transfer accepted callback
onTransferFinishedCallback, // on transfer finished callback
&transferId // place to store the transfer identifier
);
if (result != BOTA_RESULT_OK) {
printf("Unable to start BOTA transfer\n");
}
while (1) {
// Process the BOTA service
// Add some delay - in general anywhere near 10ms-100ms is sufficient
Sleep(10);
}
// Deinitialize the BOTA service
}
BotaResult
Possible BOTA function results.
Definition: bota_recipient/include/bota_defs.h:133
@ BOTA_RESULT_OK
Success.
Definition: bota_recipient/include/bota_defs.h:135
BotaResult BOTA_SENDER_StartTransfer(BotaSender *botaSender, BotaReadFunc readFunc, BotaMemoryAddr readAddr, size_t size, const EMBENET_IPV6 *recipientAddr, uint16_t recipientPort, const void *transferInfo, size_t transferInfoSize, void *transferSpace, size_t transferSpaceSize, BotaSenderTransferPolicy *transferPolicy, BotaOnTransferAcceptedCallback onTransferAccepted, BotaOnTransferFinishedCallback onTransferFinished, BotaTransferId *transferId)
Starts a new BOTA transfer.

Monitoring the transfer

At any point after starting the transfer it is possible to poll the transfer status using BOTA_SENDER_GetTransferStatus function. Also it is possible to retrieve the current number of transfer recipients and basic information about each recipient using BOTA_SENDER_GetRecipientCount and BOTA_SENDER_GetRecipient functions. The example below illustrates the usage.

// Get transfer status
BotaTransferStatus status = BOTA_SENDER_GetTransferStatus(&botaSender, transferId);
// Print out status based on the transfer state
switch (status.transferState) {
case BOTA_TRANSFER_STATE_FINISHED:
// note that in this code this will not happen, if we end the transfer immediately
// in the onTransferFinished callback
printf("Transfer was finished");
// get number of recipients
size_t recipientCount = BOTA_SENDER_GetRecipientCount(&botaSender, transferId);
// go through all recipients
for (size_t i = 0; i < recipientCount; i++) {
// get information about the recipient 'i'
printf("Transfer recipient with uid=%" PRIu64 "\n", recipient->uid);
}
// end the transfer since it was finished, removing it from the transfer list
break;
case BOTA_TRANSFER_STATE_ERROR:
printf("Transfer error\n"); // the transfer was unable to finish - better end it
break;
case BOTA_TRANSFER_STATE_UNKNOWN:
printf("Unknown transfer\n"); // whoops, something went really wrong
break;
default: printf("Transfer is ongoing. Bytes sent: %u of %u \n", (unsigned int)status.transferredBytes, (unsigned int)status.totalBytes); break;
}
size_t BOTA_SENDER_GetRecipientCount(BotaSender *botaSender, BotaTransferId transferId)
Gets the number of recipients for an ongoing BOTA transfer.
BotaSenderTransferStatus BOTA_SENDER_GetTransferStatus(BotaSender *botaSender, BotaTransferId transferId)
Gets BOTA transfer status.
BotaTransferRecipient * BOTA_SENDER_GetRecipient(BotaSender *botaSender, BotaTransferId transferId, size_t index)
Gets the status of a single BOTA transfer recipient.
Structure describing the status of the recipient.
Definition: bota_sender.h:92
EMBENET_EUI64 uid
Recipient UID.
Definition: bota_sender.h:94

Note that once we end the transfer through a call to BOTA_SENDER_EndTransfer we no longer can poll its status.