16、RDA8910(4GCAT1)CSDK二次开发:FOTA远程升级

目录

点击这里查看所有博文

  本系列博客所述资料均来自合宙官方,并不是本人原创(只有博客是自己写的),csdk只是得到了口头的允许公开授权。出于热心,本人将自己的所学笔记整理并推出相对应的使用教程,方面其他人学习。为国内的物联网事业发展尽自己的一份绵薄之力,没有为自己谋取私利的想法。若出现侵权现象,请告知本人,本人会立即停止更新,并删除相应的文章和代码。

  本系列博客基于紫光展锐的RDA8910 LTE Cat 1 bis芯片平台开发。理论上适用于合宙的Air720U、Air724U、广和通L610以及安信可的cat-01模块。

  各个厂家的部分配置文件可能不一样,也许会导致设备出现奇怪的问题,其他的模块我也不确定能不能用,自行测试。但是有一点编译下载和监视流程基本一样,可供参考。

  先不管支不支持,如果你用的模块是是紫光展锐的RDA8910,那都不妨一试,也许会有意外收获(也有可能变砖,慎重!!!)。

  我使用的是Air724UG开发板,如果在其他模块上不能用,那也不要强怼,也许是开发包不兼容吧。这里的代码是没有问题的。例程仅供参考!

一、前言

  也有快一个月没折腾csdk,实在是太忙了没时间。上个星期有个小伙伴告诉我远程升级平时用的挺多的,基本上用模块的都需要用远程升级。问我能不能写一篇csdk的远程升级教程。我想了想,好像说的挺有道理的。

在这里插入图片描述

  当时就一口答应下来的,结果让我又拖了1个星期的时间,今天好不容易抽出一点时间。就来写一下吧。

在这里插入图片描述

  细心的小伙伴可能就发现了,我们的手机系统平时更新。有时候会发现系统只更新底层文件,有时候会发现其实只更新了几个系统app。有时候在跨大版本升级的时候,比如EMUI的大版本升级,往往都会更新安卓的底层,同时伴随着系统的app也会有大量的改动。我猜市面上其他的手机也会采取这样的升级方案,而不是每次都是选择下载几个g的完整包文件而只是为了升级下系统app,这样的话不仅升级慢还浪费流量。

  这样的话我们可以大概理解为,市面上的手机远程升级会分为三种,一种是全部都升级。一种是app升级,一种是底层软件的升级

  那大家有没有想过,我为什么要提上面的这些东西呢。
在这里插入图片描述

  有这个疑问很正常,至少在嵌入式开发上我很少见到有支持上面三种远程升级方式的芯片。它们往往都是选择全量升级。官方提供直接支持的很少,需要用户自己去解析bin文件写flash,自己去实现,你说这多麻烦啊,行不行还得另外再说。

  就这一点我不得不说合宙做得很好,csdk开发包可以完美支持三种升级方式,直接就可以编译出来升级文件,放到服务器就可以了升级了。只要不更新底层core文件,只是用户app有改动的话。远程升级也需要就几十kb的流量。在物联网模块上缺的就是流量,要是动不动升级来个几兆,那成本就太高了。

在这里插入图片描述

二、准备工作

  我这里使用合宙的物联网云平台的远程升级工具,使用之前需要获取一些信息。各位小伙伴如果是使用其他的云平台,或者是使用自建的服务器,那么可以跳过这一小节了。

  点我打开物联网云平台,登录自己的账号,没有的话注册一个。
在这里插入图片描述
  点击产品信息,找到ProductKey保存下来。

在这里插入图片描述
  确认你所使用的设备imei是不是在设备列表中,设备的imei就印在芯片屏蔽盖上。
在这里插入图片描述

三、编写程序

3.1、网络连接处理

  这里是进行网络的连接,网络连接成功后,我们就可以调用OTA_Start开始进行OTA请求了。不管升级失败还是成功,在升级结束后,都会选择打印当前运行的固件版本号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
