From 9fe86a5111b33d31ede5292d7a89be0d40384fbe Mon Sep 17 00:00:00 2001 From: helei Date: Sun, 16 Aug 2026 19:14:20 +0800 Subject: [PATCH] =?UTF-8?q?V1.5:=20=E4=BF=AE=E5=A4=8DOTA=E7=A1=AE=E8=AE=A4?= =?UTF-8?q?=E9=80=9A=E7=9F=A5=E5=8F=91=E9=80=81=E5=A4=B1=E8=B4=A5(?= =?UTF-8?q?=E7=A1=AE=E8=AE=A4=E5=BB=B6=E5=90=8E=E8=87=B3+9s=E9=81=BF?= =?UTF-8?q?=E5=BC=80=E4=B8=8A=E6=8A=A5=E9=A3=8E=E6=9A=B4,=E6=9C=AC?= =?UTF-8?q?=E5=9C=B0=E7=A1=AE=E8=AE=A4=E5=85=88=E4=BA=8E=E9=80=9A=E7=9F=A5?= =?UTF-8?q?=E8=90=BD=E7=9B=98,=E9=80=9A=E7=9F=A5=E5=A4=B1=E8=B4=A530s?= =?UTF-8?q?=E9=87=8D=E8=AF=95,=E5=BC=80=E6=9C=BA=E4=B8=8A=E6=8A=A5?= =?UTF-8?q?=E6=8B=89=E5=BC=80300ms)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- V1.5/STM32F103_App1/HardWare/CAR/air780e.c | 54 ++++++++++--------- V1.5/STM32F103_App1/HardWare/CAR/air780e.h | 4 +- .../STM32F103_App1/HardWare/NETWORK/network.c | 17 +++--- .../STM32F103_App1/HardWare/NETWORK/network.h | 4 +- .../HardWare/SYSINIT/app_loop.c | 25 +++++++-- V1.5/STM32F103_App1/README.md | 6 +++ V1.5/鐗堟湰璇存槑.md | 3 ++ 7 files changed, 71 insertions(+), 42 deletions(-) diff --git a/V1.5/STM32F103_App1/HardWare/CAR/air780e.c b/V1.5/STM32F103_App1/HardWare/CAR/air780e.c index 30ffa31..d2edc81 100644 --- a/V1.5/STM32F103_App1/HardWare/CAR/air780e.c +++ b/V1.5/STM32F103_App1/HardWare/CAR/air780e.c @@ -442,23 +442,25 @@ static int ota_ver_save(uint16_t ver) { } /*==== MQTT Publish: OTA进度/结果上报 ====*/ -void air780e_mqtt_report(const char *header, const char *ota_id, int val) { - if (!header || !ota_id || !ota_id[0]) return; +int air780e_mqtt_report(const char *header, const char *ota_id, int val) { + if (!header || !ota_id || !ota_id[0]) return -1; static char cmd[300]; if (strstr(header, "progress")) { /* 进度上报:允许丢包,发了就走 */ sprintf(cmd, "AT+MPUB=\"/iot/data/up/%s\",0,0,\"{\\22header\\22:\\22%s\\22,\\22body\\22:{\\22id\\22:\\22%.16s\\22,\\22progress\\22:%d}}\"", MqttInfoStr.ClientID, header, ota_id, val); CAT1_printf("%s", cmd); + return 0; } else { /* 结果上报:平台据此判定升级完成,必须可靠送达——等 OK 并重试 */ sprintf(cmd, "AT+MPUB=\"/iot/data/up/%s\",0,0,\"{\\22header\\22:\\22%s\\22,\\22body\\22:{\\22id\\22:\\22%.16s\\22,\\22success\\22:%s}}\"", MqttInfoStr.ClientID, header, ota_id, val ? "true" : "false"); if (catSendCmd(cmd, "OK", 3, 30) != 0) { log_warn("> OTA 结果上报失败(重试 3 次无 OK)"); - } else { - log_info("> OTA 结果已上报: success=%d", val); + return -1; } + log_info("> OTA 结果已上报: success=%d", val); + return 0; } } @@ -715,31 +717,31 @@ static void air780e_trigger_ota_body(const char *host, int port, const char *pat } /* 固件更新后由 APP 调用,确认运行稳定后写入 EEPROM 版本号 */ -void air780e_ota_confirm(void) +/* 返回 0=确认通知已送达平台;1=无挂起 OTA;-1=本地已确认但通知未送达(上层应稍后重试) */ +int air780e_ota_confirm(void) { W25Q64_OtaInfo info; - if (W25Q64_OTA_ReadInfo(&info) != 0) return; - if (info.update_state != W25Q64_STATE_INSTALLED) return; - if (info.app_confirmed) return; + if (W25Q64_OTA_ReadInfo(&info) != 0) return 1; + if (info.update_state != W25Q64_STATE_INSTALLED) return 1; - /* 先向服务器上报“确认成功”(走网络层路由,4G/WiFi 通道均可) */ + if (!info.app_confirmed) { + /* 本地确认先行(版本号 + 确认标志落盘),防止通知发送失败导致 BL 误判回滚 */ + if (info.new_version > 0) ota_ver_save(info.new_version); + int ok = 0; + for (int i = 0; i < 3 && !ok; i++) { + W25Q64_OTA_SetConfirmed(); + W25Q64_OtaInfo chk; + if (W25Q64_OTA_ReadInfo(&chk) == 0 && chk.app_confirmed) ok = 1; + else bg_delay(50); + } + if (ok) log_info("> OTA 确认标志已写入"); + else log_error("> OTA 确认写入失败(已重试 3 次)"); + } + + /* 再向服务器上报"确认成功"(走网络层路由,4G/WiFi 通道均可) */ char id_buf[24] = {0}; snprintf(id_buf, sizeof(id_buf), "%u", info.new_version); /* 用版本号作为 id 上报 */ - NET_mqtt_report("iot.ota.confirm.post", id_buf, 1); - - if (info.new_version > 0) ota_ver_save(info.new_version); - - /* 确认标志必须可靠落盘,否则 BL 会一直认为未确认; - 写后回读验证,失败重试 3 次 */ - int ok = 0; - for (int i = 0; i < 3 && !ok; i++) { - W25Q64_OTA_SetConfirmed(); - W25Q64_OtaInfo chk; - if (W25Q64_OTA_ReadInfo(&chk) == 0 && chk.app_confirmed) ok = 1; - else bg_delay(50); - } - if (ok) log_info("> OTA 确认标志已写入"); - else log_error("> OTA 确认写入失败(已重试 3 次)"); + return NET_mqtt_report("iot.ota.confirm.post", id_buf, 1); } @@ -780,11 +782,11 @@ void air780e_init(void) { char pub[256]; sprintf(pub, "AT+MPUB=\"/iot/data/up/%s\",0,0,\"{\\22header\\22:\\22iot.prop.post\\22,\\22body\\22:{\\22pow\\22:1}}\"", MqttInfoStr.ClientID); CAT1_printf("%s", pub); - HAL_Delay(50); + HAL_Delay(300); /* 开机上报拉开间隔,避免模组 MQTT 发送排队撞车 */ if (s_iccid[0]) { sprintf(pub, "AT+MPUB=\"/iot/data/up/%s\",0,0,\"{\\22header\\22:\\22iot.prop.post\\22,\\22body\\22:{\\22iccid\\22:\\22%s\\22}}\"", MqttInfoStr.ClientID, s_iccid); CAT1_printf("%s", pub); - HAL_Delay(50); + HAL_Delay(300); } return; } diff --git a/V1.5/STM32F103_App1/HardWare/CAR/air780e.h b/V1.5/STM32F103_App1/HardWare/CAR/air780e.h index c110661..c6e141a 100644 --- a/V1.5/STM32F103_App1/HardWare/CAR/air780e.h +++ b/V1.5/STM32F103_App1/HardWare/CAR/air780e.h @@ -44,8 +44,8 @@ uint16_t crc16_modbus_update(uint16_t crc, uint8_t data); /* MODBUS CRC16 void air780e_init(void); void air780e_process(void); void air780e_trigger_ota(const char *host, int port, const char *path, const char *ota_id, uint16_t new_ver, uint16_t crc16); -void air780e_mqtt_report(const char *header, const char *ota_id, int percent_or_success); -void air780e_ota_confirm(void); +int air780e_mqtt_report(const char *header, const char *ota_id, int percent_or_success); /* 返回 0=送达 */ +int air780e_ota_confirm(void); /* 0=通知已送达 1=无挂起 -1=通知未送达(需重试) */ uint16_t ota_get_version(void); void CAT1_printf(const char *fmt, ...); diff --git a/V1.5/STM32F103_App1/HardWare/NETWORK/network.c b/V1.5/STM32F103_App1/HardWare/NETWORK/network.c index 55bc89e..7be07a3 100644 --- a/V1.5/STM32F103_App1/HardWare/NETWORK/network.c +++ b/V1.5/STM32F103_App1/HardWare/NETWORK/network.c @@ -352,16 +352,16 @@ void NET_publish_energy(float kwh) net_at_unlock(); } -void NET_mqtt_report(const char *header, const char *ota_id, int val) +int NET_mqtt_report(const char *header, const char *ota_id, int val) { - if (g_net_tx_busy) { log_info("> NET: 进度/结果上报被丢弃 (发送忙)"); return; } - if (net_at_trylock() != 0) { log_info("> NET: 进度/结果上报被丢弃 (AT 忙)"); return; } + if (g_net_tx_busy) { log_info("> NET: 进度/结果上报被丢弃 (发送忙)"); return -1; } + if (net_at_trylock() != 0) { log_info("> NET: 进度/结果上报被丢弃 (AT 忙)"); return -1; } if (s_backend == NET_BACKEND_4G) { - air780e_mqtt_report(header, ota_id, val); + int rc = air780e_mqtt_report(header, ota_id, val); net_at_unlock(); - return; + return rc; } - if (!header || !ota_id || !ota_id[0]) { net_at_unlock(); return; } + if (!header || !ota_id || !ota_id[0]) { net_at_unlock(); return -1; } char json[200]; if (strstr(header, "progress")) { snprintf(json, sizeof(json), @@ -374,13 +374,14 @@ void NET_mqtt_report(const char *header, const char *ota_id, int val) } net_wifi_publish_up(json); net_at_unlock(); + return 0; } -void NET_ota_confirm(void) +int NET_ota_confirm(void) { /* 两通道复用同一确认流程:无挂起 OTA 时内部直接返回; * 其中的上报已改为 NET_mqtt_report,按当前通道路由 */ - air780e_ota_confirm(); + return air780e_ota_confirm(); } /*==== 下行消息统一分发(从 air780e.c 上提,与模组无关)==== diff --git a/V1.5/STM32F103_App1/HardWare/NETWORK/network.h b/V1.5/STM32F103_App1/HardWare/NETWORK/network.h index 82287d0..5d9c3d7 100644 --- a/V1.5/STM32F103_App1/HardWare/NETWORK/network.h +++ b/V1.5/STM32F103_App1/HardWare/NETWORK/network.h @@ -54,8 +54,8 @@ void NET_publish_status(int sw1, int feeding, int sw2, int sw3, int sw4, void NET_publish_response(const char *cmd_id, int ok, const char *msg); void NET_publish_power(int on); void NET_publish_energy(float kwh); /* 上报累计电量,标识符 energy,单位 kWh */ -void NET_mqtt_report(const char *header, const char *ota_id, int val); -void NET_ota_confirm(void); +int NET_mqtt_report(const char *header, const char *ota_id, int val); /* 返回 0=送达 */ +int NET_ota_confirm(void); /* 0=确认通知已送达 1=无挂起 -1=未送达(需重试) */ /* 下行消息统一分发(与模组无关):id去重 → iot.prop.set继电器控制 → iot.ota升级。 * 4G 后端切出 +MSUB 片段后调用;WiFi 后端解出 MQTT PUBLISH payload 后调用。 diff --git a/V1.5/STM32F103_App1/HardWare/SYSINIT/app_loop.c b/V1.5/STM32F103_App1/HardWare/SYSINIT/app_loop.c index 6c87369..f98b723 100644 --- a/V1.5/STM32F103_App1/HardWare/SYSINIT/app_loop.c +++ b/V1.5/STM32F103_App1/HardWare/SYSINIT/app_loop.c @@ -139,6 +139,8 @@ static void app_task(void *arg) static uint8_t ota_simulate_fail = 0; /* =1测试:模拟 OTA 确认失败(验证回退) */ static uint8_t ota_fail_logged = 0; static uint32_t ota_confirm_tick = 0; + static uint8_t ota_report_pending = 0; + static uint32_t ota_report_tick = 0; static uint32_t mqtt_ready_tick = 0; static uint8_t mqtt_ready_recorded = 0; static uint8_t mqtt_first_status_reported = 0; @@ -160,9 +162,9 @@ static void app_task(void *arg) ota_confirm_tick = HAL_GetTick(); } - /* 首次发布成功后延迟 3s 再 OTA 确认 */ + /* 首次发布成功后延迟 9s 再 OTA 确认(避开开机上报风暴:pow/ICCID/状态/电能) */ if (!ota_confirmed && g_net_mqtt_ready && g_net_first_publish_ok && - (HAL_GetTick() - ota_confirm_tick) > 3000) { + (HAL_GetTick() - ota_confirm_tick) > 9000) { if (ota_simulate_fail) { if (!ota_fail_logged) { ota_fail_logged = 1; @@ -171,8 +173,23 @@ static void app_task(void *arg) /* 不置位 ota_confirmed,让 1 分钟健康超时触发复位 */ } else { ota_confirmed = 1; - NET_ota_confirm(); - log_info("> OTA 已确认(MQTT Ready + 首次发布成功 + 延迟3s)"); + if (NET_ota_confirm() < 0) { /* <0 才是未送达;1=无挂起 OTA 属正常 */ + ota_report_pending = 1; /* 通知未送达,30s 后重试 */ + ota_report_tick = HAL_GetTick(); + log_warn("> OTA 确认通知未送达,稍后重试"); + } else { + log_info("> OTA 已确认(MQTT Ready + 首次发布成功 + 延迟9s)"); + } + } + } + + /* OTA 确认通知未送达:每 30s 重试直到成功(本地确认标志已落盘,仅补平台通知) */ + if (ota_report_pending && g_net_mqtt_ready && + (HAL_GetTick() - ota_report_tick) > 30000) { + ota_report_tick = HAL_GetTick(); + if (NET_ota_confirm() >= 0) { + ota_report_pending = 0; + log_info("> OTA 确认通知重试成功"); } } diff --git a/V1.5/STM32F103_App1/README.md b/V1.5/STM32F103_App1/README.md index 7b91176..ad6a566 100644 --- a/V1.5/STM32F103_App1/README.md +++ b/V1.5/STM32F103_App1/README.md @@ -252,6 +252,12 @@ W25Q64 澶栭儴 Flash锛8MB锛夛細 | 妲 B | `0x040000` (256KB) | 鏂板浐浠朵笅杞芥殏瀛 | | 淇℃伅鍖 A/B | `0x080000` / `0x081000` (鍚 4KB) | OTA 鐘舵佸弻鍓湰锛坢agic + 搴忓彿 + checksum锛 | +### OTA 纭閫氱煡鍙潬鎬 + +- 纭閫氱煡锛坄iot.ota.confirm.post`锛夊欢鍚庡埌棣栨鍙戝竷鎴愬姛鍚 **9 绉**鍙戦侊紝閬垮紑寮鏈轰笂鎶ラ鏆达紙pow/ICCID/鐘舵/鐢佃兘鎸ゅ崰妯$粍 MQTT 鍙戦佺獥鍙o級 +- 鏈湴纭鏍囧織鍜岀増鏈彿**鍏堜簬**閫氱煡钀界洏鈥斺旈氱煡鍙戦佸け璐ヤ笉浼氬鑷 BL 璇垽鍥炴粴 +- 閫氱煡鏈佽揪鏃舵瘡 30s 鑷姩閲嶈瘯鐩村埌鎴愬姛锛堜粎琛ュ钩鍙伴氱煡锛夛紱寮鏈 pow/ICCID 涓婃姤闂撮殧鎷夊紑鍒 300ms + ### 鍋ュ悍妫鏌ヤ笌鍥為 鏂板浐浠"鍋ュ悍" = 4G 闄勭潃 鈫 MQTT 杩炴帴 鈫 棣栨鍙戝竷鎴愬姛銆傚洖閫鍒ゆ嵁锛**3 娆$湡瀹炴晠闅**鎵嶅洖閫锛夛細 diff --git a/V1.5/鐗堟湰璇存槑.md b/V1.5/鐗堟湰璇存槑.md index 60955c5..e17bc65 100644 --- a/V1.5/鐗堟湰璇存槑.md +++ b/V1.5/鐗堟湰璇存槑.md @@ -137,3 +137,6 @@ 锛堟爣璇嗙 iccid锛屽瓧绗︿覆锛涘钩鍙扮墿妯″瀷鍔犲瓧绗︿覆瀛楁鍗冲彲锛屼究浜庣墿鑱旂綉鍗″椁愬埌鏈熸煡璇㈢即璐癸級 - 4G 杩愯鏃跺叧闂 WiFi 妯″潡锛欵SP_EN(PA6) 榛樿浣庣數骞筹紝鍒 WiFi 鎵嶆媺楂樸佸垏鍥 4G 鎷変綆鏂數锛堢渷鐢/寤堕暱娉曟媺鐢靛缁埅锛 - 鏂數娑堟伅鎻愰燂細鐢甸噺 <10% 鍒ゅ畾浠 3s 鍘绘姈鏀逛负 100ms 蹇熷鏍革紝纭鍚庝紭鍏堝彂 pow:0銆佸啀鍐欒褰曘佺暀 500ms 璁╂ā缁勫彂瀹屽啀澶嶄綅锛5.5F 鐢靛涓嬪師閫昏緫鏉ヤ笉鍙婁笂鎶ワ級 +- 淇 OTA 纭閫氱煡鍙戦佸け璐ワ紙"閲嶈瘯 3 娆℃棤 OK"锛夛細纭浠庨娆″彂甯+3s 寤跺悗鍒 +9s 閬垮紑寮鏈轰笂鎶ラ鏆达紱 + 鏈湴纭鏍囧織/鐗堟湰鍙峰厛浜庨氱煡钀界洏锛堥氱煡澶辫触涓嶅啀褰卞搷闃插洖婊氾級锛涢氱煡鏈佽揪姣 30s 閲嶈瘯鐩村埌鎴愬姛锛 + 寮鏈 pow/ICCID 涓婃姤闂撮殧鎷夊紑鍒 300ms