This short tutorial shows how to use the BOTA Recipient API in order to accept BOTA transfers.
The general usage of the BOTA Recipient
BOTA API follows an object-oriented style of programming. In order to use the BOTA Recipient service we need its instance: BotaRecipient. First, the instance needs to be initialized through a call to BOTA_RECIPIENT_Init. Next, the service is handled through a repeating call to BOTA_RECIPIENT_Proc. Once the service is not needed, it can be deinitialized by a call to BOTA_RECIPIENT_Deinit.
In general the BOTA Recipient thread will look like this:
while (1) {
Sleep(10);
}
}
void BotaThread(void *arg)
Definition: bota_example_01_sender_service.c:15
BotaRecipient botaRecipient
[bota_example_11_callbacks]
Definition: bota_example_11_recipient_service.c:53
#define BOTA_DEFAULT_PORT
Default UDP port number for the BOTA service.
Definition: bota_recipient/include/bota_defs.h:96
void BOTA_RECIPIENT_Proc(BotaRecipient *botaRecipient)
BotaResult BOTA_RECIPIENT_Init(BotaRecipient *botaRecipient, uint16_t port, BotaTimeFunc timeFunc, BotaOnTransferStartedCallback onTransferStarted, BotaOnTransferReceivedCallback onTransferReceived, BotaOnTransferAbortedCallback onTransferAborted, BotaOnTransferDataMissingCallback onTransferDataMissing)
void BOTA_RECIPIENT_Deinit(BotaRecipient *botaRecipient)
Definition: bota_recipient.h:209
The above code initializes and runs the service, but we miss the definition of callbacks that are passed to the BOTA_RECIPIENT_Init function. These are described in the next section.
Callbacks
The BOTA_RECIPIENT_Init function requires two mandatory callbacks to be provided:
- timeFunc is time provider function, that should return a 64-bit monotonically increasing value representing passing time in milliseconds
- onTransferStarted is a callback function that will be called when new transfer is detected. The job of this callback is to decide whether the transfer should be accepted and if so - provide a description of the destination memory, where the transferred data will be stored.
In addition the function allows to hook up two other optional callbacks:
- onTransferReceived is a callback function that will be called when the whole transfer is successfully received
- onTransferAborted is a callback function that will be called when the transfer is aborted Though the last two callbacks are considered optional, the users are highly encouraged to implement these.
The following code includes exemplary implementation of these callbacks.
static uint64_t botaTimeFunc(void) {
return GetTickCount();
}
}
}
memory->
write = writeFunc;
}
puts("Transfer was received");
}
puts("Transfer was aborted");
}
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
BotaTransferReaction
Possible recipient reactions to a new BOTA transfer.
Definition: bota_recipient/include/bota_defs.h:145
@ BOTA_TRANSFER_REACTION_ACCEPT
The BOTA transfer should be accepted.
Definition: bota_recipient/include/bota_defs.h:147
BotaAbortReason
Possible reasons for aborting the transfer.
Definition: bota_recipient.h:43
Definition: bota_recipient.h:31
BotaMemoryAddr startAddr
Start address in the destination bulk memory where the bulk data will be stored.
Definition: bota_recipient.h:37
BotaReadFunc read
Function to read the destination bulk memory.
Definition: bota_recipient.h:33
BotaWriteFunc write
Function to write to the destination bulk memory.
Definition: bota_recipient.h:35
size_t capacity
Capacity of the the destination bulk memory (number of bytes). This is the number of available bytes ...
Definition: bota_recipient.h:39
Accepting or rejecting the transfer
When a new transfer is initiated by the sender and it reaches the recipient, the node decides whether it accepts the transfer or not. This is done in the onTransferStarted callback and the decision is signaled through the returned BotaDestinationMemory structure. If the BotaDestinationMemory::capacity is larger or equal the requested transfer size then the transfer is accepted. Otherwise the transfer is rejected. Thus, to reject the transfer explicitly one can set this field to zero.
A common practice is to accept transfers only from a pre-defined node (such as the Border Router) and reject others.
A transfer may optionally include additional user-defined information describing it. This information is given to the BOTA sender when the transfer is started and is later passed to the recipients. This mechanism may also be used to help to decide whether to accept or reject the transfer.
End of the transfer
The transfer will always end with a call to either onTransferReceived or onTransferAborted callback. If onTransferReceived is called this means that the transfer was successfully received and bulk data is stored in the destination memory. If onTransferAborted is called then the transfer was aborted. The callback includes information about the cause of the abort decision.
Aborting the transfer
The user can decide to abort the ongoing transfer using BOTA_RECIPIENT_AbortTransfer call. This function can also be called from within the BotaDestinationMemory::write and BotaDestinationMemory::read callbacks. This function can be used from other callbacks such as onTransferStarted as long as it aborts some other transfer, and not the one the given callback refers to. Aborting the transfer from its own onTransferStarted, onTransferReceived or onTransferAborted callback makes no sense and is not supported. Calling this function aborts the transfer and invokes the onTransferAborted callback (if it was provided).
Working example
The working example is provided below.
#include <stdio.h>
static uint64_t botaTimeFunc(void) {
return GetTickCount();
}
}
}
memory->
write = writeFunc;
}
puts("Transfer was received");
}
puts("Transfer was aborted");
}
while (1) {
Sleep(10);
}
}
The flow of the transfer process
Finally let's summarize the flow of the transfer reception.
- The BOTA Recipient service is initiated by a call to BOTA_RECIPIENT_Init
- The service is then processed through periodic calls to BOTA_RECIPIENT_Proc
- The BOTA Sender sends request to start a transfer. This invokes the onTransferStarted callback in the recipient.
- The Recipient decides if it accepts the transfer or not. If it does, it prepares the descriptor of destination memory telling how to use the destination memory.
- If the transfer is accepted, the sender starts to send bulk data in portions. Each received data portion is written to the destination memory in the recipient.
- At the end of the transfer the sender initiates a validation procedure, where the CRC of the received data is compared with the expected CRC generated by the sender. If the CRCs math then both parties know that the whole data transfer was successful. The recipient invokes the onTranferFinished callback which ends the transmission in the recipient.
- If at any time during the transfer the recipient service decides that the transfer should be aborted, the onTransferAborted callback is called with the description of the transfer and the reason for the abort decision.