static void TestTask(void *param)
{
bool NetLink = FALSE;
while (NetLink == FALSE)
{
T_OPENAT_NETWORK_CONNECT networkparam = {0};
switch (NetWorkCbMessage)
{
case OPENAT_NETWORK_DISCONNECT: //网络断开 表示GPRS网络不可用澹,无法进行数据连接,有可能可以打电话
iot_debug_print("[17_ota] OPENAT_NETWORK_DISCONNECT");
iot_os_sleep(500);
break;
case OPENAT_NETWORK_READY: //网络已连接 表示GPRS网络可用,可以进行链路激活
iot_debug_print("[17_ota] OPENAT_NETWORK_READY");
memcpy(networkparam.apn, "CMNET", strlen("CMNET"));
//建立网络连接,实际为pdp激活流程
iot_network_connect(&networkparam);
iot_os_sleep(500);
break;
case OPENAT_NETWORK_LINKED: //链路已经激活 PDP已经激活,可以通过socket接口建立数据连接
iot_debug_print("[17_ota] OPENAT_NETWORK_LINKED");
NetLink = TRUE;
break;
}
}
if (NetLink == TRUE)
{
if (OTA_Start() == true)
iot_debug_print("[17_ota] OTA_Start OK");
else
iot_debug_print("[17_ota] OTA_Start false");

while (1)
{
iot_debug_print("[17_ota] CSDK_VER :%s", CSDK_VER);
iot_os_sleep(2000);
}
}
iot_os_delete_task(TestTask_HANDLE);
}

3.2、OTA处理

  首先调用iot_fota_init初始化ota功能。OTA使用http协议向服务器发送GET请求,然后调用iot_fota_download将接收到的每一包数据全部传入进去。http请求结束后,调用iot_fota_done告诉底层,数据下载完毕。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
bool OTA_Start(void)
{

if (iot_fota_init() != 0) //fail
{
iot_debug_print("[17_ota] fota_init fail");
return false;
}

HTTP_SESSION_HANDLE pHTTP = HTTPClientOpenRequest(0);

if (HTTPClientSetVerb(pHTTP, VerbGet) != HTTP_CLIENT_SUCCESS)
{
iot_debug_print("[17_ota] HTTPClientSetVerb error");
return false;
}

if (HTTPClientAddRequestHeaders(pHTTP, "Accept", "*/*", TRUE) != HTTP_CLIENT_SUCCESS)
return false;
if (HTTPClientAddRequestHeaders(pHTTP, "Accept-Language", "cn", TRUE) != HTTP_CLIENT_SUCCESS)
return false;
if (HTTPClientAddRequestHeaders(pHTTP, "User-Agent", "*Mozilla/4.0", TRUE) != HTTP_CLIENT_SUCCESS)
return false;
if (HTTPClientAddRequestHeaders(pHTTP, "Connection", "Keep-Alive", TRUE) != HTTP_CLIENT_SUCCESS)
return false;

{
char url[256] = {0};
sprintf(url, FOTA_URL_FOR, PRODUCT_KEY, IMEI, CSDK_PRO, CORE_VER, CSDK_VER);
if (HTTPClientSendRequest(pHTTP, url, NULL, 0, TRUE, 0, 0) != HTTP_CLIENT_SUCCESS)
{
iot_debug_print("[17_ota] HTTPClientSendRequest error");
return false;
}
}

if (HTTPClientRecvResponse(pHTTP, 20000) != HTTP_CLIENT_SUCCESS)
{
iot_debug_print("[17_ota] HTTPClientRecvResponse error");
return false;
}

CHAR token[32] = {0};
UINT32 tokenSize = 32;
if (HTTPClientFindFirstHeader(pHTTP, "content-length", token, &tokenSize) != HTTP_CLIENT_SUCCESS)
{
iot_debug_print("[17_ota] HTTPClientFindFirstHeader error");
return false;
}
else
{
iot_debug_print("[17_ota] HTTPClientFindFirstHeader %d,%s", tokenSize, token);
}
int fsz = 0; //固件包总大小
if (strlen(token) > 0)
{
sscanf(token, "%*s %d", &fsz);
iot_debug_print("[17_ota]GetSize fsz: %d", fsz);
}
else
{
iot_debug_print("[17_ota]GetSize faild");
return false;
}
HTTPClientFindCloseHeader(pHTTP);
UINT32 nRetCode;
uint32 readTotalLen = 0; //总读取到字节数
do
{
char readBuff[512] = {0};
uint32 readSize = 0; //本次读取到字节数
nRetCode = HTTPClientReadData(pHTTP, readBuff, sizeof(readBuff), 300, &readSize);
//升级错误码
if (!strncmp("{\"code\":", readBuff, sizeof("{\"code\":") - 1))
{
iot_debug_print("[17_ota] Error readBuff:%s", readBuff);
return false;
}
readTotalLen += readSize;
//远程升级
if (iot_fota_download(readBuff, readSize, fsz) != 0)
{
iot_debug_print("[17_ota] iot_fota_download error");
return false;
}
} while (nRetCode == HTTP_CLIENT_SUCCESS || nRetCode != HTTP_CLIENT_EOS);

if (HTTPClientCloseRequest(&pHTTP) != HTTP_CLIENT_SUCCESS)
{
iot_debug_print("[17_ota] HTTPIntrnConnectionClose error");
return false;
}

if (iot_fota_done() < 0)
{
iot_debug_print("[17_ota]fota error");
return false;
}

return true;
}

