当前位置: 首页 > article >正文

使用ch340继电器完成随机断电测试

前言

如图所示是市面上常见的OTA压测继电器,通过ch340串口模块完成对继电器的分路控制,这里我编写了一个脚本方便对4路继电器的控制,可以设置开启时间,关闭时间,复位等功能
在这里插入图片描述

软件界面

在这里插入图片描述
在设备管理器查看串口号后,选中串口号,波特率使用9600,选择要控制的继电器,当选中后状态指示灯变为绿色,这时候再设置开启和关闭时间,启动运动即可完成控制了,为了防止状态错乱,我增加了一个复位按钮,一键把所有继电器状态置为初始状态。

运行界面

启动运行后,文本栏会打印相关显示,指示不同继电器状态
在这里插入图片描述

完整代码

代码全部使用python编写,相关依赖的环境较多,我换了电脑后执行经常出问题,就打包成exe运行了。如果有需要exe的可以评论留下邮箱

依赖的库和环境

项目推荐配置
Python 版本Python 3.9 或 3.10(64位)
操作系统Windows 10 / 11
GUI 框架tkinter(Python 标准库中已内置)

pyserial>=3.5

库名用途说明
tkinter图形用户界面(标准库)
serial (pyserial)串口通信控制继电器
threadingtime多线程控制继电器 + 定时
osjsondatetime有效期校验和配置记录处理

代码

