From 4ffff67508690c84e4dc1f1fc4371c110832ad00 Mon Sep 17 00:00:00 2001 From: Maximilian Deubel Date: Thu, 2 May 2024 16:13:40 +0200 Subject: [PATCH] wip: fota --- firmware/CMakeLists.txt | 1 + firmware/Kconfig | 1 + firmware/prj.conf | 3 + firmware/src/common/message_channel.c | 4 +- firmware/src/modules/fota/CMakeLists.txt | 7 +++ firmware/src/modules/fota/Kconfig.fota | 18 ++++++ firmware/src/modules/fota/fota.c | 80 ++++++++++++++++++++++++ firmware/src/modules/location/location.c | 6 +- 8 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 firmware/src/modules/fota/CMakeLists.txt create mode 100644 firmware/src/modules/fota/Kconfig.fota create mode 100644 firmware/src/modules/fota/fota.c diff --git a/firmware/CMakeLists.txt b/firmware/CMakeLists.txt index a9e0258b..5397a0df 100644 --- a/firmware/CMakeLists.txt +++ b/firmware/CMakeLists.txt @@ -20,6 +20,7 @@ add_subdirectory(src/modules/transport) add_subdirectory(src/modules/error) add_subdirectory(src/modules/location) add_subdirectory(src/modules/app) +add_subdirectory(src/modules/fota) # Optional modules add_subdirectory_ifdef(CONFIG_APP_LED src/modules/led) diff --git a/firmware/Kconfig b/firmware/Kconfig index e0fbd89f..1d97f6e5 100644 --- a/firmware/Kconfig +++ b/firmware/Kconfig @@ -20,6 +20,7 @@ rsource "src/modules/error/Kconfig.error" rsource "src/modules/location/Kconfig.location" rsource "src/modules/led/Kconfig.led" rsource "src/modules/app/Kconfig.app" +rsource "src/modules/fota/Kconfig.fota" endmenu diff --git a/firmware/prj.conf b/firmware/prj.conf index b9ec2f11..0d9e2cb9 100644 --- a/firmware/prj.conf +++ b/firmware/prj.conf @@ -91,6 +91,9 @@ CONFIG_MPU_ALLOW_FLASH_WRITE=y CONFIG_NRF_CLOUD_ALERT=y CONFIG_NRF_CLOUD_LOG_DIRECT=y CONFIG_NRF_CLOUD_LOG_OUTPUT_LEVEL=3 +CONFIG_NRF_CLOUD_FOTA_POLL=y +CONFIG_FOTA_DOWNLOAD=y +CONFIG_DFU_TARGET=y # These options are required because the defaults will result in errors (cool..) CONFIG_COAP_CLIENT_THREAD_PRIORITY=0 diff --git a/firmware/src/common/message_channel.c b/firmware/src/common/message_channel.c index 92c0fda5..fde95c43 100644 --- a/firmware/src/common/message_channel.c +++ b/firmware/src/common/message_channel.c @@ -14,7 +14,7 @@ ZBUS_CHAN_DEFINE(TRIGGER_CHAN, /* Name */ int, /* Message type */ NULL, /* Validator */ NULL, /* User data */ - ZBUS_OBSERVERS(sampler, app, location), /* Observers */ + ZBUS_OBSERVERS(fota, sampler, app, location), /* Observers */ ZBUS_MSG_INIT(0) /* Initial value {0} */ ); @@ -54,6 +54,6 @@ ZBUS_CHAN_DEFINE(CLOUD_CHAN, enum cloud_status, NULL, NULL, - ZBUS_OBSERVERS(app, location), + ZBUS_OBSERVERS(fota, app, location), CLOUD_DISCONNECTED ); diff --git a/firmware/src/modules/fota/CMakeLists.txt b/firmware/src/modules/fota/CMakeLists.txt new file mode 100644 index 00000000..7551f94f --- /dev/null +++ b/firmware/src/modules/fota/CMakeLists.txt @@ -0,0 +1,7 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/fota.c) diff --git a/firmware/src/modules/fota/Kconfig.fota b/firmware/src/modules/fota/Kconfig.fota new file mode 100644 index 00000000..7a49b5e7 --- /dev/null +++ b/firmware/src/modules/fota/Kconfig.fota @@ -0,0 +1,18 @@ +# +# Copyright (c) 20224 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +menuconfig APP_FOTA + bool "FOTA" + select FOTA + default y if !BOARD_NATIVE_POSIX + +if APP_FOTA + +module = APP_FOTA +module-str = FOTA +source "subsys/logging/Kconfig.template.log_config" + +endif # APP_FOTA diff --git a/firmware/src/modules/fota/fota.c b/firmware/src/modules/fota/fota.c new file mode 100644 index 00000000..f1e52825 --- /dev/null +++ b/firmware/src/modules/fota/fota.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "message_channel.h" + +/* Register log module */ +LOG_MODULE_REGISTER(fota, CONFIG_APP_FOTA_LOG_LEVEL); + +/* FOTA support context */ +static void fota_reboot(enum nrf_cloud_fota_reboot_status status); +static struct nrf_cloud_fota_poll_ctx ctx = { + .reboot_fn = fota_reboot +}; + +void fota_reboot(enum nrf_cloud_fota_reboot_status status) +{ + LOG_INF("Rebooting with FOTA status %d", status); + + /* TODO: disconnect from network? */ + + /* Flush the logging buffers */ + LOG_PANIC(); + + /* Reboot the device */ + sys_reboot(SYS_REBOOT_COLD); +} + + +void fota_callback(const struct zbus_channel *chan) +{ + int err = 0; + + if (&CLOUD_CHAN == chan) + { + enum cloud_status status = CLOUD_DISCONNECTED; + int err = zbus_chan_read(chan, &status, K_NO_WAIT); + + if (!err && status == CLOUD_CONNECTED) + { + err = nrf_cloud_fota_poll_init(&ctx); + if (err) { + LOG_ERR("nrf_cloud_fota_poll_init failed: %d", err); + return; + /* TODO: can we recover from this? */ + } + + /* Process pending FOTA job, the FOTA type is returned */ + err = nrf_cloud_fota_poll_process_pending(&ctx); + if (err < 0) { + LOG_ERR("nrf_cloud_fota_poll_process_pending failed: %d", err); + } else if (err != NRF_CLOUD_FOTA_TYPE__INVALID) { + LOG_INF("Processed pending FOTA job type: %d", err); + } + } + } + if (&TRIGGER_CHAN == chan) + { + err = nrf_cloud_fota_poll_process(&ctx); + if (err && err != -EAGAIN) { + LOG_ERR("nrf_cloud_fota_poll_process failed: %d", err); + } + } +} + + +/* Register listener - led_callback will be called everytime a channel that the module listens on + * receives a new message. + */ +ZBUS_LISTENER_DEFINE(fota, fota_callback); diff --git a/firmware/src/modules/location/location.c b/firmware/src/modules/location/location.c index 2af13d62..4163adc9 100644 --- a/firmware/src/modules/location/location.c +++ b/firmware/src/modules/location/location.c @@ -139,9 +139,9 @@ static void location_event_handler(const struct location_event_data *event_data) { case LOCATION_EVT_LOCATION: LOG_DBG("Got location: lat: %f, lon: %f, acc: %f", - event_data->location.latitude, - event_data->location.longitude, - event_data->location.accuracy); + (double) event_data->location.latitude, + (double) event_data->location.longitude, + (double) event_data->location.accuracy); /* GNSS location needs to be reported manually */ if (event_data->method == LOCATION_METHOD_GNSS) {