lib: updatehub: Add UpdateHub.io support

UpdateHub is an enterprise-grade solution which makes simple to
remotely update all your embedded devices in the field. It
handles all aspects related to sending Firmware Over-the-Air(FOTA)
updates with maximum security and efficiency, while you focus in
adding value to your product.

Signed-off-by: Christian Tavares <christian.tavares@ossystems.com.br>
Signed-off-by: Otavio Salvador <otavio@ossystems.com.br>
This commit is contained in:
Christian Tavares 2018-04-23 11:50:01 -03:00 committed by Carles Cufí
commit 297ac3765f
19 changed files with 1724 additions and 0 deletions

View file

@ -254,6 +254,7 @@
/include/sys_io.h @andrewboie @andyross
/include/toolchain.h @andrewboie @andyross @nashif
/include/toolchain/ @andrewboie @andyross
/include/updatehub.h @chtavares592 @otavio
/include/zephyr.h @andrewboie @andyross
/kernel/ @andrewboie @andyross
/lib/gui/ @vanwinkeljan
@ -270,6 +271,8 @@
/samples/basic/minimal/ @carlescufi
/samples/basic/servo_motor/*microbit* @jhe
/samples/bluetooth/ @joerchan @jhedberg @Vudentz
/lib/updatehub/ @chtavares592 @otavio
/samples/bluetooth/ @sjanc @jhedberg @Vudentz
/samples/boards/intel_s1000_crb/ @sathishkuttan @dcpleung @nashif
/samples/display/ @vanwinkeljan
/samples/drivers/CAN/ @alexanderwachter
@ -282,6 +285,8 @@
/samples/net/sockets/coap_*/ @rveerama1
/samples/net/sockets/ @jukkar @tbursztyka @pfalcon
/samples/sensor/ @MaureenHelm
/samples/net/updatehub/ @chtavares592 @otavio
/samples/sensor/ @bogdan-davidoaia
/samples/subsys/logging/ @nordic-krch @jarz-nordic
/samples/subsys/shell/ @jarz-nordic @nordic-krch
/samples/subsys/usb/ @jfischer-phytec-iot @finikorg