import tkinter as tk
from tkinter import ttk, messagebox
import serial
import serial.tools.list_ports
import threading
import time
import os
import json
from datetime import datetime, timedelta# 有效期检查函数
def check_expiry():valid_days = 182  # 6个月file_path = "activated.json"if os.path.exists(file_path):with open(file_path, "r") as f:data = json.load(f)start_time = datetime.strptime(data["start_time"], "%Y-%m-%d")else:start_time = datetime.now()with open(file_path, "w") as f:json.dump({"start_time": start_time.strftime("%Y-%m-%d")}, f)now = datetime.now()delta = now - start_timeif delta.days > valid_days:messagebox.showerror("软件已过期", "本程序有效期为6个月,已过期,无法继续使用。")root.destroy()# 获取串口列表
def get_serial_ports():return [port.device for port in serial.tools.list_ports.comports()]# 串口发送(线程安全)
serial_lock = threading.Lock()def send_hex_serial_command(port, baudrate, hex_command):try:with serial_lock:with serial.Serial(port, baudrate, timeout=1) as ser:ser.write(bytes.fromhex(hex_command))log(f"[发送] {hex_command}")except Exception as e:log(f"发送失败: {e}")# 日志输出
def log(msg):log_text.config(state='normal')log_text.insert(tk.END, msg + '\n')log_text.see(tk.END)log_text.config(state='disabled')# 是否处于运行状态
is_running = False# 控制线程(每路继电器)
def relay_loop(index, port, baudrate, get_on_time, get_off_time):on_cmds  = ["A0 01 01 A2", "A0 02 01 A3", "A0 03 01 A4", "A0 04 01 A5"]off_cmds = ["A0 01 00 A1", "A0 02 00 A2", "A0 03 00 A3", "A0 04 00 A4"]while not is_running:time.sleep(0.1)try:while is_running:if relay_enabled[index].get():on_time = get_on_time()off_time = get_off_time()send_hex_serial_command(port, baudrate, on_cmds[index])log(f"[继电器{index+1}] 打开,保持 {on_time} 秒")time.sleep(on_time)send_hex_serial_command(port, baudrate, off_cmds[index])log(f"[继电器{index+1}] 关闭,保持 {off_time} 秒")time.sleep(off_time)else:time.sleep(1)except Exception as e:log(f"[继电器{index+1}] 错误: {e}")# 启动按钮功能
def start_control_loop():global is_runningif is_running:log("控制已在运行")returnport = port_var.get()baudrate = int(baudrate_var.get())if not port:messagebox.showerror("错误", "请选择串口")returntry:[int(e.get()) for e in on_entries][int(e.get()) for e in off_entries]except ValueError:messagebox.showerror("错误", "请输入合法整数")returnis_running = Truestart_button.config(style="BigGreen.TButton")for i in range(4):t = threading.Thread(target=relay_loop,args=(i,port,baudrate,lambda i=i: int(on_entries[i].get()),lambda i=i: int(off_entries[i].get())),daemon=True)t.start()log("并行继电器控制已启动")# 启用/禁用开关
def toggle_relay(index):current = relay_enabled[index].get()relay_enabled[index].set(not current)if relay_enabled[index].get():status_labels[index].config(text="🟢", fg="green")else:status_labels[index].config(text="🔴", fg="red")# 复位按钮功能
def reset_all():global is_runningis_running = Falsefor i in range(4):relay_enabled[i].set(False)status_labels[i].config(text="🔴", fg="red")on_entries[i].delete(0, tk.END)on_entries[i].insert(0, "10")off_entries[i].delete(0, tk.END)off_entries[i].insert(0, "10")start_button.config(style="Big.TButton")log("已复位:所有继电器禁用,时间重置为10秒")# 创建主窗口
root = tk.Tk()
root.withdraw()  # 启动时先隐藏窗口
check_expiry()   # 执行有效期检查
root.deiconify() # 检查通过再显示窗口root.title("继电器并行控制器(启动前选中,启动后运行)")# 样式定义
style = ttk.Style()
style.configure("Big.TButton", font=("Arial", 13, "bold"), padding=12)
style.configure("Small.TButton", font=("Arial", 10), padding=5)
style.configure("BigGreen.TButton", font=("Arial", 13, "bold"), padding=12, background="green")
style.map("BigGreen.TButton", background=[("active", "green")])# 串口设置区域
frame_top = ttk.Frame(root)
frame_top.pack(padx=10, pady=5, anchor="w")ttk.Label(frame_top, text="串口号:").pack(side='left')
port_var = tk.StringVar()
port_menu = ttk.Combobox(frame_top, textvariable=port_var, values=get_serial_ports(), width=10)
port_menu.pack(side='left', padx=5)ttk.Label(frame_top, text="波特率:").pack(side='left')
baudrate_var = tk.StringVar(value="9600")
baudrate_menu = ttk.Combobox(frame_top, textvariable=baudrate_var, values=["9600", "115200"], width=10)
baudrate_menu.pack(side='left', padx=5)# 控制区域表格
frame_time = ttk.Frame(root)
frame_time.pack(padx=10, pady=5, anchor="w")ttk.Label(frame_time, text="继电器").grid(row=0, column=0, sticky="w")
ttk.Label(frame_time, text="名称").grid(row=0, column=1, sticky="w")
ttk.Label(frame_time, text="状态").grid(row=0, column=2, sticky="w")
ttk.Label(frame_time, text="开时间(秒)").grid(row=0, column=3, sticky="w")
ttk.Label(frame_time, text="关时间(秒)").grid(row=0, column=4, sticky="w")on_entries = []
off_entries = []
relay_enabled = []
status_labels = []
control_buttons = []def make_toggle_cmd(i):return lambda: toggle_relay(i)for i in range(4):ttk.Label(frame_time, text=f"{i+1}").grid(row=i+1, column=0, sticky="w")enable_var = tk.BooleanVar(value=False)relay_enabled.append(enable_var)btn = ttk.Button(frame_time, text=f"继电器{i+1}", command=make_toggle_cmd(i), width=10)btn.grid(row=i+1, column=1, sticky="w")control_buttons.append(btn)status = tk.Label(frame_time, text="🔴", fg="red", font=("Arial", 12))status.grid(row=i+1, column=2, sticky="w")status_labels.append(status)on_entry = ttk.Entry(frame_time, width=8)on_entry.insert(0, "10")on_entry.grid(row=i+1, column=3, sticky="w")on_entries.append(on_entry)off_entry = ttk.Entry(frame_time, width=8)off_entry.insert(0, "10")off_entry.grid(row=i+1, column=4, sticky="w")off_entries.append(off_entry)start_button = ttk.Button(frame_time,text="启动运行",command=start_control_loop,style="Big.TButton",width=14
)
start_button.grid(row=1,column=6,rowspan=4,padx=(40, 0),pady=(20, 0),sticky="ns"
)# 日志输出
log_text = tk.Text(root, height=12, state='disabled')
log_text.pack(padx=10, pady=5, fill='both', expand=True)# 复位按钮
frame_reset = ttk.Frame(root)
frame_reset.pack(padx=10, pady=(0, 5), anchor="e")reset_btn = ttk.Button(frame_reset, text="复位", command=reset_all, style="Small.TButton")
reset_btn.pack()# 启动主循环
root.mainloop()

