- #define SERVER_IP "183.230.40.50"
- #define SERVER_PORT 80
- uint8_t pro = 0;
- uint8_t ESP8266_Init(void)
- {
- switch(pro)
- {
- case 0 :
- //printf("+++");
- Uart2_Send("+++");
- Delay_S(2);
- if(ESP8266_SoftReset(50) == 0)
- pro = 1;
- break;
- case 1 :
- if(ESP8266_AT_Send("ATE0\r\n",10) == 0)
- pro = 2;
- break;
- case 2 :
- if(ESP8266_AT_Send("AT+CWMODE=1\r\n",50) == 0) //设置8266为STA模式
- pro = 3;
- break;
- case 3 :
- if(ESP8266_ConnectionAP(AP_SSID,AP_PASS,200) == 0) //8266连接AP
- pro = 4;
- break;
- case 4 :
- if(ESP8266_AT_Send("AT+CIPMODE=1\r\n",50) == 0) //8266开启透传模式
- pro = 5;
- break;
- case 5 :
- if(ESP8266_Connect_Server(SERVER_IP,SERVER_PORT,50) == 0) //8266连接TCP服务器
- {
- pro = 0;
- //USART1_Clear(); //清除串口数据
- return 1;
- }
- break;
- }
- return 0;
- }
2.上报版本号;dev_id是设备ID,authorization是鉴权参数,ver要上报的版本号,timeout发送超时时间
- //上报版本号
- uint8_t Report_Version(char *dev_id,char *authorization,char *ver,uint16_t timeout)
- {
- uint16_t time=0;
- char send_buf[296];
- USART1_Clear(); //清除串口数据
- snprintf(send_buf, sizeof(send_buf), "POST /ota/device/version?dev_id=%s HTTP/1.1\r\n"
- "Authorization:%s\r\n"
- "Host:ota.heclouds.com\r\n"
- "Content-Type:application/json\r\n"
- "Content-Length:%d\r\n\r\n"
- "{"s_version":"%s"}",
- dev_id, authorization, strlen(ver) + 16, ver);
- Uart2_Send(send_buf);
- while(time<timeout)
- {
- if(strstr( (const char *)usart_info.buf , (const char *)""errno":0"))
- break;
- Delay_Ms(100);
- time++;
- }
- if(time>=timeout)
- return 1;
- else
- return 0;
- }
3.检查升级任务;dev_id是设备ID,authorization是鉴权参数,cur_version是当前的版本号,timeout发送超时时间
- //检查升级任务
- uint8_t Detect_Task(char *dev_id,char *cur_version,char *authorization,uint16_t timeout)
- {
- uint16_t time=0;
- char send_buf[280];
- USART1_Clear(); //清除串口数据
- snprintf(send_buf, sizeof(send_buf), "GET /ota/south/check?"
- "dev_id=%s&manuf=100&model=10001&type=2&version=%s&cdn=false HTTP/1.1\r\n"
- "Authorization:%s\r\n"
- "Host:ota.heclouds.com\r\n\r\n",
- dev_id, cur_version,authorization);
- Uart2_Send(send_buf);
- while(time<timeout)
- {
- if(strstr( (const char *)usart_info.buf , (const char *)""errno":0"))
- break;
- Delay_Ms(100);
- time++;
- }
- if(time>=timeout)
- return 1;
- else
- return 0;
- }
3.下载资源(我省略了"检查token有效"步骤);ctoken是上一步“检查升级任务”返回的Token,这个每次请求都不一样,所以注意要记录;size:平台返回的固件大小(字节);bytes_range:分片大小(字节)
- /*
- ************************************************************
- * 函数名称: OTA_Download_Range
- *
- * 函数功能: 分片下载固件
- *
- * 入口参数: token:平台返回的Token
- * size:平台返回的固件大小(字节)
- * bytes_range:分片大小(字节)
- *
- * 返回参数: 0-成功 其他-失败
- *
- * 说明:
- ************************************************************
- */
- uint8_t Download_Task(char *ctoken,unsigned int size, const unsigned short bytes_range,uint16_t timeout)
- {
- MD5_CTX md5_ctx; //MD5相关变量
- unsigned char md5_t[16];
- char md5_t1[16];
- char md5_result[40];
- uint16_t time=0;
- char *data_ptr = NULL;
- char send_buf[256];
- unsigned char flash_buf[OTA_BUFFER_SIZE]; //flash读写缓存
- unsigned int bytes = 0;
- MD5_Init(&md5_ctx);
- Flash_cashu();
- while(bytes < size)
- {
- time = 0;
- memset(send_buf, 0, sizeof(send_buf));
- USART1_Clear(); //清除串口数据
- snprintf(send_buf, sizeof(send_buf), "GET /ota/south/download/"
- "%s HTTP/1.1\r\n"
- "Range:bytes=%d-%d\r\n"
- "Host:ota.heclouds.com\r\n\r\n",
- ctoken, bytes, bytes + bytes_range - 1);
- Uart2_Send(send_buf);
- //----------------------------------------------------等待数据---------------------------------------------------------------------
- while(time < 30)
- {
- if(usart_info.buf[0] != 0)
- break;
- Delay_Ms(100);
- time++;
- }
-
- if(time <= 29)
- {
- Delay_Ms(500);
- //----------------------------------------------------跳过HTTP报文头、找到固件数据--------------------------------------------------
- data_ptr = strstr( (const char *)usart_info.buf, "Range");
- data_ptr = strstr(data_ptr, "\r\n");
- data_ptr += 4;
-
- //----------------------------------------------------将固件数据写入缓存和闪存-----------------------------------------------------
- if(data_ptr != NULL)
- {
- if((size - bytes) >= OTA_BUFFER_SIZE)
- {
- memcpy(flash_buf + (bytes % OTA_BUFFER_SIZE), data_ptr, bytes_range);
- STMFLASH_Write_NoCheck(FLASH_APP1_ADDR + bytes,(uint16_t *)flash_buf,OTA_BUFFER_SIZE / 2);
- bytes = bytes + OTA_BUFFER_SIZE;
-
- MD5_Update(&md5_ctx, (unsigned char *)data_ptr, bytes_range);
- }
- else
- {
- memcpy(flash_buf + (bytes % OTA_BUFFER_SIZE), data_ptr, size - bytes);
- STMFLASH_Write_NoCheck(FLASH_APP1_ADDR + bytes , (uint16_t *)flash_buf , (size % OTA_BUFFER_SIZE) / 2);
-
- MD5_Update(&md5_ctx, (unsigned char *)data_ptr, size - bytes);
-
- bytes = size;
- }
- }
- }
- }
- //----------------------------------------------------MD校验比对------------------------------------------------------------------
- memset(md5_result, 0, sizeof(md5_result));
- MD5_Final(&md5_ctx, md5_t);
- for(int i = 0; i < 16; i++)
- {
- if(md5_t[i] <= 0x0f)
- sprintf(md5_t1, "0%x", md5_t[i]);
- else
- sprintf(md5_t1, "%x", md5_t[i]);
-
- strcat(md5_result, md5_t1);
- }
- if(strcmp(md5_result, ota_info.md5) == 0)
-
- return 0;
- else
- return 1;
- }
4.上报升级状态;这一步由于时间问题,我也省略了,总之程序已经下载到MCU上了,只是没有通知服务器而已,大家最好还是加上这一步。
5.main函数循环;
- char rrr;
-
- char dev_id[] = {"640600857"};
-
- char Authorization[] = {"version=2018-10-31&res=products%2F378414&et=1735660800&method=sha1&sign=9EgY%2Bk4r%2BlvCooIGf1ghtQFC0%2Bc%3D"};
- char Version[] = {"V10"};
- while(1)
- {
- switch(pro)
- {
- case 1 : //上报版本
- if(Report_Version(dev_id,Authorization,Version,10) == 0)
- pro++;
- break;
- case 2 : //检查任务
- if(Detect_Task(dev_id,Version,Authorization,50) == 0)
- pro++;
- break;
- case 3 : //接收token、size、md5信息
- rrr = json_get_value((char *)usart_info.buf,"token",ota_info.token);
- rrr = json_get_value((char *)usart_info.buf,"size",ota_info.csize);
- rrr = json_get_value((char *)usart_info.buf,"md5",ota_info.md5);
- ota_info.size = atoi(ota_info.csize);
- pro++;
- break;
- case 4 : //进行下载
- res = Download_Task(ota_info.token,ota_info.size,OTA_BUFFER_SIZE,10);
- if(res == 0) //校验成功
- {
- pro++;
- }
- else if(res == 1) //校验失败
- {
- pro = 1;
- }
- break;
- case 5 : //Flash写入升级完成的标志位
- USART1_Clear();
- STMFLASH_Unlock();
- STMFLASH_WriteHalfWord(FLASH_APP1_ADDR - 0x64, 0xFF02);//写入数据
- STMFLASH_Lock();
- pro++;
- break;
- case 6 : //复位或者跳转到APP
- Sys_Soft_Reset();
- //iap_load_app(FLASH_APP1_ADDR);
- break;
- }
- }
下图是我升级的历史
八.注意事项
1.鉴权参数是需要自己去算的,具体算法请见我之前写的帖子和附件(https://bbs.21ic.com/icview-3144666-1-1.html)
2.由于用的是STM32F030F4P6,RAM也非常小,所以局部变量和全局变量的数组不要超过4K,堆栈大小有改动。当前用内存管理的话就不用了。
3.OTA校验用的是MD5,需要把MD5的算法移植一下。
4.别的想不到了,太长时间了。
总结:
OTA的方法只是我个人的理解,可能有的地方不正确,欢迎大家指点。BootLoader代码也是很早之前写过的一个Demo,最简化的,传输协议、加密、升级失败的操作、回滚等等都没有涉及,只是一个OTA演示的例子,代码水平有点差,大家将就的看,参考一下就可以了哈,感谢!