/* app_loop.c - 主循环业务调度封装 * * 将 main() 中 while(1) 的业务逻辑封装到本模块, * 使 main.c 只保留初始化和 app_loop_process() 调用。 */ #include "app_loop.h" #include "app_common.h" #include "user_cmd.h" #include "relay.h" #include "network.h" #include "led.h" #include "adc.h" #include "feed_scale.h" #include "iic.h" #include "24c02.h" #include "uart.h" #include "../W25Q64/w25q64.h" /* 主循环状态变量 */ static uint8_t ota_confirmed = 0; static uint8_t ota_simulate_fail = 0; /* =1测试:模拟 OTA 确认失败 */ static uint8_t ota_fail_logged = 0; static uint32_t ota_confirm_tick = 0; static uint32_t mqtt_ready_tick = 0; static uint8_t mqtt_ready_recorded = 0; static uint8_t mqtt_first_status_reported = 0; /** * @brief 业务模块初始化:横幅 → 配置/存储 → 外设 → 网络 → 串口接收 * @note 基础硬件(时钟/GPIO/UART/看门狗)由 Board_Init() 完成 */ void app_init(void) { log_info(""); log_info("* * * * * * * * * * * * * * * * * *"); log_info("* Version: 1.3.0 *"); log_info("* * * * * * * By Helei * * * *"); /* EEPROM 中的 MQTT/版本配置 */ iicInit(); eepromReadInfo(); log_info("> Server: %s:%d", MqttInfoStr.ServerIP, MqttInfoStr.ServerPort); log_info("> Client: %s", MqttInfoStr.ClientID); log_info("> User : %s", MqttInfoStr.Username); log_info("> Firmware Ver: %u", MqttInfoStr.Ver); /* W25Q64 外部 Flash,用于 OTA 固件暂存 */ W25Q64_Init(); /* 继电器 GPIO(控制输出 + 反馈输入) */ relayInit(); /* RS485 喂料秤控制模块 */ feedScaleInit(); /* LED(LED2 心跳同时兼任外部看门狗 TPL5010 喂狗) */ ledInit(); /* ADC 电量检测 */ adcInit(); update_batter(); /* 网络模块初始化:4G Air780E 优先,无 SIM 卡时自动切换到 WiFi(ESP-01S) */ NET_init(); /* 启动各路串口接收 */ USART2_StartRx(); /* RS485 喂料秤 */ USART3_StartRx(); /* 4G MQTT 消息,DMA+IDLE */ USART1_StartRx(); /* 蓝牙调试命令 */ } void app_loop_init(void) { /* 继电器断电记忆恢复挪到 app_loop_process 的"MQTT 首次就绪"分支执行: * 无 SIM 走 WiFi 兜底时联网要等约 2 分钟,这里若按开机瞬时状态判断会永远等不到 */ } void app_loop_process(void) { /* 记录 MQTT 首次就绪时刻 */ if (!mqtt_ready_recorded && g_net_mqtt_ready) { mqtt_ready_recorded = 1; mqtt_ready_tick = HAL_GetTick(); } /* MQTT 首次就绪后:先按 EEPROM 恢复继电器上次状态(断电记忆), * 再上报一次完整状态,让平台刷新为真实状态 */ if (g_net_mqtt_ready && !mqtt_first_status_reported) { mqtt_first_status_reported = 1; relayRestoreState(); relayStatueUpdata(); log_info("> Main: first status report after MQTT ready"); ota_confirm_tick = HAL_GetTick(); } /* 更新联网指示灯 */ g_net_led_connected = g_net_mqtt_ready ? 1 : 0; /* 首次发布成功后延迟 3s 再 OTA 确认 */ if (!ota_confirmed && g_net_mqtt_ready && g_net_first_publish_ok && (HAL_GetTick() - ota_confirm_tick) > 3000) { if (ota_simulate_fail) { if (!ota_fail_logged) { ota_fail_logged = 1; log_info("> OTA confirm skipped (simulate fail for rollback test)"); } /* 不置位 ota_confirmed,让 1 分钟健康超时触发复位 */ } else { ota_confirmed = 1; NET_ota_confirm(); log_info("> OTA confirmed (MQTT ready + first publish ok + 3s delay)"); } } /* MQTT 就绪后 1 分钟仍未首次 MPUB,主动复位 */ if (!ota_confirmed && mqtt_ready_recorded && (HAL_GetTick() - mqtt_ready_tick) > OTA_HEALTH_TIMEOUT_MS) { log_warn("> OTA health timeout: MQTT ready but no MPUB in 1min, reset"); W25Q64_OTA_IncTrial(); /* 软件复位 BL 不计次,这里自行消耗一次试错 */ HAL_Delay(100); NVIC_SystemReset(); } /* 蓝牙调试命令 */ process_usart1_command(); /* 继电器命令响应状态机 */ relayCmdProcess(); /* 延迟状态上报 */ if (relay_report_pending && (HAL_GetTick() - relay_report_tick) > 100) { relay_report_pending = 0; relayStatueUpdata(); } /* 各模块轮询 */ NET_process(); update_batter(); feedScaleProcess(); activeEvents(); /* 运行时长心跳:每 30s 串口输出一次(天/时/分), * 64 位累计,HAL_GetTick 约 49 天回绕也不受影响 */ { static uint32_t up_last = 0; static uint32_t up_print = 0; static uint64_t up_ms = 0; uint32_t now = HAL_GetTick(); up_ms += (uint32_t)(now - up_last); /* 32位差值天然容忍回绕 */ up_last = now; if (now - up_print >= 30000) { up_print = now; uint32_t mins = (uint32_t)(up_ms / 60000); log_info("> Uptime: %uday--%02uh--%02um\r\n", (unsigned)(mins / 1440), (unsigned)((mins % 1440) / 60), (unsigned)(mins % 60)); } } } /* 后台任务切片:供长时间阻塞的 AT/OTA 流程在等待间隙调用。 * 只包含轮询式、可重入安全的任务;不调用 NET_process,避免递归。 */ void app_bg_service(void) { static uint32_t bg_batt_tick = 0; /* 长阻塞流程(AT/OTA 等待)在等待间隙喂狗 */ IWDG_Feed(); /* 蓝牙调试命令 */ process_usart1_command(); /* 喂料秤轮询与投喂保护 */ feedScaleProcess(); /* LED 心跳/联网指示 */ activeEvents(); /* 电量 1s 节流更新 */ if (HAL_GetTick() - bg_batt_tick >= 1000) { bg_batt_tick = HAL_GetTick(); update_batter(); } }