3.3、用户需要手动改的内容

  PRODUCT_KEY 就是上面我们保存的ProductKey。IMEI 是设备屏蔽盖上印刷的内容。

1
2
3
4
#define FOTA_URL_FOR "http://iot.openluat.com/api/site/firmware_upgrade?project_key=%s&imei=%s&firmware_name=%s_CSDK_RDA8910&core_version=%s&dfota=1&version=%s"
#define PRODUCT_KEY "O5wpMkU7KdwSGIbQ6XrufIjr9GutlMyt"
#define IMEI "866714044529146"
#define CORE_VER "1031"

  CORE_VER的话,这个要麻烦点,要自己看下 core\iot_SDK_720U\memd\ats_config.h,要填箭头所指地方的中间的一串数字。是多少就填多少,我这里填1031

在这里插入图片描述
  是不是觉得这好麻烦,还要自己手动填。实不相瞒,其实这些东西都是可以通过程序获取的。我只是懒的写,全写上的话这个demo就复杂了。有这个需求的话可以参考官方的demo_fota里面有具体的实现方法。

在这里插入图片描述

3.4、完整的代码

  完整的demo在这里,可以复制直接用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
/*
* @Author: your name
* @Date: 2020-05-19 14:05:32
* @LastEditTime: 2020-05-31 18:58:02
* @LastEditors: Please set LastEditors
* @Description: In User Settings Edit
* @FilePath: \RDA8910_CSDK\USER\user_main.c
*/

#include "string.h"
#include "cs_types.h"

#include "osi_log.h"
#include "osi_api.h"

#include "am_openat.h"
#include "am_openat_vat.h"
#include "am_openat_common.h"

#include "iot_debug.h"
#include "iot_uart.h"
#include "iot_os.h"
#include "iot_gpio.h"
#include "iot_pmd.h"
#include "iot_adc.h"
#include "iot_vat.h"
#include "iot_network.h"
#include "iot_socket.h"
#include "httpclient.h"
#include "am_openat_httpclient.h"

HANDLE TestTask_HANDLE = NULL;
uint8 NetWorkCbMessage = 0;

#define FOTA_URL_FOR "http://iot.openluat.com/api/site/firmware_upgrade?project_key=%s&imei=%s&firmware_name=%s_CSDK_RDA8910&core_version=%s&dfota=1&version=%s"
#define PRODUCT_KEY "O5wpMkU7KdwSGIbQ6XrufIjr9GutlMyt"
#define IMEI "866714044529146"
#define CORE_VER "1031"

