feat: DBNetClient Loop命令完善 + vd960DBN 发送调试打印

vd960DBN:
- loop_uart_proto.c: 所有发送函数添加 LUP Tx 调试打印
- tcp_json_srv.c: 新增 loop_version_query/loop_reset/loop_factory_init/
  loop_sens_read/loop_sens_write 命令处理器 + 延迟响应解析
- 修复 loop_sens_write 未设置命令状态机和错误使用解析函数的问题

DBNetClient:
- tcp_json_client.py: 新增 full Loop MCU API (6 条命令)
- main.py: 线圈参数标签页增加版本/复位/出厂/灵敏度操作按钮
This commit is contained in:
wangfq
2026-07-02 10:33:11 +08:00
parent e9b58a660e
commit e9c24ae736
4 changed files with 296 additions and 27 deletions

View File

@@ -184,14 +184,44 @@ class DBNetApp:
def _tab_loop(self, parent):
f = ttk.Frame(parent, padding=10)
ttk.Button(f, text="查询线圈参数 (loop_param_query)",
command=self._do_loop_param_query).pack(anchor=tk.W, pady=3)
ttk.Label(f, text="参数设置通过 BLE/小程序,或 Raw JSON 标签页发送。",
foreground="gray").pack(anchor=tk.W, pady=5)
self.loop_text = scrolledtext.ScrolledText(f, height=12, wrap=tk.WORD,
# Row 0: Version + System
ttk.Label(f, text="地感MCU 操作:", font=("", 10, "bold")).grid(row=0, column=0, sticky=tk.W, pady=(0,5))
btn_row = ttk.Frame(f)
btn_row.grid(row=1, column=0, columnspan=2, sticky=tk.W, pady=2)
ttk.Button(btn_row, text="查询版本", command=self._do_loop_version).pack(side=tk.LEFT, padx=2)
ttk.Button(btn_row, text="复位MCU", command=self._do_loop_reset).pack(side=tk.LEFT, padx=2)
ttk.Button(btn_row, text="出厂初始化", command=self._do_loop_factory_init).pack(side=tk.LEFT, padx=2)
ttk.Separator(f, orient=tk.HORIZONTAL).grid(row=2, column=0, columnspan=2,
sticky=tk.EW, pady=8)
# Row 3: 灵敏度
ttk.Label(f, text="线圈灵敏度 (0x8A):", font=("", 10, "bold")).grid(row=3, column=0, sticky=tk.W)
sens_row = ttk.Frame(f)
sens_row.grid(row=4, column=0, columnspan=2, sticky=tk.W, pady=2)
ttk.Button(sens_row, text="读取灵敏度", command=self._do_loop_sens_read).pack(side=tk.LEFT, padx=2)
ttk.Separator(f, orient=tk.HORIZONTAL).grid(row=5, column=0, columnspan=2,
sticky=tk.EW, pady=8)
# Row 6: 参数
ttk.Label(f, text="线圈参数 (0x63/0x64):", font=("", 10, "bold")).grid(row=6, column=0, sticky=tk.W)
param_row = ttk.Frame(f)
param_row.grid(row=7, column=0, columnspan=2, sticky=tk.W, pady=2)
ttk.Button(param_row, text="查询参数", command=self._do_loop_param_query).pack(side=tk.LEFT, padx=2)
ttk.Label(param_row, text="参数设置通过 Raw JSON 标签页发送 loop_param_set。",
foreground="gray").pack(side=tk.LEFT, padx=10)
ttk.Separator(f, orient=tk.HORIZONTAL).grid(row=8, column=0, columnspan=2,
sticky=tk.EW, pady=8)
# Response display
self.loop_text = scrolledtext.ScrolledText(f, height=14, wrap=tk.WORD,
font=("Consolas", 9))
self.loop_text.pack(fill=tk.BOTH, expand=True)
self.loop_text.grid(row=9, column=0, columnspan=2, sticky="nsew")
f.rowconfigure(9, weight=1)
f.columnconfigure(0, weight=1)
return f
def _tab_system(self, parent):
@@ -418,7 +448,27 @@ class DBNetApp:
topic_sub=self.iot_vars["topic_sub"].get().strip()),
lambda r: self._show_label(self.iot_resp, r))
# Loop
# Loop MCU
def _do_loop_version(self):
self._bg_run(lambda: self.client.loop_version_query(),
lambda r: self._show_resp(self.loop_text, r))
def _do_loop_reset(self):
if not messagebox.askyesno("确认", "复位地感MCU确定"):
return
self._bg_run(lambda: self.client.loop_reset(),
lambda r: self._show_resp(self.loop_text, r))
def _do_loop_factory_init(self):
if not messagebox.askyesno("确认", "地感MCU恢复出厂设置确定"):
return
self._bg_run(lambda: self.client.loop_factory_init(),
lambda r: self._show_resp(self.loop_text, r))
def _do_loop_sens_read(self):
self._bg_run(lambda: self.client.loop_sens_read(),
lambda r: self._show_resp(self.loop_text, r))
def _do_loop_param_query(self):
self._bg_run(lambda: self.client.loop_param_query(),
lambda r: self._show_resp(self.loop_text, r))

