5.2. Basic implementation
5.2.1. Project structure
This tutorial demonstrates a basic implementation of the Firmware Update process
with Anjay Lite. The tutorial source files are located in the
examples/tutorial/firmware-update
directory. This example builds on the code
presented in the Installing mandatory Objects chapter:
examples/tutorial/firmware-update/
├── CMakeLists.txt
└── src
├── firmware_update.c (new)
├── firmware_update.h (new)
└── main.c
5.2.2. Installing the Firmware Update Object
To use the Firmware Update feature, you must install the Firmware Update Object in the Anjay Lite client. Use the following API function:
int anj_dm_fw_update_object_install(anj_t *anj,
anj_dm_fw_update_entity_ctx_t *entity_ctx,
anj_dm_fw_update_handlers_t *handlers,
void *user_ptr);
The parameters description
Parameter |
Description |
---|---|
|
A pointer to the initialized Anjay Lite instance. |
|
A pointer to the firmware update entity context. |
|
A pointer to the handlers structure. See the API in Anjay Lite section. |
|
Optional pointer to context-specific data passed to each handler call. Use it to store information required during firmware operations. Set to NULL if no data is needed. |
Each mandatory input object referenced by a pointer must stay valid as long as the Firmware Update Object remains active.
Helper functions
In the tutorial, the Firmware Update Object installation and handling is
abstracted through the functions declared in firmware_update.h
:
#ifndef _FIRMWARE_UPDATE_H_
#define _FIRMWARE_UPDATE_H_
#include <anj/defs.h>
#include <anj/log/log.h>
#define log(...) anj_log(fota_example_log, __VA_ARGS__)
/**
* Checks if a Firmware Update is pending and executes it if needed.
* Should be called periodically in the main loop.
*/
void fw_update_check(void);
/**
* Installs the Firmware Update Object on the LwM2M client instance.
*
* @param anj Anjay Lite instance to operate on.
* @param firmware_version Version string of the current firmware.
* @param endpoint_name The endpoint name for register message.
*
* @return 0 on success, -1 on failure.
*/
int fw_update_object_install(anj_t *anj,
const char *firmware_version,
const char *endpoint_name);
#endif // _FIRMWARE_UPDATE_H_
These are in turn called in the main.c
:
anj_res_value_t firmware_version;
anj_dm_res_read(&anj, &ANJ_MAKE_RESOURCE_PATH(3, 0, 3), &firmware_version);
log(L_INFO, "Firmware version: %s",
(const char *) firmware_version.bytes_or_string.data);
if (fw_update_object_install(
&anj,
(const char *) firmware_version.bytes_or_string.data,
anj.endpoint_name)) {
return -1;
}
while (true) {
anj_core_step(&anj);
fw_update_check();
usleep(50 * 1000);
}
5.2.3. Implementing handlers and installation routine
This tutorial demonstrates a push-based firmware update workflow. The following steps summarize the simplified process.
Create a temporary firmware file:
static anj_dm_fw_update_result_t fu_write_start(void *user_ptr) { firmware_update_t *fu = (firmware_update_t *) user_ptr; assert(fu->firmware_file == NULL); // Ensure previous file is removed if (remove(FW_IMAGE_PATH) != 0 && errno != ENOENT) { log(L_ERROR, "Failed to remove existing firmware image"); return ANJ_DM_FW_UPDATE_RESULT_FAILED; } fu->firmware_file = fopen(FW_IMAGE_PATH, "wb"); if (!fu->firmware_file) { log(L_ERROR, "Failed to open firmware image for writing"); return ANJ_DM_FW_UPDATE_RESULT_FAILED; } log(L_INFO, "Firmware Download started"); return ANJ_DM_FW_UPDATE_RESULT_SUCCESS; }
Write the received firmware chunks to the file:
static anj_dm_fw_update_result_t fu_write(void *user_ptr, const void *data, size_t data_size) { firmware_update_t *fu = (firmware_update_t *) user_ptr; assert(fu->firmware_file != NULL); log(L_INFO, "Writing %lu bytes at offset %lu", data_size, fu->offset); fu->offset += data_size; if (fwrite(data, 1, data_size, fu->firmware_file) != data_size) { log(L_ERROR, "Failed to write firmware chunk"); return ANJ_DM_FW_UPDATE_RESULT_FAILED; } return ANJ_DM_FW_UPDATE_RESULT_SUCCESS; }
static anj_dm_fw_update_result_t fu_write_finish(void *user_ptr) { firmware_update_t *fu = (firmware_update_t *) user_ptr; assert(fu->firmware_file != NULL); if (fclose(fu->firmware_file)) { log(L_ERROR, "Failed to close firmware file"); fu->firmware_file = NULL; return ANJ_DM_FW_UPDATE_RESULT_FAILED; } fu->firmware_file = NULL; fu->offset = 0; log(L_INFO, "Firmware Download finished"); return ANJ_DM_FW_UPDATE_RESULT_SUCCESS; }
Reboot into the new firmware using the system call:
static int fu_update_start(void *user_ptr) { firmware_update_t *fu = (firmware_update_t *) user_ptr; log(L_INFO, "Firmware Update process started"); fu->waiting_for_reboot = true; return 0; }
void fw_update_check(void) { if (firmware_update.waiting_for_reboot) { log(L_INFO, "Rebooting to apply new firmware"); firmware_update.waiting_for_reboot = false; if (chmod(FW_IMAGE_PATH, 0700) == -1) { log(L_ERROR, "Failed to make firmware executable"); return; } FILE *marker = fopen(FW_UPDATED_MARKER, "w"); if (marker) { fclose(marker); } else { log(L_ERROR, "Failed to create update marker"); return; } execl(FW_IMAGE_PATH, FW_IMAGE_PATH, firmware_update.endpoint_name, NULL); log(L_ERROR, "execl() failed"); unlink(FW_UPDATED_MARKER); exit(EXIT_FAILURE); } }
Reminder
The complete definition of the Firmware Update module’s API, including all
required callbacks, auxiliary functions, types, and macros, is available in
the include_public/anj/dm/fw_update.h
header file.
Firmware download is considered complete when a marker file is created. The
fu_update_start()
function only schedules the reboot. The actual reboot
check is performed by fw_update_check()
.
The firmware file used as the Firmware Update input in this example is the
executable binary created during compilation. To prepare a firmware image for
FOTA, change device_obj_conf.firmware_version
in main.c
and recompile
the application.
For additional guidance on managing Firmware Update process from the LwM2M server perspective, refer to How to update firmware on a single device. This resource provides a practical walkthrough of initiating and managing Firmware Update process through the AVSystem Coiote LwM2M Server, and may be helpful when designing server-side logic or integration flows.
Note
This example demonstrates a simplified Firmware Update process. For production environments, you may need to:
Verify firmware integrity before applying the update
Store the firmware image in persistent memory (for example, flash)
Use secure and reliable methods to verify and apply updates
Address platform-specific behaviors appropriately