OTA(Over-the-Air Technology)即空中下载技术,物联网平台支持通过OTA方式进行设备固件升级。
背景信息
基于MQTT协议下固件升级流程如下。

OTA例程讲解
- 过OTA的API可以实现设备端固件下载。但是如何存储/使用下载到的固件,需要用户实现。
- 存储固件是指将下载到的固件存储到FLASH等介质上。
- 使用固件,包括加载新下载的固件,需要用户根据业务的具体需求(例如需要用户单击升级按钮)来实现。
下面用两个例子分别说明如何用基础版接口和高级版接口来实现OTA功能。
用基础版接口实现的OTA例程
现对照 src/ota/examples/ota_example_mqtt.c 例程分步骤讲解如何使用基础版的接口实现OTA的功能。
- OTA业务建立前的准备:导入设备证书,初始化连接信息。
int main(int argc, char *argv[]) {
...
/**< get device info*/
HAL_SetProductKey(PRODUCT_KEY);
HAL_SetDeviceName(DEVICE_NAME);
HAL_SetDeviceSecret(DEVICE_SECRET);
/**< end*/
_ota_mqtt_client()
}
- 在_ota_mqtt_client函数完成建连和OTA的主要配置逻辑。
/* Device AUTH */
if (0 != IOT_SetupConnInfo(g_product_key, g_device_name, g_device_secret, (void **)&pconn_info)) {
EXAMPLE_TRACE("AUTH request failed!");
rc = -1;
goto do_exit;
}
/* Initialize MQTT parameter */
memset(&mqtt_params, 0x0, sizeof(mqtt_params));
mqtt_params.port = pconn_info->port;
mqtt_params.host = pconn_info->host_name;
mqtt_params.client_id = pconn_info->client_id;
mqtt_params.username = pconn_info->username;
mqtt_params.password = pconn_info->password;
mqtt_params.pub_key = pconn_info->pub_key;
mqtt_params.request_timeout_ms = 2000;
mqtt_params.clean_session = 0;
mqtt_params.keepalive_interval_ms = 60000;
mqtt_params.read_buf_size = OTA_MQTT_MSGLEN;
mqtt_params.write_buf_size = OTA_MQTT_MSGLEN;
mqtt_params.handle_event.h_fp = event_handle;
mqtt_params.handle_event.pcontext = NULL;
/* Construct a MQTT client with specify parameter */
pclient = IOT_MQTT_Construct(&mqtt_params);
if (NULL == pclient) {
EXAMPLE_TRACE("MQTT construct failed");
rc = -1;
goto do_exit;
}
- 在
_ota_mqtt_client
函数进行OTA有关的初始化工作(主要是订阅跟这个设备有关的固件升级信息)。 h_ota = IOT_OTA_Init(PRODUCT_KEY, DEVICE_NAME, pclient);
if (NULL == h_ota) {
rc = -1;
EXAMPLE_TRACE("initialize OTA failed");
goto do_exit;
}
- 建立一个循环,一直去尝试接收OTA升级的消息。
int ota_over = 0;
do {
uint32_t firmware_valid;
EXAMPLE_TRACE("wait ota upgrade command....");
/* 接收MQTT消息 */
IOT_MQTT_Yield(pclient, 200);
/* 判断接收到的消息中是否有固件升级的消息 */
if (IOT_OTA_IsFetching(h_ota)) {
/* 下载OTA内容, 上报下载进度,见章节 "5. 下载OTA内容, 上报下载进度" */
/* 校验固件的md5, 见章节 "6. 校验md5的值" */
}
} while (!ota_over);
需要到服务端推送一个固件升级事件下去,IOT_OTA_IsFetching
返回才能结果为1,才能走入固件升级的逻辑。推送固件升级事件的具体步骤如下。
- 到IoT控制台的 OTA服务页面,单击新增固件。
- 单击创建固件,验证固件。
- 单击这个新增固件的批量升级按钮,从中选择设备所属产品为examples/ota/ota_mqtt-example.c中设备证书对应的产品。
- 待升级版本号点开下拉框选当前版本号,升级范围选定向升级,再从设备范围中选当前的设备证书对应的设备,单击确定即可。
- 下载OTA内容,上报下载进度。
do {
/* 下载OTA固件 */
len = IOT_OTA_FetchYield(h_ota, buf_ota, OTA_BUF_LEN, 1);
if (len > 0) {
if (1 != fwrite(buf_ota, len, 1, fp)) {
EXAMPLE_TRACE("write data to file failed");
rc = -1;
break;
}
} else {
/* 上报已下载进度 */
IOT_OTA_ReportProgress(h_ota, IOT_OTAP_FETCH_FAILED, NULL);
EXAMPLE_TRACE("ota fetch fail");
}
/* get OTA information */
/* 获取已下载到的数据量, 文件总大小, md5信息, 版本号等信息 */
IOT_OTA_Ioctl(h_ota, IOT_OTAG_FETCHED_SIZE, &size_downloaded, 4);
IOT_OTA_Ioctl(h_ota, IOT_OTAG_FILE_SIZE, &size_file, 4);
IOT_OTA_Ioctl(h_ota, IOT_OTAG_MD5SUM, md5sum, 33);
IOT_OTA_Ioctl(h_ota, IOT_OTAG_VERSION, version, 128);
last_percent = percent;
percent = (size_downloaded * 100) / size_file;
if (percent - last_percent > 0) {
/* 上报已下载进度 */
IOT_OTA_ReportProgress(h_ota, percent, NULL);
IOT_OTA_ReportProgress(h_ota, percent, "hello");
} IOT_MQTT_Yield(pclient, 100);
/* 判断下载是否结束 */
} while (!IOT_OTA_IsFetchFinish(h_ota));
- 校验md5的值。
IOT_OTA_Ioctl(h_ota, IOT_OTAG_CHECK_FIRMWARE, &firmware_valid, 4);
if (0 == firmware_valid) {
EXAMPLE_TRACE("The firmware is invalid");
} else {
EXAMPLE_TRACE("The firmware is valid");
}
ota_over = 1;
- 用户通过
IOT_OTA_Deini
t释放所有资源。 if (NULL != h_ota) {
IOT_OTA_Deinit(h_ota);
}
if (NULL != pclient) {
IOT_MQTT_Destroy(&pclient);
}
if (NULL != msg_buf) {
HAL_Free(msg_buf);
}
if (NULL != msg_readbuf) {
HAL_Free(msg_readbuf);
}
if (NULL != fp) {
fclose(fp);
}
return rc;
- 固件的存储。
在
_ota_mqtt_client
函数中通过下述方式打开,写入和关闭一个文件。
fp = fopen("ota.bin", "wb+")
...
if (1 != fwrite(buf_ota, len, 1, fp)) {
EXAMPLE_TRACE("write data to file failed");
rc = -1;
break;
}
...
if (NULL != fp) {
fclose(fp);
}
用高级版接口实现的OTA例程
现对照src/dev_model/examples/linkkit_example_solo.c分步骤讲解如何使用高级版的接口实现OTA的功能。
- 初始化主设备,注册FOTA的回调函数,建立与云端的连接。
int res = 0;
int domain_type = 0, dynamic_register = 0, post_reply_need = 0;
iotx_linkkit_dev_meta_info_t master_meta_info;
memset(&g_user_example_ctx, 0, sizeof(user_example_ctx_t));
memset(&master_meta_info, 0, sizeof(iotx_linkkit_dev_meta_info_t));
memcpy(master_meta_info.product_key, PRODUCT_KEY, strlen(PRODUCT_KEY));
memcpy(master_meta_info.product_secret, PRODUCT_SECRET, strlen(PRODUCT_SECRET));
memcpy(master_meta_info.device_name, DEVICE_NAME, strlen(DEVICE_NAME));
memcpy(master_meta_info.device_secret, DEVICE_SECRET, strlen(DEVICE_SECRET));
/* Register Callback */
...
...
IOT_RegisterCallback(ITE_FOTA, user_fota_event_handler);
domain_type = IOTX_CLOUD_REGION_SHANGHAI;
IOT_Ioctl(IOTX_IOCTL_SET_DOMAIN, (void *)&domain_type);
/* Choose Login Method */
dynamic_register = 0;
IOT_Ioctl(IOTX_IOCTL_SET_DYNAMIC_REGISTER, (void *)&dynamic_register);
/* post reply doesn't need */
post_reply_need = 1;IOT_Ioctl(IOTX_IOCTL_RECV_EVENT_REPLY, (void *)&post_reply_need);
/* Create Master Device Resources */
g_user_example_ctx.master_devid = IOT_Linkkit_Open(IOTX_LINKKIT_DEV_TYPE_MASTER, &master_meta_info);
if (g_user_example_ctx.master_devid < 0) {
EXAMPLE_TRACE("IOT_Linkkit_Open Failed\n");
return -1;}
/* Start Connect Aliyun Server */
res = IOT_Linkkit_Connect(g_user_example_ctx.master_devid);
if (res < 0) {
EXAMPLE_TRACE("IOT_Linkkit_Connect Failed\n");
return -1;
}
- 实现上述代码中的回调函数
user_fota_event_handler
。该回调函数在如下两种情况下会被触发:
- 直接收到云端下发的新固件通知时。
- 由设备端主动发起新固件查询,云端返回新固件通知时。
在收到新固件通知后,可调用
IOT_Linkkit_Query
进行固件下载。
int user_fota_event_handler(int type, const char *version){
char buffer[128] = {0};
int buffer_length = 128;
/* 0 - new firmware exist, query the new firmware */
if (type == 0) {
EXAMPLE_TRACE("New Firmware Version: %s", version);
IOT_Linkkit_Query(EXAMPLE_MASTER_DEVID, ITM_MSG_QUERY_FOTA_DATA, (unsigned char *)buffer, buffer_length);
}
return 0;
}
- 固件的存储。
用户需要实现如下3个HAL接口来实现固件的存储。
/* SDK在开始下载固件之前进行调用 */
void HAL_Firmware_Persistence_Start(void);
/* SDK在接收到固件数据时进行调用 */
int HAL_Firmware_Persistence_Write(char *buffer, uint32_t length);
/* SDK在固件下载结束时进行调用 */
int HAL_Firmware_Persistence_Stop(void);
- 用户主动发起新固件查询。
IOT_Linkkit_Query(user_example_ctx->master_devid, ITM_MSG_REQUEST_FOTA_IMAGE,
(unsigned char *)("app-1.0.0-20180101.1001"), 30);
OTA支持多模块
OTA除了可以升级设备固件外,还可以下载并升级设备的软件模块,客户需要在物联网平台的控制台上创建模块,如下图所示:
设备端开发时, 需要将module设置为与控制台的模块名一致:char* module = "mcu";
IOT_Ioctl(IOTX_IOCTL_SET_MODULE, (void *)module);
其他流程如正常的ota测试流程,在云端控制台部署任务后, 设备端会有如下日志, 具体见其中的module字段:[dbg] otamqtt_UpgrageCb(111): topic=/ota/device/upgrade/a1******PjW/foDzDj*******3PDJ9d
[dbg] otamqtt_UpgrageCb(112): len=431, topic_msg={"code":"1000","data":{"size":143360,"module":"mcu","sign":"867f1536fb********a2205436252","version":"111","url":"https://iotx-******ily.oss-cn-shanghai.aliyuncs.com/ota/338ac9db05545dcab9*********52/ck75mi***********5xbgz61vl.tar?Expires=1582951757&OSSAccessKeyId=aS4***********j6Gy&Signature=urw%2F9WAlizQui*************0A%3D","signMethod":"Md5","md5":"867f1536fb*********436252"},"id":1582865357795,"message":"success"}
[dbg] otamqtt_UpgrageCb(129): receive device upgrade
[inf] ofc_Init(47): protocol: https
received state: -0x092C(msg queue size: 0, max size: 50)
received state: -0x092C(msg enqueue w/ message type: 43)
received state: -0x092C(msg dequeue)
received state: -0x0938(alink event type: 43)
received state: -0x0938(new fota information received, 111)
user_fota_module_event_handler.219: New Firmware Version: 111, module: mcu
OTA功能API
- 用基础版接口实现OTA功能涉及的API。
- 用高级版接口实现OTA功能涉及的API。
需要实现的HAL
- 用基础版接口实现OTA功能需要实现的API。
无。
- 用高级版接口实现OTA功能需要实现的API。