写在前面的话
项目需求,使用扫码枪扫描二维码,并把内容写入单片机内。一开始的要求是把固件和生产信息同时写入
单片机的 FLASH 内,从网上采购了带 USB 扫码枪触发的离线下载器,达到了要求。
可惜现在开发环境变成了模组的 SDK 开发,只能想办法外部交互写入。
看看手头上的东西:
1、树莓派3B
2、ScanHome SH-410
3、USB转485
决定使用如下的方式,完成数据写入:
![](https://i-blog.csdnimg.cn/blog_migrate/60554c7e375cb815ed114acbcd5e93f4.png)
为什么要用树莓派呢
之前项目买的 USB 扫码枪原理是模拟键盘输出,不是工业串口输出。为了省下100多块钱,我决定用树莓派来代替。为啥没有赚了的感觉??
要求
必须要带 GUI, 但是界面要越简单越好。要有数据回显,方便使用者确认数据确实写入到设备当中。
GUI 框架选择了熟悉的 QT,编程语言选择了 Python, 原因是第三方库多,操作串口和 USB 比较简单。
实际编写过程中,发现没有拖拽的 Qt Designer 确实比较困难。虽然也可以导入 ui 文件,但是需要来回切换,很麻烦。
我决定采用极简风,没有设计就是最好的设计。两个输入框和两个按钮搞定,哈哈。
![](https://i-blog.csdnimg.cn/blog_migrate/06f7e4be5b061d58c95566698dc4451c.png)
主界面代码
UI线程和子线程分离,这样串口任务和usb扫码枪的任务不会阻塞 UI 线程.
点击启动按钮后,串口读取和usb扫描线程启动。当两个线程分别轮询到有数据到来时,主动发信信号,触发 UI 类里的自定义槽函数。
我们可以在槽函数中对数据做分析处理,做响应的动作。
from PySide2.QtCore import Slot
from PySide2.QtWidgets import QWidget, QApplication, QMainWindow
from PySide2.QtWidgets import QLineEdit, QLabel, QPushButton
from PySide2.QtWidgets import QGridLayout
from scan_code import UsbScanSH410
from serial_com import SerialComThread
import json
class MyCodeDisplay(QWidget):
def __init__(self, parent: QWidget = None):
super().__init__(parent)
self.scan_thread = UsbScanSH410("SM SM-2D PRODUCT HID KBW")
self.scan_thread.signal_received.connect(self.slot_recv_data_from_usb_scan)
self.com_thread = SerialComThread("/dev/ttyUSB0")
self.com_thread.signal_received.connect(self.slot_recv_data_from_serial)
self.line_edit_scan_input = QLineEdit()
self.line_edit_echo = QLineEdit()
self.label_scan_input = QLabel("扫描输入")
self.label_edit_echo = QLabel("设备回显")
self.btn_run = QPushButton("启动运行")
self.btn_run.clicked.connect(self.slot_run)
self.btn_stop = QPushButton("停止运行")
self.btn_stop.clicked.connect(self.slot_stop)
self.grid_layout = QGridLayout()
self.grid_layout.addWidget(self.label_scan_input, 1, 1)
self.grid_layout.addWidget(self.line_edit_scan_input, 1, 2)
self.grid_layout.addWidget(self.label_edit_echo, 2, 1)
self.grid_layout.addWidget(self.line_edit_echo, 2, 2)
self.grid_layout.addWidget(self.btn_run, 3, 1)
self.grid_layout.addWidget(self.btn_stop, 3, 2)
self.setLayout(self.grid_layout)
@Slot()
def slot_run(self):
"""
槽函数,启动子线程任务
:return:
"""
if not self.com_thread.isRunning():
self.com_thread.start()
print("{0} start success."
.format(self.com_thread.objectName()))
else:
print("Warning: {0} already run!"
.format(self.com_thread.objectName()))
if not self.scan_thread.isRunning():
self.scan_thread.start()
print("{0} start success."
.format(self.scan_thread.objectName()))
else:
print("Warning: {0} already run!"
.format(self.scan_thread.objectName()))
@Slot()
def slot_stop(self):
"""
槽函数,停止子线程任务
:return:
"""
self.scan_thread.stop()
self.com_thread.stop()
@Slot()
def slot_recv_data_from_usb_scan(self, content):
"""
槽函数,接收usb数据,通过串口发出写入指令
:param content:
:return:
"""
self.line_edit_echo.clear()
self.line_edit_scan_input.setText(content)
val = {
"func": "set_qrcode",
"content": content,
}
self.com_thread.write(json.dumps(val).encode())
@Slot()
def slot_recv_data_from_serial(self, content: bytes):
"""
槽函数,接收串口数据,并显示在输入框内
:param content:
:return:
"""
self.line_edit_echo.setText(content.decode())
def main_loop():
app = QApplication()
main_window = QMainWindow()
# 设置界面最小尺寸
main_window.setMinimumSize(450, 320)
main_window.setMaximumSize(450, 320)
# 中心窗口
q = QWidget()
main_window.setCentralWidget(q)
# TODO: 点击 X 关闭主窗口,通知子线程停止运行
d = MyCodeDisplay(q)
main_window.destroyed.connect(d.slot_stop)
main_window.show()
app.exec_()
if __name__ == "__main__":
try:
main_loop()
except KeyboardInterrupt as e:
print(e)
全部代码文件地址