76
include/updatehub.h Normal file
View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2018 O.S.Systems
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief UpdateHub Firmware Over-the-Air for Zephyr Project.
* @defgroup updatehub UpdateHub Firmware Over-the-Air
* @ingroup lib
* @{
*/
#ifndef _UPDATEHUB_H_
#define _UPDATEHUB_H_
/**
* @brief Responses messages from UpdateHub.
*
* @details These messages are used to inform the server and the
* user about the process status of the UpdateHub and also
* used to standardize the errors that may occur.
*
*/
enum updatehub_response {
UPDATEHUB_NETWORKING_ERROR = 0,
UPDATEHUB_INCOMPATIBLE_HARDWARE,
UPDATEHUB_UNCONFIRMED_IMAGE,
UPDATEHUB_METADATA_ERROR,
UPDATEHUB_DOWNLOAD_ERROR,
UPDATEHUB_INSTALL_ERROR,
UPDATEHUB_FLASH_INIT_ERROR,
UPDATEHUB_OK,
UPDATEHUB_HAS_UPDATE,
UPDATEHUB_NO_UPDATE,
};
/**
* @brief Runs UpdateHub probe and UpdateHub update automatically.
*
* @details The updatehub_autohandler handles the whole process
* in pre-determined time intervals.
*/
void updatehub_autohandler(void);
/**
* @brief The UpdateHub probe verify if there is some update to be performed.
*
* @return UPDATEHUB_HAS_UPDATE has an update available.
* @return UPDATEHUB_NO_UPDATE no update available.
* @return UPDATEHUB_NETWORKING_ERROR fail to connect to the UpdateHub server.
* @return UPDATEHUB_INCOMPATIBLE_HARDWARE if Incompatible hardware.
* @return UPDATEHUB_METADATA_ERROR fail to parse or to encode the metadata.
*/
enum updatehub_response updatehub_probe(void);
/**
* @brief Apply the update package.
*
* @details Must be used after the UpdateHub probe, if you have updates to
* be made, will perform the installation of the new image and the hardware
* will rebooting.
*
* @return Return UPDATEHUB_OK if success
* @return UPDATEHUB_NETWORKING_ERROR if fail to connect to the server.
* @return UPDATEHUB_DOWNLOAD_ERROR fail while downloading the update package.
* @return UPDATEHUB_INSTALL_ERROR fail while installing the update package.
* @return UPDATEHUB_FLASH_INIT_ERROR fail to initilialize the flash.
*/
enum updatehub_response updatehub_update(void);
/**
* @}
*/
#endif /* _UPDATEHUB_H_ */

View file

@ -8,3 +8,4 @@ add_subdirectory_ifdef(CONFIG_CMSIS_RTOS_V1 cmsis_rtos_v1)
add_subdirectory_ifdef(CONFIG_CMSIS_RTOS_V2 cmsis_rtos_v2)
add_subdirectory(gui)
add_subdirectory(os)
add_subdirectory_ifdef(CONFIG_UPDATEHUB updatehub)

View file

@ -18,4 +18,6 @@ source "lib/os/Kconfig"
source "lib/posix/Kconfig"
source "lib/updatehub/Kconfig"
endmenu

View file

@ -0,0 +1,12 @@
#
# Copyright (c) 2018 O.S.Systems
#
# SPDX -License-Identifier: Apache-2.0
#
zephyr_library()
zephyr_library_sources_ifdef(CONFIG_UPDATEHUB updatehub.c)
zephyr_library_sources_ifdef(CONFIG_UPDATEHUB updatehub_device.c)
zephyr_library_sources_ifdef(CONFIG_UPDATEHUB updatehub_firmware.c)
zephyr_library_sources_ifdef(CONFIG_UPDATEHUB_SHELL shell.c)

85
lib/updatehub/Kconfig Normal file
View file

@ -0,0 +1,85 @@
#
# Copyright (c) 2018 O.S.Systems
#
# SPDX -License-Identifier: Apache-2.0
#
menuconfig UPDATEHUB
bool"UpdateHub Firmware Over-the-Air support"
select FLASH
select REBOOT
select IMG_MANAGER
select BOOTLOADER_MCUBOOT
select MPU_ALLOW_FLASH_WRITE
select NETWORKING
select NEWLIB_LIBC
select NET_UDP
select NET_SOCKETS
select NET_SOCKETS_POSIX_NAMES
select COAP
select NET_CONFIG_NEED_IPV4
select NET_CONFIG_SETTINGS
select DNS_RESOLVER
select JSON_LIBRARY
select TINYCRYPT
select TINYCRYPT_SHA256
select HWINFO
help
UpdateHub is an enterprise-grade solution which makes simple to
remotely update all your embbeded devices in the field. It
handles all aspects related to sending Firmware Over-the-Air
(FOTA) updates with maximum security and efficiency, while you
focus in adding value to your product.
config UPDATEHUB_POLL_INTERVAL
int "Time to poll interval (in minutes)"
default 1440
range 0 43200
depends on UPDATEHUB
help
Set the interval that the UpdateHub update server will be polled.
This time interval is zero and 43200 minutes(30 days).
config UPDATEHUB_PRODUCT_UID
string "Product Unique Identifier (UID)"
depends on UPDATEHUB
help
The product unique identifier is used when communicating
with the UpdateHub server.
config UPDATEHUB_SUPPORTED_HARDWARE_MAX
int "Max number of supported hardware"
default 1
range 1 100
depends on UPDATEHUB
help
Configure the max number of supported hardware
by the same image.
config UPDATEHUB_CE
bool "Use UpdateHub Community Edition Sever"
depends on UPDATEHUB
help
Allow the use of UpdateHub Community
Server (updatehub-ce) as alternative to the
updatehub.io enterprise server.
config UPDATEHUB_SERVER
string "User address for the updatehub-ce-server"
depends on UPDATEHUB_CE
help
This configuration is default, if need to use
other address, must be set on the UpdateHub shell
config UPDATEHUB_SHELL
bool "Enable UpdateHub shell utilities"
depends on UPDATEHUB
depends on SHELL
select KERNEL_SHELL
help
Activate shell module that provides UpdateHub commands like
module = UPDATEHUB
module-str = Log level for UpdateHub
module-help = Enables logging for UpdateHub code.
source "subsys/logging/Kconfig.template.log_config"

83
lib/updatehub/shell.c Normal file
View file

@ -0,0 +1,83 @@
/*
* Copyright (c) 2018 O.S.Systems
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <shell/shell.h>
#include <flash.h>
#include <dfu/mcuboot.h>
#include <dfu/flash_img.h>
#include <updatehub.h>
#include "updatehub_firmware.h"
#include "updatehub_device.h"
#if defined(CONFIG_UPDATEHUB_CE_SERVER)
#define UPDATEHUB_SERVER CONFIG_UPDATEHUB_SERVER
#else
#define UPDATEHUB_SERVER "coap.updatehub.io"
#endif
static int cmd_run(const struct shell *shell, size_t argc,
char **argv)
{
int ret = -1;
shell_fprintf(shell, SHELL_INFO, "Starting UpdateHub run...\n");
switch (updatehub_probe()) {
case UPDATEHUB_HAS_UPDATE:
switch (updatehub_update()) {
case UPDATEHUB_OK:
ret = 0;
break;
default:
shell_fprintf(shell, SHELL_ERROR, "Error installing update.\n");
break;
}
break;
case UPDATEHUB_NO_UPDATE:
shell_fprintf(shell, SHELL_INFO, "No update found\n");
ret = 0;
break;
default:
shell_fprintf(shell, SHELL_ERROR, "Invalid response\n");
break;
}
return ret;
}
static int cmd_info(const struct shell *shell, size_t argc, char **argv)
{
ARG_UNUSED(argc);
ARG_UNUSED(argv);
char *device_id = k_malloc(DEVICE_ID_MAX_SIZE);
char *firmware_version = k_malloc(BOOT_IMG_VER_STRLEN_MAX);
updatehub_get_firmware_version(firmware_version, BOOT_IMG_VER_STRLEN_MAX);
updatehub_get_device_identity(device_id, DEVICE_ID_MAX_SIZE);
shell_fprintf(shell, SHELL_NORMAL, "Unique device id: %s\n",
device_id);
shell_fprintf(shell, SHELL_NORMAL, "Firmware Version: %s\n",
firmware_version);
shell_fprintf(shell, SHELL_NORMAL, "Product uid: %s\n",
CONFIG_UPDATEHUB_PRODUCT_UID);
shell_fprintf(shell, SHELL_NORMAL, "UpdateHub Server: %s\n",
UPDATEHUB_SERVER);
k_free(device_id);
k_free(firmware_version);
return 0;
}
SHELL_STATIC_SUBCMD_SET_CREATE(sub_updatehub, SHELL_CMD(info, NULL, "Dump UpdateHub information",
cmd_info),
SHELL_CMD(run, NULL, "Trigger an UpdateHub update run", cmd_run),
SHELL_SUBCMD_SET_END);
SHELL_CMD_REGISTER(updatehub, &sub_updatehub, "UpdateHub commands", NULL);

768
lib/updatehub/updatehub.c Normal file
View file

@ -0,0 +1,768 @@
/*
* Copyright (c) 2018 O.S.Systems
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <logging/log.h>
LOG_MODULE_REGISTER(updatehub);
#include <zephyr.h>
#include <logging/log_ctrl.h>
#include <net/socket.h>
#include <net/net_mgmt.h>
#include <net/net_ip.h>
#include <net/udp.h>
#include <net/coap.h>
#include <net/dns_resolve.h>
#include <flash.h>
#include <misc/reboot.h>
#include <tinycrypt/sha256.h>
#include <json.h>
#include <updatehub.h>
#include "updatehub_priv.h"
#include "updatehub_firmware.h"
#include "updatehub_device.h"
#define NETWORK_TIMEOUT K_SECONDS(2)
#define UPDATEHUB_POLL_INTERVAL K_MINUTES(CONFIG_UPDATEHUB_POLL_INTERVAL)
#define MAX_PATH_SIZE 255
#define MAX_PAYLOAD_SIZE 500
#define MAX_DOWNLOAD_DATA 1100
#define COAP_MAX_RETRY 3
#define MAX_IP_SIZE 30
#if defined(CONFIG_UPDATEHUB_CE)
#define UPDATEHUB_SERVER CONFIG_UPDATEHUB_SERVER
#else
#define UPDATEHUB_SERVER "coap.updatehub.io"
#endif
static struct updatehub_context {
struct coap_block_context block;
struct k_sem semaphore;
struct flash_img_context flash_ctx;
struct tc_sha256_state_struct sha256sum;
enum updatehub_response code_status;
u8_t uri_path[MAX_PATH_SIZE];
u8_t payload[MAX_PAYLOAD_SIZE];
int downloaded_size;
struct pollfd fds[1];
int sock;
int nfds;
} ctx;
static struct update_info {
char package_uid[TC_SHA256_BLOCK_SIZE + 1];
char sha256sum_image[TC_SHA256_BLOCK_SIZE + 1];
int image_size;
} update_info;
static void wait_fds(void)
{
if (poll(ctx.fds, ctx.nfds, NETWORK_TIMEOUT) < 0) {
LOG_ERR("Error in poll");
}
}
static void prepare_fds(void)
{
ctx.fds[ctx.nfds].fd = ctx.sock;
ctx.fds[ctx.nfds].events = 1;
ctx.nfds++;
}
static int metadata_hash_get(char *metadata)
{
struct tc_sha256_state_struct sha256sum;
unsigned char hash[TC_SHA256_DIGEST_SIZE];
char buffer[3];
int buffer_len = 0;
if (tc_sha256_init(&sha256sum) == 0) {
return -1;
}
if (tc_sha256_update(&sha256sum, metadata, strlen(metadata)) == 0) {
return -1;
}
if (tc_sha256_final(hash, &sha256sum) == 0) {
return -1;
}
memset(update_info.package_uid, 0, TC_SHA256_BLOCK_SIZE + 1);
for (int i = 0; i < TC_SHA256_DIGEST_SIZE; i++) {
snprintk(buffer, sizeof(buffer), "%02x",
hash[i]);
buffer_len = buffer_len + strlen(buffer);
strncat(&update_info.package_uid[i], buffer,
MIN(TC_SHA256_BLOCK_SIZE, buffer_len));
}
return 0;
}
static bool
is_compatible_hardware(struct resp_probe_some_boards *metadata_some_boards)
{
int i;
for (i = 0; i < metadata_some_boards->supported_hardware_len; i++) {
if (strncmp(metadata_some_boards->supported_hardware[i],
CONFIG_BOARD, strlen(CONFIG_BOARD)) == 0) {
return true;
}
}
return false;
}
static bool start_coap_client(void)
{
static struct addrinfo hints = { .ai_family = AF_INET,
.ai_socktype = SOCK_STREAM };
struct addrinfo *addr;
int resolve_attempts = 10;
while (resolve_attempts--) {
if (getaddrinfo(UPDATEHUB_SERVER, "5683", &hints, &addr) == 0) {
break;
}
if (resolve_attempts-- == 0) {
LOG_ERR("Could not resolve dns");
return false;
}
}
ctx.sock = socket(addr->ai_family, SOCK_DGRAM, IPPROTO_UDP);
if (ctx.sock < 0) {
LOG_ERR("Failed to create UDP socket");
return false;
}
if (connect(ctx.sock, addr->ai_addr, addr->ai_addrlen) < 0) {
LOG_ERR("Cannot connect to UDP remote");
return false;
}
prepare_fds();
return true;
}
static void cleanup_conection(void)
{
if (close(ctx.sock) < 0) {
LOG_ERR("Could not close the socket");
}
memset(&ctx.fds[1], 0, sizeof(ctx.fds[1]));
ctx.nfds = 0;
}
static int send_request(enum coap_msgtype msgtype, enum coap_method method,
enum updatehub_uri_path type)
{
struct coap_packet request_packet;
int ret = -1;
u8_t content_application_json = 50;
u8_t *data = k_malloc(MAX_PAYLOAD_SIZE);
if (data == NULL) {
LOG_ERR("Could not alloc data memory");
goto error;
}
ret = coap_packet_init(&request_packet, data, MAX_PAYLOAD_SIZE, 1,
COAP_TYPE_CON, 8, coap_next_token(), method,
coap_next_id());
if (ret < 0) {
LOG_ERR("Could not init packet");
goto error;
}
switch (method) {
case COAP_METHOD_GET:
snprintk(ctx.uri_path, MAX_PATH_SIZE,
"%s/%s/packages/%s/objects/%s", uri_path(type),
CONFIG_UPDATEHUB_PRODUCT_UID, update_info.package_uid,
update_info.sha256sum_image);
ret = coap_packet_append_option(&request_packet,
COAP_OPTION_URI_PATH,
ctx.uri_path,
strlen(ctx.uri_path));
if (ret < 0) {
LOG_ERR("Unable add option to request path");
goto error;
}
ret = coap_append_block2_option(&request_packet,
&ctx.block);
if (ret < 0) {
LOG_ERR("Unable coap append block 2");
goto error;
}
ret = coap_packet_append_option(&request_packet, 2048,
UPDATEHUB_API_HEADER, strlen(UPDATEHUB_API_HEADER));
if (ret < 0) {
LOG_ERR("Unable add option to add updatehub header");
goto error;
}
break;
case COAP_METHOD_POST:
ret = coap_packet_append_option(&request_packet,
COAP_OPTION_URI_PATH,
uri_path(type),
strlen(uri_path(type)));
if (ret < 0) {
LOG_ERR("Unable add option to request path");
goto error;
}
ret = coap_packet_append_option(&request_packet,
COAP_OPTION_CONTENT_FORMAT,
&content_application_json,
sizeof(content_application_json));
if (ret < 0) {
LOG_ERR("Unable add option to request format");
goto error;
}
ret = coap_packet_append_option(&request_packet, 2048,
UPDATEHUB_API_HEADER, strlen(UPDATEHUB_API_HEADER));
if (ret < 0) {
LOG_ERR("Unable add option to add updatehub header");
goto error;
}
ret = coap_packet_append_payload_marker(&request_packet);
if (ret < 0) {
LOG_ERR("Unable to append payload marker");
goto error;
}
ret = coap_packet_append_payload(&request_packet,
&ctx.payload,
strlen(ctx.payload));
if (ret < 0) {
LOG_ERR("Not able to append payload");
goto error;
}
break;
default:
LOG_ERR("Invalid method");
ret = -1;
goto error;
}
ret = send(ctx.sock, request_packet.data, request_packet.offset, 0);
if (ret < 0) {
LOG_ERR("Could not send request");
goto error;
}
error:
k_free(data);
return ret;
}
static void install_update_cb(void)
{
struct coap_packet response_packet;
char buffer[3], sha256_image_dowloaded[TC_SHA256_BLOCK_SIZE + 1];
u8_t *data = k_malloc(MAX_DOWNLOAD_DATA);
int i, buffer_len = 0;
int rcvd = -1;
if (data == NULL) {
LOG_ERR("Could not alloc data memory");
ctx.code_status = UPDATEHUB_DOWNLOAD_ERROR;
goto cleanup;
}
wait_fds();
rcvd = recv(ctx.sock, data, MAX_DOWNLOAD_DATA, MSG_DONTWAIT);
if (rcvd <= 0) {
ctx.code_status = UPDATEHUB_NETWORKING_ERROR;
LOG_ERR("Could not receive data");
goto cleanup;
}
if (coap_packet_parse(&response_packet, data, rcvd, NULL, 0) < 0) {
LOG_ERR("Invalid data received");
ctx.code_status = UPDATEHUB_DOWNLOAD_ERROR;
goto cleanup;
}
ctx.downloaded_size = ctx.downloaded_size +
(response_packet.max_len - response_packet.offset);
if (tc_sha256_update(&ctx.sha256sum,
response_packet.data + response_packet.offset,
response_packet.max_len - response_packet.offset) < 1) {
LOG_ERR("Could not update sha256sum");
ctx.code_status = UPDATEHUB_DOWNLOAD_ERROR;
goto cleanup;
}
if (flash_img_buffered_write(&ctx.flash_ctx,
response_packet.data + response_packet.offset,
response_packet.max_len - response_packet.offset,
ctx.downloaded_size == ctx.block.total_size) < 0) {
LOG_ERR("Error to write on the flash");
ctx.code_status = UPDATEHUB_INSTALL_ERROR;
goto cleanup;
}
if (coap_update_from_block(&response_packet, &ctx.block) < 0) {
ctx.code_status = UPDATEHUB_DOWNLOAD_ERROR;
goto cleanup;
}
if (coap_next_block(&response_packet, &ctx.block) == 0) {
LOG_ERR("Could not get the next");
ctx.code_status = UPDATEHUB_DOWNLOAD_ERROR;
goto cleanup;
}
if (ctx.downloaded_size == ctx.block.total_size) {
u8_t image_hash[TC_SHA256_DIGEST_SIZE];
if (tc_sha256_final(image_hash, &ctx.sha256sum) < 1) {
LOG_ERR("Could not finish sha256sum");
ctx.code_status = UPDATEHUB_DOWNLOAD_ERROR;
goto cleanup;
}
memset(&sha256_image_dowloaded, 0, TC_SHA256_BLOCK_SIZE + 1);
for (i = 0; i < TC_SHA256_DIGEST_SIZE; i++) {
snprintk(buffer, sizeof(buffer), "%02x", image_hash[i]);
buffer_len = buffer_len + strlen(buffer);
strncat(&sha256_image_dowloaded[i], buffer,
MIN(TC_SHA256_BLOCK_SIZE, buffer_len));
}
if (strncmp(sha256_image_dowloaded,
update_info.sha256sum_image,
strlen(update_info.sha256sum_image)) != 0) {
LOG_ERR("SHA256SUM of image are not the same");
ctx.code_status = UPDATEHUB_DOWNLOAD_ERROR;
goto cleanup;
}
}
ctx.code_status = UPDATEHUB_OK;
cleanup:
k_free(data);
}
static enum updatehub_response install_update(void)
{
int verification_download = 0;
int attempts_download = 0;
if (boot_erase_img_bank(DT_FLASH_AREA_IMAGE_1_ID) != 0) {
LOG_ERR("Failed to init flash and erase second slot");
ctx.code_status = UPDATEHUB_FLASH_INIT_ERROR;
goto error;
}
if (tc_sha256_init(&ctx.sha256sum) < 1) {
LOG_ERR("Could not start sha256sum");
ctx.code_status = UPDATEHUB_DOWNLOAD_ERROR;
goto error;
}
if (!start_coap_client()) {
ctx.code_status = UPDATEHUB_NETWORKING_ERROR;
goto error;
}
if (coap_block_transfer_init(&ctx.block, COAP_BLOCK_1024,
update_info.image_size) < 0) {
LOG_ERR("Unable init block transfer");
ctx.code_status = UPDATEHUB_NETWORKING_ERROR;
goto cleanup;
}
flash_img_init(&ctx.flash_ctx);
ctx.downloaded_size = 0;
while (ctx.downloaded_size != ctx.block.total_size) {
verification_download = ctx.downloaded_size;
if (send_request(COAP_TYPE_CON, COAP_METHOD_GET,
UPDATEHUB_DOWNLOAD) < 0) {
ctx.code_status = UPDATEHUB_NETWORKING_ERROR;
goto cleanup;
}
install_update_cb();
if (ctx.code_status != UPDATEHUB_OK) {
goto cleanup;
}
if (verification_download == ctx.downloaded_size) {
if (attempts_download == COAP_MAX_RETRY) {
LOG_ERR("Could not get the packet");
ctx.code_status = UPDATEHUB_DOWNLOAD_ERROR;
goto cleanup;
}
attempts_download++;
}
}
cleanup:
cleanup_conection();
error:
ctx.downloaded_size = 0;
return ctx.code_status;
}
static int report(enum updatehub_state state)
{
struct report report;
int ret = -1;
const char *exec = state_name(state);
char *device_id = k_malloc(DEVICE_ID_MAX_SIZE);
char *firmware_version = k_malloc(BOOT_IMG_VER_STRLEN_MAX);
if (device_id == NULL || firmware_version == NULL) {
LOG_ERR("Could not alloc device_id or firmware_version memory");
goto error;
}
if (!updatehub_get_device_identity(device_id, DEVICE_ID_MAX_SIZE)) {
goto error;
}
if (!updatehub_get_firmware_version(firmware_version, BOOT_IMG_VER_STRLEN_MAX)) {
goto error;
}
memset(&report, 0, sizeof(report));
report.product_uid = CONFIG_UPDATEHUB_PRODUCT_UID;
report.device_identity.id = device_id;
report.version = firmware_version;
report.hardware = CONFIG_BOARD;
report.status = exec;
report.package_uid = update_info.package_uid;
switch (ctx.code_status) {
case UPDATEHUB_INSTALL_ERROR:
report.previous_state =
state_name(UPDATEHUB_STATE_INSTALLING);
break;
case UPDATEHUB_DOWNLOAD_ERROR:
report.previous_state =
state_name(UPDATEHUB_STATE_DOWNLOADING);
break;
case UPDATEHUB_FLASH_INIT_ERROR:
report.previous_state =
state_name(UPDATEHUB_FLASH_INIT_ERROR);
break;
default:
report.previous_state = "";
break;
}
if (strncmp(report.previous_state, "", sizeof("") - 1) != 0) {
report.error_message = updatehub_response(ctx.code_status);
} else {
report.error_message = "";
}
memset(&ctx.payload, 0, MAX_PAYLOAD_SIZE);
ret = json_obj_encode_buf(send_report_descr,
ARRAY_SIZE(send_report_descr),
&report, ctx.payload,
MAX_PAYLOAD_SIZE - 1);
if (ret < 0) {
LOG_ERR("Could not encode metadata");
goto error;
}
if (!start_coap_client()) {
goto error;
}
ret = send_request(COAP_TYPE_NON_CON, COAP_METHOD_POST,
UPDATEHUB_REPORT);
if (ret < 0) {
goto cleanup;
}
wait_fds();
cleanup:
cleanup_conection();
error:
k_free(firmware_version);
k_free(device_id);
return ret;
}
static void probe_cb(char *metadata)
{
struct coap_packet reply;
char tmp[MAX_PAYLOAD_SIZE];
int rcvd = -1;
wait_fds();
rcvd = recv(ctx.sock, metadata, MAX_PAYLOAD_SIZE, MSG_DONTWAIT);
if (rcvd <= 0) {
LOG_ERR("Could not receive data");
ctx.code_status = UPDATEHUB_NETWORKING_ERROR;
return;
}
if (coap_packet_parse(&reply, metadata, rcvd, NULL, 0) < 0) {
LOG_ERR("Invalid data received");
ctx.code_status = UPDATEHUB_DOWNLOAD_ERROR;
return;
}
if (COAP_RESPONSE_CODE_NOT_FOUND == coap_header_get_code(&reply)) {
LOG_INF("No update available");
ctx.code_status = UPDATEHUB_NO_UPDATE;
return;
}
memset(&tmp, 0, MAX_PAYLOAD_SIZE);
memcpy(tmp, reply.data + reply.offset, reply.max_len - reply.offset);
memset(metadata, 0, MAX_PAYLOAD_SIZE);
memcpy(metadata, tmp, strlen(tmp));
ctx.code_status = UPDATEHUB_OK;
LOG_INF("Probe metadata received");
}
enum updatehub_response updatehub_probe(void)
{
struct probe request;
struct resp_probe_some_boards metadata_some_boards;
struct resp_probe_any_boards metadata_any_boards;
char *metadata = k_malloc(MAX_PAYLOAD_SIZE);
char *metadata_copy = k_malloc(MAX_PAYLOAD_SIZE);
char *device_id = k_malloc(DEVICE_ID_MAX_SIZE);
char *firmware_version = k_malloc(BOOT_IMG_VER_STRLEN_MAX);
if (device_id == NULL || firmware_version == NULL ||
metadata == NULL || metadata_copy == NULL) {
LOG_ERR("Could not alloc probe memory");
ctx.code_status = UPDATEHUB_METADATA_ERROR;
goto error;
}
k_sem_init(&ctx.semaphore, 0, 1);
if (!boot_is_img_confirmed()) {
LOG_ERR("The current image is not confirmed");
ctx.code_status = UPDATEHUB_UNCONFIRMED_IMAGE;
goto error;
}
if (!updatehub_get_firmware_version(firmware_version, BOOT_IMG_VER_STRLEN_MAX)) {
ctx.code_status = UPDATEHUB_METADATA_ERROR;
goto error;
}
if (!updatehub_get_device_identity(device_id, DEVICE_ID_MAX_SIZE)) {
ctx.code_status = UPDATEHUB_METADATA_ERROR;
goto error;
}
memset(&request, 0, sizeof(request));
request.product_uid = CONFIG_UPDATEHUB_PRODUCT_UID;
request.device_identity.id = device_id;
request.version = firmware_version;
request.hardware = CONFIG_BOARD;
memset(&ctx.payload, 0, MAX_PAYLOAD_SIZE);
if (json_obj_encode_buf(send_probe_descr,
ARRAY_SIZE(send_probe_descr),
&request, ctx.payload,
MAX_PAYLOAD_SIZE - 1) < 0) {
LOG_ERR("Could not encode metadata");
ctx.code_status = UPDATEHUB_METADATA_ERROR;
goto error;
}
if (!start_coap_client()) {
ctx.code_status = UPDATEHUB_NETWORKING_ERROR;
goto error;
}
if (send_request(COAP_TYPE_CON, COAP_METHOD_POST, UPDATEHUB_PROBE) < 0) {
ctx.code_status = UPDATEHUB_NETWORKING_ERROR;
goto cleanup;
}
memset(metadata, 0, MAX_PAYLOAD_SIZE);
probe_cb(metadata);
if (ctx.code_status != UPDATEHUB_OK) {
goto cleanup;
}
memset(&update_info, 0, sizeof(update_info));
if (metadata_hash_get(metadata) < 0) {
LOG_ERR("Could not get metadata hash");
ctx.code_status = UPDATEHUB_METADATA_ERROR;
goto cleanup;
}
memcpy(metadata_copy, metadata, strlen(metadata));
if (json_obj_parse(metadata, strlen(metadata),
recv_probe_sh_array_descr,
ARRAY_SIZE(recv_probe_sh_array_descr),
&metadata_some_boards) < 0) {
if (json_obj_parse(metadata_copy, strlen(metadata_copy),
recv_probe_sh_string_descr,
ARRAY_SIZE(recv_probe_sh_string_descr),
&metadata_any_boards) < 0) {
LOG_ERR("Could not parse json");
ctx.code_status = UPDATEHUB_METADATA_ERROR;
goto cleanup;
}
memcpy(update_info.sha256sum_image,
metadata_any_boards.objects[1].objects.sha256sum,
strlen(metadata_any_boards.objects[1].objects.sha256sum));
update_info.image_size = metadata_any_boards.objects[1].objects.size;
} else {
if (!is_compatible_hardware(&metadata_some_boards)) {
LOG_ERR("Incompatible hardware");
ctx.code_status =
UPDATEHUB_INCOMPATIBLE_HARDWARE;
goto cleanup;
}
memcpy(update_info.sha256sum_image,
metadata_some_boards.objects[1].objects.sha256sum,
strlen(metadata_some_boards.objects[1]
.objects.sha256sum));
update_info.image_size =
metadata_some_boards.objects[1].objects.size;
}
ctx.code_status = UPDATEHUB_HAS_UPDATE;
cleanup:
cleanup_conection();
error:
k_free(metadata);
k_free(metadata_copy);
k_free(firmware_version);
k_free(device_id);
return ctx.code_status;
}
enum updatehub_response updatehub_update(void)
{
if (report(UPDATEHUB_STATE_DOWNLOADING) < 0) {
LOG_ERR("Could not reporting downloading state");
goto error;
}
if (report(UPDATEHUB_STATE_INSTALLING) < 0) {
LOG_ERR("Could not reporting installing state");
goto error;
}
if (install_update() != UPDATEHUB_OK) {
goto error;
}
if (report(UPDATEHUB_STATE_DOWNLOADED) < 0) {
LOG_ERR("Could not reporting downloaded state");
goto error;
}
if (report(UPDATEHUB_STATE_INSTALLED) < 0) {
LOG_ERR("Could not reporting installed state");
goto error;
}
if (report(UPDATEHUB_STATE_REBOOTING) < 0) {
LOG_ERR("Could not reporting rebooting state");
goto error;
}
LOG_INF("Image flashed successfully, you can reboot now");
return ctx.code_status;
error:
if (ctx.code_status != UPDATEHUB_NETWORKING_ERROR) {
if (report(UPDATEHUB_STATE_ERROR) < 0) {
LOG_ERR("Could not reporting error state");
}
}
return ctx.code_status;
}
static void autohandler(struct k_delayed_work *work)
{
switch (updatehub_probe()) {
case UPDATEHUB_UNCONFIRMED_IMAGE:
LOG_ERR("Image is unconfirmed. Rebooting to revert back to previous"
"confirmed image.");
sys_reboot(SYS_REBOOT_WARM);
break;
case UPDATEHUB_HAS_UPDATE:
switch (updatehub_update()) {
case UPDATEHUB_OK:
sys_reboot(SYS_REBOOT_WARM);
break;
default:
break;
}
break;
case UPDATEHUB_NO_UPDATE:
break;
default:
break;
}
k_delayed_work_submit(work, UPDATEHUB_POLL_INTERVAL);
}
void updatehub_autohandler(void)
{
static struct k_delayed_work work;
k_delayed_work_init(&work, autohandler);
k_delayed_work_submit(&work, 0);
}

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2018 O.S.Systems
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "updatehub_device.h"
bool updatehub_get_device_identity(char *id, int id_max_len)
{
int i, id_len = 0, buf_len = 0;
char buf[3];
u8_t hwinfo_id[id_max_len];
size_t length;
length = hwinfo_get_device_id(hwinfo_id, sizeof(hwinfo_id) - 1);
if (length <= 0) {
return false;
}
memset(id, 0, id_max_len);
for (i = 0; i < length; i++) {
snprintk(buf, sizeof(buf), "%02x", hwinfo_id[i]);
id_len = strlen(id);
strncat(id, buf, id_max_len - id_len);
}
return true;
}

View file

@ -0,0 +1,17 @@
/*
* Copyright (c) 2018 O.S.Systems
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __UPDATEHUB_DEVICE_H__
#define __UPDATEHUB_DEVICE_H__
#include <zephyr.h>
#include <hwinfo.h>
#define DEVICE_ID_MAX_SIZE 65
bool updatehub_get_device_identity(char *id, int id_max_len);
#endif /* __UPDATEHUB_DEVICE_H__ */

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2018 O.S.Systems
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "updatehub_firmware.h"
bool updatehub_get_firmware_version(char *version, int version_len)
{
struct mcuboot_img_header header;
if (boot_read_bank_header(DT_FLASH_AREA_IMAGE_0_ID, &header,
version_len) != 0) {
return false;
}
snprintk(version, version_len, "%d.%d.%d",
header.h.v1.sem_ver.major,
header.h.v1.sem_ver.minor,
header.h.v1.sem_ver.revision);
return true;
}

View file

@ -0,0 +1,16 @@
/*
* Copyright (c) 2018 O.S.Systems
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __UPDATEHUB_FIRMWARE_H__
#define __UPDATEHUB_FIRMWARE_H__
#include <flash.h>
#include <dfu/mcuboot.h>
#include <dfu/flash_img.h>
bool updatehub_get_firmware_version(char *version, int version_len);
#endif /* __UPDATEHUB_FIRMWARE_H__ */

View file

@ -0,0 +1,222 @@
/*
* Copyright (c) 2018 O.S.Systems
*
* SPDX-License-Identifier: Apache-2.0
*/
/** @file
*
* @brief This file contains structures representing JSON messages
* exchanged with a UpdateHub
*/
#ifndef __UPDATEHUB_PRIV_H__
#define __UPDATEHUB_PRIV_H__
#define UPDATEHUB_API_HEADER \
"Api-Content-Type: application/vnd.updatehub-v1+json"
enum updatehub_uri_path {
UPDATEHUB_PROBE = 0,
UPDATEHUB_REPORT,
UPDATEHUB_DOWNLOAD,
};
enum updatehub_state {
UPDATEHUB_STATE_DOWNLOADING = 0,
UPDATEHUB_STATE_DOWNLOADED,
UPDATEHUB_STATE_INSTALLING,
UPDATEHUB_STATE_INSTALLED,
UPDATEHUB_STATE_REBOOTING,
UPDATEHUB_STATE_ERROR,
};
static char *updatehub_response(enum updatehub_response response)
{
switch (response) {
case UPDATEHUB_NETWORKING_ERROR:
return "Fail to connect to the UpdateHub server";
case UPDATEHUB_INCOMPATIBLE_HARDWARE:
return "Incompatible hardware";
case UPDATEHUB_METADATA_ERROR:
return "Fail to parse or to encode the metadata";
case UPDATEHUB_DOWNLOAD_ERROR:
return "Fail while downloading the update package";
case UPDATEHUB_INSTALL_ERROR:
return "Fail while installing the update package";
case UPDATEHUB_FLASH_INIT_ERROR:
return "Fail to initilialize the flash";
case UPDATEHUB_NO_UPDATE:
return "No update available";
default:
return NULL;
}
}
static const char *uri_path(enum updatehub_uri_path type)
{
switch (type) {
case UPDATEHUB_PROBE:
return "upgrades";
case UPDATEHUB_REPORT:
return "report";
case UPDATEHUB_DOWNLOAD:
return "products";
default:
return NULL;
}
}
static const char *state_name(enum updatehub_state state)
{
switch (state) {
case UPDATEHUB_STATE_DOWNLOADING:
return "downloading";
case UPDATEHUB_STATE_DOWNLOADED:
return "downloaded";
case UPDATEHUB_STATE_INSTALLING:
return "installing";
case UPDATEHUB_STATE_INSTALLED:
return "installed";
case UPDATEHUB_STATE_REBOOTING:
return "rebooting";
case UPDATEHUB_STATE_ERROR:
return "error";
default:
return NULL;
}
}
struct resp_probe_objects {
const char *mode;
const char *sha256sum;
int size;
};
struct resp_probe_objects_array {
struct resp_probe_objects objects;
};
struct resp_probe_any_boards {
struct resp_probe_objects_array objects[2];
size_t objects_len;
const char *product;
const char *supported_hardware;
};
struct resp_probe_some_boards {
struct resp_probe_objects_array objects[2];
size_t objects_len;
const char *product;
const char *supported_hardware[CONFIG_UPDATEHUB_SUPPORTED_HARDWARE_MAX];
size_t supported_hardware_len;
};
struct updatehub_config_device_identity {
const char *id;
};
struct report {
const char *product_uid;
const char *hardware;
const char *version;
struct updatehub_config_device_identity device_identity;
const char *status;
const char *package_uid;
const char *error_message;
const char *previous_state;
};
struct probe {
const char *product_uid;
const char *hardware;
const char *version;
struct updatehub_config_device_identity device_identity;
};
static const struct json_obj_descr recv_probe_objects_descr[] = {
JSON_OBJ_DESCR_PRIM(struct resp_probe_objects,
mode, JSON_TOK_STRING),
JSON_OBJ_DESCR_PRIM(struct resp_probe_objects,
sha256sum, JSON_TOK_STRING),
JSON_OBJ_DESCR_PRIM(struct resp_probe_objects,
size, JSON_TOK_NUMBER),
};
static const struct json_obj_descr recv_probe_objects_descr_array[] = {
JSON_OBJ_DESCR_OBJECT(struct resp_probe_objects_array,
objects, recv_probe_objects_descr),
};
static const struct json_obj_descr recv_probe_sh_string_descr[] = {
JSON_OBJ_DESCR_PRIM(struct resp_probe_any_boards,
product, JSON_TOK_STRING),
JSON_OBJ_DESCR_PRIM_NAMED(struct resp_probe_any_boards,
"supported-hardware", supported_hardware,
JSON_TOK_STRING),
JSON_OBJ_DESCR_ARRAY_ARRAY(struct resp_probe_any_boards,
objects, 2, objects_len,
recv_probe_objects_descr_array,
ARRAY_SIZE(recv_probe_objects_descr_array)),
};
static const struct json_obj_descr recv_probe_sh_array_descr[] = {
JSON_OBJ_DESCR_PRIM(struct resp_probe_some_boards,
product, JSON_TOK_STRING),
JSON_OBJ_DESCR_ARRAY_NAMED(struct resp_probe_some_boards,
"supported-hardware", supported_hardware,
CONFIG_UPDATEHUB_SUPPORTED_HARDWARE_MAX,
supported_hardware_len, JSON_TOK_STRING),
JSON_OBJ_DESCR_ARRAY_ARRAY(struct resp_probe_some_boards,
objects, 2, objects_len,
recv_probe_objects_descr_array,
ARRAY_SIZE(recv_probe_objects_descr_array)),
};
static const struct json_obj_descr device_identity_descr[] = {
JSON_OBJ_DESCR_PRIM(struct updatehub_config_device_identity,
id, JSON_TOK_STRING),
};
static const struct json_obj_descr send_report_descr[] = {
JSON_OBJ_DESCR_PRIM_NAMED(struct report,
"product-uid", product_uid,
JSON_TOK_STRING),
JSON_OBJ_DESCR_OBJECT_NAMED(struct report,
"device-identity", device_identity,
device_identity_descr),
JSON_OBJ_DESCR_PRIM_NAMED(struct report,
"error-message", error_message,
JSON_TOK_STRING),
JSON_OBJ_DESCR_PRIM_NAMED(struct report,
"previous-state", previous_state,
JSON_TOK_STRING),
JSON_OBJ_DESCR_PRIM(struct report,
version, JSON_TOK_STRING),
JSON_OBJ_DESCR_PRIM(struct report,
hardware, JSON_TOK_STRING),
JSON_OBJ_DESCR_PRIM_NAMED(struct report,
"package-uid", package_uid,
JSON_TOK_STRING),
JSON_OBJ_DESCR_PRIM(struct report,
status, JSON_TOK_STRING),
};
static const struct json_obj_descr send_probe_descr[] = {
JSON_OBJ_DESCR_PRIM_NAMED(struct probe,
"product-uid", product_uid,
JSON_TOK_STRING),
JSON_OBJ_DESCR_OBJECT_NAMED(struct probe,
"device-identity", device_identity,
device_identity_descr),
JSON_OBJ_DESCR_PRIM(struct probe,
version, JSON_TOK_STRING),
JSON_OBJ_DESCR_PRIM(struct probe,
hardware, JSON_TOK_STRING),
};
/**
* @}
*/
#endif /* __UPDATEHUB_PRIV_H__ */

View file

@ -0,0 +1,13 @@
# Copyright (c) 2018 O.S.Systems
#
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.13.1)
# This application has its own Kconfig options.
set(KCONFIG_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/Kconfig)
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
project(NONE)
target_sources(app PRIVATE src/main.c)

