From c346b57aef0352292a95d306fe77d09fff4b067e Mon Sep 17 00:00:00 2001
From: YuanHongbin <975559679@qq.com>
Date: Wed, 2 Sep 2026 10:50:21 +0800
Subject: [PATCH] =?UTF-8?q?Lora=E4=BF=A1=E9=81=93=E4=BB=8E0=E7=BC=96?=
=?UTF-8?q?=E5=8F=B7=E3=80=81=E6=B3=A2=E7=89=B9=E7=8E=87=E5=8F=AF=E9=80=89?=
=?UTF-8?q?=E3=80=81=E4=BF=AE=E5=A4=8D=E9=80=9A=E8=AE=AF=E6=97=A5=E5=BF=97?=
=?UTF-8?q?=E6=89=93=E5=8D=B0=E4=B8=8D=E5=85=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Lora信道以信道0为起点(470.1MHz~492.6MHz,共46信道)
- 波特率改为可编辑下拉框,默认500000
- 修复para等大数据量响应日志只打印一小段:延迟解析避免UI阻塞导致串口缓冲溢出丢包
- 说明书同步更新
---
gateway_config.py | 84 ++++++++++++++++++++++++++++++++++++----------
generate_manual.py | 22 ++++++------
2 files changed, 77 insertions(+), 29 deletions(-)
diff --git a/gateway_config.py b/gateway_config.py
index 9a30be1..0ab30a0 100644
--- a/gateway_config.py
+++ b/gateway_config.py
@@ -1,6 +1,6 @@
"""
网关配置工具
-串口配置:波特率500000,数据位8,停止位1,校验位None
+串口配置:波特率500000(可修改),数据位8,停止位1,校验位None
"""
import sys
@@ -83,19 +83,39 @@ class SerialThread(QThread):
super().__init__()
self.ser = ser
self.running = True
- self._buffer = ""
+ self._buffer = b""
def run(self):
while self.running:
try:
if self.ser and self.ser.is_open and self.ser.in_waiting > 0:
data = self.ser.read(self.ser.in_waiting)
- try:
- text = data.decode('utf-8', errors='replace')
- except Exception:
- text = data.decode('gbk', errors='replace')
- self.data_received.emit(text)
self.data_received_raw.emit(data)
+ buf = self._buffer + data
+ text = None
+ # 优先 UTF-8;若结尾是不完整字符序列则保留尾部待下次拼接,避免乱码缺字
+ for enc in ('utf-8', 'gbk'):
+ try:
+ text = buf.decode(enc)
+ self._buffer = b""
+ break
+ except UnicodeDecodeError:
+ keep = b""
+ for tail in range(1, min(4, len(buf)) + 1):
+ try:
+ text = buf[:-tail].decode(enc)
+ keep = buf[-tail:]
+ break
+ except UnicodeDecodeError:
+ continue
+ if text is not None:
+ self._buffer = keep
+ break
+ if text is None:
+ text = data.decode('utf-8', errors='replace')
+ self._buffer = b""
+ if text:
+ self.data_received.emit(text)
else:
time.sleep(0.01)
except Exception:
@@ -113,6 +133,11 @@ class GatewayConfigTool(QMainWindow):
self.serial_thread = None
self._recv_buffer = ""
self._para_mode = False
+ # para 大数据量返回时,用定时器延迟解析,避免每段数据全量解析阻塞UI导致串口缓冲溢出丢包
+ self._para_timer = QTimer(self)
+ self._para_timer.setSingleShot(True)
+ self._para_timer.setInterval(250)
+ self._para_timer.timeout.connect(self._on_para_parse)
self.upgrade_dialog = None
self.dev_dialog = None
self.init_ui()
@@ -145,7 +170,13 @@ class GatewayConfigTool(QMainWindow):
conn_layout.addWidget(self.btn_refresh)
conn_layout.addSpacing(20)
- conn_layout.addWidget(QLabel("波特率: 500000"))
+ conn_layout.addWidget(QLabel("波特率:"))
+ self.combo_baud = QComboBox()
+ self.combo_baud.setEditable(True)
+ self.combo_baud.addItems(["115200", "230400", "460800", "500000", "921600", "1000000"])
+ self.combo_baud.setCurrentText("500000")
+ self.combo_baud.setFixedWidth(90)
+ conn_layout.addWidget(self.combo_baud)
conn_layout.addWidget(QLabel("数据位: 8"))
conn_layout.addWidget(QLabel("停止位: 1"))
conn_layout.addWidget(QLabel("校验: None"))
@@ -174,12 +205,12 @@ class GatewayConfigTool(QMainWindow):
self.combo_lora_fc = QComboBox()
self.combo_lora_fc.setFixedWidth(200)
lora_freqs = []
- fc_hz = 410100000
- ch_idx = 1
- while fc_hz < 493000000:
+ fc_hz = 470100000
+ ch_idx = 0
+ while fc_hz <= 492600000:
mhz = fc_hz / 1000000
lora_freqs.append((str(fc_hz), f"信道{ch_idx}: {mhz:.1f}MHz"))
- fc_hz += 200000
+ fc_hz += 500000
ch_idx += 1
for freq, desc in lora_freqs:
self.combo_lora_fc.addItem(desc, freq)
@@ -356,10 +387,15 @@ class GatewayConfigTool(QMainWindow):
if not port_name:
QMessageBox.warning(self, "错误", "请选择有效的串口!")
return
+ try:
+ baud = int(self.combo_baud.currentText().strip())
+ except ValueError:
+ QMessageBox.warning(self, "错误", "波特率无效,请输入数字!")
+ return
try:
self.serial_port = serial.Serial(
port=port_name,
- baudrate=500000,
+ baudrate=baud,
bytesize=serial.EIGHTBITS,
stopbits=serial.STOPBITS_ONE,
parity=serial.PARITY_NONE,
@@ -372,7 +408,7 @@ class GatewayConfigTool(QMainWindow):
self.btn_connect.setText("关闭串口")
self.btn_connect.setStyleSheet("background-color:#f44336; color:white; font-weight:bold;")
- self.log_send(f"已连接 {port_name} @ 500000")
+ self.log_send(f"已连接 {port_name} @ {baud}")
self.statusBar().showMessage(f"已连接: {port_name}")
except Exception as e:
QMessageBox.critical(self, "串口错误", f"无法打开串口:\n{e}")
@@ -395,6 +431,14 @@ class GatewayConfigTool(QMainWindow):
self._recv_buffer = ""
self._para_mode = True
self.send_command("para")
+ self._para_timer.start()
+
+ def _on_para_parse(self):
+ if not self._para_mode:
+ return
+ self._try_parse_para()
+ if not self._para_mode:
+ self._para_timer.stop()
def on_lora_send(self):
value = self.combo_lora_fc.currentData()
@@ -467,7 +511,7 @@ class GatewayConfigTool(QMainWindow):
if self.dev_dialog is not None and self.dev_dialog.isVisible():
self.dev_dialog.feed_text(text)
if self._para_mode:
- self._try_parse_para()
+ self._para_timer.start()
def _try_parse_para(self):
buf = self._recv_buffer.replace('\r', '')
@@ -544,11 +588,15 @@ class GatewayConfigTool(QMainWindow):
self._recv_buffer = ""
self.statusBar().showMessage("配置读取完成")
+ @staticmethod
+ def _escape_html(s):
+ return s.replace('&', '&').replace('<', '<').replace('>', '>')
+
def log_send(self, text):
- self.text_log.append(f'{text}')
+ self.text_log.append(f'{self._escape_html(text)}')
def log_recv(self, text):
- clean = text.replace('\r', '').replace('\n', '
')
+ clean = self._escape_html(text).replace('\r', '').replace('\n', '
')
self.text_log.append(f'<<< {clean}')
def closeEvent(self, event):
@@ -673,7 +721,7 @@ class UpgradeDialog(QDialog):
def _log(self, msg, tag="info"):
color = {"ok": "#2E7D32", "err": "#D32F2F", "info": "#1565C0"}.get(tag, "#000000")
- self.log_text.append(f'{msg}')
+ self.log_text.append(f'{GatewayConfigTool._escape_html(str(msg))}')
def _log_debug(self, data):
if not data:
diff --git a/generate_manual.py b/generate_manual.py
index d360bf5..dbc2049 100644
--- a/generate_manual.py
+++ b/generate_manual.py
@@ -148,7 +148,7 @@ story.append(Spacer(1, 10 * mm))
cover_info = [
["软件名称", "网关配置工具"],
["适用设备", "物联网网关(HC32F460 + 4G/Cat1 + Ethernet)"],
- ["通信接口", "串口(UART),波特率 500000"],
+ ["通信接口", "串口(UART),波特率默认500000可修改"],
["运行环境", "Windows 7/10/11"],
["文档版本", "V1.0"],
]
@@ -208,9 +208,9 @@ story.append(Spacer(1, 2 * mm))
features = [
["功能模块", "说明"],
- ["串口通信", "通过USB转串口连接网关设备,波特率500000,实时收发指令"],
+ ["串口通信", "通过USB转串口连接网关设备,波特率默认500000可自行修改,实时收发指令"],
["配置读取", "一键读取网关全部参数,包括软件/硬件版本、MAC地址、电池状态、注册状态等"],
- ["Lora信道配置", "选择并设置Lora通信信道频率(410.1MHz ~ 492.9MHz)"],
+ ["Lora信道配置", "选择并设置Lora通信信道频率(470.1MHz ~ 492.6MHz)"],
["自定义指令", "支持手动输入任意指令与网关交互"],
["通讯日志", "实时显示发送和接收的通讯数据,便于调试"],
]
@@ -272,9 +272,9 @@ story.append(hr())
# 3.1
story.append(heading2("3.1 串口连接"))
-story.append(body("串口连接是使用本工具的前提。软件固定使用以下串口参数:"))
+story.append(body("串口连接是使用本工具的前提。串口参数(波特率可修改,默认500000):"))
serial_rows = [
- ["波特率", "500000"],
+ ["波特率", "500000(可在下拉框中选择或手动输入)"],
["数据位", "8"],
["停止位", "1"],
["校验位", "None(无校验)"],
@@ -304,8 +304,8 @@ story.append(make_table(["显示项", "指令关键字", "说明"], para_rows, c
# 3.3
story.append(heading2("3.3 Lora信道配置"))
story.append(body(
- "Lora信道配置用于设置网关的Lora无线通信频率。信道列表覆盖410.1MHz至492.9MHz范围,"
- "频率间隔0.2MHz,共415个信道可供选择。"
+ "Lora信道配置用于设置网关的Lora无线通信频率。信道列表覆盖470.1MHz至492.6MHz范围,"
+ "频率间隔0.5MHz,共46个信道可供选择。"
))
story.append(Spacer(1, 2 * mm))
@@ -319,9 +319,9 @@ for i, s in enumerate(lora_steps, 1):
story.append(Spacer(1, 2 * mm))
lora_range = [
- ["起始频率", "410.1 MHz(信道1)"],
- ["终止频率", "492.9 MHz(信道415)"],
- ["频率间隔", "0.2 MHz(200 kHz)"],
+ ["起始频率", "470.1 MHz(信道0)"],
+ ["终止频率", "492.6 MHz(信道45)"],
+ ["频率间隔", "0.5 MHz(500 kHz)"],
["指令格式", "lora fc [频率值,单位Hz]"],
]
story.append(make_table(["项目", "值"], lora_range, col_widths=[35 * mm, 115 * mm]))
@@ -401,7 +401,7 @@ story.append(hr())
warnings = [
"串口独占:同一串口在同一时间只能被一个程序打开,请确保使用前关闭其他可能占用串口的软件。",
- "波特率固定:本工具固定使用500000波特率,与网关固件匹配,请勿尝试修改。",
+ "波特率:波特率默认500000,可在下拉框选择或手动输入修改,请与网关固件实际波特率保持一致。",
f"数据备份:修改配置前建议先通过{LQ}查看配置{RQ}记录当前参数,以便需要时恢复。",
"断电保护:配置修改后请确认网关已保存参数,避免在参数写入过程中断电。",
"驱动安装:首次使用需安装USB转串口芯片驱动(如CH340、CP2102等),具体型号取决于使用的转接线。",