402 lines
17 KiB
Plaintext
402 lines
17 KiB
Plaintext
//================================1、CTP命令和描述文档======================================//
|
||
1、描述文档多加"ota_type"参数和"ota_step"参数
|
||
ota_type值为0或者没有ota_type则为双备份
|
||
ota_type值为1为单备份:"ota_type": "1"
|
||
|
||
ota_step值为0表示:单备份升级,需要先执行第一阶段
|
||
ota_step值为1为单备份正在升级(此时需要进入第二阶段升级):"ota_step": "1"
|
||
有ota_type则必有ota_step
|
||
|
||
//"ota_type" 0 无, 1单备份
|
||
//"ota_step" 0 可进行第一阶段的单备份升级, 1单备份正在升级第二阶段
|
||
|
||
2、CTP命令,
|
||
加一条命令:"NET_OTA_SINGLE" , 只有APP向设备请求,参数param":{"status":"1"}}
|
||
返回参数的status参数,status:1 则设备已准备好单备份升级
|
||
返回参数的status参数,status:0 则设备单备份升级失败
|
||
|
||
//================================2、升级流程,分两个阶段======================================//
|
||
第一阶段升级:
|
||
(1)APP连入获取ota_type和ota_step,ota_type值为1,ota_step = 0,则可以进行单备份升级第一阶段
|
||
(2)进入设备单备份升级,发送CTP命令:NET_OTA_SINGLE,参数:param":{"status":"1"}}
|
||
(3)APP收到设备的"NET_OTA_SINGLE"回应NOTIFY后,status=1,则设备成功进入单备份升级模式
|
||
(4)连接3335端口TCP,进行单备份升级,此后进入接收recv等待接收数据和读取文件
|
||
(5)设备升级完成或升级失败:
|
||
socket收到数据头的status=NET_UPDATE_STATUS_ERR,或者接收超时一定次数,升级失败。
|
||
socket收到数据头的status=NET_UPDATE_STATUS_OK,升级成功。
|
||
socket收到数据头的status=NET_UPDATE_STATUS_SAME,升级的是同一个固件。
|
||
(6)升级成功后,设备重启,APP要手动连接设备热点
|
||
(7)如果APP主动断开则,需要往3335的socket发送NET_UPDATE_STATUS_STOP状态
|
||
|
||
第二阶段升级:
|
||
(1)APP连入3333的CTP端口,发送接入设备的ACCEPT,设备回应APP_ACCESS,带参数{"op":"NOTIFY","param":{"type":"0","ver":"20906"}},即可进入下一步
|
||
(2)APP连入http获取ota_type和ota_step,ota_type值为1,ota_step = 1,则可以进行单备份升级第二阶段
|
||
(3)连接3335端口TCP,进行单备份升级,此后进入接收recv等待接收数据和读取文件
|
||
(4)设备升级完成或升级失败:
|
||
socket收到数据头的status=NET_UPDATE_STATUS_ERR,或者接收超时一定次数,升级失败。
|
||
socket收到数据头的status=NET_UPDATE_STATUS_OK,升级成功。
|
||
socket收到数据头的status=NET_UPDATE_STATUS_SAME,升级的是同一个固件。
|
||
(5)升级成功,设备重启
|
||
(6)如果APP主动断开则,需要往3335的socket发送NET_UPDATE_STATUS_STOP状态
|
||
|
||
|
||
第二阶段升级中,如果数据被擦了,wifi异常断开了,可以重连wifi进入继续升级(不能断电),设备断电重启之后会一直进入升级模式,
|
||
那么重启后,则默认AP模式名字为:WIFI_OTA_XXXXXX,无密码。
|
||
因此:注意:在第二阶段升级,禁止设备断电!!!!
|
||
|
||
|
||
|
||
|
||
//=================================3、协议说明=================================================//
|
||
//APP最多42K请求数据量(不可更改,设备端一定<42K)
|
||
#define NET_BUF_MAX_SIZE (42 * 1024)
|
||
|
||
//升级端口
|
||
#define NET_UPDATE_PORT 3335
|
||
|
||
|
||
//status:升级状态
|
||
#define NET_UPDATE_STATUS_DOING (0 << 0)//设备端主动发送
|
||
#define NET_UPDATE_STATUS_OK (1 << 0)//设备端主动发送
|
||
#define NET_UPDATE_STATUS_ERR (1 << 1)//设备端主动发送
|
||
#define NET_UPDATE_STATUS_READ (1 << 2)//设备端主动发送
|
||
#define NET_UPDATE_STATUS_STOP (1 << 3)//APP主动的停止升级
|
||
#define NET_UPDATE_STATUS_SAME (1 << 4)//升级的是同一个固件
|
||
|
||
|
||
//协议:头部20字节
|
||
struct net_ota{
|
||
unsigned int sum_check;//设备端:头部16字节和校验,APP端:头部16字节+读取字节的和校验,(sum_check不加上和校验计算)
|
||
unsigned int seek_addr;//文件偏移地址
|
||
unsigned int read_size;//读取文件长度
|
||
unsigned int all_size;//升级过程中需要的文件总字节数(用在进度条),整个升级过程中会有一次的all_size > 0, 其他次all_size = 0
|
||
unsigned int status:8;//升级状态,NET_UPDATE_STATUS_NONE正在升级,NET_UPDATE_STATUS_OK升级成功,NET_UPDATE_STATUS_ERR升级失败
|
||
unsigned int download_percent:8;//升级过程中设备的百分比
|
||
unsigned int resv:16;//预留,最后收到这4个字节代表成功和失败
|
||
unsigned char buf[0];
|
||
};
|
||
|
||
//发送数据流程:可以一次性发送:协议头+数据, 或者先发头部再发数据(数据的和校验要进行计算)
|
||
//(1)设备项APP请求:发送协议,只有数据头,长度为sizeof(struct net_ota);
|
||
//(2)APP响应设备请求:发送协议,有数据头+数据,长度为sizeof(struct net_ota)+数据长度;
|
||
|
||
注意:协议中,除了头部的sum_check没有加上和校验,其他和数据都需要进行和校验计算,详情下面的:windows socket测试代码。
|
||
|
||
//===================================4、APP端的windows socket测试代码====================================================//
|
||
//(注意:该测试程序在windows测试,设备wifi启动后执行net_ota_single_back_init(),如在wifi_app_task.c的net_app_init函数,则就不需要发命令NET_OTA_SINGLE即可进入运行改程序测试)
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <winsock2.h>
|
||
|
||
//第一阶段需要:先连接CTP 3333端口,发送NET_OTA_SINGLE命令进入升级模式
|
||
//第二阶段需要:先连接CTP 3333端口,再连接HTTP 8080端口,再连接升级3335端口
|
||
|
||
//#define NET_UPDATE_PORT 3333 //CTP端口号
|
||
//#define NET_UPDATE_PORT 8080 //HTTP端口号
|
||
#define NET_UPDATE_PORT 3335 //网络升级端口号
|
||
|
||
|
||
|
||
#define CTP_VER "{\"op\":\"NOTIFY\",\"param\":{\"type\":\"0\",\"ver\":\"20906\"}}"
|
||
|
||
|
||
|
||
#define NET_SERVER_IP "192.168.1.1" //IP地址
|
||
#define NET_BUF_MAX_SIZE (42 * 1024) //设备端最多42K请求数据量(不可更改,设备端一定<42K)
|
||
#define NET_UPDATE_FILE "E:\\update.ufw" //升级文件路径
|
||
|
||
#define NET_UPDATE_STATUS_DOING (0 << 0)//设备端主动发送
|
||
#define NET_UPDATE_STATUS_OK (1 << 0)//设备端主动发送
|
||
#define NET_UPDATE_STATUS_ERR (1 << 1)//设备端主动发送
|
||
#define NET_UPDATE_STATUS_READ (1 << 2)//设备端主动发送
|
||
#define NET_UPDATE_STATUS_STOP (1 << 3)//APP主动的停止升级
|
||
#define NET_UPDATE_STATUS_SAME (1 << 4)//升级的是同一个固件
|
||
|
||
|
||
|
||
static int sk_fd = 0;
|
||
static FILE *fd;
|
||
static int file_size = 0;
|
||
|
||
//协议
|
||
struct net_ota{
|
||
unsigned int sum_check;//设备端:头部16字节和校验,APP端:头部16字节+读取字节的和校验,(sum_check不加上和校验计算)
|
||
unsigned int seek_addr;//文件偏移地址
|
||
unsigned int read_size;//读取文件长度
|
||
unsigned int all_size;//升级过程中需要的文件总字节数(用在进度条),整个升级过程中会有一次的all_size > 0, 其他次all_size = 0
|
||
unsigned int status:8;//升级状态,NET_UPDATE_STATUS_NONE正在升级,NET_UPDATE_STATUS_OK升级成功,NET_UPDATE_STATUS_ERR升级失败
|
||
unsigned int download_percent:8;//升级过程中设备的百分比
|
||
unsigned int resv:16;//预留
|
||
unsigned char buf[0];
|
||
};
|
||
unsigned int sumget(void *buf, int len)
|
||
{
|
||
unsigned char *pbuf = (unsigned char *)buf;
|
||
unsigned int sum = 0;
|
||
int i = 0;
|
||
while(i < len){
|
||
sum += pbuf[i++];
|
||
}
|
||
return sum;
|
||
}
|
||
int sumcheck(void *buf, int len, unsigned int sum)
|
||
{
|
||
unsigned char *pbuf = (unsigned char *)buf;
|
||
unsigned int sum_check = 0;
|
||
int i = 0;
|
||
while(i < len){
|
||
sum_check += pbuf[i++];
|
||
}
|
||
if(sum_check != sum){
|
||
printf("err sumcheck = 0x%x, 0x%x\n",sum_check,sum);
|
||
return -1;
|
||
}
|
||
return 0;
|
||
}
|
||
int socket_init(void)
|
||
{
|
||
int err;
|
||
int send_to_millsec = 5000;
|
||
int recv_to_millsec = 5000;//5s超时
|
||
int len;
|
||
struct sockaddr_in serv_addr = {0};
|
||
|
||
fd = fopen(NET_UPDATE_FILE, "rb");
|
||
if(!fd){
|
||
printf("err in open file \r\n");
|
||
return -1;
|
||
}
|
||
fseek(fd, 0, SEEK_END);
|
||
file_size = ftell(fd);
|
||
fseek(fd, 0, SEEK_SET);
|
||
|
||
/* 3 . socket */
|
||
sk_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||
if(sk_fd < 0)
|
||
{
|
||
printf(" . ! socket error !!!\r\n");
|
||
return -1;
|
||
}
|
||
setsockopt(sk_fd, SOL_SOCKET, SO_RCVTIMEO, (const void *)&recv_to_millsec, sizeof(recv_to_millsec));
|
||
setsockopt(sk_fd, SOL_SOCKET, SO_SNDTIMEO, (const void *)&send_to_millsec, sizeof(send_to_millsec));
|
||
|
||
serv_addr.sin_family = AF_INET;
|
||
serv_addr.sin_port = htons(NET_UPDATE_PORT);//指定服务器端口
|
||
serv_addr.sin_addr.s_addr = inet_addr(NET_SERVER_IP);//指定服务器IP
|
||
printf(" . server ip : %s port : %d \r\n",NET_SERVER_IP,NET_UPDATE_PORT);
|
||
|
||
|
||
/* 4 . connect */
|
||
for (int i = 0; i <= 3; i++)
|
||
{
|
||
err = connect(sk_fd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr_in));
|
||
if (!err) {
|
||
break;
|
||
}
|
||
if (err < 0 && i >= 3) {
|
||
printf(" . ! connect error !!!\r\n");
|
||
shutdown(sk_fd,2);
|
||
closesocket(sk_fd);
|
||
return -1;
|
||
}
|
||
printf(" . Retry to connect ~~~\r\n");
|
||
}
|
||
printf(" . connect server ok \r\n");
|
||
return 0;
|
||
}
|
||
int socket_send_stop(struct net_ota *net_ota_data)
|
||
{
|
||
//生成发送数据头部和数据的和校验
|
||
net_ota_data->seek_addr = 0;
|
||
net_ota_data->read_size = 0;
|
||
net_ota_data->all_size = 0;
|
||
net_ota_data->seek_addr = 0;
|
||
net_ota_data->status = NET_UPDATE_STATUS_STOP;
|
||
net_ota_data->sum_check = sumget((char*)net_ota_data + sizeof(net_ota_data->sum_check), sizeof(struct net_ota) - sizeof(net_ota_data->sum_check));
|
||
//发送数据
|
||
return send(sk_fd, net_ota_data, sizeof(struct net_ota), 0);
|
||
}
|
||
int socket_send(struct net_ota *net_ota_data, void *buf, int len)
|
||
{
|
||
//生成发送数据头部和数据的和校验
|
||
net_ota_data->seek_addr = 0;
|
||
net_ota_data->read_size = 0;
|
||
net_ota_data->all_size = 0;
|
||
net_ota_data->seek_addr = 0;
|
||
net_ota_data->status = 0;
|
||
net_ota_data->sum_check = sumget((char*)net_ota_data + sizeof(net_ota_data->sum_check), sizeof(struct net_ota) - sizeof(net_ota_data->sum_check));
|
||
//发送数据
|
||
return send(sk_fd, net_ota_data, sizeof(struct net_ota), 0);
|
||
}
|
||
int socket_loop(void)
|
||
{
|
||
int err = 0;
|
||
int cnt = 0;
|
||
int slen;
|
||
int len;
|
||
int net_file_all_size = 0;
|
||
|
||
//申请缓存
|
||
struct net_ota *net_ota_data = malloc(sizeof(struct net_ota) + NET_BUF_MAX_SIZE);
|
||
if(!net_ota_data){
|
||
return -1;
|
||
}
|
||
//初始化socket连接服务器
|
||
if(socket_init()){
|
||
return -1;
|
||
}
|
||
if(NET_UPDATE_PORT == 8080){//http协议发送http请求,测试模式APP的http
|
||
socket_send_stop(net_ota_data);
|
||
len = recv(sk_fd, net_ota_data->buf, 4096, 0);
|
||
if(len){
|
||
printf("---> http recv : %s \n",net_ota_data->buf);
|
||
}
|
||
goto exit;
|
||
}
|
||
if(NET_UPDATE_PORT == 3333){//CTP协议发送,测试模式APP的CTP命令
|
||
send(sk_fd, CTP_VER, strlen(CTP_VER), 0);
|
||
len = recv(sk_fd, net_ota_data->buf, 4096, 0);
|
||
if(len){
|
||
printf("---> ctp recv : %s \n",net_ota_data->buf + 0x14);
|
||
}
|
||
goto exit;
|
||
}
|
||
|
||
//socket读写
|
||
while(1){
|
||
//先读取协议头部字节
|
||
memset(net_ota_data, 0, sizeof(struct net_ota));
|
||
len = recv(sk_fd, net_ota_data, sizeof(struct net_ota), 0);
|
||
if(len < 0){
|
||
cnt++;
|
||
if(cnt > 10){
|
||
printf("err recv timeout \n");
|
||
err = -1;
|
||
break;
|
||
}
|
||
continue;
|
||
}else if(!len){
|
||
printf("err : server disconneted !\n");
|
||
err = -1;
|
||
break;
|
||
}
|
||
cnt = 0;
|
||
|
||
//对头部进行和校验,和校验出错则继续接收头部信息
|
||
if(sumcheck((char*)net_ota_data + sizeof(net_ota_data->sum_check), sizeof(struct net_ota) - sizeof(net_ota_data->sum_check), net_ota_data->sum_check)){
|
||
continue;
|
||
}
|
||
|
||
if(net_ota_data->status == NET_UPDATE_STATUS_OK){//升级完成状态,才显示100%,提示升级成功
|
||
printf("net update complet >>> 100%% \r\n");
|
||
printf("net update ok \r\n");
|
||
break;
|
||
}else if(net_ota_data->status == NET_UPDATE_STATUS_ERR){//升级出错状态
|
||
printf("net update err \r\n");
|
||
err = -1;
|
||
break;
|
||
}else if(net_ota_data->status == NET_UPDATE_STATUS_SAME){//升级同一个固件,没有必要升级
|
||
printf("net update all same \r\n");
|
||
break;
|
||
}else if(net_ota_data->status != NET_UPDATE_STATUS_READ){//不是读取则继续
|
||
//读取请求的总长度
|
||
if(net_ota_data->all_size){//all_size为本次升级的总字节数,不需要读文件操作
|
||
net_file_all_size = net_ota_data->all_size;//收到all_size,代表设备已不可恢复
|
||
}
|
||
continue;
|
||
}
|
||
|
||
//收到read为0,或者状态不是读取 说明设备异常,终止升级
|
||
if(!net_ota_data->read_size){
|
||
printf("net update read_size err\r\n");
|
||
err = -1;
|
||
break;
|
||
}
|
||
printf("---> update %d%%\n",net_ota_data->download_percent);
|
||
if(fd){
|
||
//偏移文件地址,并读取对应字节数
|
||
if(net_ota_data->seek_addr){
|
||
fseek(fd, net_ota_data->seek_addr, SEEK_SET);
|
||
if((net_ota_data->seek_addr + net_ota_data->read_size) > file_size){//检测读取超件大小
|
||
net_ota_data->read_size = file_size - net_ota_data->seek_addr;
|
||
}
|
||
}else{
|
||
if((ftell(fd) + net_ota_data->read_size) > file_size){//检测读取超文件大小
|
||
net_ota_data->read_size = file_size - net_ota_data->seek_addr;
|
||
}
|
||
}
|
||
len = fread(net_ota_data->buf, 1, net_ota_data->read_size, fd);
|
||
if(len == net_ota_data->read_size){
|
||
//生成发送数据头部和数据的和校验
|
||
net_ota_data->sum_check = sumget((char*)net_ota_data + sizeof(net_ota_data->sum_check), net_ota_data->read_size + sizeof(struct net_ota) - sizeof(net_ota_data->sum_check));
|
||
slen = net_ota_data->read_size + sizeof(struct net_ota);
|
||
//发送数据
|
||
len = send(sk_fd, net_ota_data, slen, 0);
|
||
if(len != slen){//socket出错
|
||
printf("net send err = %d!!!\n",len);
|
||
err = -1;
|
||
break;
|
||
}
|
||
}else{//文件出错
|
||
memset(net_ota_data, 0, sizeof(struct net_ota));//读文件出错,发送读字节为0给服务器
|
||
send(sk_fd, net_ota_data, sizeof(struct net_ota), 0);
|
||
err = -1;
|
||
printf("fread err !!!\n");
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if(err){
|
||
socket_send_stop(net_ota_data);
|
||
if(net_file_all_size){//收到all_size,代表设备已不可恢复,此时APP提示:可以重连wifi重新升级
|
||
printf("please connet WIFI_OTA_XXXX to do again \r\n");
|
||
}else{//没有收到all_size,则设备可以恢复原来
|
||
printf("please reset device to do again \r\n");
|
||
}
|
||
}
|
||
if(net_ota_data){
|
||
free(net_ota_data);
|
||
net_ota_data = NULL;
|
||
}
|
||
if(fd){
|
||
fclose(fd);
|
||
fd = 0;
|
||
}
|
||
exit:
|
||
if(sk_fd){
|
||
shutdown(sk_fd,2);
|
||
closesocket(sk_fd);
|
||
sk_fd = 0;
|
||
}
|
||
printf("======= close socket =======\n");
|
||
return err;
|
||
}
|
||
|
||
|
||
int main()
|
||
{
|
||
int index=0;
|
||
WSADATA wsa;
|
||
WSAStartup(MAKEWORD(2,2),&wsa);
|
||
socket_loop();
|
||
while(1){
|
||
Sleep(1000);
|
||
}
|
||
WSACleanup();
|
||
return 0;
|
||
}
|
||
|
||
|
||
//===================================5、设备端开发====================================================//
|
||
设备端程序:net_single_backup_update.c
|
||
1、app_config.c开启:CONFIG_DOUBLE_BANK_ENABLE 为 0,可支持单备份wifi网络升级
|
||
2、在接收到"NET_OTA_SINGLE"后,设备执行:net_ota_single_back_init(),建立3335端口的TCP监听,等待APP连接
|
||
(这个步骤在windows测试则,可以直接在设备wifi启动后执行net_ota_single_back_init(),比如在wifi_app_task.c的net_app_init函数,则就不需要发命令进入)。
|
||
3、在请求升级文件数据,设备的第一阶段升级文件读取是从文件前往后读取(但在第二阶段的ota.bin的net_ota.bin是从文件后往前读取,一般客户忽略此说明),
|
||
设备的应用层程序net_single_backup_update.c的NET_OTA_FILE_RESV宏控制。
|
||
4、在设备第一阶段升级完成后,会把VM地址和VM长度传进第二阶段使用wifi信息,详情:update.c的update_mode_api_v2函数的vm_reverse_addr_size_get()。
|
||
5、设备第一阶段升级后需要断开所有wifi,函数wifi_all_sta_discon(),否则重启后有些手机或者其他设备没有断开,tcp无法通信。
|
||
6、如在第二阶段,APP无法连接,则需要重启APP。
|
||
|