fix(vd960DBN): OTA 下载失败 — hex 提取兼容 json.dumps 空格

现象: DBNMQTTool 第一片 ota_data code=1 crc/gap, received=0, 重试全败

根因: 工具 json.dumps 默认带空格 ("data": "hex"), 设备端 strstr 找
"data":" 无空格格式 → 提取失败 → hexbuf 空 → 解码失败 code=1
(simple_parse_json 容错空格所以其他命令正常, 只有手工 strstr 踩坑)

修复:
- ota_srv.c/h: 新增 ota_json_extract_hex() 兼容有无空格 (扫描 data 键跳过空白)
- iot_mqtt_srv.c: ota_data 改用该函数, 删 strstr 手工提取 + hp 残留
- 单测 +test_json_extract_hex: 无空格/带空格(事故场景)/512hex/找不到 4 场景

验证: 单测 11/11 PASS; 语法 0 错误; 待板级重测下载
This commit is contained in:
wangfq
2026-08-20 14:13:06 +08:00
parent 2484a03326
commit c305ca651f
5 changed files with 547 additions and 459 deletions
@@ -124,6 +124,9 @@ int ota_cmd_flash(uint8_t slot, uint8_t force);
/* 分片落盘用: hex → bin (n 字节), 成功 0 */
int ota_hex_decode(const char *hex, uint8_t *out, uint32_t n);
/* 从 MQTT JSON 中提取 ota_data 的 data 字段 hex 值 (兼容 "data":"..." 与 "data": "...")
返回 hex 字符串长度, 0=未找到/为空 */
int ota_json_extract_hex(const char *json, char *out, int out_len);
/* CRC-32/ISO-HDLC (逐位实现, 零查表 RAM) */
uint32_t ota_crc32(const uint8_t *data, uint32_t len);
@@ -755,7 +755,6 @@ static void iot_handle_publish(const char *topic, uint8_t *payload, int payload_
} else if (strcmp(cmd_str, "ota_data") == 0) {
/* V1.08: 分片下发 (256B/片, 单片 CRC32; 重复片幂等, 乱序拒收指示续传) */
const char *hp;
uint32_t offset = 0, crc32 = 0;
int rc;
memset(tmp, 0, sizeof(tmp));
@@ -764,14 +763,8 @@ static void iot_handle_publish(const char *topic, uint8_t *payload, int payload_
memset(tmp, 0, sizeof(tmp));
simple_parse_json(json, "\"crc32\"", tmp);
if (strlen(tmp) > 0) crc32 = (uint32_t)strtoul(tmp, NULL, 10);
/* hex 值提取: data 对象内字段是 "data":"...", 顶层 data 对象是 "data":{ — 前者唯一 */
hp = strstr(json, "\"data\":\"");
memset(_ota_io.hexbuf, 0, sizeof(_ota_io.hexbuf));
if (hp) {
hp += 8; /* 跳过 "data":" */
strncpy(_ota_io.hexbuf, hp, sizeof(_ota_io.hexbuf) - 1);
{ char *q = strchr(_ota_io.hexbuf, '"'); if (q) *q = '\0'; }
}
/* hex 值提取: ota_json_extract_hex 兼容 "data":"..." 与 "data": "..." (json.dumps 带空格) */
ota_json_extract_hex(json, _ota_io.hexbuf, (int)sizeof(_ota_io.hexbuf));
rc = ota_cmd_data(offset, crc32, _ota_io.hexbuf);
if (rc == 0) {
const OtaMeta *m = ota_meta();
@@ -99,6 +99,39 @@ int ota_hex_decode(const char *hex, uint8_t *out, uint32_t n)
return 0;
}
/* 从 MQTT JSON 提取 ota_data 的 data 字段 hex 值。
⚠ 2026-08-20 事故: json.dumps 默认带空格 ("data": "hex"),
strstr("\"data\":\"") 无空格匹配 → 提取失败 → 单片 CRC 全错。
本函数扫描 "data" 键后跳过任意空格/冒号, 兼容两种格式。 */
int ota_json_extract_hex(const char *json, char *out, int out_len)
{
const char *p = json;
int n = 0;
if (!json || !out || out_len <= 0) return 0;
out[0] = '\0';
while (*p) {
if (strncmp(p, "\"data\"", 6) == 0) {
const char *q = p + 6;
while (*q == ' ' || *q == '\t') q++;
if (*q == ':') {
q++;
while (*q == ' ' || *q == '\t') q++;
if (*q == '"') {
q++; /* 跳过起始引号 */
while (*q && *q != '"' && n < out_len - 1) {
out[n++] = *q;
q++;
}
out[n] = '\0';
return n;
}
}
}
p++;
}
return 0;
}
/*===========================================================================
* 元数据读写 (双备份, 同扇区 0x010000~0x0103FF)
* 写: 显式擦扇区 (45ms) + NoCheck 写主 + 写备份