View file

@ -0,0 +1,25 @@
#
# Copyright (c) 2018 O.S.Systems
#
# SPDX-License-Identifier: Apache-2.0
#
mainmenu "Sample app Configuration"
config UPDATEHUB_MANUAL
bool "UpdateHub manual mode"
help
Manual mode requires the user to call the server probe and then, if
there is an available update, also requires the user to decide if
it is appropriate to update now or later.
if !UPDATEHUB_MANUAL
config UPDATEHUB_POLLING
bool "UpdateHub polling mode"
default y
help
Polling mode runs automatically on a predefined period, probing the
server for updates and installing them without requiring user
intervention.
endif #!UPDATEHUB_MANUAL
source "$ZEPHYR_BASE/Kconfig.zephyr"

View file

@ -0,0 +1,246 @@
UpdateHub sample
################
Overview
********
UpdateHub is an enterprise-grade solution which makes it simple to remotely
update all your embedded devices. It handles all aspects
related to sending Firmware Over-the-Air (FOTA) updates with maximum
security and efficiency, while you focus on adding value to your product.
It is possible to read more about at `docs.updatehub.io`_.
This sample shows how to use UpdateHub in both a polling and manual update
mode.
Polling mode runs automatically on a predefined period, probing the server
for updates and installing them without requiring user intervention. You
can access the sample source code for this mode updatehub_polling.
Manual mode requires the user to call the server probe and then, if there is
an available update, also requires the user to decide if it is appropriate to
update now or later. You can access the sample source code for this mode
updatehub_manual.
Caveats
*******
* The Zephyr port of ``UpdateHub`` is configured to run on a :ref:`Freedom-K64F <frdm_k64f>`
MCU by default. The application should build and run for other platforms
with offer support internet connection. Some platforms need some modification.
Overlay files would be needed to support BLE 6lowpan, 802.15.4 or OpenThread
configurations as well as the understanding that most other connectivity
options would require an edge gateway of some sort (Border Router, etc).
* The MCUboot bootloader is required for ``UpdateHub`` to function
properly. More information about the Device Firmware Upgrade subsystem and
MCUboot can be found in :ref:`mcuboot`.
Building and Running
********************
The below steps describe how to build and run the ``UpdateHub`` sample in
Zephyr. Where examples are given, it is assumed the sample is being built for
the Freedom-K64F Development Kit (``BOARD=fdrm_k64f``).
Step 1: Build MCUboot
=====================
Build MCUboot by following the instructions in the :ref:`mcuboot` documentation
page.
Step 2: Flash MCUboot
=====================
Flash the resulting image file to the 0x0 address of the flash memory. This can
be done in multiple ways, but the most common ones would be using make or ninja:
.. code-block:: console
make flash
# or
ninja flash
Step 3: Start the updatehub Community Edition
=============================================
By default, the updatehub application is set to start on the UpdateHub Cloud.
For more details on how to use the UpdateHub Cloud please refer to the
documentation on `updatehub.io`_.
If you would like to use your own server, the steps below explain how
updatehub works with updatehub-ce running, started by the
following Docker command:
.. code-block:: console
docker run -it -p 8080:8080 -p 5683:5683/udp --rm updatehub/updatehub-ce:latest
Using this server the user need create own ``overaly-prj.conf`` setting the option
:option:`CONFIG_UPDATEHUB_SERVER` with your local ip address and the option
:option:`CONFIG_UPDATEHUB_CE` with true. If the user will use polling mode on
UpdateHub need too set the option :option:`CONFIG_UPDATEHUB_POLL_INTERVAL` with the period of
your preference, remembering that the limit is between 0 minute until 43200 minutes(30 days).
Step 4: Build UpdateHub
=======================
``UpdateHub`` can be built for the fdrm_k64f as follows:
.. zephyr-app-commands::
:zephyr-app: samples/net/updatehub/
:board: fdrm_k64f
:conf: "prj.conf overlay-prj.conf"
:goals: build
.. _updatehub_sample_sign:
Step 5: Sign the first image
============================
From this section onwards you use a binary (``.bin``) image format.
Using MCUboot's :file:`imgtool.py` script, sign the :file:`zephyr.bin`
file you built in Step 3. In the below example, the MCUboot repo is located at
:file:`~/src/mcuboot`.
.. code-block:: console
~/src/mcuboot/scripts/imgtool.py sign \
--key ~/src/mcuboot/root-rsa-2048.pem \
--align 8 \
--version 1.0.0 \
--header-size 0x200 \
--slot-size <image-slot-size> \
--pad \
<path-to-zephyr.bin> signed.bin
The command above creates an image file called :file:`signed.bin` in the
current directory.
Step 6: Flash the first image
=============================
Upload the :file:`signed.bin` file from Step 4 to image slot-0 of your
board. The location of image slot-0 varies by board, as described in
:ref:`mcuboot_partitions`. For the fdrm_k64f, slot-0 is located at address
``0xc000``.
Using :file:`pyocd` you don't need to specify the slot-0 starting address.
.. code-block:: console
sudo pyocd-flashtool <path-to-signed.bin>
Step 7: Signing the test image
==============================
For the update to be correctly validated on the server, you must need sign the
(``bin``) image, piping the output to another file.
.. code-block:: console
~/src/mcuboot/scripts/imgtool.py sign \
--key ~/src/mcuboot/root-rsa-2048.pem \
--align 8 \
--version 2.0.0 \
--header-size 0x200 \
--slot-size <image-slot-size> \
--pad \
<path-to-zephyr.bin> signed_v2.bin
Step 8: Create a package with UpdateHub Utilities (uhu)
=======================================================
First, install UpdateHub Utilities (``uhu``) on your system, using:
.. code-block:: console
pip3 install --user uhu
After installing uhu you will need to set the ``product-uid``:
.. code-block:: console
uhu product use "e4d37cfe6ec48a2d069cc0bbb8b078677e9a0d8df3a027c4d8ea131130c4265f"
Then, add the package and its mode (``zephyr``):
.. code-block:: console
uhu package add signed_v2.bin -m zephyr
Then inform what ``version`` this image is:
.. code-block:: console
uhu package version 2.0.0
And finally you can build the package by running:
.. code-block:: console
uhu package archive --output <name-of-package>.pkg
Step 9: Add the package to server
==================================
Now, add the package to the updatehub-ce by, opening your browser to
the server URL, ``<your-ip-address>:8080``, and logging into the server using
``admin`` as the login and password by default. After logging in, click on
the package menu, then ``UPLOAD PACKAGE``, and select the package built in
step 7.
Step 10: Register device on server
==================================
Register your device at updatehub-ce by using a terminal session on
the system where you were debugging the board, and type the following command:
.. code-block:: console
updatehub run
If everything is alright, it will print on the screen ``No update available``.
Step 11: Create a rollout
=========================
In the browser where the updatehub-ce is open, click on ``menu Rollout``
and then ``CREATE ROLLOUT``. Select the version of the package that you added
in step 9. With that, the update is published, and the server is ready to
accept update requests.
Step 12: Run the update
=======================
Back in the terminal session that you used for debugging the board, type the
following command:
.. code-block:: console
updatehub run
And then wait. The board will ping the server, check if there are any new
updates, and then download the update package you've just created. If
everything goes fine the message ``Image flashed successfully, you can reboot
now`` will be printed on the terminal.
Step 13: Reboot the system
==========================
In the terminal you used for debugging the board, type the following command:
.. code-block:: console
kernel reboot cold
Your board will reboot and then start with the new image. After rebooting the
board will automatically ping the server again and the message ``No update
available`` will be printed on the terminal.
.. _updatehub.io: https://updatehub.io
.. _docs.updatehub.io: https://docs.updatehub.io/

