Firmware update¶
Mira comes with support for a method of distributing binary files to all nodes in a network. This method is intended for performing firmware updates over the air (FOTA), but can also be used for other purposes.
On the gateway¶
Move the firmware file to firmwares/X.bin
, where X is the slot_id
. The gateway will automatically create a version number for it and start distributing it to the rest of the network.
Transfer method¶
Figure 1: A file transfer throughout the network
A file is transferred hop by hop along the routing tree. It starts by the first node (root node) having the new file in its file swap storage. Its children download the file when they detect that there is a new version. This process continues throughout the network.
The maximum number of simultaneous transfers to a node's children is configured in the mira_net_config_t
argument to mira_net_init()
.
Increasing the number of simultaneous transfers on a fast rate node can improve the propagation time of the file transferred if the children of the fast rate node is running a slower rate. Along with a high number of slow rate nodes directly connected to the fast rate node.
Update of Firmware¶
The transfer method allows for arbitrary binary files. The application is responsible for using this file in the correct way. The developer should take aspects of for example file integrity into consideration when designing the firmware file format.
Developers are responsible for building their bootloader to have support for updating firmware.
Note
It is recommended to propagate the firmware to all nodes in the network before restarting nodes into the new firmware.
Using the supported transfer method is optional, alternate methods may be used if the developer finds that more suitable for the application.
Custom driver¶
It is possible to store the temporary image on an external flash, or any other device, by providing a custom driver.
The custom driver is set via mira_fota_set_driver()
A driver is defined as a set of callbacks that are called when the backend needs access to data.
The backend organizes multiple sessions, which can be in read, write or idle state. Multiple read sessions can run in parallel, but only one write, making it possible to have multiple parallel read requests at the same time.
An operation is terminated by calling the done_callback()
with the argument storage
, provided to the driver callback. It is up to the driver to call the done_callback()
once, and only once, for each successful request. Requests that can't be handled should have a non-zero return code, to indicate that the done_callback()
will not be called, and the operation fails. Failing to properly call done_callback()
will result in undefined behaviour.
An example of some parallel sessions:
time | session 1 | session 2 | session 3 |
---|---|---|---|
0 | -> read() | ||
1 | busy | -> read() | |
2 | busy | <- done() | |
3 | busy | -> read() | |
4 | busy | <- done() | |
5 | <- done() | ||
6 | -> erase() | ||
7 | <- done() | ||
8 | -> write() | ||
9 | <- done() | ||
10 | -> write() | ||
11 | <- done() | ||
12 | -> write() | ||
13 | <- done() | ||
14 | -> read() | ||
15 | -> read() | busy | |
16 | <- done() | busy | |
17 | <- done() |
As seen in the example, multiple read request can be called in parallel.
Creating swap area¶
To create a valid swap area, a program usually writes the image via the Mira FOTA API.
During development one might instead want to flash the swap area with a valid image. To create the valid image for that, use the fota_header_tool.py program. It can read a hex file, extract a range of data from it, put it in the swap area's addresses and add a valid header. The resuling hex file can then be flashed to a node.
Run it with --help
as argument to see how it is used.
Creating FOTA images for Mira Gateway¶
When using FOTA with Mira Gateway, the gateway expects binary files and will create the header automatically, so the fota_header_tool shouldn't be used there. Use objcopy to create a binary file from a hex or elf file instead.
Defines¶
MIRA_FOTA_MAX_SLOT¶
Number of available FOTA slots.
MIRA_FOTA_HEADER_SIZE¶
The size of the FOTA header in the swap storage.
FOTA places a header of this size first in the swap storage, before the actual binary.
Types¶
mira_fota_done_callback_t¶
Callback to indicate result is finished.
Parameters¶
Name | Description |
---|---|
storage | Pointer to internal storage |
mira_fota_cb_init_t¶
FOTA driver callback, to initialize driver.
Called to initialize FOTA driver
mira_fota_cb_get_size_t¶
typedef int(* mira_fota_cb_get_size_t) (uint16_t slot_id, uint32_t *size, mira_fota_done_callback_t done_callback, void *storage);
FOTA driver callback, to get the size of a slot.
Called to get the available size for a given slot. Note that this is the size of the slot, not the size of the data written.
On successful operation, call done_callback(storage). The callback does not have to be called within the method itself. However, it:* must be called once and only once if the method returns 0 / success
- must not be called if the method returns an error.
If the slot id is not supported, return an error code.
Parameters¶
Name | Description |
---|---|
slot_id | ID of the slot top operate on |
size | output of the operation |
done_callback | callback to be called when operation is finished |
storage | pointer to pass to the done_callback to track the operation |
Return value¶
0 on successful request, non-zero if error
mira_fota_cb_read_t¶
typedef int(* mira_fota_cb_read_t) (uint16_t slot_id, void *data, uint32_t address, uint32_t length, mira_fota_done_callback_t done_callback, void *storage);
FOTA driver callback, to read data.
Multiple read sessions may occur in parallel, but only one write.
On successful operation, call done_callback(storage). The callback does not have to be called within the method itself. However, it:* must be called once and only once if the method returns 0 / success
- must not be called if the method returns an error.
If the slot id is not supported, return an error code.
Parameters¶
Name | Description |
---|---|
slot_id | ID of the slot top operate on |
data | buffer to store result, valid until done_callback() is called |
address | offset in slot to write to |
length | number of bytes to be written |
done_callback | callback to be called when operation is finished |
storage | pointer to pass to the done_callback to track the operation |
Return value¶
0 on successful request, non-zero if error
mira_fota_cb_read_header_t¶
typedef int(* mira_fota_cb_read_header_t) (uint16_t slot_id, void *data, mira_fota_done_callback_t done_callback, void *storage);
FOTA driver callback, to read header data.
Multiple read sessions may occur in parallel, but only one write.
On successful operation, call done_callback(storage). The callback does not have to be called within the method itself. However, it:* must be called once and only once if the method returns 0 / success
- must not be called if the method returns an error.
If the slot id is not supported, return an error code.
Use MIRA_FOTA_HEADER_SIZE macro for length of the header.
Parameters¶
Name | Description |
---|---|
slot_id | ID of the slot top operate on |
data | buffer to store result, valid until done_callback() is called |
done_callback | callback to be called when operation is finished |
storage | pointer to pass to the done_callback to track the operation |
Return value¶
0 on successful request, non-zero if error
mira_fota_cb_write_t¶
typedef int(* mira_fota_cb_write_t) (uint16_t slot_id, const void *data, uint32_t address, uint32_t length, mira_fota_done_callback_t done_callback, void *storage);
FOTA driver callback, to write data to driver.
Multiple read sessions may occur in parallel, but only one write. Erase counts as a write operation.
On successful operation, call done_callback(storage). The callback does not have to be called within the method itself. However, it:* must be called once and only once if the method returns 0 / success
- must not be called if the method returns an error.
If the slot id is not supported, return an error code.
Parameters¶
Name | Description |
---|---|
slot_id | ID of the slot top operate on |
data | data to be written, valid until done_callback() is called |
address | offset in slot to write to |
length | number of bytes to be written |
done_callback | callback to be called when operation is finished |
storage | pointer to pass to the done_callback to track the operation |
Return value¶
0 on successful request, non-zero if error
mira_fota_cb_write_header_t¶
typedef int(* mira_fota_cb_write_header_t) (uint16_t slot_id, const void *data, mira_fota_done_callback_t done_callback, void *storage);
FOTA driver callback, to write header data to driver.
Multiple read sessions may occur in parallel, but only one write. Erase counts as a write operation.
On successful operation, call done_callback(storage). The callback does not have to be called within the method itself. However, it:* must be called once and only once if the method returns 0 / success
- must not be called if the method returns an error.
If the slot id is not supported, return an error code.
Use MIRA_FOTA_HEADER_SIZE macro for length of the header.
Parameters¶
Name | Description |
---|---|
slot_id | ID of the slot top operate on |
data | data to be written, valid until done_callback() is called |
done_callback | callback to be called when operation is finished |
storage | pointer to pass to the done_callback to track the operation |
Return value¶
0 on successful request, non-zero if error
mira_fota_cb_erase_t¶
typedef int(* mira_fota_cb_erase_t) (uint16_t slot_id, mira_fota_done_callback_t done_callback, void *storage);
FOTA driver callback, erase an entire slot.
Called to erase the slot.
Multiple read sessions may occur in parallel, but only one write. Erase counts as a write operation.
On successful operation, call done_callback(storage). The callback does not have to be called within the method itself. However, it:* must be called once and only once if the method returns 0 / success
- must not be called if the method returns an error.
If the slot id is not supported, return an error code.
Parameters¶
Name | Description |
---|---|
slot_id | ID of the slot top operate on |
done_callback | callback to be called when operation is finished |
storage | pointer to pass to the done_callback to track the operation |
Return value¶
0 on successful request, non-zero if error
Structs¶
mira_fota_header_t¶
Mira FOTA header.
Name | Type | Description |
---|---|---|
size |
uint32_t |
Size of the image to be transferred. |
checksum |
uint32_t |
CRC-32 of the image. |
type |
uint16_t |
Identifier for the type of image. |
flags |
uint8_t |
Flags for future use. |
version |
uint8_t |
Image version number that changes with each new update.
|
mira_fota_driver_t¶
Note
The struct mira_fota_driver_t can be updated with more fields in future versions. Therefore, it is recommended that the entire memory block of the structure is zeroed out before setting the desired fields.
Name | Type | Description |
---|---|---|
init |
mira_fota_cb_init_t |
|
get_size |
mira_fota_cb_get_size_t |
|
read |
mira_fota_cb_read_t |
|
write |
mira_fota_cb_write_t |
|
erase |
mira_fota_cb_erase_t |
|
read_header |
mira_fota_cb_read_header_t |
Optional, set to NULL to use read callback instead |
write_header |
mira_fota_cb_write_header_t |
Optional, set to NULL to use write callback instead |
Functions¶
mira_fota_set_driver¶
Set FOTA backend driver.
Exchange the default built-in driver with a custom driver. Useful for use with external storage.
The driver must implement all functions except for the optional read_header
and write_header
functions. The optional functions can be used when separate implementations for FOTA headers are required.
The default built-in driver stores the FOTA image on internal flash on CPU, if available. In that case, only slot_id = 0 is available. slot_id other than 0 for the mira_fota_*() calls results in failure, returning -1.
FOTA starts every operation by calling the associated operation callback. These callbacks must execute the desired action. Finish the operation by calling done_callback(storage).
Note that done_callback(storage) does not need to be called directly from the operation callback: it may be called later. However, done_callback(storage) must be called once and only once per started operation. The driver itself is responsible for handling timeouts.
Return 0 from every operation callback to indicate that the operation started without error. Non-zero return value from the callback means that the operation failed to start, and the driver must not call done_callback(storage).
Multiple operations may be requested in parallel. If only one operation can be executed at any given time, the driver needs to hold a queue of the requested operations.
Note
Do not set a custom FOTA driver after the call to mira_fota_init().
Parameters¶
Name | Description |
---|---|
fota_driver | FOTA driver's functions. |
Return value¶
Status of the operation.
Return value¶
Name | Description |
---|---|
MIRA_SUCCESS | The operation was successful. |
MIRA_ERROR_ALREADY_INITIALIZED | The FOTA is already initialized. |
mira_fota_init¶
Initialize firmware transfer OTA.
The maximum amount of simultaneous FOTA transfers to children can be configured in the mira_net_config_t argument to mira_net_init().
This function allocates memory. If enough memory can't be allocated, a message will be written to stdout and the system will hang until the watchdog resets the system.
Note
Call mira_net_init() before calling mira_fota_set_driver().
Return value¶
Status of the operation.
Return value¶
Name | Description |
---|---|
MIRA_SUCCESS | The operation was successful. |
MIRA_ERROR_ALREADY_INITIALIZED | The FOTA is already initialized. |
MIRA_ERROR_NOT_INITIALIZED | mira_net_init has not been called. |
mira_fota_is_valid¶
Check if image in slot is valid.
Result is valid both during read operation and outside of read operation.
The result is guaranteed not to change during a read operation (between mira_fota_read_start has finished, and mira_fota_read_end).
Outside read operation, the result might change due to started OTA transfer, and may only be used for statistical purposes.
Parameters¶
Name | Description |
---|---|
slot_id | Slot number. |
Return value¶
If the image in the FOTA swap storage is valid.
Return value¶
Name | Description |
---|---|
MIRA_TRUE | The FOTA is valid. |
MIRA_FALSE | The FOTA is not valid. |
mira_fota_get_image_size¶
Get size of FOTA image in swap storage.
The result is guaranteed not to change during a read operation (between mira_fota_read_start has finished, and mira_fota_read_end).
Outside read operation, the result might change due to started OTA transfer, and may only be used for statistical purposes.
Parameters¶
Name | Description |
---|---|
slot_id | Slot number. |
Return value¶
0 if the image is not valid, otherwise the size of the image.
mira_fota_get_version¶
Get FOTA image version.
The result is guaranteed not to change during a read operation (between mira_fota_read_start has finished, and mira_fota_read_end).
The image version is a sequence number for the update session. It is useful for statistics for which nodes have got the image.
The version number is in the range 0-254. 255 means version number is not present.
Parameters¶
Name | Description |
---|---|
slot_id | Slot number. |
Return value¶
255 if the image is not valid, otherwise the version number.
mira_fota_read_start¶
Start a read session.
A read session allows for the use of mira_fota_read().
Return value MIRA_SUCCESS means that a read session is requested. The read session only starts once mira_fota_is_working() returns MIRA_FALSE. See mira_fota_is_working() for more information.
Parameters¶
Name | Description |
---|---|
slot_id | Slot number. |
Return value¶
Status of the operation.
Return value¶
Name | Description |
---|---|
MIRA_SUCCESS | The read session is successfully requested. |
MIRA_ERROR_ALREADY_INITIALIZED | Another FOTA session is already active. |
MIRA_FOTA_ERROR_LOCK | Unable to access a lock to the FOTA. |
mira_fota_read¶
mira_status_t mira_fota_read(uint8_t *dst, mira_size_t *dst_length, mira_size_t offset, mira_size_t length);
Read from swap storage.
Start a read request from the FOTA swap storage.
Return value MIRA_SUCCESS means that a read operation is requested. The operation is finished when mira_fota_is_working() returns MIRA_FALSE.
Once the operation is finished, content is available at address of dst, and the size of the written data is available at dst_length.
Parameters¶
Name | Description |
---|---|
dst | Pointer to where the read data should be stored. |
dst_length | Pointer to the length of the read. |
offset | Offset in the FOTA swap storage from where to start the reading. |
length | Size of block to read. |
Return value¶
Status of the operation.
Return value¶
Name | Description |
---|---|
MIRA_SUCCESS | The read request is successfully requested. |
MIRA_ERROR_NOT_INITIALIZED | A read session has not been started yet (mira_fota_read_start()). |
MIRA_FOTA_ERROR_LOCK | Unable to access a lock to the FOTA. |
mira_fota_read_end¶
Close the read session.
Return value¶
Status of the operation.
Return value¶
Name | Description |
---|---|
MIRA_SUCCESS | The read session was successfully closed. |
MIRA_ERROR_NOT_INITIALIZED | A read session has not been started yet (mira_fota_read_start()). |
MIRA_FOTA_ERROR_LOCK | Unable to access a lock to the FOTA. |
mira_fota_write_start¶
Start a write session to the FOTA swap storage.
A write session allows for the use of mira_fota_write().
Return value of MIRA_SUCCESS means that a write session is requested. The write session only starts once mira_fota_is_working() returns MIRA_FALSE. See mira_fota_is_working() for more information.
Parameters¶
Name | Description |
---|---|
slot_id | Slot number. |
Return value¶
Status of the operation.
Return value¶
Name | Description |
---|---|
MIRA_SUCCESS | A write session is successfully requested. |
MIRA_ERROR_ALREADY_INITIALIZED | Another FOTA session is already active. |
MIRA_FOTA_ERROR_LOCK | Unable to access a lock to the FOTA. |
mira_fota_write¶
Write to the FOTA swap storage.
Writing to the FOTA swap storage is only available during a write session.
If the return value is MIRA_SUCCESS, a write operation is requested. The operation is finished when mira_fota_is_working() returns MIRA_FALSE.
During the entire operation, the content of the src memory must not change.
If the return value is not MIRA_SUCCESS, the write operation has not started.
Parameters¶
Name | Description |
---|---|
offset | Offset of the location to write to. |
src | Data to write. |
length | Size of the write. |
Return value¶
Status of the operation.
Return value¶
Name | Description |
---|---|
MIRA_SUCCESS | A write request is successfully requested. |
MIRA_ERROR_NOT_INITIALIZED | A write session has not been started yet (mira_fota_write_start()). |
MIRA_FOTA_ERROR_LOCK | Unable to access a lock to the FOTA. |
mira_fota_erase¶
Erase the FOTA swap storage.
Erasing the FOTA swap storage is only available during a write session.
Return value MIRA_SUCCESS means that an erase operation is requested. The operation is finished when mira_fota_is_working() returns MIRA_FALSE.
Return value¶
Status of the operation.
Return value¶
Name | Description |
---|---|
MIRA_SUCCESS | An erase request is successfully requested. |
MIRA_ERROR_NOT_INITIALIZED | A write session has not been started yet (mira_fota_write_start()). |
MIRA_FOTA_ERROR_LOCK | Unable to access a lock to the FOTA read. |
mira_fota_write_header¶
mira_status_t mira_fota_write_header(mira_size_t size, uint32_t checksum, uint16_t type, uint8_t flags, uint8_t version);
Write a header to the FOTA swap storage.
The header is necessary to validate the content of the swap storage, so that transfer can proceed.
The version number is a reference number which must change for every new image. Version numbers do not have to be incremented in order. The version number must not be 255.
The checksum is of type CRC-32 of the content written to the swap storage. It is used to validate the content integrity for transfer errors. The checksum uses polynom defined as 0x04C11DB7, which in reversed bit order is defined as 0xEDB88320. Mira CRC API can be used to generate the checksum.
Return value MIRA_SUCCESS means that a write operation is requested. The operation is finished when mira_fota_is_working() returns MIRA_FALSE.
Parameters¶
Name | Description |
---|---|
size | The size of the image to transfer. |
checksum | Checksum of the content in the swap storage, which must match the data for the content to be treated as valid. |
type | An integer to identify the information in the swap storage. |
flags | Flags for future use, set to 0. |
version | A sequence number that should change for each new update. |
Return value¶
Status of the operation.
Return value¶
Name | Description |
---|---|
MIRA_SUCCESS | A write request is successfully requested. |
MIRA_ERROR_NOT_INITIALIZED | A write session has not been started yet (mira_fota_write_start()). |
MIRA_ERROR_INVALID_VALUE | The version parameter was 255. |
MIRA_FOTA_ERROR_LOCK | Unable to access a lock to the FOTA read. |
mira_fota_write_end¶
Close the write session.
The write operation will continue to work while mira_fota_is_working is returning MIRA_TRUE.
Return value¶
Status of the operation.
Return value¶
Name | Description |
---|---|
MIRA_SUCCESS | The write session was successfully closed. |
MIRA_ERROR_NOT_INITIALIZED | A write session has not been started yet (mira_fota_write_start()). |
MIRA_FOTA_ERROR_LOCK | Unable to access a lock to the FOTA read. |
mira_fota_is_working¶
Check if the last operation is still running.
The process that called the previous operation will be polled when the operation is finished. Recommended usage:
Note
Validation of the FOTA image is not included in this function. Therefore mira_fota_is_valid() might return MIRA_FALSE for a few seconds after mira_fota_is_working() starts to return MIRA_FALSE following a write session.
Return value¶
If a FOTA operation is currently working.
Return value¶
Name | Description |
---|---|
MIRA_FALSE | No FOTA operation is currently executing. |
MIRA_TRUE | There is a FOTA operation which is currently executing. |
mira_fota_force_request¶
Force the node to check if a new firmware is available.
During normal operation the node regularly asks if there is a new firmware available, in some cases it can be useful to immediately check if there is a new firmware available. This can be used for that.
Note
It is important that this is only triggered from external user interaction to the device or used during development and testing. It should not be used in normal background operation.
Return value¶
Status of the request.
Return value¶
Name | Description |
---|---|
MIRA_ERROR_NOT_INITIALIZED | FOTA not initialized or not joined to a network. |
MIRA_ERROR_RESOURCE_NOT_AVAILABLE | FOTA is already in progress. |
MIRA_SUCCESS | Succesful FOTA request was sent. |