O exemplo ./demos/fota_multi_file_demo.c demonstra como um dispositivo baixa um pacote de atualização OTA com múltiplos arquivos via HTTPS e executa a atualização OTA.
Informações básicas
Para obter mais informações sobre o recurso de atualização OTA, consulte Visão geral da atualização OTA.
O recurso de atualização OTA baseia-se em uma conexão MQTT. Para mais detalhes sobre o código de conexão MQTT, consulte Conexão MQTT.
-
Em comparação com o Exemplo 1
./demo/fota_posix_demo.c), este exemplo difere apenas nas seguintes seções:
Apenas os Link SDKs em C baixados após 9 de setembro de 2021 suportam pacotes de atualização OTA com múltiplos arquivos.
Para saber como obter o SDK, consulte Obter o Link SDK em C.
Etapa 1: Inicializar o recurso OTA
-
Adicione o arquivo de cabeçalho.
…… …… #include "aiot_ota_api.h" …… -
Configure as dependências subjacentes e a saída de log.
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile); aiot_state_set_logcb(demo_state_logcb); -
Chame aiot_ota_init para criar uma instância OTA.
ota_handle = aiot_ota_init(); if (NULL == ota_handle) { printf("aiot_ota_init failed\r\n"); aiot_mqtt_deinit(&mqtt_handle); return -2; }
Etapa 2: Configurar o recurso OTA
Chame aiot_ota_setopt para definir as opções a seguir.
-
Associe o handle da conexão MQTT.
ImportanteAntes de configurar os parâmetros OTA,
defina parâmetros como as credenciais do dispositivo. Para mais informações, consulte
Configurar parâmetros de conexão para MQTT
.
-
aiot_ota_setopt(ota_handle, AIOT_OTAOPT_MQTT_HANDLE, mqtt_handle); -
Item de configuração
Exemplo
Descrição
AIOT_OTAOPT_MQTT_HANDLE
mqtt_handle
As solicitações do recurso OTA dependem de conexões MQTT. Este item associa o handle da conexão MQTT.
-
-
Defina o callback para mensagens de instrução de atualização OTA.
-
aiot_ota_setopt(ota_handle, AIOT_OTAOPT_RECV_HANDLER, demo_ota_recv_handler); -
Item de configuração
Exemplo
Descrição
AIOT_OTAOPT_MQTT_HANDLER
demo_ota_recv_handler
Esta função de callback é invocada quando o dispositivo recebe uma instrução de atualização OTA do IoT Platform.
-
Etapa 3: Reportar a versão atual do dispositivo
Após estabelecer a conexão MQTT, chame aiot_ota_report_version para informar o número da versão atual do dispositivo. O IoT Platform usa esse número para determinar se uma atualização é necessária.
No código de exemplo abaixo, o número da versão reportado pelo dispositivo antes da atualização OTA é 1.0.0. Na sua aplicação, obtenha o número real da versão na área de configuração do dispositivo e ajuste o código conforme necessário.
O dispositivo deve reportar seu número de versão pelo menos uma vez antes de iniciar uma atualização 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);
}
Etapa 4: Receber instruções de atualização
-
Ao adicionar um pacote de atualização e iniciar uma tarefa de atualização no console do IoT Platform, a plataforma envia uma instrução de atualização ao dispositivo.
O dispositivo chama aiot_mqtt_recv para receber mensagens. Se a mensagem for identificada como uma instrução de atualização OTA, a função de callback
demo_ota_recv_handlerserá chamada.-
Implemente a lógica de processamento da função de callback.
Utilize as informações a seguir para implementar a lógica de processamento da função de callback:
-
O IoT Platform envia instruções de pacotes de atualização OTA para o dispositivo por meio do tópico
/ota/device/upgrade/${ProductKey}/${DeviceName}.Para mais detalhes sobre ${ProductKey} e ${DeviceName}, consulte Obter credenciais do dispositivo.
-
O tipo da instrução de atualização OTA é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; } …… …… } O tipo de estrutura de dados para mensagens de instrução de atualização OTA é aiot_ota_recv_t. O Link SDK analisa automaticamente as mensagens de instrução de atualização recebidas.
Consulte o código de exemplo para implementar a lógica de processamento da função de callback. Para mais informações, veja Etapa 5: Baixar o pacote de atualização e executar a atualização OTA.
-
Etapa 5: Baixar o pacote de atualização e executar a atualização OTA
O dispositivo não baixa automaticamente o pacote de atualização ao receber a mensagem do IoT Platform. Chame uma API do Link SDK para iniciar o download.
Quando a função demo_ota_recv_handler é acionada, o downloader inicia uma solicitação de download via HTTPS para baixar o pacote de atualização e realizar a atualização OTA.
-
Inicialize o downloader.
Chame
para criar uma sessão de
baixe
NotaAtualizações OTA suportam pacotes com múltiplos arquivos. Para mais informações, consulte Adicionar um pacote de atualização.
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); -
Defina os parâmetros de download.
Chame
para configurar os parâmetros da tarefa de download.
NotaAtualizações OTA suportam pacotes com múltiplos arquivos. Durante o download, distinga o ID, a quantidade e o progresso de download de cada arquivo.
/* 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 a solicitação de download.
-
Inicie a thread de download
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); } -
Na thread de download
demo_ota_download_thread, chame aiot_download_send_request para enviar uma solicitação GET HTTPS ao servidor de armazenamento do pacote de atualização 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. * */ …… …… } -
Baixe o pacote de atualização em segmentos usando AIOT_DLOPT_RANGE_START e AIOT_DLOPT_RANGE_END.
Por exemplo, para baixar um pacote de atualização de 1024 bytes em duas partes, defina os parâmetros da seguinte forma:
Primeira parte:
AIOT_DLOPT_RANGE_START=0,AIOT_DLOPT_RANGE_END=511Segunda parte:
AIOT_DLOPT_RANGE_START=512,AIOT_DLOPT_RANGE_END=1023
-
-
-
Receba o pacote de atualização.
-
Após enviar a solicitação de download, chame aiot_download_recv na thread de download
demo_ota_download_threadpara receber os dados do pacote de atualização. Os dados recebidos acionam a função de callbackdemo_download_recv_handler. Nessa função de callback, salve o pacote de atualização baixado no armazenamento local ou no sistema de arquivos do 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 a função de callback
demo_download_recv_handlerincluindo a lógica para armazenar e aplicar o pacote após a conclusão do download.NotaO código de exemplo apenas imprime informações e não inclui a lógica para armazenar e gravar o pacote de atualização. Escreva o código para salvar o pacote de atualização de acordo com seu ambiente e, em seguida, grave o firmware para concluir a atualização OTA.
Se a atualização OTA utilizar um pacote de arquivo único, grave o arquivo em um local de armazenamento local específico.
Caso a atualização OTA utilize um pacote com múltiplos arquivos, cada arquivo pode corresponder a um endereço de armazenamento diferente. Identifique o 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; } …… …… }
-
-
Reporte o progresso do download.
Na função de callback
demo_download_recv_handler, chame aiot_download_report_progress para informar ao IoT Platform o progresso do download e quaisquer exceções ocorridas durante o processo de atualização, como falha na gravação ou desconexão de rede.-
Visualize o progresso reportado:
O progresso é exibido no console do IoT Platform. Para mais informações, consulte Visualizar status da atualização.
-
Relatório de progresso para downloads normais e anômalos:
Em downloads normais, o progresso é reportado ao IoT Platform como uma porcentagem inteira. O Link SDK calcula automaticamente o valor do parâmetro percent e o envia ao IoT Platform.
Se o download apresentar anomalias ou ocorrer uma exceção na gravação do firmware após o download, reporte a exceção ao IoT Platform. Para consultar a lista de códigos de erro de protocolo entre o dispositivo e o IoT Platform, veja aiot_ota_protocol_errcode_t.
-
Métodos de relatório de progresso:
Para atualizações OTA com pacote de arquivo único, o progresso reportado corresponde ao progresso total.
-
Em pacotes com múltiplos arquivos, evite reportar o progresso de download de arquivos individuais para evitar confusão. Ao reportar o progresso, recomenda-se calcular a porcentagem usando o tamanho total dos arquivos como denominador e o total de bytes baixados de todos os arquivos como numerador. Em seguida, reporte o progresso de download calculado.
Desenvolva cenários para reportar o progresso do pacote de atualização conforme suas necessidades de negócio. Por exemplo:
Reporte 0% no início do download. Depois, reporte o progresso apenas quando o progresso total atingir um múltiplo inteiro de 10%, até chegar a 100%.
Para simplificar o relatório de progresso, reporte 0% no início do download e 100% após a conclusão. Assim, o progresso é reportado apenas duas vezes.
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); } } } -
-
Encerre o downloader.
Após o download do conteúdo do pacote de atualização, chame aiot_download_deinit para destruir a sessão de
download. A thread de download será então encerrada.aiot_download_deinit(&dl_handle); printf("download thread exit\n");
Etapa 6: Reportar o número da versão após a atualização
Se a atualização OTA usar um pacote de arquivo único contendo apenas um firmware, reporte o número da versão do firmware incluído no pacote ao IoT Platform para concluir a correspondência de versões.
Caso a atualização OTA utilize um pacote com múltiplos arquivos, os arquivos podem corresponder a um ou mais firmwares. Se cada arquivo tiver um número de versão diferente, como
a1,b1ec1, reporte o número da versão combinada do pacote, comoa1b1c1.
Para ver o código de exemplo sobre como reportar o número da versão, consulte Etapa 3: Reportar a versão atual do dispositivo.
Após concluir a atualização OTA, o dispositivo deve reportar o número da versão mais recente. Caso contrário, o IoT Platform considerará que a tarefa de atualização OTA falhou.
Se for necessário reiniciar o dispositivo após a atualização, ele deverá reportar o número da versão mais recente após a reinicialização.
O código de exemplo não inclui a lógica para reportar o número da versão após a conclusão da atualização. Adicione essa lógica ao seu código.
Etapa 7: Desconectar
O protocolo MQTT é geralmente usado em dispositivos que exigem conexão persistente. Portanto, o programa normalmente não chega a este ponto.
No programa de exemplo, a thread principal é responsável por configurar os parâmetros e estabelecer a conexão. Após o estabelecimento da conexão, a thread principal pode entrar em hibernação.
Chame aiot_mqtt_disconnect para enviar uma mensagem de desconexão ao IoT Platform e encerrar a conexão de rede.
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;
}
Etapa 8: Sair do programa OTA
Chame aiot_ota_deinit para destruir a instância OTA.
aiot_ota_deinit(&ota_handle);
Próximos passos
-
./output/fota-multi-file_demo.Para mais informações, consulte Compilar e executar.