L'exemple ./demos/fota_multi_file_demo.c montre comment un appareil télécharge un package de mise à jour OTA multi-fichiers via HTTPS et effectue la mise à jour.
Informations contextuelles
Pour plus d'informations sur la fonctionnalité de mise à jour OTA, reportez-vous à la rubrique Présentation des mises à jour OTA.
La fonctionnalité de mise à jour OTA repose sur une connexion MQTT. Pour obtenir des informations sur le code relatif à la connexion MQTT, consultez la page Connexion MQTT.
-
Par rapport à l'Exemple 1
./demo/fota_posix_demo.c), cet exemple ne diffère que dans les sections suivantes :
Seuls les SDK Link C téléchargés après le 9 septembre 2021 prennent en charge les packages de mise à jour OTA multi-fichiers.
Pour savoir comment obtenir le SDK, reportez-vous à la rubrique Obtenir le SDK Link C.
Étape 1 : Initialiser la fonctionnalité OTA
-
Ajoutez le fichier d'en-tête.
…… …… #include "aiot_ota_api.h" …… -
Configurez les dépendances sous-jacentes et la sortie des journaux.
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile); aiot_state_set_logcb(demo_state_logcb); -
Appelez la fonction aiot_ota_init pour créer une instance OTA.
ota_handle = aiot_ota_init(); if (NULL == ota_handle) { printf("aiot_ota_init failed\r\n"); aiot_mqtt_deinit(&mqtt_handle); return -2; }
Étape 2 : Configurer la fonctionnalité OTA
Appelez la fonction aiot_ota_setopt pour configurer les options suivantes.
-
Associez le handle de la connexion MQTT.
ImportantAvant de configurer les paramètres OTA,
vous devez définir les paramètres tels que les identifiants de l'appareil. Pour plus d'informations, consultez la section
Configurer les paramètres de connexion pour MQTT
.
-
aiot_ota_setopt(ota_handle, AIOT_OTAOPT_MQTT_HANDLE, mqtt_handle); -
Élément de configuration Exemple Description AIOT_OTAOPT_MQTT_HANDLE mqtt_handle Les requêtes de la fonctionnalité OTA reposent sur des connexions MQTT. Cet élément de configuration associe le handle de connexion MQTT.
-
-
Configurez le rappel pour les messages d'instruction de mise à jour OTA.
-
aiot_ota_setopt(ota_handle, AIOT_OTAOPT_RECV_HANDLER, demo_ota_recv_handler); -
Élément de configuration Exemple Description AIOT_OTAOPT_MQTT_HANDLER demo_ota_recv_handler Cette fonction de rappel est appelée lorsque l'appareil reçoit une instruction de mise à jour OTA depuis IoT Platform.
-
Étape 3 : Signaler la version actuelle de l'appareil
Une fois que l'appareil a établi une connexion MQTT, appelez la fonction aiot_ota_report_version pour signaler le numéro de version actuel de l'appareil. IoT Platform détermine si une mise à jour est nécessaire en fonction du numéro de version.
Dans l'exemple de code suivant, le numéro de version signalé par l'appareil avant la mise à jour OTA est 1.0.0. Dans votre application, vous devez obtenir le numéro de version réel à partir de la zone de configuration de l'appareil et modifier le code en conséquence.
L'appareil doit signaler son numéro de version au moins une fois avant une mise à jour 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);
}
Étape 4 : Recevoir les instructions de mise à jour
-
Après avoir ajouté un package de mise à jour et lancé une tâche de mise à jour dans la console IoT Platform, IoT Platform envoie une instruction de mise à jour à l'appareil.
L'appareil appelle la fonction aiot_mqtt_recv pour recevoir les messages. Lorsqu'un message est identifié comme une instruction de mise à jour OTA, la fonction de rappel
demo_ota_recv_handlerest appelée.-
Rédigez la logique de traitement pour la fonction de rappel.
Vous pouvez utiliser les informations suivantes pour rédiger la logique de traitement de la fonction de rappel :
-
IoT Platform envoie les instructions du package de mise à jour OTA à l'appareil via le topic
/ota/device/upgrade/${ProductKey}/${DeviceName}.Pour plus d'informations sur ${ProductKey} et ${DeviceName}, reportez-vous à la rubrique Obtenir les identifiants de l'appareil.
-
Le type de l'instruction de mise à jour OTA est 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; } …… …… } Le type de structure de données pour les messages d'instruction de mise à jour OTA est aiot_ota_recv_t. Le SDK Link analyse automatiquement les messages d'instruction de mise à jour reçus.
Vous pouvez vous référer à l'exemple de code pour rédiger la logique de traitement de la fonction de rappel. Pour plus d'informations, consultez la section Étape 5 : Télécharger le package de mise à jour et effectuer une mise à jour OTA.
-
Étape 5 : Télécharger le package de mise à jour et effectuer une mise à jour OTA
L'appareil ne télécharge pas automatiquement le package de mise à jour après avoir reçu le message du package de mise à jour depuis IoT Platform. Vous devez appeler une API du SDK Link pour lancer le téléchargement.
Lorsque la fonction demo_ota_recv_handler est déclenchée, le programme de téléchargement initie une requête de téléchargement via HTTPS pour télécharger le package de mise à jour et effectuer la mise à jour OTA.
-
Initialisez le programme de téléchargement.
Appelez la fonction
pour créer une session de
téléchargement.
RemarqueLes mises à jour OTA prennent en charge les packages de mise à jour multi-fichiers. Pour plus d'informations, consultez la rubrique Ajouter un package de mise à jour.
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); -
Configurez les paramètres de téléchargement.
Appelez la fonction
pour configurer les paramètres de la tâche de téléchargement.
RemarqueLes mises à jour OTA prennent en charge les packages de mise à jour multi-fichiers. Lors du téléchargement d'un package, vous devez distinguer l'ID, la quantité et la progression du téléchargement de chaque fichier.
/* 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); } -
Initiez une requête de téléchargement.
-
Démarrez le thread de téléchargement
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); } -
Dans le thread de téléchargement
demo_ota_download_thread, appelez la fonction aiot_download_send_request pour envoyer une requête GET HTTPS au serveur de stockage du package de mise à jour spécifié.-
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. * */ …… …… } -
Vous pouvez télécharger le package de mise à jour par segments en utilisant les paramètres AIOT_DLOPT_RANGE_START et AIOT_DLOPT_RANGE_END.
Par exemple, pour télécharger un package de mise à jour de 1 024 octets en deux parties, vous pouvez définir les paramètres comme suit :
Première partie :
AIOT_DLOPT_RANGE_START=0,AIOT_DLOPT_RANGE_END=511Deuxième partie :
AIOT_DLOPT_RANGE_START=512,AIOT_DLOPT_RANGE_END=1023
-
-
-
Recevez le package de mise à jour.
-
Une fois la requête de téléchargement envoyée, appelez la fonction
aiot_download_recvdans le thread de téléchargementdemo_ota_download_threadpour recevoir les données du package de mise à jour. Les données reçues déclenchent la fonction de rappeldemo_download_recv_handler. Dans cette fonction de rappel, vous devez enregistrer le package de mise à jour téléchargé dans le stockage local ou le système de fichiers de l'appareil.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; } } …… …… } -
Définissez la fonction de rappel
demo_download_recv_handlerafin d'inclure la logique de stockage et de mise à jour du package une fois le téléchargement terminé.RemarqueL'exemple de code se contente d'imprimer des informations et n'inclut pas la logique de stockage et de gravure du package de mise à jour. Vous devez écrire le code pour enregistrer le package de mise à jour en fonction de votre environnement, puis graver le micrologiciel pour finaliser la mise à jour OTA.
Si la mise à jour OTA utilise un package mono-fichier, vous pouvez graver le fichier à un emplacement de stockage local spécifié.
Si la mise à jour OTA utilise un package multi-fichiers, chaque fichier peut correspondre à une adresse de stockage différente. Vous devez identifier le champ
file_namede chaque micrologiciel.
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; } …… …… }
-
-
Signalez la progression du téléchargement.
Dans la fonction de rappel
demo_download_recv_handler, appelez la fonction aiot_download_report_progress pour signaler la progression du téléchargement et toute anomalie survenant pendant le processus de mise à jour, telle qu'un échec de gravure ou une déconnexion réseau, à IoT Platform.-
Affichez la progression signalée :
La progression s'affiche dans la console IoT Platform. Pour plus d'informations, consultez la rubrique Afficher l'état de la mise à jour.
-
Signaler la progression pour les téléchargements normaux et anormaux :
Si le téléchargement est normal, la progression du téléchargement est signalée à IoT Platform sous forme de pourcentage entier. Le SDK Link calcule automatiquement la valeur du paramètre percent et la signale à IoT Platform.
En cas de téléchargement anormal ou d'exception de gravure du micrologiciel après le téléchargement, vous devez signaler l'anomalie à IoT Platform. Pour la liste des codes d'erreur de protocole entre l'appareil et IoT Platform, consultez la page aiot_ota_protocol_errcode_t.
-
Méthodes de signalement de la progression :
Si la mise à jour OTA utilise un package mono-fichier, la progression signalée correspond à la progression totale.
-
Si la mise à jour OTA utilise un package multi-fichiers, ne signalez pas la progression du téléchargement des fichiers individuels, car cela peut prêter à confusion. Lors du signalement de la progression, nous vous recommandons de calculer le pourcentage en utilisant la taille totale des fichiers comme dénominateur et le nombre total d'octets téléchargés pour tous les fichiers comme numérateur. Vous pouvez ensuite signaler la progression du téléchargement calculée.
Vous pouvez élaborer des scénarios de signalement de la progression du package de mise à jour en fonction de vos besoins métier. Par exemple :
Signalez 0 % au début du téléchargement. Ensuite, signalez la progression uniquement lorsque la progression totale du téléchargement atteint un multiple entier de 10 %, jusqu'à 100 %.
Pour simplifier le signalement de la progression, vous pouvez signaler 0 % au début du téléchargement et 100 % une fois celui-ci terminé. Cela signifie que vous ne signalez la progression que deux fois.
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); } } } -
-
Quittez le programme de téléchargement.
Une fois le contenu du package de mise à jour téléchargé, appelez la fonction aiot_download_deinit pour détruire la session de
download. Le thread de téléchargement se termine alors.aiot_download_deinit(&dl_handle); printf("download thread exit\n");
Étape 6 : Signaler le numéro de version après la mise à jour
Si la mise à jour OTA utilise un package mono-fichier contenant un seul micrologiciel, signalez le numéro de version du micrologiciel inclus dans le package à IoT Platform pour finaliser la correspondance des versions.
Si la mise à jour OTA utilise un package multi-fichiers, les fichiers peuvent correspondre à un ou plusieurs micrologiciels. Si chaque fichier possède un numéro de version de micrologiciel différent, tel que
a1,b1etc1, signalez le numéro de version du package combiné, tel quea1b1c1.
Pour un exemple de code sur le signalement du numéro de version, consultez la section Étape 3 : Signaler le numéro de version actuel de l'appareil.
Une fois que l'appareil a terminé la mise à jour OTA, il doit signaler le dernier numéro de version. Sinon, IoT Platform considère que la tâche de mise à jour OTA a échoué.
Si l'appareil doit être redémarré après la mise à jour, il doit signaler le dernier numéro de version après le redémarrage.
L'exemple de code n'inclut pas la logique de signalement du numéro de version une fois la mise à jour terminée. Vous devez ajouter cette logique à votre code.
Étape 7 : Se déconnecter
MQTT est généralement utilisé pour les appareils qui nécessitent une connexion persistante. Par conséquent, le programme n'atteint généralement pas ce point.
Dans l'exemple de programme, le thread principal est chargé de configurer les paramètres et d'établir la connexion. Une fois la connexion établie, le thread principal peut entrer en veille.
Vous pouvez appeler la fonction aiot_mqtt_disconnect pour envoyer un message de déconnexion à IoT Platform et vous déconnecter du réseau.
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;
}
Étape 8 : Quitter le programme OTA
Appelez la fonction aiot_ota_deinit pour détruire l'instance OTA.
aiot_ota_deinit(&ota_handle);
Étapes suivantes
-
./output/fota-multi-file_demo.Pour plus d'informations, consultez la section Compiler et exécuter.