USB继电器控制小工具

USB继电器控制小工具

1. 基础知识介绍

  • tkinter 是一个简单入手,但是功能十分强大的GUI编程库,学习入门很快,如果你还不会,点击阅读Tkinter详细教程:
    1. 强大的的GUI开发库 - Tkinter详细教程一
    2. 强大的的GUI开发库 - Tkinter详细教程二
    3. 强大的的GUI开发库 - Tkinter详细教程三
  • 串口Uart是嵌入式最基础,最简单,也是使用最广范的一种通信协议,在诸多通信都使用; pyserial是python中处理串口通讯的一个模块名,操做十分简单,如果还不了解,点击阅读详细教程:
    • python serial模块详细教程

2. 需求背景介绍说明

  • usb控制继电器模块常用在控制设备的上下电管理,常常用来自动化控制自动化测试

  • USB继电器就是通过串口通信协议控制继电器的,如下图是一个4路的USB继电器控制模块,通过USB连接电脑,控制继电器的开关,从而控制设备的上下电。
    在这里插入图片描述

  • 继电器的控制指令很简单,按照下图所示的说明,即可控制对应端口的开闭;控制协议为4个字节,含义如下:

    1. 数据1:启动标识,默认使用0xA0
    2. 数据2:地址码,即控制那路继电器,OUT 1就是0x01OUT 2就是0x02
    3. 数据3:控制码,0x00为关不反馈;0x01为开不反馈;0x02为关并反馈;0x03为开并反馈;0x04为取反并反馈;0x05为查询状态;0x06为闪断并反馈
    4. 数据4:校验码,前面三个数据相加的和
      在这里插入图片描述
  • 控制工具需求说明

    1. 软件工具能够查找到指定的串口端口,并进行断开和连接
    2. 能够选择对应的端口
    3. 能够控制端口的闭合时长和断开时长
    4. 能够统计继电器工作时长
    5. 能够统计继电器的执行次数
  • 按照工具需求,设计出的软件工具界面示例图如下图所示:
    在这里插入图片描述

  • 设计源码分成2部分

    1. GUI界面设计,设计出工具的界面
    2. 继电器控制,并将GUI的界面事件和控制指令进行绑定

3. GUI界面开发源码

  • GUI界面源码文件名:PowerControl.py
  • 源码如下:
import sys, os
sys.path.append( os.path.dirname( os.path.dirname( os.path.abspath(__file__))))

import ttkbootstrap as tkbot
from ttkbootstrap.constants import * 
import time
import PowerDriver as PowerDrv