相关文章:

使用ch340继电器完成随机断电测试

前言 如图所示是市面上常见的OTA压测继电器,通过ch340串口模块完成对继电器的分路控制,这里我编写了一个脚本方便对4路继电器的控制,可以设置开启时间,关闭时间,复位等功能 软件界面 在设备管理器查看串口号后&…...

基于谷歌ADK的 智能产品推荐系统(2): 模块功能详解

在我的上一篇博客:基于谷歌ADK的 智能产品推荐系统(1): 功能简介-CSDN博客 中我们介绍了个性化购物 Agent 项目,该项目展示了一个强大的框架,旨在模拟和实现在线购物环境中的智能导购。它不仅仅是一个简单的聊天机器人,更是一个集…...

VSCode 没有添加Windows右键菜单

关键字:VSCode;Windows右键菜单;注册表。 文章目录 前言一、工程环境二、配置流程1.右键文件打开2.右键文件夹打开3.右键空白处打开文件夹 三、测试总结 前言 安装 VSCode 时没有注意,实际使用的时候发现 VSCode 在 Windows 菜单栏…...

vxe-table vue 表格复选框多选数据,实现快捷键 Shift 批量选择功能

vxe-table vue 表格复选框多选数据&#xff0c;实现快捷键 Shift 批量选择功能 查看官网&#xff1a;https://vxetable.cn 效果 代码 通过 checkbox-config.isShift 启用批量选中,启用后按住快捷键和鼠标批量选取 <template><div><vxe-grid v-bind"gri…...

Android Framework预装traceroute执行文件到system/bin下

文章目录 Android SDK中寻找traceroute代码内置traceroute到SDK中traceroute参数说明-I 参数&#xff08;使用 ICMP Echo 请求&#xff09;-T 参数&#xff08;使用 TCP SYN 包&#xff09; 相关文章 Android SDK中寻找traceroute代码 设备使用的是Android 11&#xff0c;在/s…...

生信服务器 | 做生信为什么推荐使用Linux服务器?

原文链接&#xff1a;生信服务器 | 做生信为什么推荐使用Linux服务器&#xff1f; 一、 做生信为什么推荐使用服务器&#xff1f; 大家好&#xff0c;我是小杜。在做生信分析的同学&#xff0c;或是将接触学习生信分析的同学&#xff0c;<font style"color:rgb(53, 1…...

react-pdf(pdfjs-dist)如何兼容老浏览器(chrome 49)

之前都是使用react-pdf来渲染pdf文件&#xff0c;这次有个需求是要兼容xp环境&#xff0c;xp上chrome最高支持到49&#xff0c;虽然说iframe或者embed都可以实现预览pdf&#xff0c;但为了后续的定制化需求&#xff0c;还是需要使用js库来渲染。 chrome 49测试环境 能用的测试…...

RKNN开发环境搭建2-RKNN Model Zoo 环境搭建

目录 1.简介2.环境搭建2.1 启动 docker 环境2.2 安装依赖工具2.3 下载 RKNN Model Zoo2.4 RKNN模型转化2.5编译C++1.简介 RKNN Model Zoo基于 RKNPU SDK 工具链开发, 提供了目前主流算法的部署例程. 例程包含导出RKNN模型, 使用 Python API, CAPI 推理 RKNN 模型的流程.   本…...

AT模式下的全局锁冲突如何解决?

一、全局锁冲突解决方案 1. 业务层重试机制&#xff08;推荐方案&#xff09; Service public class OrderService {GlobalTransactionalRetryable(maxAttempts 3, backoff Backoff(delay 100))public void createOrder(OrderDTO order) {// 库存扣减&#xff08;自动加全…...

20250609在荣品的PRO-RK3566开发板的Android13下解决串口可以执行命令但是脚本执行命令异常的问题

20250609在荣品的PRO-RK3566开发板的Android13下解决串口可以执行命令但是脚本执行命令异常的问题 2025/6/9 20:54 缘起&#xff0c;为了跨网段推流&#xff0c;千辛万苦配置好了网络参数。 但是命令iptables -t filter -F tetherctrl_FORWARD可以在调试串口/DEBUG口正确执行。…...

