2026-07-17 09:08:53 +08:00
|
|
|
|
#include "app_manager.h"
|
|
|
|
|
|
#include "xtell.h"
|
|
|
|
|
|
#include "ui.h"
|
|
|
|
|
|
#include "rgb.h"
|
|
|
|
|
|
#include "event.h"
|
|
|
|
|
|
#include "system.h"
|
|
|
|
|
|
#include "database.h"
|
2026-07-17 19:43:02 +08:00
|
|
|
|
#include "clock.h"
|
2026-07-17 09:08:53 +08:00
|
|
|
|
#include "../Drivers/rf.h"
|
|
|
|
|
|
|
|
|
|
|
|
// 外部全局变量引用
|
|
|
|
|
|
extern SystemState current_state;
|
|
|
|
|
|
extern u8 captured_type;
|
|
|
|
|
|
extern u32 captured_addr;
|
|
|
|
|
|
extern volatile u16 ms_tick;
|
|
|
|
|
|
|
|
|
|
|
|
// 对码状态机定义
|
|
|
|
|
|
enum {
|
|
|
|
|
|
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; // 对码失败时的起始秒数
|
2026-07-17 09:41:29 +08:00
|
|
|
|
static u32 pair_pulse_timer = 0; // 对码超时毫秒计数器
|
2026-07-17 09:08:53 +08:00
|
|
|
|
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 PairApp 启动回调
|
|
|
|
|
|
*/
|
|
|
|
|
|
static void PairApp_OnStart(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 1. 为了防止后台射频监听与对码射频接收冲突,将后台射频监听应用暂时卸载
|
|
|
|
|
|
AppManager_DetachFromBackground(&rf_monitor_app);
|
|
|
|
|
|
|
|
|
|
|
|
pair_state = PAIR_STATE_WAIT;
|
|
|
|
|
|
last_radar_sec = 99; // 强制刷写
|
|
|
|
|
|
current_state = STATE_PAIR_WAIT;
|
2026-07-17 09:41:29 +08:00
|
|
|
|
pair_pulse_timer = 0;
|
2026-07-17 09:08:53 +08:00
|
|
|
|
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] Started (WAIT)\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 秒的对码时间上限
|
2026-07-17 09:41:29 +08:00
|
|
|
|
pair_pulse_timer += tick_diff;
|
|
|
|
|
|
if (pair_pulse_timer >= 30000UL) {
|
|
|
|
|
|
pair_pulse_timer = 0;
|
2026-07-17 09:08:53 +08:00
|
|
|
|
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_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;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (evt->key_event == KEY_EVENT_CONFIRM_LONG) {
|
|
|
|
|
|
// 长按 ■ 确认键放弃本次捕获,直接返回二级主菜单
|
|
|
|
|
|
RGB_Send(0, 0, 0, 0, 0, 0);
|
|
|
|
|
|
AppManager_SwitchToForeground(&menu_app);
|
|
|
|
|
|
return EVENT_HANDLED;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 在对码搜寻或确认防区期间,长按 ▼ 键均代表强行取消对码直接退回菜单页
|
|
|
|
|
|
if (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;
|
|
|
|
|
|
}
|