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:
static uint64_t botaTimeFunc(void) {
return GetTickCount();
}
while (1) {
Sleep(10);
}
}
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.";
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:
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");
}
printf("Transfer was finished\n");
}
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.
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.";
memcpy(dst, bulkToSend + addr, size);
}
static void onTransferAcceptedCallback(
BotaTransferId transferId,
const EMBENET_IPV6* recepientAddr) {
printf("Transfer was accepted\n");
}
printf("Transfer was finished\n");
}
void* transferSpace = malloc(transferSpaceSize);
readFunc,
0,
sizeof(bulkToSend),
NULL,
"lorem ipsum",
12,
transferSpace,
transferSpaceSize,
onTransferAcceptedCallback,
onTransferFinishedCallback,
&transferId
);
printf("Unable to start BOTA transfer\n");
}
while (1) {
Sleep(10);
}
}
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.
switch (status.transferState) {
case BOTA_TRANSFER_STATE_FINISHED:
printf("Transfer was finished");
for (size_t i = 0; i < recipientCount; i++) {
printf(
"Transfer recipient with uid=%" PRIu64
"\n", recipient->
uid);
}
break;
case BOTA_TRANSFER_STATE_ERROR:
printf("Transfer error\n");
break;
case BOTA_TRANSFER_STATE_UNKNOWN:
printf("Unknown transfer\n");
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.