[QMT量化交易小白入门]-六十二、ETF轮动中简单的评分算法如何获取历史年化收益32.7%

本专栏主要是介绍QMT的基础用法,常见函数,写策略的方法,也会分享一些量化交易的思路,大概会写100篇左右。 QMT的相关资料较少,在使用过程中不断的摸索,遇到了一些问题,记录下来和大家一起沟通,共同进步。 文章目录 相关阅读1. 策略概述2. 趋势评分模块3 代码解析4 木头…...

21-Oracle 23 ai-Automatic SQL Plan Management(SPM)

小伙伴们&#xff0c;有没有迁移数据库完毕后或是突然某一天在同一个实例上同样的SQL&#xff0c; 性能不一样了、业务反馈卡顿、业务超时等各种匪夷所思的现状。 于是SPM定位开始&#xff0c;OCM考试中SPM必考。 其他的AWR、ASH、SQLHC、SQLT、SQL profile等换作下一个话题…...

性能优化中,多面体模型基本原理

1&#xff09;多面体编译技术是一种基于多面体模型的程序分析和优化技术&#xff0c;它将程序 中的语句实例、访问关系、依赖关系和调度等信息映射到多维空间中的几何对 象&#xff0c;通过对这些几何对象进行几何操作和线性代数计算来进行程序的分析和优 化。 其中&#xff0…...

【Zephyr 系列 16】构建 BLE + LoRa 协同通信系统:网关转发与混合调度实战

🧠关键词:Zephyr、BLE、LoRa、混合通信、事件驱动、网关中继、低功耗调度 📌面向读者:希望将 BLE 和 LoRa 结合应用于资产追踪、环境监测、远程数据采集等场景的开发者 📊篇幅预计:5300+ 字 🧭 背景与需求 在许多 IoT 项目中,单一通信方式往往难以兼顾近场数据采集…...

二维数组 行列混淆区分 js

二维数组定义 行 row&#xff1a;是“横着的一整行” 列 column&#xff1a;是“竖着的一整列” 在 JavaScript 里访问二维数组 grid[i][j] 表示 第i行第j列的元素 let grid [[1, 2, 3], // 第0行[4, 5, 6], // 第1行[7, 8, 9] // 第2行 ];// grid[i][j] 表示 第i行第j列的…...

HTML版英语学习系统

HTML版英语学习系统 这是一个完全免费、无需安装、功能完整的英语学习工具&#xff0c;使用HTML CSS JavaScript实现。 功能 文本朗读练习 - 输入英文文章&#xff0c;系统朗读帮助练习听力和发音&#xff0c;适合跟读练习&#xff0c;模仿学习&#xff1b;实时词典查询 - 双…...

【threejs】每天一个小案例讲解:创建基本的3D场景

代码仓 GitHub - TiffanyHoo/three_practices: Learning three.js together! 可自行clone&#xff0c;无需安装依赖&#xff0c;直接liver-server运行/直接打开chapter01中的html文件 运行效果图 知识要点 核心三要素 场景&#xff08;Scene&#xff09; 使用 THREE.Scene(…...

C#中用于控制自定义特性(Attribute)

我们来详细解释一下 [AttributeUsage(AttributeTargets.Class, AllowMultiple false, Inherited false)] 这个 C# 属性。 在 C# 中&#xff0c;Attribute&#xff08;特性&#xff09;是一种用于向程序元素&#xff08;如类、方法、属性等&#xff09;添加元数据的机制。Attr…...

2025 后端自学UNIAPP【项目实战:旅游项目】7、景点详情页面【完结】

1、获取景点详情的请求【my_api.js】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http(/login/getWXSessionKey, {code,avatar}); };//…...

项目进度管理软件是什么?项目进度管理软件有哪些核心功能?

无论是建筑施工、软件开发&#xff0c;还是市场营销活动&#xff0c;项目往往涉及多个团队、大量资源和严格的时间表。如果没有一个系统化的工具来跟踪和管理这些元素&#xff0c;项目很容易陷入混乱&#xff0c;导致进度延误、成本超支&#xff0c;甚至失败。 项目进度管理软…...

iOS 项目怎么构建稳定性保障机制?一次系统性防错经验分享(含 KeyMob 工具应用)

