在 Tkinter 文本框中显示从串行端口读取的数据时,发出处理数据

Posted

技术标签:

【中文标题】在 Tkinter 文本框中显示从串行端口读取的数据时,发出处理数据【英文标题】:Issue processing data read from serial port, when displaying it in a Tkinter textbox 【发布时间】:2019-11-04 10:03:34 【问题描述】:

所以我正在从串行连接读取(并使用 tkinter 文本框显示)数据,但我无法按我的意愿处理返回的数据,以便运行我的测试。更简单地说,即使显示了machine response = 0x1,我也无法从全局serBuffer 中读取它。

在将其显示给textbox 之前,我将从测试function 中读取,然后检查响应是否在string 中,但现在我将读取的数据(字符串)传递给全局变量并然后尝试阅读它,它似乎不起作用,除非我从readserial 中删除serBuffer = ""。但这会导致一个新问题。当我按下按钮发送命令时,它发送它,但仅在我第二次按下它之后才收到响应,并且每次之后。因此,如果我运行一次测试,我会得到一个Fail,但之后我每次都会通过。

带有期望响应的图片(test function 不读取 0x1 并且总是返回 FAIL)

带有 non-desired 响应的图片(仅在第二次按下它之后才收到响应,并且每次之后都收到响应。因此,如果我运行一次测试,我会得到一个失败,但之后我每次都通过)。

import tkinter as tk
import serial
from serial import *


serialPort = "COM3"
baudRate = 115200
ser = Serial(serialPort, baudRate, timeout=0, writeTimeout=0) #ensure non-blocking


#make a TkInter Window
mainWindow = tk.Tk()
mainWindow.wm_title("Reading Serial")
mainWindow.geometry('1650x1000+500+100')




scrollbar = tk.Scrollbar(mainWindow)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

log = tk.Text ( mainWindow, width=60, height=60, takefocus=0)
log.pack()

log.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=log.yview)

#make our own buffer
#useful for parsing commands
#Serial.readline seems unreliable at times too
serBuffer = ""
ser.write(b'\r\n')

def readSerial():
    while True:
        c = (ser.read().decode('utf-8', 'ignore'))  # attempt to read a character from Serial
        # was anything read?
        if len(c) == 0:
            break

        # get the buffer from outside of this function
        global serBuffer

        # check if character is a delimeter
        if c == '\r':
            serBuffer += "\n" # don't want returns. chuck it

        if c == '\n':
            serBuffer += "\n"  # add the newline to the buffer

            # add the line to the TOP of the log
            log.insert('1.1', serBuffer)
            serBuffer = ""  # empty the buffer
        else:
            serBuffer += c  # add to the buffer

    mainWindow.after(100, readSerial)  # check serial again soon

def test():
    command = b" test command \r\n"
    ser.write(command)
    global serBuffer
    time.sleep(0.5)
    if "0x1" in serBuffer:
        print('PASS')
        return 'PASS'
    else:
        print('FAIL')
        return 'FAIL'

button = tk.Button(mainWindow, text="Pone Test", font=40, bg='#b1c62d', command=test)
button.place(relx=0.8, rely=0, relwidth=0.1, relheight=0.05)


# after initializing serial, an arduino may need a bit of time to reset
mainWindow.after(100, readSerial)

mainWindow.mainloop()

【问题讨论】:

你在if "0x1" in serBuffer:之前做serBuffer = "" # empty the buffer。分为local read_bufferglobal last_command 感谢您的回答!你能编辑我的代码来理解你的意思吗? 【参考方案1】:

评论:但仅在第二次按下它之后才收到响应,并且每次按下它之后。因此,如果我运行一次测试,我会失败,但之后每次都会通过

将第一个超时从100 提高到500 或更多。

        # after initializing serial, an arduino may need a bit of time to reset
        mainWindow.after(100, self.readSerial)

要找出第一个响应的延迟,请尝试以下操作:

注意:您必须在不运行def readSerial 的情况下执行此操作,以防止同时清空in buffer"

    command = b" test command \r\n"
    self.ser.write(command)

    delay = 0.0

    # wait until you get `.in_waiting` data.
    while not self.ser.in_waiting:
        time.sleep(0.1)
        delay += 0.1
        print('.', end='')
        if delay >= 10:
            print('BREAK after  no in_waiting'.format(int(delay * 10)))
            break

    print('Delay:, in_waiting:'.format(delay, self.ser.in_waiting))

