网络做事器多种多样,常用的有阿里云、百度云、腾讯云、移动云等等,有条件的,还可以利用自己的做事器。总之须要实现:网络做事器可以与我们的无线模块进行大数据通信。 我这里选用的是OneNet移动云(OTA做事之前是免费,现在是前100个设备免费,之后每增加一个设备1元钱永久),我觉得OneNet相对付阿里云较为大略,没有阿里云那么繁琐,不过阿里云还是比OneNet更专业一点(个人见地),其他的没有用过,大家都可以去试试。
我这里利用的是OneNet的做事器,它的OTA做事是通过Http协议进行传输的,有对应的API,我们可以通过OneNet开释的API去访问OTA做事。

OneNet的OTA升级流程紧张为6步: 1. 上报版本号---客户端(MCU)上报当前的一个版本号 2. 检测升级任务---检讨做事器是否有待升级的版本 3. 检测Token有效性---检讨Token密钥,可省略 4. 下载固件---运用程序传输 5. 上报升级状态---上报做事端升级是否成功,不堪利有对应的相应码
六. OneNet做事端配置
1.首先注册OneNet的账号,进入开拓者中央,在导航栏选择全部产品->远程升级OTA板块。
2.进入远程升级OTA界面,选择须要升级的模块;然后点击右上角的添加升级包按钮。FOTA升级:对设备中的模组进行升级。SOTA升级:对设备中的运用程序进行升级,我这里选用的是SOTA,由于我要对MCU的运用程序升级。
3.在添加升级包对话框中,输入固件信息,上传固件包文件。产品选你要升级的设备,全部设备也可以;厂商名称选其他,紧张是与之后发的对应上即可;模组型号同理;目标版本是你要更新到的版本号,比如你现在是V01,你这里添加的固件是V02的,这个版本号就要填V02;然后上传升级包,只支持Bin和压缩包格式的。
4.点击验证升级按钮,选择验证类型(完全包或者差分包),选择进行测试升级的设备,进行验证。一样平常跳过验证就行,我这里选的是整包,差分包事理一样。
5.单击升级设备列表,进入升级行列步队模块,在右上角单击添加升级设备按钮,新增设备升级任务。在添加待升级设备对话框中输入对应参数值。初始版本:便是升级前的版本,也是上次升级的版本;升级范围便是你须要给哪些设备升级;升级机遇:便是立即升级或是定时在什么时段升级;重试策略:不重试便是如果升级失落败就完事了,重试那就失落败了还能重试;旗子暗记强度和剩余电量只是一个信息的接口,有须要的可以读取来用。
6.上述完成后,会涌现“待升级”的设备,做事器这边就算配置完了,后续要我们M客户端进行操作了。
七.客户端(MCU)API访问做事端进行OTA升级
无线模组用的是ESP8266,由于OneNet的OTA做事用的是HTTP协议,但是ESP8266没有HTTP协议,以是我利用TCP协议,封装成HTTP的报文格式。1.ESP8266初始化;连接Wifi,AP_SSID,AP_PASS是WiFi的账号和密码;SERVER_IP和SERVER_PORT是OneNet的Ip和端口号。
复制
#define SERVER_IP "183.230.40.50"#define SERVER_PORT 80uint8_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演示的例子,代码水平有点差,大家姑息的看,参考一下就可以了哈,感谢!
附件见论坛原文。
原标题:还不会OTA升级?手把手教你基于STM32的BootLoader的OTA远程升级
原作者:小叶三千
本文为21ic有奖征文作品,详情请见21ic论坛活动专区:第二届万元红包——蓝V达人有奖征文活动,如果您也有兴趣参与征文,欢迎进入论坛参与活动~