崩溃、内存飙升、后台任务未释放、页面卡顿、日志丢失——稳定性问题&#xff0c;不一定会立刻崩&#xff0c;但一旦积累&#xff0c;就是“上线后救不回来的代价”。 稳定性保障不是某个工具的功能&#xff0c;而是一套贯穿开发、测试、上线全流程的“观测分析防范”机制。 …...

02-性能方案设计

需求分析与测试设计 根据具体的性能测试需求&#xff0c;确定测试类型&#xff0c;以及压测的模块(web/mysql/redis/系统整体)前期要与相关人员充分沟通&#xff0c;初步确定压测方案及具体的性能指标QA完成性能测试设计后&#xff0c;需产出测试方案文档发送邮件到项目组&…...

window 显示驱动开发-如何查询视频处理功能(三)

​D3DDDICAPS_GETPROCAMPRANGE请求类型 UMD 返回指向 DXVADDI_VALUERANGE 结构的指针&#xff0c;该结构包含特定视频流上特定 ProcAmp 控件属性允许的值范围。 Direct3D 运行时在D3DDDIARG_GETCAPS的 pInfo 成员指向的变量中为特定视频流的 ProcAmp 控件属性指定DXVADDI_QUER…...

MySQL基本操作(续)

第3章&#xff1a;MySQL基本操作&#xff08;续&#xff09; 3.3 表操作 表是关系型数据库中存储数据的基本结构&#xff0c;由行和列组成。在MySQL中&#xff0c;表操作包括创建表、查看表结构、修改表和删除表等。本节将详细介绍这些操作。 3.3.1 创建表 在MySQL中&#…...

JUC并发编程(二)Monitor/自旋/轻量级/锁膨胀/wait/notify/锁消除

目录 一 基础 1 概念 2 卖票问题 3 转账问题 二 锁机制与优化策略 0 Monitor 1 轻量级锁 2 锁膨胀 3 自旋 4 偏向锁 5 锁消除 6 wait /notify 7 sleep与wait的对比 8 join原理 一 基础 1 概念 临界区 一段代码块内如果存在对共享资源的多线程读写操作&#xf…...

SpringCloud优势

目录 完善的微服务支持 高可用性和容错性 灵活的配置管理 强大的服务网关 分布式追踪能力 丰富的社区生态 易于与其他技术栈集成 完善的微服务支持 Spring Cloud 提供了一整套工具和组件来支持微服务架构的开发,包括服务注册与发现、负载均衡、断路器、配置管理等功能…...

Electron简介(附电子书学习资料)

一、什么是Electron&#xff1f; Electron 是一个由 GitHub 开发的 开源框架&#xff0c;允许开发者使用 Web技术&#xff08;HTML、CSS、JavaScript&#xff09; 构建跨平台的桌面应用程序&#xff08;Windows、macOS、Linux&#xff09;。它将 Chromium浏览器内核 和 Node.j…...

深入理解 C++ 左值右值、std::move 与函数重载中的参数传递

在 C 编程中&#xff0c;左值和右值的概念以及std::move的使用&#xff0c;常常让开发者感到困惑。特别是在函数重载场景下&#xff0c;如何合理利用这些特性来优化代码性能、确保语义正确&#xff0c;更是一个值得深入探讨的话题。 在开始之前&#xff0c;先提出几个问题&…...

【大厂机试题解法笔记】矩阵匹配

题目 从一个 N * M&#xff08;N ≤ M&#xff09;的矩阵中选出 N 个数&#xff0c;任意两个数字不能在同一行或同一列&#xff0c;求选出来的 N 个数中第 K 大的数字的最小值是多少。 输入描述 输入矩阵要求&#xff1a;1 ≤ K ≤ N ≤ M ≤ 150 输入格式 N M K N*M矩阵 输…...

java 局域网 rtsp 取流 WebSocket 推送到前端显示 低延迟

众所周知 摄像头取流推流显示前端延迟大 传统方法是服务器取摄像头的rtsp流 然后客户端连服务器 中转多了&#xff0c;延迟一定不小。 假设相机没有专网 公网 1相机自带推流 直接推送到云服务器 然后客户端拉去 2相机只有rtsp &#xff0c;边缘服务器拉流推送到云服务器 …...