View File

@@ -273,12 +273,41 @@ class DBNetClient:
def device_reset(self) -> dict:
return self._tcp.send_command("device_reset")
# Loop
# Loop MCU Commands
def loop_version_query(self) -> dict:
"""获取地感MCU版本号 (CMD 0x4A)"""
return self._tcp.send_command("loop_version_query")
def loop_reset(self) -> dict:
"""复位地感MCU (CMD 0x6D)"""
return self._tcp.send_command("loop_reset")
def loop_factory_init(self) -> dict:
"""地感MCU出厂初始化 (CMD 0x92)"""
return self._tcp.send_command("loop_factory_init")
def loop_sens_read(self) -> dict:
"""读取线圈灵敏度列表 (CMD 0x8A Read)"""
return self._tcp.send_command("loop_sens_read")
def loop_sens_write(self, channels: list[dict]) -> dict:
"""写入线圈灵敏度列表 (CMD 0x8A Write)
channels: [{"ch": 1, "sens_in": 100, "sens_out": 80}, ...]
"""
return self._tcp.send_command("loop_sens_write",
{"channels": channels})
def loop_param_query(self) -> dict:
"""读取地感多路参数 (CMD 0x64)"""
return self._tcp.send_command("loop_param_query")
def loop_param_set(self, channels: list[dict],
auto_mode: bool = False) -> dict:
"""设置地感多路参数 (CMD 0x63)
channels: [{"ch": 1, "sensitivity": 7, "freq_level": 0, ...}, ...]
"""
return self._tcp.send_command("loop_param_set",
{"auto_mode": auto_mode,
"channels": channels})

View File

@@ -483,18 +483,30 @@ void lup_cmd_on_response(const uint8_t *pkg, uint16_t len)
* High-level Send Commands
*===========================================================================*/
static void lup_debug_tx(const uint8_t *buf, uint16_t len)
{
uint8_t i;
PRINT("LUP Tx:");
for (i = 0; i < len; i++) {
PRINT(" %02X", buf[i]);
}
PRINT("\n");
}
void lup_send_get_version(void)
{
uint8_t buf[LUP_MAX_PKG_LEN];
uint16_t len = lup_build_get_version(buf);
lup_debug_tx(buf, len);
UART2_SendString(buf, len);
lup_cmd_begin(LUP_CMD_GET_VERSION, 200); // 200ms timeout
lup_cmd_begin(LUP_CMD_GET_VERSION, 200);
}
void lup_send_reset(void)
{
uint8_t buf[LUP_MAX_PKG_LEN];
uint16_t len = lup_build_reset(buf);
lup_debug_tx(buf, len);
UART2_SendString(buf, len);
// No response expected
}
@@ -503,6 +515,7 @@ void lup_send_factory_init(void)
{
uint8_t buf[LUP_MAX_PKG_LEN];
uint16_t len = lup_build_factory_init(buf);
lup_debug_tx(buf, len);
UART2_SendString(buf, len);
lup_cmd_begin(LUP_CMD_FACTORY_INIT, 500);
}
@@ -511,6 +524,7 @@ void lup_send_sensitivity_read(void)
{
uint8_t buf[LUP_MAX_PKG_LEN];
uint16_t len = lup_build_sensitivity_read(buf);
lup_debug_tx(buf, len);
UART2_SendString(buf, len);
lup_cmd_begin(LUP_CMD_SENSITIVITY, 200);
}
@@ -519,6 +533,7 @@ void lup_send_set_param(const LUP_ParamSet *ps)
{
uint8_t buf[LUP_MAX_PKG_LEN];
uint16_t len = lup_build_set_param(buf, ps);
lup_debug_tx(buf, len);
UART2_SendString(buf, len);
lup_cmd_begin(LUP_CMD_SET_PARAM, 500);
}
@@ -527,6 +542,7 @@ void lup_send_get_param(void)
{
uint8_t buf[LUP_MAX_PKG_LEN];
uint16_t len = lup_build_get_param(buf);
lup_debug_tx(buf, len);
UART2_SendString(buf, len);
lup_cmd_begin(LUP_CMD_GET_PARAM, 500);
}

