移植4G MQTT主动健康检查(每60s查MQTTSTATU,连续3次不在线复位,OTA期间暂停;RSN_MQTT_LOST=18),根治断网假死

This commit is contained in:
helei 2026-09-18 14:56:01 +08:00
parent 721b0f479a
commit 5bc543237e
5 changed files with 65 additions and 0 deletions

View File

@ -998,6 +998,61 @@ static void air780e_gps_poll(void)
MqttInfoStr.ClientID, lon, lat);
}
/*==== MQTT 主动健康检查: 每60s查询 AT+MQTTSTATU, 连续3次不在线则整机复位 ====
* : CLOSED/+NO Service URC (1.5.11 ),
* , URC ,
* air780e_process ( net_at_lock), OTA (g_net_tx_busy) */
#define MQTT_HEALTH_INTERVAL_MS 60000 /* 查询周期 */
#define MQTT_HEALTH_FAIL_MAX 3 /* 连续失败上限 */
static void air780e_mqtt_health(void)
{
static uint32_t s_hl_tick = 0;
static uint8_t s_hl_fail = 0;
if (!g_air780e_mqtt_ready) { s_hl_tick = 0; s_hl_fail = 0; return; }
if (g_net_tx_busy) return; /* OTA 下载期间禁止插 AT */
uint32_t now = HAL_GetTick();
if (s_hl_tick == 0) { s_hl_tick = now; return; }
if (now - s_hl_tick < MQTT_HEALTH_INTERVAL_MS) return;
s_hl_tick = now;
U2_CopyIndex = 0; U2_CopyBuff[0] = 0; U2_CopyFlag = 0;
CAT1_printf("AT+MQTTSTATU");
int online = -1; /* -1=超时 0/2=离线 1=在线 */
int t = 20; /* 最长 2s */
while (--t) {
bg_delay(100);
if (U2_CopyFlag) {
/* 平台下行消息优先, 让 dispatch 处理, 本次不计失败 */
if (strstr((char *)U2_CopyBuff, "+MSUB:")) return;
char *p = strstr((char *)U2_CopyBuff, "+MQTTSTATU");
if (p) {
char *c = strchr(p, ':');
if (c && c[1]) online = (c[1] == ' ') ? (c[2] - '0') : (c[1] - '0');
break;
}
}
}
if (online == 1) {
if (s_hl_fail) log_info("> 4G: MQTTSTATU 恢复在线");
s_hl_fail = 0;
return;
}
s_hl_fail++;
if (online < 0) log_warn("> 4G: MQTTSTATU 查询超时 (%d/%d)", s_hl_fail, MQTT_HEALTH_FAIL_MAX);
else log_warn("> 4G: MQTTSTATU 离线(status=%d) (%d/%d)", online, s_hl_fail, MQTT_HEALTH_FAIL_MAX);
if (s_hl_fail >= MQTT_HEALTH_FAIL_MAX) {
log_error("> 4G 异常: MQTT 连接丢失(主动查询连续失败),复位");
reset_log_mark(RSN_MQTT_LOST);
HAL_Delay(1000);
NVIC_SystemReset();
}
}
/*==== Process ====*/
void air780e_process(void){
/* 循环处理缓冲区中所有完整 MQTT 消息 */
@ -1006,5 +1061,6 @@ void air780e_process(void){
dispatch_one_mqtt_msg();
}
air780e_gps_poll();
air780e_mqtt_health();
}

View File

@ -57,6 +57,7 @@ static const char *reason_str(uint8_t r)
case RSN_POWER_LOSS: return "外部断电(法拉电容记录)";
case RSN_4G_MODULE: return "4G模组异常自重启";
case RSN_OTA_FAIL: return "OTA失败中止";
case RSN_MQTT_LOST: return "MQTT断开(主动查询)";
case RSN_SOFT_GENERIC: return "软件复位(未标记)";
default: return "未知";
}

View File

@ -39,6 +39,7 @@ typedef struct {
#define RSN_4G_MODULE 15 /* 4G 模组异常自重启(boot.rom) */
#define RSN_SOFT_GENERIC 16 /* 软件复位(未标记细分原因,如 JLink 复位) */
#define RSN_OTA_FAIL 17 /* OTA 下载/前置检查失败,中止后复位 */
#define RSN_MQTT_LOST 18 /* MQTT 断开(主动健康查询连续失败) */
static inline uint32_t boot_share_sum(const volatile boot_share_t *s)
{

View File

@ -37,6 +37,9 @@ typedef struct {
#define RSN_CMD_RECOVERY 13 /* 串口 RECOVERY 命令 */
#define RSN_POWER_LOSS 14 /* 外部 12V 断电(法拉电容续航中记录) */
#define RSN_4G_MODULE 15 /* 4G 模组异常自重启(boot.rom) */
#define RSN_SOFT_GENERIC 16 /* 软件复位(未细分原因, 如 JLink 复位) */
#define RSN_OTA_FAIL 17 /* OTA 失败/前置检查失败, 中止复位 */
#define RSN_MQTT_LOST 18 /* MQTT 断开(主动健康查询连续失败) */
static inline uint32_t boot_share_sum(const volatile boot_share_t *s)
{

View File

@ -21,6 +21,10 @@
控制命令改三段式FC03 预读(判定在线 + 快照命令前反馈)→ FC05 → 150ms → FC03 校验跳变;
命令链路最终无应答即判离线并应答平台 `ext relay board offline`,下次命令自动重新探测恢复;
断电恢复触发点不变(离线→上线比对 EEPROM 逐路补发)
- **4G MQTT 主动健康检查**(移植自老工程 V1.6.6根治断网假死MQTT Ready 后每 60s 查一次
`AT+MQTTSTATU`,连续 3 次不在线(离线/未认证/查询超时整机复位OTA 下载期间g_net_tx_busy暂停查询
被动 URC 处理保留双保险;新增复位原因码 RSN_MQTT_LOST(18)BL 的 boot_share.h 同步补齐 16/17/18
WiFi 通道不加——软件 MQTT 心跳阶梯本身就是主动探测,无"被动等 URC"盲区
- **继电器/投喂平台应答文案全部中文化**UTF-8 字节转义,平台只认 UTF-8直接写汉字=GBK 实测乱码):
负载断开或继电器损坏 / 开关·投喂参数无效 / 重量未就绪暂不能投喂 / 本设备无称重秤 / 扩展板不在线·忙·无应答等
- **指令应答改可靠发送**(修复"继电器已动作但平台响应超时"):新增 `NET_publish_response_wait()`