View file

@ -0,0 +1,21 @@
#
# Copyright (c) 2018 O.S.Systems
#
# SPDX-License-Identifier: Apache-2.0
#
#Example of product_uid, you must set you product uid.
CONFIG_UPDATEHUB_PRODUCT_UID="e4d37cfe6ec48a2d069cc0bbb8b078677e9a0d8df3a027c4d8ea131130c4265f"
#If you use the autorandler, need set UPDATEHUB_POLL_INTERVAL.
#This example, the updatehub will poll on the server
#request for updates one to one minute.
#(min: 0 minute, máx: 30 days)
CONFIG_UPDATEHUB_POLL_INTERVAL=1
CONFIG_UPDATEHUB_CE=y
#You need set the updatehub-ce ip Address.
#This is a example of ip address
CONFIG_UPDATEHUB_SERVER="10.5.3.67"

View file

@ -0,0 +1,16 @@
# Turn on the updatehub
CONFIG_UPDATEHUB=y
#Minimal Heap mem pool size for the updatehub working
CONFIG_HEAP_MEM_POOL_SIZE=2048
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
CONFIG_NET_DHCPV4=y
#Optional if you would like test on the your server
CONFIG_SHELL=y
CONFIG_UPDATEHUB_SHELL=y
# Debug helpers
CONFIG_LOG=y
CONFIG_UPDATEHUB_LOG_LEVEL_INF=y