View File

@@ -562,6 +562,93 @@ static void handle_report_config(uint8_t socket, uint32_t msg_id, const char *js
PRINT("JSON: report_config (push not yet implemented)\n");
}
/* 4.16 loop_version_query — 获取地感MCU版本号 (CMD 0x4A) */
static void handle_loop_version_query(uint8_t socket, uint32_t msg_id, const char *json) {
lup_send_get_version();
g_json_pending.active = 1;
g_json_pending.socket = socket;
g_json_pending.msg_id = msg_id;
strncpy(g_json_pending.cmd, "loop_version_query", sizeof(g_json_pending.cmd) - 1);
g_json_pending.deadline = mstick() + 300;
PRINT("JSON: loop_version_query sent\n");
}
/* 4.17 loop_reset — 复位地感MCU (CMD 0x6D, 无回复) */
static void handle_loop_reset(uint8_t socket, uint32_t msg_id, const char *json) {
lup_send_reset();
json_send_ok(socket, msg_id, "loop_reset", NULL);
PRINT("JSON: loop_reset sent (no response expected)\n");
}
/* 4.18 loop_factory_init — 地感MCU出厂初始化 (CMD 0x92) */
static void handle_loop_factory_init(uint8_t socket, uint32_t msg_id, const char *json) {
lup_send_factory_init();
g_json_pending.active = 1;
g_json_pending.socket = socket;
g_json_pending.msg_id = msg_id;
strncpy(g_json_pending.cmd, "loop_factory_init", sizeof(g_json_pending.cmd) - 1);
g_json_pending.deadline = mstick() + 600;
PRINT("JSON: loop_factory_init sent\n");
}
/* 4.19 loop_sens_read — 读取线圈灵敏度列表 (CMD 0x8A Read) */
static void handle_loop_sens_read(uint8_t socket, uint32_t msg_id, const char *json) {
lup_send_sensitivity_read();
g_json_pending.active = 1;
g_json_pending.socket = socket;
g_json_pending.msg_id = msg_id;
strncpy(g_json_pending.cmd, "loop_sens_read", sizeof(g_json_pending.cmd) - 1);
g_json_pending.deadline = mstick() + 300;
PRINT("JSON: loop_sens_read sent\n");
}
/* 4.20 loop_sens_write — 写入线圈灵敏度列表 (CMD 0x8A Write) */
static void handle_loop_sens_write(uint8_t socket, uint32_t msg_id, const char *json) {
char *data = json_get_data_str(json);
if (!data) {
json_send_error(socket, msg_id, "loop_sens_write", JSON_CODE_PARAM_ERR, "missing data");
return;
}
LUP_Sensitivity sens;
memset(&sens, 0, sizeof(sens));
sens.rw = LUP_SENS_RW_WRITE;
uint32_t amount = json_get_uint_field(data, "\"amount\"");
if (amount == 0 || amount > LUP_COIL_COUNT) amount = LUP_COIL_COUNT;
sens.amount = (uint8_t)amount;
char key[16];
uint8_t i;
for (i = 0; i < sens.amount; i++) {
snprintf(key, sizeof(key), "\"ch%d\"", i + 1);
char ch_data[256] = {0};
reset_tmp();
simple_parse_json(data, key, g_tmp_value);
if (strlen(g_tmp_value) == 0) continue;
strncpy(ch_data, g_tmp_value, sizeof(ch_data) - 1);
sens.sens_in[i] = (uint16_t)json_get_uint_field(ch_data, "\"sens_in\"");
sens.sens_out[i] = (uint16_t)json_get_uint_field(ch_data, "\"sens_out\"");
}
free(data);
uint8_t buf[LUP_MAX_PKG_LEN];
uint16_t len = lup_build_sensitivity_write(buf, &sens);
UART2_SendString(buf, len);
PRINT("LUP Tx:");
for (i = 0; i < len; i++) PRINT(" %02X", buf[i]);
PRINT("\n");
lup_cmd_begin(LUP_CMD_SENSITIVITY, 300);
g_json_pending.active = 1;
g_json_pending.socket = socket;
g_json_pending.msg_id = msg_id;
strncpy(g_json_pending.cmd, "loop_sens_write", sizeof(g_json_pending.cmd) - 1);
g_json_pending.deadline = mstick() + 300;
PRINT("JSON: loop_sens_write sent (ch=%d)\n", sens.amount);
}
/*===========================================================================
* Command Dispatch Table
@@ -573,21 +660,26 @@ typedef struct {
} JsonCmdEntry;
static const JsonCmdEntry g_cmd_table[] = {
{"pwd_verify", handle_pwd_verify, 0},
{"dev_serial_set", handle_dev_serial_set, 1},
{"dev_info_query", handle_dev_info_query, 1},
{"ssc_net_set", handle_ssc_net_set, 1},
{"ssc_net_query", handle_ssc_net_query, 1},
{"iot_net_set", handle_iot_net_set, 1},
{"iot_net_query", handle_iot_net_query, 1},
{"iot_topic_set", handle_iot_topic_set, 1},
{"iot_topic_query", handle_iot_topic_query, 1},
{"pwd_set", handle_pwd_set, 1},
{"factory_reset", handle_factory_reset, 1},
{"device_reset", handle_device_reset, 1},
{"loop_param_set", handle_loop_param_set, 1},
{"loop_param_query", handle_loop_param_query, 1},
{"report_config", handle_report_config, 1},
{"pwd_verify", handle_pwd_verify, 0},
{"dev_serial_set", handle_dev_serial_set, 1},
{"dev_info_query", handle_dev_info_query, 1},
{"ssc_net_set", handle_ssc_net_set, 1},
{"ssc_net_query", handle_ssc_net_query, 1},
{"iot_net_set", handle_iot_net_set, 1},
{"iot_net_query", handle_iot_net_query, 1},
{"iot_topic_set", handle_iot_topic_set, 1},
{"iot_topic_query", handle_iot_topic_query, 1},
{"pwd_set", handle_pwd_set, 1},
{"factory_reset", handle_factory_reset, 1},
{"device_reset", handle_device_reset, 1},
{"loop_param_set", handle_loop_param_set, 1},
{"loop_param_query", handle_loop_param_query, 1},
{"loop_version_query", handle_loop_version_query, 1},
{"loop_reset", handle_loop_reset, 1},
{"loop_factory_init", handle_loop_factory_init, 1},
{"loop_sens_read", handle_loop_sens_read, 1},
{"loop_sens_write", handle_loop_sens_write, 1},
{"report_config", handle_report_config, 1},
};
#define JSON_CMD_COUNT (sizeof(g_cmd_table) / sizeof(g_cmd_table[0]))
@@ -830,10 +922,7 @@ static int format_sensor_json(char *buf, uint16_t buf_size, const LUP_SensorRepo
static void json_check_pending(void) {
if (!g_json_pending.active) return;
uint8_t cmd = g_lup_cmd.pending_cmd;
if (g_lup_cmd.state == LUP_STATE_RESPONSE_READY) {
// Response received!
if (strcmp(g_json_pending.cmd, "loop_param_set") == 0) {
uint8_t success = 0;
int ret = lup_parse_set_param_resp(g_lup_cmd.resp_buf, g_lup_cmd.resp_len, &success);
@@ -864,6 +953,91 @@ static void json_check_pending(void) {
g_json_pending.cmd, JSON_CODE_INTERNAL_ERR,
"Failed to parse Loop MCU response");
}
} else if (strcmp(g_json_pending.cmd, "loop_version_query") == 0) {
LUP_VersionInfo info;
memset(&info, 0, sizeof(info));
int ret = lup_parse_version(g_lup_cmd.resp_buf, g_lup_cmd.resp_len, &info);
if (ret == 0) {
snprintf(info.version_str, sizeof(info.version_str),
"V%d.%d.%d (HW:%d.%d.%d)",
info.soft_main, info.soft_sub, info.soft_ssub,
info.hard_main, info.hard_sub, info.hard_ssub);
char data_json[256];
snprintf(data_json, sizeof(data_json),
"{\"soft_ver\":\"%d.%d.%d\",\"hard_ver\":\"%d.%d.%d\","
"\"version_str\":\"%s\"}",
info.soft_main, info.soft_sub, info.soft_ssub,
info.hard_main, info.hard_sub, info.hard_ssub,
info.version_str);
json_send_ok(g_json_pending.socket, g_json_pending.msg_id,
g_json_pending.cmd, data_json);
} else {
json_send_error(g_json_pending.socket, g_json_pending.msg_id,
g_json_pending.cmd, JSON_CODE_INTERNAL_ERR,
"Failed to parse version");
}
} else if (strcmp(g_json_pending.cmd, "loop_factory_init") == 0) {
uint8_t success = 0;
int ret = lup_parse_factory_init_resp(g_lup_cmd.resp_buf, g_lup_cmd.resp_len, &success);
if (ret == 0 && success) {
json_send_ok(g_json_pending.socket, g_json_pending.msg_id,
g_json_pending.cmd, NULL);
} else {
json_send_error(g_json_pending.socket, g_json_pending.msg_id,
g_json_pending.cmd, JSON_CODE_INTERNAL_ERR,
ret == 0 ? "Factory init failed" : "Parse error");
}
} else if (strcmp(g_json_pending.cmd, "loop_sens_read") == 0) {
LUP_Sensitivity sens;
memset(&sens, 0, sizeof(sens));
int ret = lup_parse_sensitivity_resp(g_lup_cmd.resp_buf, g_lup_cmd.resp_len, &sens);
if (ret == 0) {
char data_json[512];
char *p = data_json;
int rem = sizeof(data_json);
int w = snprintf(p, rem, "{\"amount\":%d,\"channels\":[", sens.amount);
p += w; rem -= w;
uint8_t i;
for (i = 0; i < sens.amount; i++) {
w = snprintf(p, rem,
"%s{\"ch\":%d,\"sens_value\":%d}",
(i > 0) ? "," : "", i + 1, sens.sens_in[i]);
p += w; rem -= w;
}
snprintf(p, rem, "]}");
json_send_ok(g_json_pending.socket, g_json_pending.msg_id,
g_json_pending.cmd, data_json);
} else {
json_send_error(g_json_pending.socket, g_json_pending.msg_id,
g_json_pending.cmd, JSON_CODE_INTERNAL_ERR,
"Failed to parse sensitivity");
}
} else if (strcmp(g_json_pending.cmd, "loop_sens_write") == 0) {
// 0x8A Write response returns sensitivity list
LUP_Sensitivity sens;
memset(&sens, 0, sizeof(sens));
int ret = lup_parse_sensitivity_resp(g_lup_cmd.resp_buf, g_lup_cmd.resp_len, &sens);
if (ret == 0) {
char data_json[512];
char *p = data_json;
int rem = sizeof(data_json);
int w = snprintf(p, rem, "{\"amount\":%d,\"channels\":[", sens.amount);
p += w; rem -= w;
uint8_t i;
for (i = 0; i < sens.amount; i++) {
w = snprintf(p, rem,
"%s{\"ch\":%d,\"sens_value\":%d}",
(i > 0) ? "," : "", i + 1, sens.sens_in[i]);
p += w; rem -= w;
}
snprintf(p, rem, "]}");
json_send_ok(g_json_pending.socket, g_json_pending.msg_id,
g_json_pending.cmd, data_json);
} else {
json_send_error(g_json_pending.socket, g_json_pending.msg_id,
g_json_pending.cmd, JSON_CODE_INTERNAL_ERR,
"Failed to parse sensitivity response");
}
}
lup_cmd_done();