bool OTA_Start(void)
{

if (iot_fota_init() != 0) //fail
{
iot_debug_print("[17_ota] fota_init fail");
return false;
}

HTTP_SESSION_HANDLE pHTTP = HTTPClientOpenRequest(0);

if (HTTPClientSetVerb(pHTTP, VerbGet) != HTTP_CLIENT_SUCCESS)
{
iot_debug_print("[17_ota] HTTPClientSetVerb error");
return false;
}

if (HTTPClientAddRequestHeaders(pHTTP, "Accept", "*/*", TRUE) != HTTP_CLIENT_SUCCESS)
return false;
if (HTTPClientAddRequestHeaders(pHTTP, "Accept-Language", "cn", TRUE) != HTTP_CLIENT_SUCCESS)
return false;
if (HTTPClientAddRequestHeaders(pHTTP, "User-Agent", "*Mozilla/4.0", TRUE) != HTTP_CLIENT_SUCCESS)
return false;
if (HTTPClientAddRequestHeaders(pHTTP, "Connection", "Keep-Alive", TRUE) != HTTP_CLIENT_SUCCESS)
return false;

{
char url[256] = {0};
sprintf(url, FOTA_URL_FOR, PRODUCT_KEY, IMEI, CSDK_PRO, CORE_VER, CSDK_VER);
if (HTTPClientSendRequest(pHTTP, url, NULL, 0, TRUE, 0, 0) != HTTP_CLIENT_SUCCESS)
{
iot_debug_print("[17_ota] HTTPClientSendRequest error");
return false;
}
}

if (HTTPClientRecvResponse(pHTTP, 20000) != HTTP_CLIENT_SUCCESS)
{
iot_debug_print("[17_ota] HTTPClientRecvResponse error");
return false;
}

CHAR token[32] = {0};
UINT32 tokenSize = 32;
if (HTTPClientFindFirstHeader(pHTTP, "content-length", token, &tokenSize) != HTTP_CLIENT_SUCCESS)
{
iot_debug_print("[17_ota] HTTPClientFindFirstHeader error");
return false;
}
else
{
iot_debug_print("[17_ota] HTTPClientFindFirstHeader %d,%s", tokenSize, token);
}
int fsz = 0; //固件包总大小
if (strlen(token) > 0)
{
sscanf(token, "%*s %d", &fsz);
iot_debug_print("[17_ota]GetSize fsz: %d", fsz);
}
else
{
iot_debug_print("[17_ota]GetSize faild");
return false;
}
HTTPClientFindCloseHeader(pHTTP);
UINT32 nRetCode;
uint32 readTotalLen = 0; //总读取到字节数
do
{
char readBuff[512] = {0};
uint32 readSize = 0; //本次读取到字节数
nRetCode = HTTPClientReadData(pHTTP, readBuff, sizeof(readBuff), 300, &readSize);
//升级错误码
if (!strncmp("{\"code\":", readBuff, sizeof("{\"code\":") - 1))
{
iot_debug_print("[17_ota] Error readBuff:%s", readBuff);
return false;
}
readTotalLen += readSize;
//远程升级
if (iot_fota_download(readBuff, readSize, fsz) != 0)
{
iot_debug_print("[17_ota] iot_fota_download error");
return false;
}
} while (nRetCode == HTTP_CLIENT_SUCCESS || nRetCode != HTTP_CLIENT_EOS);

if (HTTPClientCloseRequest(&pHTTP) != HTTP_CLIENT_SUCCESS)
{
iot_debug_print("[17_ota] HTTPIntrnConnectionClose error");
return false;
}

if (iot_fota_done() < 0)
{
iot_debug_print("[17_ota]fota error");
return false;
}

return true;
}