class PowerControl_App(tkbot.Frame):
    def __init__(self, master=None) : 
        super().__init__(master)
        self.master = master
        self.AllComChannel = [ 
                            "Not find"]     # 端口设置
        self.PowerChannel = [ 
                            "OUT 1" , 
                            "OUT 2" ,
                            "OUT 3" ,
                            "OUT 4"]     # 继电器端口设置
        self.LogDispalyList = []         # 数据显示窗口显示内容
        self.PowerDriver = PowerDrv.PowerDriver()  # 串口工具对象
        self.ConnectTime = tkbot.StringVar()
        self.DisConnectTime = tkbot.StringVar()
        self.ConnectTime.set('120')
        self.DisConnectTime.set('10')

        self.IsFindPort = False        # 是否检测到串口
        self.IsSeriolConnect = False   # 当前串口是否连接
        self.IsPowerWork = False       # 继电器是否工作

        self.TimeHour = 0              # 执行时间统计
        self.TimeMine = 0
        self.TimeS = 0
        self.createWidget(master)

    def createWidget(self, master) :
        InformDispalyFrame = tkbot.Labelframe(master, text="  显示窗口  ", bootstyle="info")
        InformDispalyFrame.place(x=13, y=5, width=700, height=340)
    
        logsb = tkbot.Scrollbar(InformDispalyFrame, bootstyle="info-round")
        logsb.pack(side=RIGHT, fill=Y)
        Result_Display = tkbot.Text(InformDispalyFrame, bg="white", font=("宋体",12), yscrollcommand=logsb.set)
        Result_Display.place(x=10, y=10, width=670, height=290)
        logsb.config(command=Result_Display.yview)
        
        def UpDateLogDisplay():
            for item in self.LogDispalyList:
                Result_Display.insert(END, item)
            self.LogDispalyList.clear()
            master.after(100, UpDateLogDisplay)
        UpDateLogDisplay()
        
        def findSeriol() :
            if self.IsSeriolConnect :
                self.LogDispalyList.append("通信串口已连接"+"\n")
            else :
                if self.PowerDriver.Find_Seriollist() :
                    self.AllComChannel = self.PowerDriver.Get_Seriollist()
                    PortChannelCombobox["value"] = self.AllComChannel
                    PortChannelCombobox.current(0)
                    SerilPort = str(self.AllComChannel[0])
                    SerilPortStr = SerilPort[0:5]
                    self.PowerDriver.Set_SeriolChanel(SerilPortStr)
                    self.IsFindPort = True
                    Length_text = "设置通信串口 : " + SerilPortStr +"\n"
                    self.LogDispalyList.append(Length_text)
                else :
                    self.IsFindPort = False
                    self.AllComChannel = ["Not find"]
                    PortChannelCombobox["value"] = self.AllComChannel
                    PortChannelCombobox.current(0)
                    self.LogDispalyList.append("未检测任何到串口"+"\n")

        Refresh_Btn = tkbot.Button(master, text="串口刷新", bootstyle="info-outline",  
                        command= findSeriol) 
        Refresh_Btn.place(x=30, y=360, width=100, height=40)
        
        PortChannelLable = tkbot.Label(master, text="串口选择", font=("楷体",12), bootstyle="danger")
        PortChannelLable.place(x=160, y=360, width=100, height=40)
        
        def SetChosePort(event):
            if self.IsFindPort :
                SerilPort = str(PortChannelCombobox.get())
                SerilPortStr = SerilPort[0:5]
                self.PowerDriver.Set_SeriolChanel(SerilPortStr)
                Length_text = "设置通信串口 : " + SerilPortStr +"\n"
                self.LogDispalyList.append(Length_text)
            else :
                self.LogDispalyList.append("未检测任何到串口"+"\n")

        PortChannelCombobox = tkbot.Combobox(master, font=("宋体",12), width=18)
        PortChannelCombobox.place(x=260, y=363, width=160, height=40)
        PortChannelCombobox["value"] = self.AllComChannel
        PortChannelCombobox.current(0)
        PortChannelCombobox.bind("<<ComboboxSelected>>", SetChosePort)


        def DeviceConnect():
            if self.IsFindPort :
                if self.IsSeriolConnect :
                    self.LogDispalyList.append("通信串口已经连接成功"+"\n")
                else :
                    if self.PowerDriver.Open_Seriol() :
                        self.IsSeriolConnect = True
                        Length_text = "打开串口成功"+"\n"
                        self.LogDispalyList.append(Length_text)
                    else :
                        Length_text = "打开串口失败"+"\n"
                        self.IsSeriolConnect = False
                        self.LogDispalyList.append(Length_text)
            else :
                self.IsSeriolConnect = False
                Length_text = "未连接任何串口, 打开失败" +"\n"
                self.LogDispalyList.append(Length_text)

        Send_Btn = tkbot.Button(master, text="串口连接", bootstyle="info-outline",  
                        command=DeviceConnect) 
        Send_Btn.place(x=460, y=360, width=100, height=40)
        
        def DeviceDisConnect():
            if self.IsSeriolConnect :
                self.IsSeriolConnect = False
                self.IsPowerWork = False
                self.PowerDriver.Close_Seriol()
                Length_text = "断开通信串口,继电器停止工作"+"\n"
                self.LogDispalyList.append(Length_text)
                Length_text = "本次执行次数 : " + str(self.PowerDriver.Get_RunCount()) \
                                +", 执行时间 : " + str(self.TimeHour)+"H:"+str(self.TimeMine)+"M:"+str(self.TimeS)+"S" + "\n"
                self.LogDispalyList.append(Length_text) 
            else :
                Length_text = "通信串口尚未连接"+"\n"
                self.LogDispalyList.append(Length_text)


        Stop_Btn = tkbot.Button(master, text="串口断开", bootstyle="info-outline",
                        command=DeviceDisConnect) 
        Stop_Btn.place(x=590, y=360, width=100, height=40)
        
        PowerChannelLable = tkbot.Label(master, text="继电器端口选择", font=("楷体",12), bootstyle="danger")
        PowerChannelLable.place(x=20, y=420, width=150, height=40)
        
        def SetPowerOutPort(event):
            SerilPort = str(PowerChannelCombobox.get())
            if SerilPort == "OUT 1" :
                PortIndex = 1
            elif SerilPort == "OUT 2" :
                PortIndex = 2
            elif SerilPort == "OUT 3" :
                PortIndex = 3
            elif SerilPort == "OUT 4" :
                PortIndex = 4
            self.PowerDriver.Set_PowerOutIndex(PortIndex)
            Length_text = "选择继电器输出端口 : " + str(PortIndex) +"\n"
            self.LogDispalyList.append(Length_text)

        PowerChannelCombobox = tkbot.Combobox(master, font=("宋体",12), width=18)
        PowerChannelCombobox.place(x=175, y=423, width=85, height=40)
        PowerChannelCombobox["value"] = self.PowerChannel
        PowerChannelCombobox.current(0)
        PowerChannelCombobox.bind("<<ComboboxSelected>>", SetPowerOutPort)
        
        def StartWork():
            if self.IsSeriolConnect :
                if self.IsPowerWork :
                    self.LogDispalyList.append("继电器已近开始开始工作了"+"\n")
                else :
                    self.IsPowerWork = True
                    self.TimeHour = 0
                    self.TimeMine = 0
                    self.TimeS = 0
                    ConnectTime = int(self.ConnectTime.get())
                    self.PowerDriver.Set_ConnectTime(ConnectTime)
                    DisConnectTime = int(self.DisConnectTime.get())
                    self.PowerDriver.Set_DisconnectTime(DisConnectTime)
                    self.PowerDriver.Start_PowerControl()
                    Length_text = "继电器开始工作, " +"闭合 : "+ str(ConnectTime) +" s, " \
                                +"断开 : "+ str(DisConnectTime) +" s" + "\n"
                    self.LogDispalyList.append(Length_text) 
            else :
                self.IsPowerWork = False
                Length_text = "通信串口尚未连接, 继电器无法工作"+"\n"
                self.LogDispalyList.append(Length_text)


        CloseTimeLable = tkbot.Label(master, text="闭合时长", font=("楷体",12), bootstyle="danger")
        CloseTimeLable.place(x=290, y=420, width=80, height=40)
        CloseTime = tkbot.Entry(master, textvariable=self.ConnectTime, font=("宋体",15))
        CloseTime.place(x=380, y=420, width=80, height=40)
        TimeSLable = tkbot.Label(master, text="S", font=("楷体",12), bootstyle="danger")
        TimeSLable.place(x=465, y=420, width=20, height=40)

        OpenLable = tkbot.Label(master, text="断开时长", font=("楷体",12), bootstyle="danger")
        OpenLable.place(x=500, y=420, width=80, height=40)
        OpenTime = tkbot.Entry(master, textvariable=self.DisConnectTime, font=("宋体",15))
        OpenTime.place(x=590, y=420, width=80, height=40)
        TimeS1Lable = tkbot.Label(master, text="S", font=("楷体",12), bootstyle="danger")
        TimeS1Lable.place(x=675, y=420, width=20, height=40)

        PowerStart_Btn = tkbot.Button(master, text="继电器启动", bootstyle="info-outline",  
                        command= StartWork) 
        PowerStart_Btn.place(x=200, y=480, width=120, height=40)
        
        def StoptWork():
            if self.IsPowerWork :
                self.IsPowerWork = False
                self.PowerDriver.Stop_PowerControl()
                Length_text = "继电器停止工作" + "\n"
                self.LogDispalyList.append(Length_text)
                Length_text = "本次执行次数 : " + str(self.PowerDriver.Get_RunCount()) \
                            +", 执行时间 : " + str(self.TimeHour)+"H:"+str(self.TimeMine)+"M:"+str(self.TimeS)+"S" + "\n"
                self.LogDispalyList.append(Length_text) 
            else :
                Length_text = "继电器尚未工作" + "\n"
                self.LogDispalyList.append(Length_text)

        PowerStop_Btn = tkbot.Button(master, text="继电器停止", bootstyle="info-outline",
                        command=StoptWork) 
        PowerStop_Btn.place(x=400, y=480, width=120, height=40)

        countLable = tkbot.Label(master, text="当前时间", font=("楷体",12))
        countLable.place(x=15, y=540, width=100, height=40)
        countEntry = tkbot.Label(master, text="0H:0M:0S", font=("宋体",16), 
                                 bootstyle="inverse-secondary", width=12)
        countEntry.place(x=105, y=540, width=120, height=40)
        
        TimeLable = tkbot.Label(master, text="运行时间", font=("楷体",12))
        TimeLable.place(x=255, y=540, width=100, height=40)
        TimeEntry = tkbot.Label(master,text="00H:00M:00S", font=("宋体",16),
                                bootstyle="inverse-secondary", width=12)
        TimeEntry.place(x=345, y=540, width=160, height=40)
        
        CountLable = tkbot.Label(master, text="执行次数", font=("楷体",12))
        CountLable.place(x=535, y=540, width=100, height=40)
        CountEntry = tkbot.Label(master,text="0", font=("宋体",16),
                                bootstyle="inverse-secondary", width=12)
        CountEntry.place(x=625, y=540, width=80, height=40)
        
        def UpdateTiming():
            if self.IsPowerWork :
                if self.TimeS == 59 :
                    self.TimeMine = self.TimeMine + 1
                    self.TimeS = 0
                else:
                    self.TimeS = self.TimeS + 1
                    
                if self.TimeMine == 59 :
                    self.TimeHour = self.TimeHour + 1
                    self.TimeMine = 0
            TimeEntry.config(text=str(self.TimeHour)+"H:"+str(self.TimeMine)+"M:"+str(self.TimeS)+"S")
            
            Nowtimestr = time.strftime("%H:%M:%S")
            countEntry.config(text=Nowtimestr)
            Runcount = str(self.PowerDriver.Get_RunCount())
            CountEntry.config(text=Runcount)
            master.after(1000, UpdateTiming)  
        UpdateTiming()
        
