168 lines
5.1 KiB
C
168 lines
5.1 KiB
C
|
|
/* 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.1.8 *");
|
|||
|
|
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();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 后台任务切片:供长时间阻塞的 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();
|
|||
|
|
}
|
|||
|
|
}
|