static void TestTask(void *param)
{
bool NetLink = FALSE;
while (NetLink == FALSE)
{
T_OPENAT_NETWORK_CONNECT networkparam = {0};
switch (NetWorkCbMessage)
{
case OPENAT_NETWORK_DISCONNECT: //网络断开 表示GPRS网络不可用澹,无法进行数据连接,有可能可以打电话
iot_debug_print("[17_ota] OPENAT_NETWORK_DISCONNECT");
iot_os_sleep(500);
break;
case OPENAT_NETWORK_READY: //网络已连接 表示GPRS网络可用,可以进行链路激活
iot_debug_print("[17_ota] OPENAT_NETWORK_READY");
memcpy(networkparam.apn, "CMNET", strlen("CMNET"));
//建立网络连接,实际为pdp激活流程
iot_network_connect(&networkparam);
iot_os_sleep(500);
break;
case OPENAT_NETWORK_LINKED: //链路已经激活 PDP已经激活,可以通过socket接口建立数据连接
iot_debug_print("[17_ota] OPENAT_NETWORK_LINKED");
NetLink = TRUE;
break;
}
}
if (NetLink == TRUE)
{
if (OTA_Start() == true)
iot_debug_print("[17_ota] OTA_Start OK");
else
iot_debug_print("[17_ota] OTA_Start false");

while (1)
{
iot_debug_print("[17_ota] CSDK_VER :%s", CSDK_VER);
iot_os_sleep(2000);
}
}
iot_os_delete_task(TestTask_HANDLE);
}
static void NetWorkCb(E_OPENAT_NETWORK_STATE state)
{
NetWorkCbMessage = state;
}
//main函数
int appimg_enter(void *param)
{
//注册网络状态回调函数
iot_network_set_cb(NetWorkCb);

//关闭看门狗,死机不会重启。默认打开
iot_debug_set_fault_mode(OPENAT_FAULT_HANG);
//打开调试信息,默认关闭
iot_vat_send_cmd("AT^TRACECTRL=0,1,3\r\n", sizeof("AT^TRACECTRL=0,1,3\r\n"));

//创建一个任务
TestTask_HANDLE = iot_os_create_task(TestTask, NULL, 4096, 10, OPENAT_OS_CREATE_DEFAULT, "TestTask");
return 0;
}

//退出提示
void appimg_exit(void)
{
OSI_LOGI(0, "application image exit");
}

四、编译程序

  编译的话要注意下,这里和平时不太一样。先说下编译的命令把,编译的命令有三个。

  我们等会需要使用.\17_OTA.bat fota app 来编译两个不同的版本。

4.1、上传升级文件

  合宙的物联网云平台升级会校验软件版本。为了节省时间,测试的话我们需要先编译一个高版本的软件传上去。

  打开project\17_OTA.bat文件,将CSDK_VER修改为1.0.1。然后就可以编译了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@echo off
set CSDK_VER=1.0.1
set CSDK_PRO=17_OTA
set FOTA_FLAG=%1
set FOTA_TYPE=%2
set AM_MODEL=iot_SDK_720U
call ..\tools\core_launch.bat 17_OTA

REM/*+\NEW\chenzhimin\2020.07.22\*/
cd %PROJECT_OUT%
cmake ..\.. -G Ninja ^
-D CONFIG_BUILD_APP_HTTP=ON
ninja
cd ..\..\project
REM/*-\NEW\chenzhimin\2020.07.22\*/


  编译成功后,回到物联网平台。点击下图新建固件

在这里插入图片描述

  选择刚刚编译的hex\Air720U_CSDK_demo_17_OTA\17_OTA_1.0.1_CSDK_V1031_RDA8910.dfota.bin文件,点击确定
在这里插入图片描述
  添加需要升级的设备imei号。
在这里插入图片描述
  填你自己的,点确定。(在芯片屏蔽盖上)
在这里插入图片描述

4.2、编译低版本下载

  打开project\17_OTA.bat文件,将CSDK_VER修改为1.0.0。然后就可以编译下载了。下载程序之前打开coolwatch看日志!!!注意了是下载普通的pac包,你别把bin文件下载了。

在这里插入图片描述
  升级一共花了十秒钟时间,日志已经提示OTA_Start OK。也能看到当前版本是1.0.0。
在这里插入图片描述
  重启下看看,是不是还能继续升级。日志提示升级失败,错误码:27(意思就是当前运行的是最新版本)。当前当前是1.0.1。说明刚才的升级是成功的。

在这里插入图片描述
  有关上述升级的错误码,点我查看

不会下载的点击这里,进去查看我的RDA8910 CSDK二次开发入门教程专题第一篇博文1、RDA8910CSDK二次开发:环境搭建里面讲了怎么下载
这里只是我的学习笔记,拿出来给大家分享,欢迎大家批评指正,本篇教程到此结束

上次更新 2021-01-28