以下内容对我有用。

注意:我使用OOP 语法。

    last_command

    serBuffer = ""
    last_command = None
    

    将准备好的read_buffer复制到last_command,只为空read_buffer

    def readSerial(self):
        while True:
            c = (self.ser.read().decode('utf-8', 'ignore'))  # attempt to read a character from Serial
            # was anything read?
            if len(c) == 0:
                break
    
            # get the buffer from outside of this function
            global serBuffer
    
            # check if character is a delimeter
            if c == '\r':
                serBuffer += "\n"  # don't want returns. chuck it
    
            if c == '\n':
                serBuffer += "\n"  # add the newline to the buffer
    
                global last_command
                last_command = serBuffer
    
                # add the line to the TOP of the log
                # log.insert('1.1', last_command)
                print('readSerial.last_command:""'.format(bytes(last_command, 'utf-8')))
    
                serBuffer = ""  # empty the buffer
    
            else:
                serBuffer += c  # add to the buffer
                print('readSerial:""'.format(bytes(serBuffer, 'utf-8')))
    
        self.after(100, self.readSerial)  # check serial again soon
    

    test()

    def test(self, write=True):
        print('test(write=)'.format(write))
    
        if write:
            command = b" test command \r\n"
            self.ser.write(command)
            self.after(500, self.test, False)
    
        elif last_command is not None:
            print('last_command:'.format(bytes(last_command, 'utf-8')))
    
            if "0x1" in last_command:
                print('PASS')
            else:
                print('FAIL')
        else:
            # ATTENTION: This could lead to a infinit loop
            # self.after(500, self.test, False)
            pass
    

输出

test(write=True)
readSerial:"b' '"
readSerial:"b' t'"
readSerial:"b' te'"
readSerial:"b' tes'"
readSerial:"b' test'"
readSerial:"b' test '"
readSerial:"b' test c'"
readSerial:"b' test co'"
readSerial:"b' test com'"
readSerial:"b' test comm'"
readSerial:"b' test comma'"
readSerial:"b' test comman'"
readSerial:"b' test command'"
readSerial:"b' test command '"
readSerial:"b' test command \n\r'"
readSerial.last_command:"b' test command \n\r\n'"
test(write=False)
last_command:b' test command \n\r\n'
FAIL

注意:我得到FAIL,因为last_command 中没有0x1,因为我使用PORT = 'loop://',它与所写的内容相呼应!

【讨论】:

我收到一个错误:TypeError: argument of type 'NoneType' is not iterable 那么你的def readSerial(): 没有到达last_command = serBuffer。这意味着你没有收到任何东西,或者你要早点去做if "0x1" in last_command: 我会添加一些延迟,看看它是否解决了问题,谢谢你的帮助! 好吧显然增加延迟并没有使它起作用,我也尝试了ser.send_break,但这也不起作用。我很确定我正在接收数据,但当我没有将它显示到文本框时我已经完成了它。唯一的区别是我正在阅读 Pycharm @Deezer:更新我的答案,TEST 关于第一次延迟。【参考方案2】:

我做了一些更改,检查一下。

def readSerial():
    while True:
        c = (ser.read(1).decode('utf-8', 'ignore')) from Serial

        if len(c) == 0:
            break


        global serBuffer
        if c == '\r':
            serBuffer += "" 

        if c == '\n':
            serBuffer += "\n" 



            log.insert(tk.END, serBuffer)
            log.see(tk.END)
            log.update_idletasks()
            serBuffer = ""  
        else:
            serBuffer += c  

    mainWindow.after(500, readSerial)  

【讨论】:

以上是关于在 Tkinter 文本框中显示从串行端口读取的数据时,发出处理数据的主要内容,如果未能解决你的问题,请参考以下文章

如何从文本框中读取串口名称(可编辑)

每当有来自串行端口 python 3.x 的新数据时,从串行数据更新 tkinter 标签

多线程 Python Tkinter 串行监视器中的按钮问题

具有多个事件的连续串行端口读取

在 tkinter 滚动条上显示串行传入数据

问题:关闭端口后,python 仍然读取来自 arduino 的串行数据。串口无法关闭