EC800_FTP_OTA(7027).c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. /* 连接到FTP服务器 ,从FTP服务器中下载文件,下载逻辑 */
  2. /* includes ----------------------------------------------------------*/
  3. #include "EC800_FTP_OTA.h"
  4. /* typedef -----------------------------------------------------------*/
  5. typedef struct{
  6. const char *account; // 用户名
  7. const char *passWord; // 密码
  8. uint8_t fileType; // 文件类型 1;ASCII
  9. uint8_t transmode; // 传输模式 1:被动
  10. uint8_t rsptimeout; // 最大响应时间 90
  11. const char * ftpAddr; // ftp服务器地址
  12. uint16_t ftpPort; // ftp服务器端口
  13. const char * textDirectory; // 下载文件的文件所在目录
  14. const char *textName; // 下载文件的文件名称
  15. uint32_t filesize; // 下载的文件大小
  16. uint8_t startAddr; // 下载文件的起始字节
  17. uint8_t byteNum; // 一次获取的字节个数
  18. }ftpInfo;
  19. ftpInfo s_ftpInfo = {
  20. .account = "cdzupdate",
  21. .passWord = "cdz2021!Z",
  22. .fileType = 1,
  23. .transmode = 1,
  24. .rsptimeout = 90,
  25. .ftpAddr = "39.98.211.244",
  26. .ftpPort = 21,
  27. .textDirectory = "/data/cdz",
  28. .textName = "centralCtrSys.bin",
  29. };
  30. /* define ------------------------------------------------------------*/
  31. // 登陆到FTP服务器
  32. // 第一步:配置和激活 PDP 上下文
  33. #define AT_QIACT_1 "AT+QIACT=1\r\n" // 激活 PDP 上下文 1
  34. #define AT_QIACT_query "AT+QIACT?\r\n" // 查询 PDP 上下文状态
  35. #define AT_QFTPCFG_ID_1 "AT+QFTPCFG=\"contextid\",1\r\n" // 查询 PDP 上下文状态
  36. // 第二步: 配置用户账号和传输设置
  37. #define AT_QFTPCFG_ACCOUNT(account_id,password) "AT+QFTPCFG=\"account\",\"" #account_id "\",\"" #password "\"\r\n" // 设置用户名和密码。
  38. #define AT_QFTPCFG_FILE(fileType) "AT+QFTPCFG=\"filetype\"," #fileType "\r\n" // 设置文件类型为ASCII AT+QFTPCFG="filetype",1
  39. #define AT_QFTPCFG_TRANS(mode) "AT+QFTPCFG=\"transmode\"," #mode "\r\n" // 设置为被动传输方式AT+QFTPCFG="transmode",1
  40. #define AT_QFTPCFG_TIMEOUT(time) "AT+QFTPCFG=\"rsptimeout\"," #time "\r\n" // 设置最大响应时间(默认为90秒)
  41. //第三步:登录FTP服务器。
  42. #define AT_QFTPOPEN(addr,port) "AT+QFTPOPEN=\"" #addr "\"," #port "\r\n" // 登录FTP服务器。
  43. // 从FTP服务器下载文件 本项目文件较小,选择直接通过COM口输出下载数据
  44. #define AT_QFTPCWD(directory) "AT+QFTPCWD=\"" #directory "\"\r\n" // 设置当前目录。
  45. #define AT_QFTPSIZE(textName) "AT+QFTPSIZE=\"" #textName "\"\r\n" // 查询 FTP(S)服务器 test_my1.txt 文件大小
  46. #define AT_QFTPGET(textName, startByte, downloadNum) \
  47. "AT+QFTPGET=\"" #textName "\",\"COM:\"," #startByte "," #downloadNum "\r\n" // 下载文件, 通过COM口输出特定字段的数据
  48. /* macro -------------------------------------------------------------*/
  49. /* variables ---------------------------------------------------------*/
  50. /* function prototypes -----------------------------------------------*/
  51. /* 1.登陆到FTP服务器--------------------------------------------------------*/
  52. static uint8_t logIn_step = 0;
  53. /**
  54. * @brief 通过EC800登录FTP
  55. * @param NONE
  56. * @note NONE
  57. * @retval 无
  58. */
  59. void EC800_LogIn_FTP(void){
  60. char* found = NULL;
  61. uint8_t errorCnt = 0; // 错误计数
  62. ftpInfo *p_ftpInfo = &s_ftpInfo;
  63. p_ftpInfo->fileType = p_ftpInfo->fileType; // 避免报警告
  64. switch(logIn_step){
  65. case 0: // 配置和激活 PDP 上下文
  66. EC800M_SendCommand(AT_QIACT_1);
  67. found = EC800M_RecRespond(g_usart3_rx_buf, AT_RESP_OK);
  68. if (found != NULL) {
  69. printf("Activation successful\r\n");
  70. }else{
  71. errorCnt++;
  72. printf("Activation unsuccessful\r\n");
  73. }
  74. EC800M_SendCommand(AT_QIACT_query);
  75. EC800M_RecRespond(g_usart3_rx_buf, AT_RESP_OK);
  76. found = EC800M_RecRespond(g_usart3_rx_buf, AT_RESP_OK);
  77. if (found != NULL) {
  78. printf("PDP Context Status OK\r\n");
  79. }else{
  80. errorCnt++;
  81. printf("PDP Context Status error\r\n");
  82. }
  83. EC800M_SendCommand(AT_QFTPCFG_ID_1);
  84. found = EC800M_RecRespond(g_usart3_rx_buf, AT_RESP_OK);
  85. if (found != NULL) {
  86. printf("Set PDP context to 1.\r\n");
  87. }else{
  88. errorCnt++;
  89. printf("ERROR! Set PDP context to 1.\r\n");
  90. }
  91. if(errorCnt == 0){
  92. logIn_step = 1;
  93. }
  94. break;
  95. case 1: // 配置用户账号和传输设置
  96. EC800M_SendCommand(AT_QFTPCFG_ACCOUNT(p_ftpInfo->account, p_ftpInfo->passWord));
  97. found = EC800M_RecRespond(g_usart3_rx_buf, AT_RESP_OK);
  98. if (found != NULL) {
  99. printf("FINISH! Set username and password.\r\n");
  100. }else{
  101. errorCnt++;
  102. printf("ERROR! Set username and password.\r\n");
  103. }
  104. EC800M_SendCommand(AT_QFTPCFG_FILE(p_ftpInfo->fileType));
  105. found = EC800M_RecRespond(g_usart3_rx_buf, AT_RESP_OK);
  106. if (found != NULL) {
  107. printf("FINISH! Set username and password.\r\n");
  108. }else{
  109. errorCnt++;
  110. printf("ERROR! Set username and password.\r\n");
  111. }
  112. EC800M_SendCommand(AT_QFTPCFG_TRANS(p_ftpInfo->transmode));
  113. found = EC800M_RecRespond(g_usart3_rx_buf, AT_RESP_OK);
  114. if (found != NULL) {
  115. printf("FINISH! Set to passive transfer mode.\r\n");
  116. }else{
  117. errorCnt++;
  118. printf("ERROR! Set to passive transfer mode.\r\n");
  119. }
  120. EC800M_SendCommand(AT_QFTPCFG_TIMEOUT(p_ftpInfo->rsptimeout));
  121. found = EC800M_RecRespond(g_usart3_rx_buf, AT_RESP_OK);
  122. if (found != NULL) {
  123. printf("FINISH! Set username and password.\r\n");
  124. }else{
  125. errorCnt++;
  126. printf("ERROR! Set username and password.\r\n");
  127. }
  128. if(errorCnt == 0){
  129. logIn_step = 1;
  130. }
  131. break;
  132. case 2: // 登录FTP服务器。
  133. EC800M_SendCommand(AT_QFTPOPEN(p_ftpInfo->ftpAddr, p_ftpInfo->ftpPort));
  134. found = EC800M_RecRespond(g_usart3_rx_buf, AT_RESP_OK);
  135. found = EC800M_RecRespond(g_usart3_rx_buf, "+QFTPOPEN: 0,0");
  136. if (found != NULL) {
  137. printf("FINISH! Log in to the FTP server.\r\n");
  138. }else{
  139. errorCnt++;
  140. printf("ERROR! Log in to the FTP server.\r\n");
  141. }
  142. if(errorCnt == 0){
  143. logIn_step = 3;
  144. }
  145. break;
  146. default :
  147. break;
  148. }
  149. }
  150. /* 2.从FTP服务器上下载文件--------------------------------------------------------*/
  151. //int extract_data(const char* input, char* output, size_t output_size) {
  152. // const char* start_marker = "CONNECT\r\n";
  153. // const char* end_marker = "\r\nOK";
  154. // const char* start;
  155. // const char* end;
  156. //
  157. // // 寻找开始标记
  158. // start = strstr(input, start_marker);
  159. // if (start == NULL) {
  160. // return HAL_ERROR;
  161. // }
  162. //
  163. // // 移动指针越过开始标记
  164. // start += strlen(start_marker);
  165. //
  166. // // 寻找结束标记
  167. // end = strstr(start, end_marker);
  168. // if (end == NULL) {
  169. // return HAL_ERROR;
  170. // }
  171. //
  172. // // 计算要提取数据的长度
  173. // size_t data_length = end - start;
  174. // if (data_length >= output_size) { // 确保输出缓冲区能够存放提取的数据
  175. // return HAL_ERROR;
  176. // }
  177. //
  178. // // 提取数据
  179. // memcpy(output, start, data_length);
  180. //
  181. // // 在输出字符串最后添加一个null终止符
  182. // output[data_length] = '\0';
  183. //
  184. // return HAL_OK;
  185. //}
  186. int extract_data_as_uint32(const char* input, uint32_t* output, size_t output_size) {
  187. const char* start_marker = "CONNECT\r\n";
  188. const char* end_marker = "\r\nOK";
  189. const char* start;
  190. const char* end;
  191. // 寻找开始标记
  192. start = strstr(input, start_marker);
  193. if (start == NULL) {
  194. return HAL_ERROR;
  195. }
  196. // 移动指针越过开始标记
  197. start += strlen(start_marker);
  198. // 寻找结束标记
  199. end = strstr(start, end_marker);
  200. if (end == NULL) {
  201. return HAL_ERROR;
  202. }
  203. // 计算要提取数据的长度
  204. size_t data_length = end - start;
  205. // 确保提取的数据长度为4的倍数,适用于 uint32_t
  206. if (data_length % sizeof(uint32_t) != 0) {
  207. return HAL_ERROR;
  208. }
  209. // 计算 uint32_t 的数量
  210. size_t data_length_uint32 = data_length / sizeof(uint32_t);
  211. if (data_length_uint32 > output_size) { // 确保输出缓冲区能够存放提取的数据
  212. return HAL_ERROR;
  213. }
  214. // 提取数据并转换为 uint32_t 数组
  215. memcpy(output, start, data_length_uint32 * sizeof(uint32_t));
  216. return HAL_OK;
  217. }
  218. void UpdateFirmware(void) {
  219. char* found = NULL;
  220. ftpInfo *p_ftpInfo = &s_ftpInfo;
  221. uint32_t totalBytesReceived = 0;
  222. uint32_t currentFlashAddress = APP1_ADDRESS;
  223. uint32_t buffer[FLASH_PAGE_SIZE / sizeof(uint32_t)]; // 2KB缓冲区
  224. uint8_t returnTemp = 0; // 用于接收返回值的临时变量
  225. while (totalBytesReceived < (p_ftpInfo->filesize)) {
  226. // 计算剩余要接收的字节数
  227. uint32_t remaining = (p_ftpInfo->filesize) - totalBytesReceived;
  228. uint32_t bytesToRead = remaining < FLASH_PAGE_SIZE ? remaining : FLASH_PAGE_SIZE;
  229. p_ftpInfo->startAddr = totalBytesReceived;
  230. p_ftpInfo->byteNum = bytesToRead;
  231. // 发送AT指令以开始接收
  232. EC800M_SendCommand(AT_QFTPGET(p_ftpInfo->textName, p_ftpInfo->startAddr, p_ftpInfo->byteNum));
  233. EC800_recEnable(); // 开始接收数据
  234. found = strstr(g_usart3_rx_buf, AT_RESP_OK);
  235. while(!found) osDelay(1);
  236. // 接收数据处理到buffer 剔除除去固件的其他的数据
  237. returnTemp = extract_data_as_uint32(g_usart3_rx_buf, buffer, FLASH_PAGE_SIZE / sizeof(uint32_t));
  238. if(returnTemp != HAL_OK){
  239. printf("ERROR! UpdateFirmware_extract_data\n");
  240. break;
  241. }
  242. // 将接收到的数据写入Flash
  243. if (FLASH_Write(currentFlashAddress, buffer, FLASH_PAGE_SIZE / sizeof(uint32_t)) != HAL_OK) {
  244. // 错误处理
  245. printf("ERROR! UpdateFirmware_FLASH_Write\n");
  246. break;
  247. }
  248. // 更新地址和接收统计
  249. currentFlashAddress += FLASH_PAGE_SIZE;
  250. totalBytesReceived += bytesToRead;
  251. }
  252. // 根据实际情况实现接收完成后的操作
  253. }
  254. uint8_t downloadStep = 0;
  255. uint8_t textBuff[2048] = {0}; // 下载的文件缓冲区
  256. /**
  257. * @brief FTP服务器上下载文件
  258. * @param NONE
  259. * @note NONE
  260. * @retval 无
  261. */
  262. void EC800_FTP_DownloadText(void){
  263. char* found = NULL;
  264. uint8_t errorCnt = 0; // 错误计数
  265. ftpInfo *p_ftpInfo = &s_ftpInfo;
  266. switch(downloadStep){
  267. case 0: // 设置文件目录
  268. EC800M_SendCommand(AT_QFTPCWD(p_ftpInfo->textDirectory));
  269. found = EC800M_RecRespond(g_usart3_rx_buf, AT_RESP_OK);
  270. found = EC800M_RecRespond(g_usart3_rx_buf, "+QFTPCWD: 0,0");
  271. if (found != NULL) {
  272. printf("FINISH! Set the file directory.\r\n");
  273. }else{
  274. errorCnt++;
  275. printf("ERROR! Set the file directory.\r\n");
  276. }
  277. if(errorCnt == 0){
  278. downloadStep = 1;
  279. }
  280. break;
  281. case 1: // 获取文件长度
  282. EC800M_SendCommand(AT_QFTPSIZE(p_ftpInfo->textName));
  283. found = EC800M_RecRespond(g_usart3_rx_buf, AT_RESP_OK);
  284. EC800_recEnable();
  285. // 使用sscanf从字符串中解析整数
  286. if(sscanf(g_usart3_rx_buf, "+QFTPSIZE: %*d,%u", &(p_ftpInfo->filesize)) == 1) {
  287. // 解析成功,filesize变量现在包含值1000
  288. printf("The filesize is: %u\n", p_ftpInfo->filesize);
  289. } else {
  290. // 解析失败
  291. errorCnt++;
  292. printf("Failed to parse the filesize.\n");
  293. }
  294. if(errorCnt == 0){
  295. downloadStep = 2;
  296. }
  297. break;
  298. case 2:
  299. UpdateFirmware();
  300. break;
  301. default :
  302. break;
  303. }
  304. }
  305. /* 3. OTA升级从FTP流程--------------------------------------------------------*/
  306. uint8_t upgradeStep = 0;
  307. /**
  308. * @brief OTA升级从FTP
  309. * @param NONE
  310. * @note NONE
  311. * @retval 无
  312. */
  313. void EC800_FTP_OTA_Upgrade(void){
  314. switch (upgradeStep){
  315. case 0: // 登陆到FTP服务器
  316. EC800_LogIn_FTP();
  317. if(logIn_step == 3){
  318. upgradeStep = 1;
  319. }
  320. break;
  321. case 1: // 从FTP服务器上下载文件
  322. EC800_FTP_DownloadText();
  323. break;
  324. default:
  325. break;
  326. }
  327. }