Python网络编程-远程更新STM32APP程序
来源:互联网 发布:r语言编程代码 编辑:程序博客网 时间:2024/06/14 11:25
目标:设计STM32的BootLoader程序,实现STM32远程更新APP程序。
工具:STM32ZET6和SIM800c模块(客户端),一个可以上网的SIM卡,一个有外网的电脑(服务器端)。
思路:stm32通过SIM800c的GPRS通信模块可以与服务器进行通信,服务器端发送APP程序,STM32接收并写入特定地址的flash中,接收完毕后在flash中固定位置处运行,即可实现APP程序的远程更新。服务器和STM32通信可通过简单的SOCKET进行。首先需要了解一些基础知识。
TCP/UDP协议
在了解socket通信之前,需要了解一下什么是TCP协议。TCP/IP事实上是一些协议(protocols)的合集。当前大多数使用中的通信都是使用TCP协议。为了实现共享,TCP是通过把需要发送的数据流分解为很多小信息包在Internet上传输的,而这些信息包到了接收者的地方会再次合成在一起。通过分解成小的信息包,其他程序的信息包也可以同时被传送。
TCP需要远程识别机器,每台机器都有一个唯一的IP的地址,通过端口号可以知道是这台电脑的哪个程序在发送和接收信息包。所以每个TCP连接的端点是由一个IP地址和一个端口号来唯一标识的。
TCP是一个可靠的协议,除非整个网络出了问题,数据将完好的正确的传输到另一端。
此外还有UDP也被广泛的使用,经常被用来从一个系统向其他系统传送非常短的消息。只能保证接收到的数据是完整的,UDP比TCP低级,即不能保证数据是否真的能被接收到,也不能保证数据是不是只接收一次,还不能保证接收到的信息次数是否和发送时候的一致,但是优点是,由于它不能提供上面的那些保证,所以比TCP低级,TCP建立和关闭连接要花费时间,而UDP对连接没有什么概念,不存在花费时间建立和关闭连接的问题。
对于我们的问题,需要可靠的数据传输,而且需要传输大量的数据,所以需要选择TCP协议。
Python socket通信
在网络编程中的一个基本组件就是套接字(socket)。套接字基本上是两个端点的程序之间的“信息通信”。在Python中的大多数网络编程都隐藏了socket模块的基本细节,不直接和套接字交互。
套接字包括服务器套接字和客户机套接字。对于我们的问题,首先需要建立服务器套接字,直接运用Python的socket模块。
服务器端程序
思路是这样的:
1.首先运用socket模块中的socket类实例化一个socket对象,设置通信类型为IPv4,协议家族为TCP(socket类示例化默认参数即为IPv4和TCP);
2.设置socket选项(如果没有这个选项,当用ctrl+c终止该服务时,需要等上一段时间才能使用同一个端口号,有的时候确实要等很久);
3.然后使用blind方法,再调用listen方法去监听某个特定的地址
以上是最简单的服务器流程,对于我们的服务器,需要读取APP文件,即后缀为.bin的文件,一般文件的大小为几k~几百k,需要将文件的内容分段发送,否则一次发送大量文件会出现丢包的情况,比如我将文件分为若干个1024个字符,每次发送1024个字符,字符前面加上特定的序列表示此次发送的是文件中的那一段,在发送完成后,需要一个接收函数,接收stm32端返回的序列,如果1s内没有接收到返回,则重新发送这段序列的内容,直到接收到返回的序列,代表发送完成。
具体的程序如下:
import socketimport osimport timeimport mathsk = socket.socket() #sk.settimeout(1)sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)#当程序终止后,端口释放sk.bind(("182.92.108.54",9995)) sk.listen(5)waitTime = 1 #接收等待时间为1s,可以根据自己的要求来设定while True: print("wait for connecting... ... ....") conn,address = sk.accept() print("connected!") #获取文件大小 dataSize = os.stat("RTCNew.bin").st_size #conn.sendall(str(dataSize)) print("Data Size:%d" % dataSize) sendSizeOnce = 1024 sendOnce = [] #time.sleep(5) #发送包的格式为:序号+“;”+数据,便于客户端知道传送的是哪个数据段 sendOnce = str(0)+";"+str(dataSize) print("send line:0 ") #先将文件大小包发送至客户端 while True: try: print("try send data to client") conn.sendall(sendOnce) time.sleep(waitTime) recvData = conn.recv(1024,socket.MSG_DONTWAIT)#如果没有接收到数据,则会触发错误,直接进入except中 print("%s" %recvData) if recvData == '0':#如果满足条件才退出 print("recvDataFromClientSuccess:%s,complete data sending" %recvData) break except: print("Not data received by client,send again... ...") #将文件拆分为若干个1024个字节再发送 with open("RTCNew.bin","rb") as f: allData = f.read() dataLength = 0 numSend = int(math.ceil(dataSize/float(sendSizeOnce))) for lineIndex in range(numSend): if lineIndex != numSend: sendOnce = str(lineIndex+1) + ";"+allData[lineIndex * sendSizeOnce:(lineIndex+1) * sendSizeOnce]; else: sendOnce = str(lineIndex+1) + ";"+allData[lineIndex * sendSizeOnce:dataSize]; dataLength += len(sendOnce) - len(str(lineIndex+1)) - 1 print("send line:%d data Length:%d Complete: %dpercent" %(lineIndex+1,dataLength,dataLength * 100/dataSize)) #sendOnce = [] while True: try: conn.sendall(sendOnce) time.sleep(waitTime) recvData = conn.recv(1024,socket.MSG_DONTWAIT) if recvData == str(lineIndex+1): print("recvDataFromClientSuccess:%s,complete data sending" %recvData) break except: print("Not data received by client,send again... ...")
客户端
思路是这样的:
1.STM32连接SIM800C模块,需要插入一个能上网的SIM卡;
2.通过TCP连接服务器端;
3.接收到数据,将序号立刻返回至服务器端;
4.将APP数据合并为16位数据,写入flash特定地址中;
5.重复3,4步直到所有数据接收完成;
6.从flash特定地址运行flash中的程序。
具体的实现方法可以参考这篇文章以及正点原子的《STM32F1开发指南(精英版)-库函数版本_V1.0》有关IAP的实验和《AN1604B ATK-SIM800C GSM/GPRS 模块使用说明》.
STM32核心代码如下:
if(USART3_RX_STA&0X8000)//接收到一次数据 { USART3_RX_BUF[USART3_RX_STA&0X7FFF]=0; //添加结束位 p2=(u8*)strstr((const char*)USART3_RX_BUF,"+IPD");//用p2指针变量指向+IPD出现的位置的首地址 if(p2) { //将USART3_RX_BUF拆分获得数据包的其他信息 p4=(u8*)strstr((const char*)p2,","); p3=(u8*)strstr((const char*)p2,":"); p2=(u8*)strstr((const char*)p2,";"); p2[0]=0;// p3[0]=0; sequence = atoi((char*)(p3+1));//序列号 //序号为0时为文件的大小 if(sequence == 0) { appLengthRecv = atoi((char*)(p2+1)); printf("recv data length:%d\r\n",appLengthRecv); } if(sequence >= 1 && sequence < 10) appLengthOnceRecv = atoi((char*)(p4+1)) - 2; if(sequence >= 10 && sequence < 100) appLengthOnceRecv = atoi((char*)(p4+1)) - 3; if(sequence >= 100) appLengthOnceRecv = atoi((char*)(p4+1)) - 4; printf("序号:%d len:%d\r\n",sequence,appLengthOnceRecv); if(sequence >= 1) { for(t=0;t<appLengthOnceRecv;t+=2) { //变为二字节 temp=(u16)p2[2+t]<<8; temp+=(u16)p2[1+t]; recvBuf[iIndex++]=temp; } fwaddr=FLASH_APP1_ADDR + 1024 * (sequence -1);//偏移量 STMFLASH_Write(fwaddr,recvBuf,iIndex); sumOnceRecv+=appLengthOnceRecv; iIndex = 0; } USART3_RX_STA=0; //将序列返回至服务器端 sim800c_send_cmd("AT+CIPSEND",">",500); //·¢ËÍÊý¾Ý u3_printf((char*)(p3+1)); if(sim800c_send_cmd((u8*)0X1A,"SEND OK",1000)==0);//printf("Send!\r\n"); if(sumOnceRecv == appLengthRecv) { printf("写入flash完成\r\n"); break; } USART3_RX_STA=0; } USART3_RX_STA=0; }
参考文献:
JohnGoerzen. Python网络编程基础[M]. 电子工业出版社, 2007.
- Python网络编程-远程更新STM32APP程序
- [python&php 网络编程]获取远程机器的信息
- Black Hat Python 之 网络编程的Helloworld级别程序
- LWIP实现网络远程IAP下载更新
- python远程调试Django程序
- pycharm远程开发python程序
- Android在线更新 远程安装程序
- Android在线更新 远程安装程序
- linux网络程序编程
- 程序媛:网络编程
- Python网络编程例子
- Python的网络编程
- python网络编程
- Python网络编程
- Python的网络编程
- python_网络编程
- Python PycURL 网络编程
- python中的网络编程
- centos7安装AMD显卡驱动和AMD opencl SDK的过程
- IntelliJ IDEA 2017 破解
- Flex布局详解
- springmvc整合dubbo分布式系统的搭建demo
- Spring乱码,forceEncodeing=true
- Python网络编程-远程更新STM32APP程序
- request.setattribute怎么在jsp取值
- iOS中的KVC和KVO的理解
- 天文学中常用的坐标系
- 【Java】解决FTPClient上传中文目录出现中文名乱码问题
- 释放Windows为硬件保留内存,双系统安装释放法。
- VS2013/MFC编程:(对话框:为控件添加消息处理函数)
- hive的row_number()、rank()和dense_rank()的区别以及具体使用
- struts2学习之---国际化