#include "app_manager.h" #include "xtell.h" #include "ui.h" #include "rgb.h" #include "event.h" #include "system.h" #include "database.h" #include "clock.h" #include "../Drivers/rf.h" // 外部全局变量引用 extern SystemState current_state; extern u8 captured_type; extern u32 captured_addr; extern u8 menu_select; extern volatile u16 ms_tick; // 二级传感器对码菜单中,各类传感器显示名称的字符串 ID,实际文案由 Lang_Get() 按当前语言取出 const StringID device_menu_ids[5] = { STR_DEV_PORTE, // 0: 门磁探测器 STR_DEV_PIR, // 1: 红外探测器 STR_DEV_FUMEE, // 2: 烟雾传感器 STR_DEV_GAZ, // 3: 燃气探测器 STR_DEV_EAU // 4: 水浸探测器 }; // 对码状态机定义 enum { PAIR_STATE_SELECT_TYPE, // 选择要学习的传感器类型 (门磁/PIR/烟感/气感/水浸) PAIR_STATE_WAIT, // 等待射频探测器触发 (扩散雷达页面) PAIR_STATE_CONFIRM, // 已捕获,进入分配防区确认页面 PAIR_STATE_SUCCESS, // 对码绑定成功界面 (SUCCESS) PAIR_STATE_FAIL // 超时或失败界面 (FAIL) }; // 本地私有变量 static u8 pair_state; // 当前的对码阶段状态 static u8 last_radar_sec; // 备份上一次雷达动画的帧索引 static u8 captured_zone = 1; // 准备分配的防区号 (1~99) static u8 success_start_sec = 0;// 对码成功时的起始秒数 (延迟退场) static u8 fail_start_sec = 0; // 对码失败时的起始秒数 static u32 pair_pulse_timer = 0; // 对码超时毫秒计数器 static u16 last_run_tick = 0; // 上一次执行 onRun 时的毫秒滴答数 // 声明本应用的生命周期回调函数 static void PairApp_OnStart(void); static void PairApp_onRun(void); static void PairApp_onClose(void); static EventResult PairApp_onEvent(SystemEvent *evt); // 实体化定义对码应用控制块 WristbandApp pair_app = { APP_ID_PAIR, "Pair", PairApp_OnStart, PairApp_onRun, PairApp_onClose, PairApp_onEvent }; /** * @brief 进入雷达搜索等待阶段 (选定传感器类型之后) */ static void PairApp_EnterWait(void) { pair_state = PAIR_STATE_WAIT; last_radar_sec = 99; // 强制刷写 current_state = STATE_PAIR_WAIT; pair_pulse_timer = 0; last_run_tick = ms_tick; captured_zone = 1; RF_SetMode(1); // 开启无线接收模式 // 初始化对码搜索页面绘制 UI_ShowPairWaitPage(0); // 开启白色幻彩双灯珠常亮 (R=150, G=150, B=150),作为对码状态视觉指示 RGB_Send(150, 150, 150, 150, 150, 150); XTELL_LOG("[PairApp] Entering WAIT\r\n"); } /** * @brief PairApp 启动回调 */ static void PairApp_OnStart(void) { // 为了防止后台射频监听与对码射频接收冲突,将后台射频监听应用暂时卸载 AppManager_DetachFromBackground(&rf_monitor_app); pair_state = PAIR_STATE_SELECT_TYPE; menu_select = 0; current_state = STATE_PAIR_MENU; RF_SetMode(0); // 选类型阶段暂不需要天线,防止接收杂波干扰屏幕绘制 UI_ShowPairMenuPage(menu_select); // 绘制传感器类型选择页 RGB_Send(0, 0, 0, 0, 0, 0); XTELL_LOG("[PairApp] Started (SELECT_TYPE)\r\n"); } /** * @brief PairApp 轮询主回调 */ static void PairApp_onRun(void) { u32 rx_addr; u8 rx_data; u8 flash; u8 frame; u16 tick_diff; // 计算非阻塞的毫秒时间增量 if (ms_tick >= last_run_tick) { tick_diff = ms_tick - last_run_tick; } else { tick_diff = 1000 + ms_tick - last_run_tick; } last_run_tick = ms_tick; if (pair_state == PAIR_STATE_WAIT) { // ====== 等待射频数据期间,白光 3Hz 高频闪烁 (周期 333ms) ====== flash = (ms_tick / 166) % 2; if (flash) RGB_Send(150, 150, 150, 150, 150, 150); else RGB_Send(0, 0, 0, 0, 0, 0); // 每隔 250ms,滚动绘制雷达搜索的 3 帧图片,构成动效 frame = (u8)((ms_tick / 250) % 3); if (frame != last_radar_sec) { last_radar_sec = frame; UI_ShowPairWaitPage(frame); } // 累加计算 30 秒的对码时间上限 pair_pulse_timer += tick_diff; if (pair_pulse_timer >= 30000UL) { pair_pulse_timer = 0; pair_state = PAIR_STATE_FAIL; current_state = STATE_PAIR_FAIL; fail_start_sec = current_sec; UI_ShowPairFailPage(1); // 绘制超时大红叉页 // 红色双灯常亮警告 RGB_Send(200, 0, 0, 200, 0, 0); XTELL_LOG("[PairApp] Timeout fail!\r\n"); return; } // ====== 物理射频包解调接收监听 ====== if (EV1527_Decode(&rx_addr, &rx_data)) { // 保护机制:排除 SOS 码 (0x08) if (rx_data != 0x08) { captured_addr = rx_addr; // 缓存捕获的物理 ID 地址 (含数据码) pair_state = PAIR_STATE_CONFIRM; // 进入防区微调确认模式 current_state = STATE_PAIR_CONFIRM; RF_SetMode(0); // 暂时关闭物理射频,防止再次覆盖 captured_zone = 1; UI_ShowPairConfirmPage(captured_type, captured_zone); XTELL_LOG("[PairApp] Sensor captured. Addr="); XTELL_LOG_HEX32(rx_addr); XTELL_LOG("\r\n"); } } } else if (pair_state == PAIR_STATE_CONFIRM) { // ====== 确认状态:橙色慢闪 (1.5Hz,周期 666ms) ====== u8 flash_orange = (ms_tick / 333) % 2; if (flash_orange) RGB_Send(100, 180, 0, 100, 180, 0); // 橙色 (G=180, R=100) else RGB_Send(0, 0, 0, 0, 0, 0); } else if (pair_state == PAIR_STATE_SUCCESS) { // ====== 成功展示状态:延时 2 秒自动返回主菜单 ====== u8 elapsed = (current_sec >= success_start_sec) ? (current_sec - success_start_sec) : (60 + current_sec - success_start_sec); if (elapsed >= 2) { AppManager_SwitchToForeground(&menu_app); } } else if (pair_state == PAIR_STATE_FAIL) { // ====== 失败展示状态:延时 5 秒自动返回主菜单 ====== u8 elapsed = (current_sec >= fail_start_sec) ? (current_sec - fail_start_sec) : (60 + current_sec - fail_start_sec); if (elapsed >= 5) { AppManager_SwitchToForeground(&menu_app); } } } /** * @brief PairApp 退出清理 */ static void PairApp_onClose(void) { RGB_Send(0, 0, 0, 0, 0, 0); // 关闭 RGB MOTOR = 0; // 停震 RF_SetMode(1); // 开启无线接收 // 重新挂载后台射频监控应用,以便回到时钟状态下能继续捕获报警 AppManager_AttachToBackground(&rf_monitor_app); XTELL_LOG("[PairApp] Closed\r\n"); } /** * @brief PairApp 事件拦截处理回调 */ static EventResult PairApp_onEvent(SystemEvent *evt) { if (pair_state == PAIR_STATE_SELECT_TYPE) { if (evt->key_event == KEY_EVENT_UP_CLICK) { // 短按 ▲ 键上移焦点,允许循环切换 (到顶绕到底) menu_select = (menu_select == 0) ? 4 : (menu_select - 1); UI_ShowPairMenuPage(menu_select); return EVENT_HANDLED; } if (evt->key_event == KEY_EVENT_DOWN_CLICK) { // 短按 ▼ 键下移焦点,允许循环切换 (到底绕到顶) menu_select = (menu_select == 4) ? 0 : (menu_select + 1); UI_ShowPairMenuPage(menu_select); return EVENT_HANDLED; } if (evt->key_event == KEY_EVENT_CONFIRM_CLICK) { // 确认选择:缓存用户选好的传感器类别,短震反馈并进入雷达搜索阶段 captured_type = menu_select; MOTOR = 1; Delay_ms(100); MOTOR = 0; PairApp_EnterWait(); return EVENT_HANDLED; } } if (pair_state == PAIR_STATE_CONFIRM) { if (evt->key_event == KEY_EVENT_UP_CLICK) { // 短按 ▲ 键增加目标分配防区号 (上限 99) if (captured_zone < 99) { captured_zone++; UI_ShowPairConfirmPage(captured_type, captured_zone); } return EVENT_HANDLED; } if (evt->key_event == KEY_EVENT_DOWN_CLICK) { // 短按 ▼ 键减小目标分配防区号 (下限 1) if (captured_zone > 1) { captured_zone--; UI_ShowPairConfirmPage(captured_type, captured_zone); } return EVENT_HANDLED; } if (evt->key_event == KEY_EVENT_CONFIRM_CLICK) { // 短按 ■ 确认键,写入 EEPROM 传感器数据库并保存 Add_Sensor_With_Zone(captured_addr, captured_type, captured_zone); pair_state = PAIR_STATE_SUCCESS; current_state = STATE_PAIR_SUCCESS; success_start_sec = current_sec; UI_ShowPairSuccessPage(); // 绘制打勾页 // 绿色双灯珠常亮,震动马达长震 400ms 反馈 RGB_Send(0, 200, 0, 0, 200, 0); MOTOR = 1; Delay_ms(400); MOTOR = 0; XTELL_LOG("[PairApp] Sensor saved successfully.\r\n"); return EVENT_HANDLED; } } // 在任何阶段,长按 ■ 确认键或长按 ▼ 键都代表放弃/取消对码,统一退回主菜单 (父 App) if (evt->key_event == KEY_EVENT_CONFIRM_LONG || evt->key_event == KEY_EVENT_DOWN_LONG) { RGB_Send(0, 0, 0, 0, 0, 0); AppManager_SwitchToForeground(&menu_app); return EVENT_HANDLED; } return EVENT_IGNORED; }