python 使用pyserial串口庫開發串口工具

# !/usr/bin/python 3.6.5
# coding=utf-8

import serial
import serial.tools.list_ports
import threading

'''
    1、掃描串口列表
    2、打開串口
    3、向串口寫數據
    4、從串口讀數據
    5、關閉串口
    
    init 初始化串口
        --> 創建線程:
            1. 如果串口未打開,則用於實時掃描串口,檢測是否有新設備插入
                有新設備插入:添加到設備列表,並刷新UI的設備列表提供選擇
                沒有新設備:繼續掃描
            2. 如果串口已打開,則用於監測串口是否有接收到數據
                如果有則讀取數據,併發送給UI
                如果沒有則繼續掃描
        參數:主窗口,端口列表,接收窗口
        返回:線程實例(用於關閉窗口前,先關閉線程)
        
    open 打開串口
        --> 參數:端口號,波特率,超時時間
            返回:串口實例(打開成功)/ None(打開失敗)
    
    close 關閉串口
        --> 參數:串口實例
            返回:0(關閉成功)/ -1(關閉失敗)
            
    write 寫數據
    read 讀數據
        
'''

SERIAL_IS_OPEN = False      # 默認串口關閉
port_name_list = []         # 端口名稱列表
port_com_list = []          # 端口號列表
MySerial = None             # 打開的串口

def 掃描串口():
    port_list = list(serial.tools.list_ports.comports())
    if len(port_list) > 0:
        return port_list
    else:
        return None

def 打開串口(port="COM4", bps=115200, timex=5):
    try:
        # 打開串口
        ser = serial.Serial(port, bps, timeout=timex)

        if ser.is_open:
            global SERIAL_IS_OPEN
            SERIAL_IS_OPEN = True
            print("--- 串口打開 ---")
            return ser

    except Exception as e:
        print("--- 打開異常 ---: ", e)
        return None

def 發送數據(ser, text, code="utf-8"):
    try:
        result = ser.write(text.encode(code))
        if result == len(text):
            print("--- 發送成功 ---:", text)
            return result
        else:
            print("--- 發送錯誤 ---:", "data len:", len(text), "send len:", result)
            return None
    except Exception as e:
        print("--- 發送異常 ---:", e)

def 讀取數據(ser, code="utf-8"):
    if ser.in_waiting:
        str = ser.read(ser.in_waiting).decode(code)
        print("--- 讀到數據 ---:", str)
        return str
    else:
        return None


def 關閉串口(ser):
    if ser.is_open:
        try:
            global SERIAL_IS_OPEN
            SERIAL_IS_OPEN = False
            ser.close()
            print("--- 串口關閉 ---")
            return 0
        except Exception as e:
            print("--- 關閉異常 ---:", e)
            return -1
    else:
        print("--- 錯誤 ---:串口未打開!")
        return -1

class SERIAL:
    def __init__(self):
        self.ser = None
        self.get_str = ''
        self.master = None
        self.show_com = None
        self.read_text_win = None
        self.serialThread = None

    def serial_thread(self, master, list_port, text_read):
        global SERIAL_IS_OPEN, port_name_list, port_com_list
        global MySerial
        while True:
            if SERIAL_IS_OPEN:
                self.get_str = 讀取數據(MySerial)
                if self.get_str:
                    # print(self.get_str)
                    text_read.insert(tk.END, self.get_str)
                    master.update()
            else:
                port_list = 掃描串口()
                if len(port_name_list) is not len(port_list):   # 只判斷列表長度,不可靠。需修改爲列表內容判斷比較可靠
                    port_name_list.clear()
                    port_com_list.clear()
                    for i in range(0, len(port_list)):
                        port_name_list.append(port_list[i].description)
                        port_com_list.append(port_list[i].device)
                        list_port["values"] = port_name_list
                        if list_port.get() is "":  # 如果當前UI中的端口列表爲空,則指定顯示第一個
                            list_port.current(0)
                    master.update()

    @classmethod
    def init(cls, master, list_port, text_read):
        cls.master = master
        cls.show_com = list_port
        cls.read_text_win = text_read
        cls.serialThread = threading.Thread(target=cls.serial_thread,
                                            args=(SERIAL, cls.master, cls.show_com, cls.read_text_win))
        cls.serialThread.start()
        return cls.serialThread

    @classmethod
    def open(cls, port=None, bps=115200, timex=5):
        global port_name_list, port_com_list, MySerial
        if not port:
            port_name = cls.show_com.get()
            index = port_name_list.index(port_name)
            MySerial = 打開串口(port_com_list[index], bps, timex)
        else:
            MySerial = 打開串口(port, bps, timex)

    @staticmethod
    def write(text, coding="gbk"):
        global MySerial, SERIAL_IS_OPEN
        if SERIAL_IS_OPEN:
            發送數據(MySerial, text, coding)

    @staticmethod
    def read(coding="gbk"):
        global MySerial, SERIAL_IS_OPEN
        str = None
        if SERIAL_IS_OPEN:
            str = 讀取數據(MySerial, coding)
        return str

    @staticmethod
    def close():
        global MySerial, SERIAL_IS_OPEN
        if SERIAL_IS_OPEN and MySerial:
            關閉串口(MySerial)


if __name__ == "__main__":
    import tkinter as tk
    from tkinter import ttk
    root = tk.Tk()
    root.title("測試窗口")
    root.geometry("300x300")

    list_box = ttk.Combobox(root, width=22, textvariable=port_name_list, state="readonly")
    list_box.place(x=10, y=10)

    text_box = tk.Text(root, width=25, heigh=10)
    text_box.place(x=10, y=50)

    def open_serial():
        SERIAL.open()

    def send_date():
        SERIAL.write("hello python serial\n", coding="gbk")

    def close_serial():
        SERIAL.close()

    SERIAL.init(root, list_box, text_box)

    button0 = tk.Button(root, text="打開串口", command=open_serial)
    button0.place(x=10, y=200)
    button1 = tk.Button(root, text="發送數據", command=send_date)
    button1.place(x=70, y=200)
    button2 = tk.Button(root, text="關閉串口", command=close_serial)
    button2.place(x=130, y=200)

    root.mainloop()
    

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章