El ejemplo ./demos/fota_multi_file_demo.c muestra cómo un dispositivo descarga un paquete de actualización OTA de varios archivos mediante HTTPS y ejecuta una actualización OTA.
Contexto
Para obtener más información sobre la función de actualización OTA, consulte OTA update overview.
La función de actualización OTA se basa en una conexión MQTT. Para obtener más información sobre el código de una conexión MQTT, consulte MQTT connection.
-
En comparación con Sample 1
./demo/fota_posix_demo.c), este ejemplo solo difiere en las siguientes secciones:
Solo los Link SDK de C descargados después del 9 de septiembre de 2021 admiten paquetes de actualización OTA de varios archivos.
Para obtener más información sobre cómo obtener el SDK, consulte Obtain C Link SDK.
Paso 1: Inicializar la función OTA
-
Agregue el archivo de encabezado.
…… …… #include "aiot_ota_api.h" …… -
Configure las dependencias subyacentes y la salida de registros.
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile); aiot_state_set_logcb(demo_state_logcb); -
Llame a aiot_ota_init para crear una instancia de OTA.
ota_handle = aiot_ota_init(); if (NULL == ota_handle) { printf("aiot_ota_init failed\r\n"); aiot_mqtt_deinit(&mqtt_handle); return -2; }
Paso 2: Configurar la función OTA
Llame a aiot_ota_setopt para configurar las siguientes opciones.
-
Asocie el identificador de la conexión MQTT.
ImportanteAntes de configurar los parámetros de OTA,
debe configurar parámetros como las credenciales del dispositivo. Para obtener más información, consulte
Configure connection parameters for MQTT
.
-
aiot_ota_setopt(ota_handle, AIOT_OTAOPT_MQTT_HANDLE, mqtt_handle); -
Elemento de configuración
Ejemplo
Descripción
AIOT_OTAOPT_MQTT_HANDLE
mqtt_handle
Las solicitudes de la función OTA se basan en conexiones MQTT. Este elemento de configuración asocia el identificador de la conexión MQTT.
-
-
Configure la función callback para los mensajes de instrucción de actualización OTA.
-
aiot_ota_setopt(ota_handle, AIOT_OTAOPT_RECV_HANDLER, demo_ota_recv_handler); -
Elemento de configuración
Ejemplo
Descripción
AIOT_OTAOPT_MQTT_HANDLER
demo_ota_recv_handler
Cuando el dispositivo recibe una instrucción de actualización OTA desde IoT Platform, se llama a esta función callback.
-
Paso 3: Notificar la versión actual del dispositivo
Después de que el dispositivo establece una conexión MQTT, llame a aiot_ota_report_version para notificar el número de versión actual del dispositivo. IoT Platform determina si es necesaria una actualización según el número de versión.
En el siguiente código de ejemplo, el número de versión que notifica el dispositivo antes de la actualización OTA es 1.0.0. En su aplicación, debe obtener el número de versión real del área de configuración del dispositivo y modificar el código en consecuencia.
El dispositivo debe notificar su número de versión como mínimo una vez antes de una actualización OTA.
cur_version = "1.0.0";
res = aiot_ota_report_version(ota_handle, cur_version);
if (res < STATE_SUCCESS) {
printf("aiot_ota_report_version failed: -0x%04X\r\n", -res);
}
Paso 4: Recibir instrucciones de actualización
-
Después de agregar un paquete de actualización e iniciar una tarea de actualización en la consola de IoT Platform, IoT Platform envía una instrucción de actualización al dispositivo.
El dispositivo llama a aiot_mqtt_recv para recibir mensajes. Cuando un mensaje se identifica como una instrucción de actualización OTA, se llama a la función callback
demo_ota_recv_handler.-
Escriba la lógica de procesamiento para la función callback.
Puede usar la siguiente información para escribir la lógica de procesamiento de la función callback:
-
IoT Platform envía instrucciones de paquetes de actualización OTA al dispositivo a través del topic
/ota/device/upgrade/${ProductKey}/${DeviceName}.Para obtener más información sobre ${ProductKey} y ${DeviceName}, consulte Obtain device credentials.
-
El tipo de la instrucción de actualización OTA es AIOT_OTARECV_FOTA.
void demo_ota_recv_handler(void *ota_handle, aiot_ota_recv_t *ota_msg, void *userdata) { switch (ota_msg->type) { case AIOT_OTARECV_FOTA: { uint32_t res = 0; uint16_t port = 443; uint32_t max_buffer_len = (8 * 1024); aiot_sysdep_network_cred_t cred; void *dl_handle = NULL; multi_download_status_t *download_status = NULL; if (NULL == ota_msg->task_desc) { break; } …… …… } El tipo de estructura de datos para los mensajes de instrucción de actualización OTA es aiot_ota_recv_t. Link SDK analiza sintácticamente de forma automática los mensajes de instrucción de actualización recibidos.
Puede consultar el código de ejemplo para escribir la lógica de procesamiento de la función callback. Para obtener más información, consulte Step 5: Download the update package and perform an OTA update.
-
Paso 5: Descargar el paquete de actualización y ejecutar una actualización OTA
El dispositivo no descarga automáticamente el paquete de actualización después de recibir el mensaje del paquete de actualización desde IoT Platform. Debe llamar a una API de Link SDK para iniciar la descarga.
Cuando se activa la función demo_ota_recv_handler, el descargador inicia una solicitud de descarga mediante HTTPS para descargar el paquete de actualización y ejecutar la actualización OTA.
-
Inicialice el descargador.
Llame a
para crear una sesión de
descarga
NotaLas actualizaciones OTA admiten paquetes de actualización de varios archivos. Para obtener más información, consulte Add an update package.
dl_handle = aiot_download_init(); if (NULL == dl_handle) { break; } if (NULL != ota_msg->task_desc->file_name) { printf("\r\nTotal file number is %d, current file id is %d, with file_name %s\r\n", ota_msg->task_desc->file_num, ota_msg->task_desc->file_id, ota_msg->task_desc->file_name); } printf("OTA target firmware version: %s, size: %u Bytes \r\n", ota_msg->task_desc->version, ota_msg->task_desc->size_total); if (NULL != ota_msg->task_desc->extra_data) { printf("extra data: %s\r\n", ota_msg->task_desc->extra_data); } memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t)); cred.option = AIOT_SYSDEP_NETWORK_CRED_SVRCERT_CA; cred.max_tls_fragment = 16384; cred.x509_server_cert = ali_ca_cert; cred.x509_server_cert_len = strlen(ali_ca_cert); -
Configure los parámetros de descarga.
Llame a
para configurar los parámetros de la tarea de descarga.
NotaLas actualizaciones OTA admiten paquetes de actualización de varios archivos. Al descargar un paquete, debe distinguir el ID, la cantidad y el progreso de descarga de cada archivo.
/* Set the download protocol to TLS. */ aiot_download_setopt(dl_handle, AIOT_DLOPT_NETWORK_CRED, (void *)(&cred)); /* Set the server port for the download. */ aiot_download_setopt(dl_handle, AIOT_DLOPT_NETWORK_PORT, (void *)(&port)); /* Set the download task information. This is obtained from the task_desc member of the ota_msg input parameter. It includes the download URL, firmware size, firmware signature, and more. */ aiot_download_setopt(dl_handle, AIOT_DLOPT_TASK_DESC, (void *)(ota_msg->task_desc)); /* Set the callback function that the SDK calls when the downloaded content arrives. */ aiot_download_setopt(dl_handle, AIOT_DLOPT_RECV_HANDLER, (void *)(demo_download_recv_handler)); /* Set the maximum cache length for a single download. The user is notified each time this memory buffer is full. */ aiot_download_setopt(dl_handle, AIOT_DLOPT_BODY_BUFFER_MAX_LEN, (void *)(&max_buffer_len)); /* Set the data to be shared between different calls of AIOT_DLOPT_RECV_HANDLER. For example, the sample stores the progress here. */ last_percent = malloc(sizeof(uint32_t)); if (NULL == last_percent) { aiot_download_deinit(&dl_handle); break; } memset(download_status, 0, sizeof(multi_download_status_t)); download_status->file_id = ota_msg->task_desc->file_id; download_status->file_num = ota_msg->task_desc->file_num; aiot_download_setopt(dl_handle, AIOT_DLOPT_USERDATA, (void *)download_status); /* If this is the first download task, report a progress of 0. */ if (0 == ota_msg->task_desc->file_id) { aiot_download_report_progress(dl_handle, 0); } -
Inicie una solicitud de descarga.
-
Inicie el hilo de descarga
demo_ota_download_thread.res = pthread_create(&g_download_thread, NULL, demo_ota_download_thread, dl_handle); if (res != 0) { printf("pthread_create demo_ota_download_thread failed: %d\r\n", res); aiot_download_deinit(&dl_handle); free(download_status); } else { /* The download thread is set to the detach type. It can exit on its own after the firmware content is obtained. */ pthread_detach(g_download_thread); } -
En el hilo de descarga
demo_ota_download_thread, llame a aiot_download_send_request para enviar una solicitud GET HTTPS al servidor de almacenamiento del paquete de actualización especificado.-
void *demo_ota_download_thread(void *dl_handle) { int32_t ret = 0; printf("\r\nstarting download thread in 2 seconds ......\r\n"); sleep(2); /* Request to download from the update package storage server. */ /* * TODO: The following code uses one request to get the entire firmware content. * For devices with limited resources or poor network conditions, you can also download in segments. To do this, combine the following statements: * * aiot_download_setopt(dl_handle, AIOT_DLOPT_RANGE_START, ...); * aiot_download_setopt(dl_handle, AIOT_DLOPT_RANGE_END, ...); * aiot_download_send_request(dl_handle); * * In this case, you need to place the combined statements in a loop and call send_request and recv multiple times. * */ …… …… } -
Puede descargar el paquete de actualización por segmentos mediante AIOT_DLOPT_RANGE_START y AIOT_DLOPT_RANGE_END.
Por ejemplo, para descargar un paquete de actualización de 1024 bytes en dos partes, puede establecer los parámetros de la siguiente manera:
Primera parte:
AIOT_DLOPT_RANGE_START=0,AIOT_DLOPT_RANGE_END=511Segunda parte:
AIOT_DLOPT_RANGE_START=512,AIOT_DLOPT_RANGE_END=1023
-
-
-
Reciba el paquete de actualización.
-
Después de enviar la solicitud de descarga, llame a aiot_download_recv en el hilo de descarga
demo_ota_download_threadpara recibir los datos del paquete de actualización. Los datos recibidos activan la función callbackdemo_download_recv_handler. En la función callback, debe guardar el paquete de actualización descargado en el almacenamiento local o sistema de archivos del dispositivo.void *demo_ota_download_thread(void *dl_handle) { …… …… aiot_download_send_request(dl_handle); // while (1) { while (should_stop == 0) { /* Receive the firmware content from the server over the network. */ ret = aiot_download_recv(dl_handle); /* When the entire firmware is downloaded, the return value of aiot_download_recv() is STATE_DOWNLOAD_FINISHED. Otherwise, it is the number of bytes obtained in the current call. */ if (STATE_DOWNLOAD_FINISHED == ret) { printf("download completed\r\n"); break; } if (STATE_DOWNLOAD_RENEWAL_REQUEST_SENT == ret) { printf("download renewal request has been sent successfully\r\n"); continue; } if (ret <= STATE_SUCCESS) { printf("download failed, error code is %d, try to send renewal request\r\n", ret); continue; } } …… …… } -
Defina la función callback
demo_download_recv_handlerpara incluir la lógica de almacenamiento y actualización del paquete una vez completada la descarga.NotaEl código de ejemplo solo imprime información y no incluye la lógica para almacenar y grabar el paquete de actualización. Debe escribir el código para guardar el paquete de actualización según su entorno y luego grabar el firmware para completar la actualización OTA.
Si la actualización OTA utiliza un paquete de un solo archivo, puede grabar el archivo en una ubicación de almacenamiento local específica.
Si la actualización OTA utiliza un paquete de varios archivos, cada archivo puede corresponder a una dirección de almacenamiento diferente. Debe identificar el campo
file_namede cada firmware.
void demo_download_recv_handler(void *handle, const aiot_download_recv_t *packet, void *userdata) { uint32_t data_buffer_len = 0; int32_t last_percent = 0; int32_t percent = 0; multi_download_status_t *download_status = (multi_download_status_t *)userdata; /* Currently, only the case where packet->type is AIOT_DLRECV_HTTPBODY is supported. */ if (!packet || AIOT_DLRECV_HTTPBODY != packet->type) { return; } percent = packet->data.percent; /* userdata can store data that needs to be shared between different entries into demo_download_recv_handler(). */ /* Here, it is used to store the firmware download percentage from the last time this callback function was entered. */ if (userdata) { last_percent = (download_status->last_percent); } data_buffer_len = packet->data.len; /* If percent is negative, a packet reception exception or a digest verification error occurred. */ if (percent < 0) { printf("exception: percent = %d\r\n", percent); if (userdata) { free(userdata); } return; } …… …… }
-
-
Notifique el progreso de la descarga.
En la función callback
demo_download_recv_handler, llame a aiot_download_report_progress para notificar a IoT Platform el progreso de la descarga y cualquier excepción que ocurra durante el proceso de actualización, como un error de grabación o una desconexión de red.-
Consulte el progreso notificado:
El progreso se muestra en la consola de IoT Platform. Para obtener más información, consulte View update status.
-
Notificación de progreso para descargas normales y anómalas:
Si la descarga es normal, el progreso de la descarga se notifica a IoT Platform como un porcentaje entero. Link SDK calcula automáticamente el valor del parámetro percent y lo notifica a IoT Platform.
Si la descarga es anómala o ocurre una excepción de grabación de firmware después de la descarga, debe notificar la excepción a IoT Platform. Para obtener una lista de códigos de error de protocolo entre el dispositivo e IoT Platform, consulte aiot_ota_protocol_errcode_t.
-
Métodos de notificación de progreso:
Si la actualización OTA utiliza un paquete de un solo archivo, el progreso notificado es el progreso total.
-
Si la actualización OTA utiliza un paquete de varios archivos, no notifique el progreso de descarga de archivos individuales porque esto puede causar confusión. Al notificar el progreso, se recomienda calcular el porcentaje utilizando el tamaño total de archivos como denominador y el número total de bytes descargados de todos los archivos como numerador. Luego, puede notificar el progreso de descarga calculado.
Puede desarrollar escenarios para notificar el progreso del paquete de actualización según sus necesidades comerciales. Por ejemplo:
Notifique 0% cuando comience la descarga. Luego, notifique el progreso solo cuando el progreso total de la descarga alcance un múltiplo entero de 10%, hasta llegar al 100%.
Para simplificar la notificación de progreso, puede notificar 0% cuando comience la descarga y 100% cuando termine. Esto significa que solo notifica el progreso dos veces.
void demo_download_recv_handler(void *handle, const aiot_download_recv_t *packet, void *userdata) { …… …… /* * TODO: When a segment of firmware is successfully downloaded, the user should save the memory * starting at packet->data.buffer with a length of packet->data.len to a local storage location. * * If the burning fails, you should also call aiot_download_report_progress(handle, -4) to report the failure to IoT Platform. * Note: The error codes agreed upon with the cloud platform in the protocol are in the aiot_ota_protocol_errcode_t type. For example: * -1: indicates an update failure. * -2: indicates a download failure. * -3: indicates a verification failure. * -4: indicates a burning failure. * */ /* When the value of the percent input parameter is 100, it means the SDK has finished downloading the entire firmware content. */ if (percent == 100) { g_finished_task_num++; /* * TODO: At this point, all firmware burning should be complete. Save the current work, restart the device, and start with the new firmware. The new firmware must report the new version number to IoT Platform using the following code. For example, if updating from 1.0.0 to 1.1.0, the value of new_version is 1.1.0. aiot_ota_report_version(ota_handle, new_version); IoT Platform considers the update successful only after receiving the new version number. Otherwise, the update is considered failed. If the update fails after a successful download, you should also call aiot_download_report_progress(handle, -1) to report the failure type. */ } /* Simplified output. The progress is printed and reported to the server only when the download progress has increased by 5% or more since the last report. */ if (percent - last_percent >= 5 || percent == 100) { if (NULL != download_status) { printf("file_id %d, download %03d%% done, +%d bytes\r\n", download_status->file_id, percent, data_buffer_len); download_status->last_percent = percent; if (g_finished_task_num == download_status->file_num) { /* Considering concurrent downloads by multiple threads, report 100% progress only after all files are downloaded. */ aiot_download_report_progress(handle, 100); } } if (percent == 100 && userdata) { free(userdata); } } } -
-
Salga del descargador.
Una vez descargado el contenido del paquete de actualización, llame a aiot_download_deinit para destruir la sesión de
download. A continuación, el hilo de descarga finaliza.aiot_download_deinit(&dl_handle); printf("download thread exit\n");
Paso 6: Notificar el número de versión después de la actualización
Si la actualización OTA utiliza un paquete de un solo archivo que contiene solo un firmware, notifique a IoT Platform el número de versión de firmware incluido en el paquete para completar la coincidencia de versiones.
Si la actualización OTA utiliza un paquete de varios archivos, los archivos pueden corresponder a uno o más firmwares. Si cada archivo tiene un número de versión de firmware diferente, como
a1,b1yc1, notifique el número de versión combinado del paquete, comoa1b1c1.
Para ver código de ejemplo sobre cómo notificar el número de versión, consulte Step 3: Report the current device version number.
Después de que el dispositivo completa la actualización OTA, debe notificar el número de versión más reciente. De lo contrario, IoT Platform considera que la tarea de actualización OTA ha fallado.
Si es necesario reiniciar el dispositivo después de la actualización, debe notificar el número de versión más reciente después del reinicio.
El código de ejemplo no incluye la lógica para notificar el número de versión una vez completada la actualización. Debe agregar esta lógica a su código.
Paso 7: Desconectarse
MQTT se usa normalmente para dispositivos que requieren una conexión persistente. Por lo tanto, el programa generalmente no llega a este punto.
En el programa de ejemplo, el hilo principal se encarga de configurar los parámetros y establecer una conexión. Una vez establecida la conexión, el hilo principal puede entrar en hibernación.
Puede llamar a aiot_mqtt_disconnect para enviar un mensaje de desconexión a IoT Platform y desconectarse de la red.
res = aiot_mqtt_disconnect(mqtt_handle);
if (res < STATE_SUCCESS) {
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_disconnect failed: -0x%04X\n", -res);
return -1;
}
Paso 8: Salir del programa OTA
Llame a aiot_ota_deinit para destruir la instancia de OTA.
aiot_ota_deinit(&ota_handle);
Siguientes pasos
-
./output/fota-multi-file_demo.Para obtener más información, consulte Compile and run.