View file

@ -0,0 +1,63 @@
/*
* Copyright (c) 2018 O.S.Systems
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <updatehub.h>
#include <dfu/mcuboot.h>
#include <misc/printk.h>
#include <logging/log.h>
LOG_MODULE_REGISTER(main);
int main(void)
{
int ret = -1;
LOG_INF("UpdateHub sample app started");
/* The image of application needed be confirmed */
LOG_INF("Confirming the boot image");
ret = boot_write_img_confirmed();
if (ret < 0) {
LOG_ERR("Error to confirm the image");
}
#if defined(CONFIG_UPDATEHUB_POLLING)
LOG_INF("Starting UpdateHub polling mode");
updatehub_autohandler();
#endif
#if defined(CONFIG_UPDATEHUB_MANUAL)
LOG_INF("Starting UpdateHub manual mode");
enum updatehub_response resp;
switch (updatehub_probe()) {
case UPDATEHUB_HAS_UPDATE:
switch (updatehub_update()) {
case UPDATEHUB_OK:
ret = 0;
sys_reboot(SYS_REBOOT_WARM);
break;
default:
LOG_ERR("Error installing update.");
break;
}
case UPDATEHUB_NO_UPDATE:
LOG_INF("No update found");
ret = 0;
break;
default:
LOG_ERR("Invalid response");
break;
}
#endif
return ret;
}