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.

原创粉丝点击