asp向串口发送_Python自动化调用串口实操指南

63e8a51d41376272bcde114f399abf1d.png

我在调试工作中经常用到串口,为了实现自动化做了一些尝试,

下文总结了,操作串口涉及的动作如何自动化地执行的方法

主要由这以下几个话题:

1、创建串口对象,链接串口

2、经过串口收发字符串

3、串口打印解码(读取中英文打印)

4、等待串口出现某个字符串,计算等待时间

5、以十六进制形式向串口发送字符

6、以十六进制形式向串口发送某个文件

7、自动打开串口UI窗口,方便手动操作

8、自动启动 secureCRT 记录log

下面将实操的过程记录如下

一、创建串口对象

import serial
s_obj = serial.Serial(comPort, baudrate=115200, bytesize=8, parity='N', stopbits=1, timeout=1)

这里面 comPort 是串口的标志字符串,

比如在windows下是类似这样的文本"com1" , 在linux 下是类似这样的字符串 "/dev/ttyUSB1"

windows 下启动设备管理器查看 端口可以查看字符串

b37a44dee19c00714a726a29a5b021a9.png

linux 下查看可用的串口,使用如下命令查询

ls -l /dev/*

一般情况下,linux如果使用的是默认的串口,串口名称是dev下的ttyS* ,一般ttyS0对应com1,ttyS1对应com2

按照上面的写法,可以创建一个串口通信的对象,下面讲讲如何使用它

二、串口收发字符串

2.1 发送字符串

为了经过串口发送字符串,可以使用下面的写法

text = "pwd" 
s_obj.write(text.encode())

这里需要用 write(data) 方法发送字符串命令,需要注意的是,pyserial的文档注明了,write的输入参数必须是bytes 格式的(也就是二进制数据),

这里说明一下,python3里对字符串和二进制数据流有明确的区分,文本总是unicode编码储存的,由str类型表示。二进制数据则由bytes类型表示

所以字符串数据需要encode()函数将其编码为二进制数据,然后才可以顺利发送。(encode方法默认的编码是UTF-8)

这里发送的字符一般是英文短语。后面会说到如何发送十六进制的数据

2.2 接收打印,然后解码

为了从串口接收字符串,可以使用下面的写法

s_obj = serial.Serial(comPort, baudrate=115200, bytesize=8, parity='N', stopbits=1, timeout=2)
data = s_obj.read(150*60)

print_out=data.decode('utf-8','replace')
print(print_out)

print_out=data.decode('gb18030','replace') 
print(print_out)

这里先创建了一个serial对象,紧接着,读取数据data(是二进制数据)。

关于读取这里,说明一下,这里read函数的输入参数是 bytes 的数量 ,如果是UTF-8编码,1个数量可以表示一个英文字母,这里我写的非常大的一个值 150*60。原因有2:

  • 第一个原因是,serial模块的官方文档里是这样写的,如果创建 serial 对象的时候,设置了timeout时间,则读取一个非常大数量的行为会变成,在timeout时间内,能读到多少就读取多少打印。相反,如果没有设置timeout,读取非常大的数量的行为就会让程序block住,一直等着串口打印出足数的字符才罢休。上面我设置了2秒的timeout,所以不会卡住,而是2秒内能收多少字符就收多少(2秒的时间是很长的),到了2秒就停止读取串口打印。
pySerial API - pySerial 3.0 documentation​pythonhosted.org
  • 第二个原因是,这里讨论的场景是串口一次性打印很多数据,可以稍等几秒(比如等待3秒后再来读取数据,并不关心耗费的时间),一次读取大量字符符合这个场景的需求。

接下来对收到的 data 解码,

如果接收到的 data 全是英文,就可以用 utf-8 编码将二进制数据解码为unicode字符串。如果data里包含中文,则最好以 gb18030 编码将二进制数据解码为 unicode 字符串。

需要说明一点的是,

有时候由于带中文,但是强制用 utf-8 解码,或者由于串口的传输线缆出现接触不良等原因,会产生错误或者乱码,如果直接解码,就会报错,为了能够顺畅的解码串口打印,避免这种情况发送,decode的参数里加上 “replace” 即可。它实现的作用是,如果解码的过程中遇到错误,会自动以问号 ? 代替解码失败的字符。

三、计算打印耗时

有时候在接收串口打印的同时,还希望能知道等待了多久才收到某个打印

可以使用下面的写法

target_text 

在这个例子里,使用了 while 循环来不停的尝试,尝试的语句是

if s_obj.in_waiting > 0

serial 对象的in_waiting 属性返回的是串口的接收buffer 收到的字节数,如果大于0表示有东西打印出来,然后就可以继续下面的动作:

开始一个子过程:使用 read(4)方法接收了4个字节,然后解码成 unicode 明文字符,再追加 ser_text_pool 这个字符串里,

最后判断目标字符串在不在 ser_text_pool 里面,子过程结束。

这里子过程里仅接收4个字节,有2个目的:

  • 第一个目的是,设置读取量非常小,可以让一次读取时间尽量的小,这样的话,多次执行子过程就会“一点点”地累加收到串口的打印,每次都会判断有没有收到目标字符串,一旦发现目标字符串,就会计算耗时,这样得到的等待时间比较精确(实操证实了)。
  • 如果设置为2个字符,又有点过短,小概率会出错(解码后累加后出现乱码导致有打印但是匹配不到),所以用4个字节最为合适

下面就是基本操作了,用逻辑语句分开找到和没有找到两种情况,如果需要将每种情况遇到的串口都可以打印出来。

四、以十六进制形式发送

4.1 以十六进制发送字符串

有部分时候,经过串口发送字符串的时候,希望以十六进制的形式发送,

可以用下面的写法实现

cmd = "ef aa 21 00 00 00 00 21" 
ser_obj.write(bytes.fromhex(cmd))

可以看出来,这里面核心的过程是 将十六进制的str类型字符串转换为二进制的bytes类型数据,然后就可以发送了

4.2 以十六进制发送文件

如果发送的内容由简单的字符串,改为二进制文件,情况则稍有不同

可以使用下面的写法实现

def sendbin(ser_obj, ser_baudrate, file_obj,read_step_size=8, sleep_dividend=512, sleep_time=0.0005):
    print("开始写入hex")     
    ser_obj.baudrate = ser_baudrate
    i = 0     
    while 1:         
        c = file_obj.read(read_step_size)         
        ssss = str(binascii.b2a_hex(c))[2:-1]         
        if not c:             
            break         
        ser_obj.write(bytes().fromhex(ssss))  # 将16进制转换为字节         
        if i % sleep_dividend == 0:             
            time.sleep(sleep_time)         
        i += 1     
    print("写入完成")

这个代码的主要内容是:从一个文件里每次读取一点点数据,然后以16进制重新编码后写入串口,重复这个过程直到文件发完。

这其中,文件的读取 file.read(byte size) 方法的工作原理是,每次读取完指针就停留在末尾,第二次读取接着上次的指针的位置继续读。所以可以循环执行。

其中信息的传递的主要过程是:

  • c = file.read() 从二进制数据的文件里,读取到变量c里,数据格式是bytes,表示方式是二进制
  • binascii.b2a_hex(c) 这一步操作是将 bytes类型的二进制表示数据,转换为同为bytes类型的16进制表示数据。 输入数据的每个字节都被转换成相应的2位十六进制表示。因此,返回的bytes对象是输入数据长度的两倍。
  • 之后的str(newC)[2:-1] 将16进制的表示,剥离掉了引号,提取出十六进制的明文字符串,此时输出类型为str
  • 最后一步写入的时候是这样写的 bytes().fromhex(ssss) 这个动作是将一个十六进制表示的str 转换为二进制的bytes

以上就是信息传递和写入的一次过程,重复这个过程就可以实现持续的写入文件到串口。

实际上还有最后一个问题,真实的串口是有buffer的,不可能一直持续地写入,如果硬来,就会卡死。

所以需要串口每写一会儿要等待一会儿时间,给串口一个发送的消化时间,这里我写了等待了0.5毫秒,已经足够了,实际过程是每发送512次后停0.5毫秒,接着继续发送,直到读文件结束。

五、自动打开串口UI工具

调试工作中,有时候需要半自动化的操作串口,为人工介入操作提供方便。

一个实现的思路如下:

# -*- coding: utf-8 -*-
import os
import time
import serial.tools.list_ports as lps

def change_ini_and_open_sscom(number, comport):
    print([comport])
    ui_width = 373
    
    with open(r'D:toolssscomsscom51.ini',"r") as f:
        lines = f.readlines() 
    
    with open(r'D:toolssscomsscom51.ini',"w") as f_w:
        for line in lines:
            # 程序启动自动打开串口
            if 'N1063=' in line:
                line = line.replace(line,'N1063=,Yn')
            # 修改串口号
            if 'N1080=' in line:
                line = line.replace(line,'N1080=,{0}n'.format(comport))
            #设置窗口高度
            if 'N1083=' in line: 
                line = line.replace(line,'N1083=,826n')
            #设置窗口最高点的坐标
            if 'N1085=' in line:
                line = line.replace(line,'N1085=,3n')
            #设置窗口宽度
            if 'N1082=' in line:
                line = line.replace(line,'N1082=,%sn' % ui_width)
            #窗口左边坐标
            if 'N1084=' in line:
                line = line.replace(line,'N1084=,%sn' % (ui_width*number) )
            f_w.write(line)
    p = os.popen(r'D:toolssscomsscom5.13.1.exe')
    time.sleep(1)

def find_all_port_and_open_them():
    port_list = list(lps.comports())
    if len(port_list) == 0:
       print('找不到串口')
    else:
        for i in range(0,len(port_list)):
            port_str = str(port_list[i]).split(' -')[0]
            if port_str != "COM1":
                change_ini_and_open_sscom(i,port_str)

if __name__ == '__main__':
    find_all_port_and_open_them ()

该代码是用于驱动 sscom 串口工具,可以方便的一键打开多个串口UI窗口,方便快速打开多个UI窗口,辅助手动调试。

六、自动启动 secureCRT 记录log

如果希望长时间的监控串口,虽然可以使用python的pyserial包来编写一个程序实现,但是最稳妥的方法还是调用成熟的第三方工具来做,至少不容易崩。

如果还希望实现某种自动化,还是否可行呢?答案是肯定的

一个实现的思路如下(类似sscom的启动方式,但是附带了log):

comman_log_name = '%s_print.log'

def setupSecureCRT(baudrate,comport,secureCRTpath,logpath,tabName,windowsName='wrong-wake-test'):
    cmd = f"{secureCRTpath} /POS 50 100 /LOG {logpath} /N {tabName} /TITLEBAR {windowsName} /SERIAL {comport} /BAUD {baudrate}"
    mprocess = subprocess.Popen(cmd)

portpool = 33,34,35
baudrate = 115200
secureCRTpath = "C:SecureCRTSecureCRT.exe"

for i in range(len(portpool)):
    comport = 'com' + portpool[i]
    tabName = tagpool[i]
    logpath = comman_log_name % comport
    tabname = "tab_%s" % comport
    time.sleep(4)
    p = Process(target=setupSecureCRT,args=(baudrate,comport,secureCRTpath,logpath,tabName))
    p.start()

以上code是运行于windows平台的,能够自动启动多线程驱动打开串口UI(通过命令行启动 vandyke公司的secureCRT 软件),可以实现同时记录多个串口,并指定log路径的目的,可用于自动化长时间监控操作。

如果希望记录的log里带时间戳

这样设置

f7444f37b27d89199be58297f02398bc.png

选择 编辑默认session

8c2ad457202daf12ae66556f6c27b387.png

如上图勾选,上start log upon connect ,然后如图写上

on each line : [%h:%m:%s.%t]---

这样设置后,再运行上面的python脚本启动它,就可以自动保存带时间戳的log了。

欢迎任何人或组织转载本专栏文章,转载请注明出处【知乎-GooD白-测试脚本】

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值