1. 修改支持总开关关闭记忆开关状态功能

2. 新增强制复位功能
This commit is contained in:
2025-08-07 09:08:41 +08:00
parent 1a40c6f348
commit 0a4e83dad7
7 changed files with 387 additions and 68 deletions

View File

@ -30,7 +30,7 @@ extern "C" {
#define DEVICE_HIVERSION "1.0.0"
/* 设备固件版本号 */
#define FIRMWARE_VER "1.0.4"
#define FIRMWARE_VER "1.0.5"
/* 设备硬件版本号 */
#define HARDWARE_VER "1.0.0"
/* 设备软件版本号 */

View File

@ -43,6 +43,9 @@
#define CONFIG_BLINK_MS (3*60*1000) // 配网前3分钟闪烁时间
#define PANEL_BLINK_MS 1000 // 面板背光快闪时间(1秒)
#define LED_BLINK_FREQ_HZ 1 // 配网时LED闪烁频率(1Hz)
#define CONSECUTIVE_PRESS_COUNT 4 // 强制解绑需要的连续按键次数
#define CONSECUTIVE_PRESS_TIMEOUT_MS 2000 // 连续按键超时时间(2秒)
#define FORCE_UNBIND_WAIT_TIMEOUT_MS 3000 // 等待长按超时时间(3秒)
//====================== 设备状态定义 ======================
// 开关状态
@ -96,7 +99,7 @@ typedef struct {
bool long_press_handled; // 长按处理标志
} switch_runtime_info_t;
// 持久化设备状态(需要保存到Flash
// V1版本的持久化设备状态(老版本兼容
typedef struct {
switch_persistent_info_t switches[SWITCH_COUNT]; // 4个开关的持久化状态
bool master_switch; // 总开关状态 - 持久化
@ -106,7 +109,24 @@ typedef struct {
uint32_t magic; // 魔数标识
uint32_t version; // 版本号
uint32_t reserved[8]; // 保留字段
} device_persistent_state_t;
} device_persistent_state_v1_t;
// V2版本的持久化设备状态新版本当前使用
typedef struct {
switch_persistent_info_t switches[SWITCH_COUNT]; // 4个开关的持久化状态
bool master_switch; // 总开关状态 - 持久化
bool panel_led; // 面板背光状态 - 持久化
bool is_bound; // 设备绑定状态 - 持久化
bool is_first_boot; // 是否第一次上电 - 持久化
bool memory_switches[SWITCH_COUNT]; // APP关闭总开关时记忆的子开关状态 - 持久化
bool has_memory_state; // 是否有记忆状态 - 持久化
uint32_t magic; // 魔数标识
uint32_t version; // 版本号
uint32_t reserved[6]; // 保留字段减少2个给新字段使用
} device_persistent_state_v2_t;
// 当前使用的持久化状态结构V2
typedef device_persistent_state_v2_t device_persistent_state_t;
// 运行时设备状态(不需要持久化,断电丢失)
typedef struct {
@ -119,7 +139,13 @@ typedef struct {
bool factory_test_running; // 产测是否运行中
uint32_t last_save_time; // 上次保存时间
bool is_online;
uint32_t reserved[16]; // 保留字段
// 强制解绑连续按键检测状态
uint32_t consecutive_press_count; // 连续按键计数
uint32_t first_press_time; // 第一次按键时间
uint32_t last_press_time; // 最后一次按键时间
bool waiting_long_press; // 等待长按标志
uint32_t wait_long_press_start_time; // 开始等待长按的时间
uint32_t reserved[11]; // 保留字段减少5个给新字段使用
} device_runtime_state_t;
@ -142,7 +168,9 @@ typedef struct {
} device_data_t;
#define DEVICE_DATA_MAGIC 0x4C505426 // "LPT&"的ASCII码
#define DEVICE_DATA_VERSION 1 // 数据版本号
#define DEVICE_DATA_VERSION_V1 1 // 数据版本号V1老版本无记忆功能
#define DEVICE_DATA_VERSION_V2 2 // 数据版本号V2新版本支持记忆功能
#define DEVICE_DATA_VERSION DEVICE_DATA_VERSION_V2 // 当前版本
//====================== 配网相关定义 ======================
#define FACTORY_TEST_SSID "ShuorongSelfTest" // 产测热点名称
@ -202,6 +230,18 @@ void fast_report_switch(int switch_id);
void fast_report_master_switch(void);
void set_device_mode(system_mode_t mode);
// 恢复出厂设置相关函数
void perform_factory_reset_and_reboot(void);
// 记忆状态管理函数
void save_memory_switches(void);
void restore_memory_switches(void);
void clear_memory_switches(void);
// 强制解绑检测函数
bool check_consecutive_press(int key_id);
void check_force_unbind_timeout(void);
// 状态访问便利函数
bool get_switch_state(int switch_id);
bool get_master_switch_state(void);
@ -289,4 +329,4 @@ bool switch_panel_ble_is_enabled(void);
int switch_panel_ble_fast_report(const char *svc_id);
int start_hilink_ble_net_config(int32_t net_cfg_time_s);
#endif // __SWITCH_PANEL_H__
#endif // __SWITCH_PANEL_H__

View File

@ -5,6 +5,7 @@
#include "securec.h"
#include "cJSON.h"
#include "switch_panel.h"
#include "hfsys.h"
static int handle_get_switch_common(int switch_id, const char* svc_id,
const char* in, unsigned int in_len,
@ -79,16 +80,15 @@ void handle_device_unbind(void) {
// 重置为出厂默认状态
reset_persistent_state();
g_persistent_state.is_bound = false; // 保持未绑定状态
g_persistent_state.is_first_boot = false; // 标记为出厂模式
g_persistent_state.is_first_boot = true; // 标记为出厂模式,重启后自动配网
// 同步硬件状态
sync_hardware_state();
// 保存状态
save_persistent_state();
// HILINK_RestoreFactorySettings();
save_persistent_state_sync(); // 使用同步保存确保立即写入
e_printf("设备已重置为出厂默认状态\r\n");
e_printf("设备已重置为出厂默认状态,重启后将自动进入配网模式\r\n");
}
// 同步所有状态到云端
@ -253,4 +253,100 @@ static int handle_get_switch_common(int switch_id, const char* svc_id,
// 获取设备当前模式
system_mode_t get_current_mode(void) {
return g_runtime_state.mode;
}
//====================== 恢复出厂设置函数 ======================
// 执行恢复出厂设置并重启
void perform_factory_reset_and_reboot(void) {
e_printf("开始执行恢复出厂设置...\r\n");
// 调用HiLink恢复出厂设置API该API内部会
// 1. 清除HiLink相关的所有绑定信息和云端数据
// 2. 调用handle_device_unbind函数进行本地状态清除
// 3. 自动重启设备
int ret = HILINK_RestoreFactorySettings();
if (ret != 0) {
e_printf("HiLink恢复出厂设置失败错误码: %d\r\n", ret);
return;
}
e_printf("HiLink恢复出厂设置API调用成功等待系统重启...\r\n");
// 注意这里代码可能不会执行到很多因为HILINK_RestoreFactorySettings会触发重启
}
//====================== 记忆状态管理函数 ======================
// 保存当前子开关状态到记忆区
void save_memory_switches(void) {
e_printf("保存当前子开关状态到记忆区\r\n");
for (int i = 0; i < SWITCH_COUNT; i++) {
g_persistent_state.memory_switches[i] = g_persistent_state.switches[i].switch_on;
e_printf("记忆开关%d状态: %s\r\n", i + 1,
g_persistent_state.memory_switches[i] ? "" : "");
}
g_persistent_state.has_memory_state = true;
// 立即保存到Flash
save_persistent_state();
e_printf("子开关状态记忆保存完成\r\n");
}
// 从记忆区恢复子开关状态
void restore_memory_switches(void) {
if (!g_persistent_state.has_memory_state) {
e_printf("没有记忆状态,无法恢复\r\n");
return;
}
e_printf("从记忆区恢复子开关状态\r\n");
bool any_switch_changed = false;
for (int i = 0; i < SWITCH_COUNT; i++) {
bool new_state = g_persistent_state.memory_switches[i];
if (g_persistent_state.switches[i].switch_on != new_state) {
g_persistent_state.switches[i].switch_on = new_state;
g_persistent_state.switches[i].led_state = new_state;
// 同步硬件状态
set_switch_output(i, new_state);
set_led_output(i, new_state ? LED_WHITE : LED_YELLOW);
e_printf("恢复开关%d状态: %s\r\n", i + 1, new_state ? "" : "");
any_switch_changed = true;
}
}
if (any_switch_changed) {
// 保存状态变化
save_persistent_state();
// 同步到云端
if (g_persistent_state.is_bound) {
fast_report_all_switches_async();
}
}
e_printf("子开关状态恢复完成\r\n");
}
// 清除记忆状态
void clear_memory_switches(void) {
if (!g_persistent_state.has_memory_state) {
return;
}
e_printf("清除记忆状态\r\n");
g_persistent_state.has_memory_state = false;
memset(g_persistent_state.memory_switches, 0, sizeof(g_persistent_state.memory_switches));
// 立即保存到Flash
save_persistent_state();
e_printf("记忆状态已清除\r\n");
}

View File

@ -14,11 +14,14 @@ void update_switch_state(int switch_id, bool state) {
}
// 新逻辑:移除总开关限制,允许本地按键自由控制子开关
// 本地按键控制子开关时,如果要开启子开关且总开关关闭,则自动打开总开关
// 本地按键控制子开关时,如果要开启子开关且总开关关闭,则自动打开总开关并清除记忆状态
bool need_auto_master_on = false;
bool need_clear_memory = false;
if (state && !g_persistent_state.master_switch) {
need_auto_master_on = true;
e_printf("子开关%d激活将自动打开总开关\r\n", switch_id + 1);
need_clear_memory = true; // 物理按键操作时清除记忆状态
e_printf("子开关%d激活将自动打开总开关并清除记忆状态\r\n", switch_id + 1);
}
// 更新开关状态
@ -35,6 +38,11 @@ void update_switch_state(int switch_id, bool state) {
e_printf("开关%d 状态更新: %s\r\n",
switch_id + 1, state ? "" : "");
// 如果需要清除记忆状态
if (need_clear_memory) {
clear_memory_switches();
}
// 如果需要自动打开总开关
if (need_auto_master_on) {
g_persistent_state.master_switch = true;
@ -78,16 +86,16 @@ void update_master_switch(bool state) {
void apply_master_switch_control(void) {
if (!g_persistent_state.master_switch) {
// 总开关关闭时,强制关闭所有子开关
e_printf("总开关关闭,强制关闭所有子开关\r\n");
// 总开关关闭时,先保存当前子开关状态到记忆区,再强制关闭所有子开关
e_printf("总开关关闭,保存当前状态并强制关闭所有子开关\r\n");
// 保存当前子开关状态到记忆区
save_memory_switches();
for (int i = 0; i < SWITCH_COUNT; i++) {
// 如果子开关之前是开着的,需要同步状态
if (g_persistent_state.switches[i].switch_on) {
g_persistent_state.switches[i].switch_on = false;
g_persistent_state.switches[i].led_state = false;
e_printf("强制关闭子开关%d\r\n", i + 1);
}
// 强制关闭所有子开关
g_persistent_state.switches[i].switch_on = false;
g_persistent_state.switches[i].led_state = false;
// 更新硬件状态
set_switch_output(i, false);
@ -99,27 +107,16 @@ void apply_master_switch_control(void) {
fast_report_all_switches_async();
}
} else {
// 总开关开启时,自动开启所有子开关
e_printf("总开关开启,自动开启所有子开关\r\n");
// 总开关开启时,尝试恢复记忆状态,如果没有记忆状态则不操控子开关
e_printf("总开关开启\r\n");
bool any_switch_changed = false;
for (int i = 0; i < SWITCH_COUNT; i++) {
if (!g_persistent_state.switches[i].switch_on) {
g_persistent_state.switches[i].switch_on = true;
g_persistent_state.switches[i].led_state = true; // true表示白灯
// 同步硬件状态
set_switch_output(i, true);
set_led_output(i, LED_WHITE);
e_printf("自动开启子开关%d\r\n", i + 1);
any_switch_changed = true;
}
}
// 如果有子开关状态变化,同步到云端
if (any_switch_changed && g_persistent_state.is_bound) {
fast_report_all_switches_async();
if (g_persistent_state.has_memory_state) {
// 恢复记忆的子开关状态
e_printf("恢复记忆的子开关状态\r\n");
restore_memory_switches();
} else {
// 没有记忆状态,不操控子开关(保持当前状态)
e_printf("没有记忆状态,保持子开关当前状态不变\r\n");
}
}
@ -152,6 +149,9 @@ int key_scan_task(void *arg) {
while (1) {
uint32_t current_time = hfsys_get_time();
// 检查强制解绑超时
check_force_unbind_timeout();
// 扫描所有按键
for (int i = 0; i < SWITCH_COUNT; i++) {
bool new_raw_state = get_key_input(i);
@ -178,6 +178,21 @@ int key_scan_task(void *arg) {
press_start_time[i] = current_time;
is_long_press_handled[i] = false;
e_printf("按键%d 按下\r\n", i + 1);
// 如果正在等待长按强制解绑
if (g_runtime_state.waiting_long_press) {
if (i == 0) {
// 第一个按键:停止超时检测,等待长按处理
e_printf("检测到第一个按键按下,停止强制解绑超时检测\r\n");
g_runtime_state.wait_long_press_start_time = 0; // 停止超时检测
} else {
// 其他按键:取消等待状态
e_printf("检测到其他按键按下,取消强制解绑等待\r\n");
g_runtime_state.waiting_long_press = false;
g_runtime_state.wait_long_press_start_time = 0;
g_runtime_state.consecutive_press_count = 0;
}
}
} else {
// 按键松开
uint32_t press_duration = current_time - press_start_time[i];
@ -238,6 +253,15 @@ void handle_key_press(int key_id) {
return;
}
// 如果是第一个按键且设备已绑定,检测连续按键(用于强制解绑)
if (key_id == 0 && g_persistent_state.is_bound) {
if (check_consecutive_press(key_id)) {
e_printf("检测到连续4次按键等待长按10秒执行强制解绑\r\n");
// 不执行正常的开关切换操作,等待长按
return;
}
}
// 正常模式下切换开关状态
bool current_state = g_persistent_state.switches[key_id].switch_on;
update_switch_state(key_id, !current_state);
@ -251,21 +275,35 @@ void handle_key_long_press(int key_id) {
e_printf("处理按键%d 长按事件\r\n", key_id + 1);
// 只有第一个按键支持长按进入配网模式
// 只有第一个按键支持长按进入配网模式或强制解绑
if (key_id == 0) {
e_printf("长按第一个按键,检查配网条件\r\n");
e_printf("长按第一个按键,检查操作条件\r\n");
// 只有在正常模式下且设备未绑定时才能进入配网模式
if (g_runtime_state.mode == MODE_NORMAL && !g_persistent_state.is_bound) {
extern int g_config_key_id;
g_config_key_id = key_id; // 设置触发配网的按键ID
enter_config_mode();
} else {
if (g_persistent_state.is_bound) {
e_printf("设备已绑定,不能进入配网模式\r\n");
// 检查设备状态并执行相应操作
if (g_runtime_state.mode == MODE_NORMAL) {
if (!g_persistent_state.is_bound) {
// 未绑定设备:直接进入配网模式
e_printf("设备未绑定,进入配网模式\r\n");
extern int g_config_key_id;
g_config_key_id = key_id; // 设置触发配网的按键ID
enter_config_mode();
} else {
e_printf("非正常模式,不能进入配网模式\r\n");
// 已绑定设备:检查是否在等待长按状态(强制解绑)
if (g_runtime_state.waiting_long_press) {
// 执行强制解绑
e_printf("检测到强制解绑条件连续4次短按+长按10秒执行强制解绑\r\n");
// 清除等待状态
g_runtime_state.waiting_long_press = false;
g_runtime_state.wait_long_press_start_time = 0;
g_runtime_state.consecutive_press_count = 0;
perform_factory_reset_and_reboot();
} else {
// 普通长按只有未绑定设备才能配网已绑定设备需要连续4次短按后长按
e_printf("设备已绑定需要先连续按4次再长按10秒才能强制解绑\r\n");
}
}
} else {
e_printf("非正常模式,不能进入配网模式或执行解绑\r\n");
}
} else {
e_printf("非第一个按键的长按,忽略\r\n");
@ -311,4 +349,75 @@ void fast_report_switch(int switch_id) {
// 快速上报总开关状态(兼容旧接口,内部使用异步上报)
void fast_report_master_switch(void) {
fast_report_master_switch_async();
}
//====================== 强制解绑连续按键检测 ======================
// 检测连续按键返回true表示达到连续按键条件可以等待长按
bool check_consecutive_press(int key_id) {
// 只有第一个按键支持强制解绑
if (key_id != 0) {
return false;
}
uint32_t current_time = hfsys_get_time();
// 如果是首次按键或者超时,重置计数
if (g_runtime_state.consecutive_press_count == 0 ||
(current_time - g_runtime_state.last_press_time) > CONSECUTIVE_PRESS_TIMEOUT_MS) {
g_runtime_state.consecutive_press_count = 1;
g_runtime_state.first_press_time = current_time;
g_runtime_state.last_press_time = current_time;
g_runtime_state.waiting_long_press = false;
e_printf("连续按键检测第1次按键\r\n");
return false;
}
// 增加按键计数
g_runtime_state.consecutive_press_count++;
g_runtime_state.last_press_time = current_time;
e_printf("连续按键检测:第%d次按键\r\n", g_runtime_state.consecutive_press_count);
// 检查是否达到连续按键次数
if (g_runtime_state.consecutive_press_count >= CONSECUTIVE_PRESS_COUNT) {
uint32_t total_time = current_time - g_runtime_state.first_press_time;
e_printf("连续按键检测完成:%d次按键在%ums内完成\r\n",
g_runtime_state.consecutive_press_count, total_time);
// 设置等待长按标志和开始时间
g_runtime_state.waiting_long_press = true;
g_runtime_state.wait_long_press_start_time = current_time;
// 重置连续按键计数,为下次检测做准备
g_runtime_state.consecutive_press_count = 0;
e_printf("开始等待长按3秒内必须开始长按否则超时\r\n");
return true; // 可以等待长按了
}
return false;
}
// 检查强制解绑超时
void check_force_unbind_timeout(void) {
// 只有在等待长按状态下且设置了超时开始时间才检查超时
if (!g_runtime_state.waiting_long_press || g_runtime_state.wait_long_press_start_time == 0) {
return;
}
uint32_t current_time = hfsys_get_time();
uint32_t wait_time = current_time - g_runtime_state.wait_long_press_start_time;
// 检查是否超时3秒
if (wait_time >= FORCE_UNBIND_WAIT_TIMEOUT_MS) {
e_printf("强制解绑超时等待长按超过3秒退出检测流程\r\n");
// 清除等待状态
g_runtime_state.waiting_long_press = false;
g_runtime_state.wait_long_press_start_time = 0;
g_runtime_state.consecutive_press_count = 0; // 完全重置
}
}

View File

@ -359,21 +359,76 @@ static bool write_device_data_to_addr(uint32_t addr, uint8_t* data, uint32_t len
}
// 从V1版本数据升级到V2版本
static void upgrade_from_v1_to_v2(device_persistent_state_v1_t* v1_state, device_persistent_state_t* v2_state) {
e_printf("检测到V1版本数据升级到V2版本\r\n");
// 复制V1的所有字段到V2
memcpy(v2_state->switches, v1_state->switches, sizeof(v1_state->switches));
v2_state->master_switch = v1_state->master_switch;
v2_state->panel_led = v1_state->panel_led;
v2_state->is_bound = v1_state->is_bound;
v2_state->is_first_boot = v1_state->is_first_boot;
// 初始化V2新增的记忆功能字段
v2_state->has_memory_state = false;
memset(v2_state->memory_switches, 0, sizeof(v2_state->memory_switches));
// 更新版本信息
v2_state->magic = DEVICE_DATA_MAGIC;
v2_state->version = DEVICE_DATA_VERSION_V2;
memset(v2_state->reserved, 0, sizeof(v2_state->reserved));
e_printf("V1数据升级完成\r\n");
}
// 从 Flash 加载持久化状态
int load_persistent_state(void) {
int ret = 0;
device_persistent_state_t state;
device_persistent_state_v1_t v1_state;
bool valid = false;
bool is_v1_data = false;
// 尝试读取数据
// 尝试读取V2版本数据
valid = read_device_data_from_addr(DEVICE_DATA_FLASH_ADDR, (uint8_t*)&state, sizeof(state));
// 如果主数据区无效,尝试读取备份区
if (!valid) {
valid = read_device_data_from_addr(DEVICE_DATA_BACKUP_ADDR, (uint8_t*)&state, sizeof(state));
}
// 两个存储区都失败,使用默认状态
// 如果V2读取失败尝试读取V1版本数据
if (!valid) {
e_printf("V2数据读取失败尝试读取V1版本数据\r\n");
valid = read_device_data_from_addr(DEVICE_DATA_FLASH_ADDR, (uint8_t*)&v1_state, sizeof(v1_state));
if (!valid) {
valid = read_device_data_from_addr(DEVICE_DATA_BACKUP_ADDR, (uint8_t*)&v1_state, sizeof(v1_state));
}
if (valid) {
// 检查是否确实是V1版本数据
if (v1_state.magic == DEVICE_DATA_MAGIC && v1_state.version == DEVICE_DATA_VERSION_V1) {
is_v1_data = true;
e_printf("检测到V1版本数据\r\n");
} else {
valid = false;
}
}
} else {
// 检查V2数据版本
if (state.version != DEVICE_DATA_VERSION_V2) {
if (state.version == DEVICE_DATA_VERSION_V1) {
e_printf("读取到的数据是V1版本格式需要升级\r\n");
// 将读取的数据重新解释为V1格式
memcpy(&v1_state, &state, sizeof(v1_state));
is_v1_data = true;
} else {
e_printf("未知的数据版本: %d使用默认状态\r\n", state.version);
valid = false;
}
}
}
// 所有存储区都失败,使用默认状态
if (!valid) {
e_printf("存储区数据损坏,使用默认状态\r\n");
reset_persistent_state();
@ -381,21 +436,29 @@ int load_persistent_state(void) {
return 0;
}
// 更新持久化状态
e_printf("持久化状态恢复:\r\n");
e_printf("首次启动: %d => %d\r\n", g_persistent_state.is_first_boot, state.is_first_boot);
e_printf("配网状态: %d => %d\r\n", g_persistent_state.is_bound, state.is_bound);
e_printf("总开关: %d => %d\r\n", g_persistent_state.master_switch, state.master_switch);
e_printf("面板背光: %d => %d\r\n", g_persistent_state.panel_led, state.panel_led);
for (int i = 0; i < SWITCH_COUNT; i++) {
e_printf("开关%d(%s): %d => %d, LED%d: %d => %d\r\n",
i + 1, state.switches[i].name,
g_persistent_state.switches[i].switch_on, state.switches[i].switch_on,
i + 1, g_persistent_state.switches[i].led_state, state.switches[i].led_state);
// 如果是V1数据需要升级到V2
if (is_v1_data) {
upgrade_from_v1_to_v2(&v1_state, &state);
// 升级后立即保存新版本数据
memcpy(&g_persistent_state, &state, sizeof(device_persistent_state_t));
save_persistent_state();
} else {
// 直接使用V2数据
memcpy(&g_persistent_state, &state, sizeof(device_persistent_state_t));
}
memcpy(&g_persistent_state, &state, sizeof(device_persistent_state_t));
// 打印恢复的状态信息
e_printf("持久化状态恢复 (版本%s):\r\n", is_v1_data ? "V1->V2" : "V2");
e_printf("首次启动: %d, 配网状态: %d\r\n", g_persistent_state.is_first_boot, g_persistent_state.is_bound);
e_printf("总开关: %d, 面板背光: %d\r\n", g_persistent_state.master_switch, g_persistent_state.panel_led);
e_printf("记忆功能: %s\r\n", g_persistent_state.has_memory_state ? "有记忆" : "无记忆");
for (int i = 0; i < SWITCH_COUNT; i++) {
e_printf("开关%d(%s): %s, LED: %s\r\n",
i + 1, g_persistent_state.switches[i].name,
g_persistent_state.switches[i].switch_on ? "" : "",
g_persistent_state.switches[i].led_state ? "白灯" : "黄灯");
}
return 0;
}
@ -581,6 +644,10 @@ void reset_persistent_state(void) {
g_persistent_state.is_bound = false; // 设备未绑定
g_persistent_state.is_first_boot = true; // 标记为首次启动
// 清除记忆状态
g_persistent_state.has_memory_state = false;
memset(g_persistent_state.memory_switches, 0, sizeof(g_persistent_state.memory_switches));
// 所有开关默认关闭所有LED默认黄灯初始化默认名字
for (int i = 0; i < SWITCH_COUNT; i++) {
g_persistent_state.switches[i].switch_on = false; // 开关关闭
@ -604,6 +671,13 @@ void init_runtime_state(void) {
g_runtime_state.factory_test_running = false;
g_runtime_state.last_save_time = 0;
// 初始化强制解绑连续按键检测状态
g_runtime_state.consecutive_press_count = 0;
g_runtime_state.first_press_time = 0;
g_runtime_state.last_press_time = 0;
g_runtime_state.waiting_long_press = false;
g_runtime_state.wait_long_press_start_time = 0;
// 初始化所有开关的运行时状态
for (int i = 0; i < SWITCH_COUNT; i++) {
g_runtime_state.switches[i].physical_key = true; // 按键松开

Binary file not shown.