if __name__ == "__main__" :
    
    # 主窗口设置
    root = tkbot.Window()
    root.title("电源继电器小工具 QwQ")
    withset = 725
    hightset = 600 
    root.geometry("%dx%d"%(withset, hightset))
    PowerControlApp = PowerControl_App(root)

    root.mainloop()

4. 串口驱动源码

  • GUI界面源码文件名:PowerDriver.py
  • 源码如下:
import serial #导入模块
import serial.tools.list_ports
import threading
import time

class PowerDriver():
    def __init__(self):
        super().__init__()
        self.Seriollist = []             # 串口对象获取
        self.Seriol = serial.Serial()    # 串口对象
        self.SeriolChanel = "com6"       # 端口号
        self.SeriolBaudRate = 9600       # 波特率
        self.PowerOutIndex = 1           # 电源输出端口
        self.ConnectTime = 1             # 闭合时长
        self.DisconnectTime = 1          # 断开时长
        self.RunCount = 0                # 执行次数
        self.PowerTask_IsAlive = False   # 继电器任务
        self.ConnectRunCount = 0         # 闭合计时计数
        self.DisConnectRunCount = 0      # 断开计时计数
        self.Do_Connect = False          # 当前是在执行闭合还是断开互斥标志

    
    def Set_SeriolChanel(self, SeriolChanel):
        self.SeriolChanel = SeriolChanel

    def Set_PowerOutIndex(self, PowerOutIndex):
        self.PowerOutIndex = PowerOutIndex

    def Set_ConnectTime(self, ConnectTime):
        self.ConnectTime = ConnectTime

    def Set_DisconnectTime(self, DisconnectTime):
        self.DisconnectTime = DisconnectTime

    def Get_RunCount(self):
        return self.RunCount
    
    def Find_Seriollist(self):
        port_list = list(serial.tools.list_ports.comports())
        if len(port_list) == 0:
            self.Seriollist = [] 
            return False
        else:
            self.Seriollist = port_list
            first_port = str(port_list[0])
            self.SeriolChanel = first_port[0:5]  # 及时修改端口
            return True

    def Get_Seriollist(self):
            return self.Seriollist

    def Open_Seriol(self):
        try:
            # 打开串口,并得到串口对象
            self.Seriol = serial.Serial(self.SeriolChanel, self.SeriolBaudRate, timeout=5)
            # 判断是否打开成功
            if(False == self.Seriol.isOpen):
                return False 
            else :
                SeriolRx_Thread = threading.Thread(target=self.PowerTask)
                SeriolRx_Thread.setDaemon(True)
                SeriolRx_Thread.start()
                return True
        except Exception as error:
            return False

    def Close_Seriol(self):
        self.Seriol.close()
        self.Do_Connect = False
        self.PowerTask_IsAlive = False
        self.ConnectRunCount = 0        
        self.DisConnectRunCount = 0   

    def Power_Connect(self):
        if self.PowerOutIndex == 1:
            Order = [0xA0, 0x01, 0x00, 0xA1]
        elif self.PowerOutIndex == 2:
            Order = [0xA0, 0x02, 0x00, 0xA2]
        elif self.PowerOutIndex == 3:
            Order = [0xA0, 0x03, 0x00, 0xA3]
        else :
            Order = [0xA0, 0x04, 0x00, 0xA4]
        self.Seriol.write(Order)

    def Power_DisConnect(self):
        if self.PowerOutIndex == 1:
            Order = [0xA0, 0x01, 0x01, 0xA2]
        elif self.PowerOutIndex == 2:
            Order = [0xA0, 0x02, 0x01, 0xA3]
        elif self.PowerOutIndex == 3:
            Order = [0xA0, 0x03, 0x01, 0xA4]
        else :
            Order = [0xA0, 0x04, 0x01, 0xA5]
        self.Seriol.write(Order)
    
    def PowerTask(self):
        while True :
            if self.PowerTask_IsAlive :
                if self.Do_Connect :
                    if self.ConnectRunCount <  self.ConnectTime :
                        self.ConnectRunCount = self.ConnectRunCount + 1
                    else :
                        self.Do_Connect = False
                        self.ConnectRunCount = 0
                        self.Power_DisConnect()
                else :
                    if self.DisConnectRunCount < self.DisconnectTime :
                        self.DisConnectRunCount = self.DisConnectRunCount + 1
                    else :
                        self.Do_Connect = True
                        self.DisConnectRunCount = 0
                        self.RunCount = self.RunCount + 1
                        self.Power_Connect()
            time.sleep(1)

    def Start_PowerControl(self):
        self.Power_Connect()
        self.RunCount = 0
        self.RunCount = self.RunCount + 1
        self.Do_Connect = True
        self.PowerTask_IsAlive = True
        self.ConnectRunCount = 0        
        self.DisConnectRunCount = 0  

    def Stop_PowerControl(self):
        self.Do_Connect = False
        self.PowerTask_IsAlive = False
        self.ConnectRunCount = 0        
        self.DisConnectRunCount = 0    

感谢阅读 若有错误 欢迎指正 !!!


  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

园长QwQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值